特殊按键--休眠键驱动

来源:互联网 发布:汇编语言转换成c语言 编辑:程序博客网 时间:2024/06/14 02:21
这是一个关于休眠和关机的按键驱动。板子:pxa31X系列  内核:2.6.25
这个驱动用到了内核文件操作,内核线程,等待队列,异步通知,并介绍了一种调试驱动的方法。
#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>。#defineKERN_EMERG"<0>"/* system is unusable紧急事件消息,系统崩溃前提示,表示系统不可用*/#defineKERN_ALERT"<1>"/* action must be taken immediately报告消息,表示必须马上采取措施*/#defineKERN_CRIT"<2>"/* critical conditions临界情况,通常用于涉及严重的硬件或软件操作失败*/#defineKERN_ERR"<3>"/* error conditions错误情况,驱动程序常用来报告硬件错误*/#defineKERN_WARNING"<4>"/* warning conditions警告,对可能出现问题的情况进行警告*/#defineKERN_NOTICE"<5>"/* normal but significant condition正常但又重要的情况,常用于提醒与安全相关的消息*/#defineKERN_INFO"<6>"/* informational提示信息*/#defineKERN_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_WAKEUP0PXA_IRQ(49)/* EXT_WAKEUP0 */#define IRQ_WAKEUP1PXA_IRQ(50)/* EXT_WAKEUP1 *///一个重要的全局结构体static struct powerkey_t pwk;/*struct powerkey_t {int ifopen;    //check if device is openedunsigned 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 queuestruct tast_struct *p_thread;int checkforsleep;    //check if which event for sleepint 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 interruptPECR &= ~PECR_DIR0;//as inputreturn 0;}static int disable_wakeup(void){PECR &= ~PECR_E0IE;//disable wakeup0 interruptreturn 0;}static int wakeup_ack_irq(void){PECR |= PECR_E0IS;//interrupt state, write 1 to clearreturn 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 modeif(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#endifstatic 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){platform_driver_unregister(&pb_driver);printk("bye pb !\n");}module_init(pb_init);  module_exit(pb_exit); MODULE_AUTHOR("William Wang");  MODULE_LICENSE("GPL");


原创粉丝点击