程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 特殊按鍵--休眠鍵驅動

特殊按鍵--休眠鍵驅動

編輯:C++入門知識

這是一個關於休眠和關機的按鍵驅動。板子:pxa31X系列  內核:2.6.25 這個驅動用到了內核文件操作,內核線程,等待隊列,異步通知,並介紹了一種調試驅動的方法。 [cpp]   #include <linux/init.h>   #include <linux/module.h>   #include <linux/kernel.h>   #include <linux/fs.h>   #include <linux/syscalls.h>   #include <linux/unistd.h>   #include <linux/miscdevice.h>   #include <linux/platform_device.h>   #include <linux/uaccess.h>   #include <linux/string.h>   #include <asm/arch/pxa-regs.h>   #include <asm/arch/pxa3xx-regs.h>   #include <asm/arch/mfp-pxa300.h>   #include <asm/arch/gpio.h>   #include <asm/uaccess.h>    //用於內核線程   #include <linux/irq.h>    #include <linux/interrupt.h>   #include <linux/ioctl.h>   #include <linux/sched.h>   #include <linux/kthread.h>   #include <linux/errno.h>   #include <linux/spinlock.h>   #include <linux/mutex.h>   #include <linux/wait.h>   #include <asm/semaphore.h>      #include "pmb.h"      #define PB_DEVICE_NAME "william_pmb"                  //#define DEBUG   #ifdef DEBUG       #define pr_debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)   #else       #define pr_debug(fmt, arg...) printk(KERN_INFO fmt, ##arg)   #endif   /*  用於調試驅動的一種方法。  其中printk函數中的參數,定義於<linux/kernel.h>。  #define KERN_EMERG  "<0>" /* system is unusable緊急事件消息,系統崩潰前提示,表示系統不可用*/   #define KERN_ALERT  "<1>" /* action must be taken immediately 報告消息,表示必須馬上采取措施*/   #define KERN_CRIT   "<2>" /* critical conditions臨界情況,通常用於涉及嚴重的硬件或軟件操作失敗*/   #define KERN_ERR    "<3>" /* error conditions錯誤情況,驅動程序常用來報告硬件錯誤*/   #define KERN_WARNING    "<4>" /* warning conditions警告,對可能出現問題的情況進行警告*/   #define KERN_NOTICE "<5>" /* normal but significant condition正常但又重要的情況,常用於提醒與安全相關的消息*/   #define KERN_INFO   "<6>" /* informational提示信息*/   #define KERN_DEBUG  "<7>" /* debug-level messages調試級別消息*/   */      /*硬件上的一些定義*/   #define PECR_E0IS   (1 << 29) // EXT_WAKEUP<0> Interrupt Status   #define PECR_E0IE   (1 << 28) // EXT_WAKEUP<0> Pin Interrupt Enable   #define PECR_DIR0(1 << 4) //Direction for EXT_WAKEUP<0>: 0/1= input/output   #define PECR_IVE0   (1 << 0)  //Input Value for EXT_WAKEUP<0>      //Currently we have   #define IRQ_WAKEUP0 PXA_IRQ(49) /* EXT_WAKEUP0 */   #define IRQ_WAKEUP1 PXA_IRQ(50) /* EXT_WAKEUP1 */      //一個重要的全局結構體   static struct powerkey_t pwk;   /*  struct powerkey_t   {         int ifopen;     //check if device is opened      unsigned int pressed;       //current key state     [0/1=release/press]      int event;  //event:        [0/1/2/3=narmal/sleep/deepsleep/wakeup]      wait_queue_head_t keywaitq;     //powerkey queue      struct tast_struct *p_thread;      int checkforsleep;      //check if which event for sleep      int ifhandshake;        //check if need to handshake with app[]      int handshake;      struct fasync_struct *pwrkey_async_queue;      int ifreleasehandshakecnt;      //0: you can release handshake. >0: can't release handshake    };  */      static int pb_handshake(int sig,int mode);      static int wakeup_init(void)   {          PECR |= PECR_E0IE;  //enable wakeup0 interrupt       PECR &= ~PECR_DIR0; //as input       return 0;   }      static int disable_wakeup(void)   {          PECR &= ~PECR_E0IE; //disable wakeup0 interrupt       return 0;   }      static int wakeup_ack_irq(void)   {          PECR |= PECR_E0IS;  //interrupt state, write 1 to clear       return 0;   }         static int pb_sleep_exe(int sleep)   {       int ret;       struct file *fd;       mm_segment_t old_fs;       //printk("%s\n",__FUNCTION__);              fd = filp_open("sys/power/state",O_RDWR,0);       if(IS_ERR(fd))       {           printk("Open sys/power/state fail,ret = %ld \n",IS_ERR(fd));           return -1;       }       old_fs = get_fs();       set_fs(KERNEL_DS);       switch(sleep)       {           case SLEEP_EVENT:               printk("sleep!\n");               ret = fd->f_op->write(fd,"mem",3,&fd->f_pos);               if(ret != 3)               {                   printk("Write to sleep fail!\n");               }               //printk("sleep write ok!\n");               break;           case DEEPSLEEP_EVENT:               ret = fd->f_op->write(fd,"deepsleep",9,&fd->f_pos);               if(ret != 9)               {                   printk("Write to deepsleep fail!\n");               }               break;           default:               break;       }       set_fs(old_fs);       filp_close(fd ,NULL);              return 0;   }   /*  內核文件操作  strcut file* filp_open(const char* filename, int open_mode, int mode);  該函數返回strcut file*結構指針,供後繼函數操作使用,該返回值用IS_ERR()來檢驗其有效性。    操作之前先要定位  void set_fs(mm_segment_t fs);  該函數的作用是改變kernel對內存地址檢查的處理方式,其實該函數的參數fs只有兩個取值:USER_DS,KERNEL_DS,分別代表用戶空間和內核空間  get_fs();    取得當前的設置  off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)  offset是偏移量。  若origin是SEEK_SET(0),則將該文件的位移量設置為距文件開始處offset 個字節。  若origin是SEEK_CUR(1),則將該文件的位移量設置為其當前值加offset, offset可為正或負。  若origin是SEEK_END(2),則將該文件的位移量設置為文件長度加offset, offset可為正或負。    ret = fd->f_op->write(fd,"mem",3,&fd->f_pos);    文件讀寫函數    最後關閉文件 int filp_close(struct file*filp, fl_owner_t id);  */      static int wait_wakeup_handshake(void)   {       int ret;       struct powerkey_t *ppwk = &pwk;          //printk("%s\n",__FUNCTION__);       ppwk->event = WAKEUP_EVENT;       if(ppwk->ifopen > NOOPENED)          {           if(ppwk->ifhandshake == REQUESTHANDSHAKE)               {               if(pb_handshake(SIGIO,POLL_IN))               {                   printk("Posting Handshake fail\n");                   return -1;               }               else               {                   ppwk->handshake = NONEEDHANDSHAKE;                   ret = wait_event_interruptible_timeout(ppwk->keywaitq, ppwk->handshake == HANDSHAKE_EVENT_OK, (20*HZ));                   if( ret == 0)                   {                       printk("Wake up handshake timeout\n");                   }               }           }           else               {               ppwk->handshake = NONEEDHANDSHAKE;               ret = wait_event_interruptible_timeout(ppwk->keywaitq, ppwk->handshake == HANDSHAKE_EVENT_OK, (20*HZ));               if( ret == 0)               {                   printk("Wake up handshake timeout\n");               }           }       }       ppwk->handshake = NONEEDHANDSHAKE;              return 0;   }         static irqreturn_t extwakeup_handle(int irq, void *dev_id)   {       int readreg;       struct powerkey_t *ppwk = &pwk;       //printk("%s\n",__FUNCTION__);              wakeup_ack_irq();              readreg = PECR;            if(readreg & PECR_IVE0)       {           //printk("key is pressed !\n");           ppwk->checkforsleep = NEEDCHECKSLEEP;           ppwk->pressed = KEYPRESSED;         }       else       {           //printk("key released!\n");           ppwk->pressed = KEYRELEASE;       }       wake_up_interruptible(&ppwk->keywaitq);       wakeup_ack_irq();       return IRQ_HANDLED;   }         static int pb_irq_init(void)   {       int err;       //printk("%s\n",__FUNCTION__);          err = request_irq(IRQ_WAKEUP0, &extwakeup_handle,NULL,                     "ext_wakeup0_detect", NULL);              return err;   }      static int preevent_sleep(const int sleepevent)   {       int ret;       struct powerkey_t *p = &pwk;              if(sleepevent == DEEPSLEEP_EVENT)       {           if(p->ifopen > NOOPENED)                 ret = wait_event_interruptible_timeout(p->keywaitq, p->pressed == KEYRELEASE, (3*HZ));           disable_wakeup();           wakeup_ack_irq();              }             if(p->ifopen > NOOPENED)       {           p->handshake = NONEEDHANDSHAKE;           ret = wait_event_interruptible_timeout(p->keywaitq, p->handshake == HANDSHAKE_EVENT_OK, (20*HZ));           if( ret == 0)           {               printk("Handshake timeout\n");           }           p->handshake = NONEEDHANDSHAKE;       }          if(sleepevent == DEEPSLEEP_EVENT)           {           wakeup_init();           wakeup_ack_irq();          }          pb_sleep_exe(sleepevent);       wait_wakeup_handshake();             return 0;   }         static int wait_handshake_sleep(int sleepevent)   {       int ret;       struct powerkey_t *p = &pwk;              p->event = sleepevent;       if(p->ifhandshake == REQUESTHANDSHAKE)       {   //for signal mode           if(pb_handshake(SIGIO,POLL_IN))           {               printk("Posting Handshake fail\n");               return -1;             }           ret = preevent_sleep(sleepevent);       }       else    //for polling mode       {           ret = preevent_sleep(sleepevent);       }              return 0;   }   /*  wait_event_interruptible_timeout(queue, condition, timeout)  使用例如:  (1)初始化等待隊列  int flags = 0;  wait_queue_head_t    select_wait;  init_waitqueue_head(&select_wait);  (2)等待事件的發生(條件滿足)  {  ...      wait_event_interruptible_timeout(select_wait, flags != 0, HZ/10);  ...  }  (3)喚醒等待隊列  {  ...     if(waitqueue_active(&select_wait))     {         flags = 1;         wake_up_interruptible( &nd->select_in_wait );     }  ...  }      */      static int driver_data_init(void)   {       struct powerkey_t *ppwk = &pwk;              if(PECR & PECR_IVE0)       {           ppwk->pressed = KEYPRESSED;       }       else       {           ppwk->pressed = KEYRELEASE;       }       ppwk->ifopen = NOOPENED;       ppwk->event = NORMAL_EVENT;       ppwk->handshake = NONEEDHANDSHAKE;       ppwk->checkforsleep = NONEEDCHECKSLEEP;       ppwk->ifhandshake = FREEHANDSHAKE;       ppwk->ifreleasehandshakecnt = 0;       init_waitqueue_head(&ppwk->keywaitq);       return 0;    }      static int powerkey_thread(void *data)   {       struct powerkey_t *ppwk = &pwk;       long ret;   //  unsigned long flags;          //printk("%s\n",__FUNCTION__);              while(!kthread_should_stop())       {           set_current_state(TASK_INTERRUPTIBLE);           //if(kthread_should_stop()) break;           while(ppwk->checkforsleep == NEEDCHECKSLEEP)           {               ret = wait_event_interruptible_timeout(ppwk->keywaitq, ppwk->pressed == KEYRELEASE, (2*HZ));               if(ret == 0)               {   //time out                   //disable_wakeup0_int();                   //wakeup0_ack_irq();                   ret = wait_handshake_sleep(DEEPSLEEP_EVENT);                   ppwk->checkforsleep = NONEEDCHECKSLEEP;                   //wakeup0_init();                   //wakeup0_ack_irq();               }               else               {                   disable_wakeup();                   wakeup_ack_irq();                   ret = wait_handshake_sleep(SLEEP_EVENT);                   ppwk->checkforsleep = NONEEDCHECKSLEEP;                   wakeup_init();                   wakeup_ack_irq();               }           }                      //schedule_timeout();           ret = wait_event_interruptible_timeout(ppwk->keywaitq, ppwk->checkforsleep == NEEDCHECKSLEEP, (2*HZ));       }       printk("%s exit!\n",__FUNCTION__);       return 0;   }         static int create_powerkey_thread(void)   {       struct powerkey_t *ppwk = &pwk;       //printk("%s\n",__FUNCTION__);       ppwk->p_thread = kthread_run(&powerkey_thread,NULL,"powerkey_thread");       if(ppwk->p_thread == NULL)       {           printk("%s failed\n",__FUNCTION__);           return -1;       }       return 0;   }      static void delete_powerkey_thread(void)   {       struct powerkey_t *ppwk = &pwk;       //printk("%s\n",__FUNCTION__);       if(ppwk->p_thread)       {           kthread_stop(ppwk->p_thread);       }       ppwk->p_thread = NULL;   }   /*  struct task_struct *kthread_create(int (*threadfn)(void *data),void *data,const char *namefmt, ...);  線程創建後,不會馬上運行,而是需要將kthread_create() 返回的task_struct指針傳給wake_up_process(),然後通過此函數運行線程。  struct task_struct *kthread_run(int (*threadfn)(void *data),void *data,const char *namefmt, ...);    創建並啟動線程  int kthread_stop(struct task_struct *thread);  線程一旦啟動起來後,會一直運行,除非該線程主動調用do_exit函數,或者其他的進程調用kthread_stop函數,結束線程的運行  kthread_should_stop()函數,我們需要在開啟的線程中嵌入該函數並檢查此函數的返回值,否則kthread_stop是不起作用的  類似,我們也可以創建其他幾條線程。  */      static int pb_fasync(int fd,struct file* filp,int mode)   {       struct powerkey_t *ppwk = &pwk;       //printk("%s\n",__FUNCTION__);       ppwk->ifhandshake = REQUESTHANDSHAKE;       ppwk->ifreleasehandshakecnt++;       return fasync_helper(fd,filp,mode,&ppwk->pwrkey_async_queue);   }         static int pb_handshake(int sig,int mode)   {       struct powerkey_t *ppwk = &pwk;       //printk("%s\n",__FUNCTION__);       if(ppwk->pwrkey_async_queue)       {           kill_fasync(&ppwk->pwrkey_async_queue,sig,mode);           return 0;       }       //printk("%s failed.no async_queue\n",__FUNCTION__);       return -1;   }         static void release_fasync(struct file *file)   {       int ret;       struct powerkey_t *p = &pwk;       //printk("%s\n",__FUNCTION__);              if(p->ifreleasehandshakecnt != 0)           p->ifreleasehandshakecnt--;          if(p->ifhandshake == REQUESTHANDSHAKE)   //have       {           if(p->ifreleasehandshakecnt == 0)           {               ret = fasync_helper(-1,file,0,&p->pwrkey_async_queue);               printk("%s fasync_helper ret=%d\n",__FUNCTION__,ret);               if(p->pwrkey_async_queue)               {                   p->pwrkey_async_queue = NULL;               }               p->ifhandshake = FREEHANDSHAKE;           }       }   }   /*  異步通知:一旦設備就緒,則主動通知應用程序,不需要查詢。  設備驅動中異步通知比較簡單,主要用到fasync_struct結構體,還有下面兩個函數。  int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);   處理FASYNC標志變更的函數。記得要釋放  void kill_fasync(struct fasync_struct **fa, int sig, int band);    釋放信號用的函數  sig信號用的最多的是SIGIO,可讀時band設置為POLL_IN,可寫時band設置為POLL_OUT。  */      int pb_open(struct inode *inode,struct file *filp)   {       struct powerkey_t *p = &pwk;       p->ifopen++;       return 0;   }      int pb_release(struct inode *inode,struct file *filp)   {       struct powerkey_t *p = &pwk;              if(p->ifopen > NOOPENED)           p->ifopen--;       release_fasync(filp);    //處理FASYNC標志       return 0;   }   /*  static ssize_t pb_read(struct file *filp,char *buf,size_t size,loff_t *ppos)      {         return 0;  }    static ssize_t pb_write(struct file *filp,const char *buf,size_t size,loff_t *ppos)  {      return 0;  }  */   static int pb_ioctl(struct inode *inodep,struct file *filp,unsigned int cmd,unsigned long arg)   {       int ret = 0;       struct powerkey_t *ppwk = &pwk;          switch(cmd)           {               case HANDSHAKE_EVENT_OK:                   ppwk->handshake = HANDSHAKE_EVENT_OK;                   wake_up_interruptible(&ppwk->keywaitq);                   break;               case GET_EVENT_STATE:                   if(copy_to_user((int *)arg,&ppwk->event,sizeof(ppwk->event)))                       {                           ret = -1;                       }                   else                       {                           ppwk->event = NORMAL_EVENT;                       }                   break;               case FREE_HANDSHAKE_EVENT:                   release_fasync(filp);                   break;               //case SUSPEND_EVENT:                   //break;               default:                   break;           }          return 0;   }      static struct file_operations pb_fops = {       .owner = THIS_MODULE,       //.read = pb_read,       //.write = pb_write,       .ioctl = pb_ioctl,       .open = pb_open,               .release = pb_release,       .fasync = pb_fasync,   };      static struct miscdevice pb_miscdev = {       .minor = 100,           .name = PB_DEVICE_NAME,       .fops = &pb_fops,   };          static int pb_probe(struct platform_device *pdev)   {       int ret;              ret = misc_register(&pb_miscdev);          wakeup_init();       wakeup_ack_irq();       ret = pb_irq_init();    //中斷初始化       if(ret)           {               misc_deregister(&pb_miscdev);           }       driver_data_init();       ret = create_powerkey_thread();    //創建內核線程       if(ret)           {               free_irq(IRQ_WAKEUP0,NULL);               misc_deregister(&pb_miscdev);           }              return 0;   }      static int pb_remove(struct platform_device *pdev)   {       int ret;       struct powerkey_t *ppwk = &pwk;       ret = wakeup_ack_irq();       wake_up_interruptible(&ppwk->keywaitq);    //喚醒等待隊列       delete_powerkey_thread();    //干掉內核線程       free_irq(IRQ_WAKEUP0, NULL);    //干掉中斷              misc_deregister(&pb_miscdev);       return 0;   }      #ifdef CONFIG_PM          static int pb_suspend(struct platform_device *pdev, pm_message_t state)   {          wakeup_ack_irq();       disable_wakeup();       free_irq(IRQ_WAKEUP0, NULL);       return 0;   }      static int pb_resume(struct platform_device *pdev)   {       pb_irq_init();       return 0;   }   #else   #define pb_suspend NULL   #define pb_resume NULL   #endif      static struct platform_driver pb_driver = {       .driver = {           .name = "william_pmb",                     },       .probe = pb_probe,       .remove = pb_remove,       .suspend = pb_suspend,       .resume = pb_resume,   };      static int __init pb_init(void)   {       printk("hello pb !\n");       return platform_driver_register(&pb_driver);   }      static void __exit pb_exit(void)   {  www.2cto.com     platform_driver_unregister(&pb_driver);       printk("bye pb !\n");   }      module_init(pb_init);     module_exit(pb_exit);       MODULE_AUTHOR("William Wang");     MODULE_LICENSE("GPL");  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved