kobox: key_wq.c - v1
说明:
TQ2440按键驱动,对四个按键的驱动处理
key_wq.c和key.c类似,与key.c按键驱动的不同之处:
key.c中使用定时器,在中断发生100ms后调用定时器处理函数来防止按键抖动
key_wq.c使用工作队列,在内核调度共享工作队列,在工作队列中延时100ms然后判断按键状态来防止按键抖动
问题:
只有内核共享工作队列,且不延时的情况下,程序执行才正常:
/* 使用内核共享队列,立即调度,延时放到中断函数中 */
schedule_work(&gpio_key_work[key]);//执行正常
使用其他三种情况,程序都会崩掉:
/* 使用内核共享队列,延时调度 */
// schedule_delayed_work(&gpio_key_work[key], KEY_TIMER_DELAY2);//会OOPS
/* 使用单独队列,延时调度 */
// queue_delayed_work(&key_wq[key], &gpio_key_work[key], KEY_TIMER_DELAY2);//同样崩掉!
/* 使用内核共享队列,立即调度,延时放到中断函数中 */
// queue_work(&key_wq[key], &gpio_key_work[key]);//同样崩掉!
目前还不清楚原因
源码如下:
#include "key.h" #define S3C_ADDR_BASE 0xF6000000 #define S3C_ADDR(x) (S3C_ADDR_BASE + (x)) #define S3C2410_PA_UART (0x50000000) #define S3C2410_PA_GPIO (0x56000000) #define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */ #define S3C24XX_PA_UART S3C2410_PA_UART #define S3C24XX_VA_UART S3C_VA_UART #define S3C24XX_PA_GPIO S3C2410_PA_GPIO #define S3C24XX_VA_GPIO ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART) #define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO) #define S3C2410_GPBCON S3C2410_GPIOREG(0x10) #define S3C2410_GPBDAT S3C2410_GPIOREG(0x14) #define S3C2410_GPBUP S3C2410_GPIOREG(0x18) #define S3C2410_GPFCON S3C2410_GPIOREG(0x50) #define S3C2410_GPFDAT S3C2410_GPIOREG(0x54) #define S3C2410_GPFUP S3C2410_GPIOREG(0x58) #define S3C2410_EXTINT0 S3C2410_GPIOREG(0x88) #define S3C2410_EXTINT1 S3C2410_GPIOREG(0x8C) #define S3C2410_EXTINT2 S3C2410_GPIOREG(0x90) #define S3C2410_CPUIRQ_OFFSET (16) #define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET) /* main cpu interrupts */ #define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */ #define IRQ_EINT1 S3C2410_IRQ(1) /* 17 */ #define IRQ_EINT2 S3C2410_IRQ(2) /* 18 */ #define IRQ_EINT4t7 S3C2410_IRQ(4) /* 20 */ #define IRQ_EINT4 S3C2410_IRQ(36) /* 52 */ #define IRQF_DISABLED 0x00000020 #define IRQF_SHARED 0x00000080 #define IRQF_PROBE_SHARED 0x00000100 #define __IRQF_TIMER 0x00000200 #define IRQF_PERCPU 0x00000400 #define IRQF_NOBALANCING 0x00000800 #define IRQF_IRQPOLL 0x00001000 #define IRQF_ONESHOT 0x00002000 #define IRQF_NO_SUSPEND 0x00004000 #define IRQF_FORCE_RESUME 0x00008000 #define IRQF_NO_THREAD 0x00010000 #define IRQF_EARLY_RESUME 0x00020000 typedef struct gpioRes { int irqNum; /* 中断号 */ unsigned int ctrlReg; /* 控制寄存器,用于设置复用为GPIO */ unsigned int ctrlBit; /* 控制寄存器的哪一位,用于复用为GPIO */ unsigned int trigReg; /* 中断方式寄存器,设置中断的触发方式 */ unsigned int trigBit; /* 中断方式寄存器哪一位,设置中断的触发方式 */ unsigned int irqFlag; /* 共享还是不共享,注册中断的flag */ char irqName[32]; /* 中断名称 */ unsigned int gpfPin; /* GPF的第几个pin */ char Reserved[10]; /* 保留 */ }gpioRes; #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) unsigned int pressCnt[4] = {0, 0, 0, 0}; /* 定义一个work_queue数组 */ struct work_struct gpio_key_work[4]; static struct workqueue_struct *key_wq[4] = {NULL, NULL, NULL, NULL}; static void gpio_key_wq0_handler(struct work_struct *work); static void gpio_key_wq1_handler(struct work_struct *work); static void gpio_key_wq2_handler(struct work_struct *work); static void gpio_key_wq3_handler(struct work_struct *work); /* 定义一个函数指针数组,分别处理上面四个work_queue */ int (*gpio_key_wq_handler[4])(struct work_struct *work) = { gpio_key_wq0_handler, gpio_key_wq1_handler, gpio_key_wq2_handler, gpio_key_wq3_handler, }; static int kobox_key_open(struct inode *inode, struct file *file) { return 0; } static int kobox_key_release(struct inode *inode, struct file *file) { return 0; } static long kobox_key_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return 0; } static int kobox_key_read(struct file *file, char __user *buff, size_t count, loff_t *pos) { printk("Enter [%s][%d]\n", __FUNCTION__,__LINE__); copy_to_user(buff, &pressCnt[0], sizeof(pressCnt)); return 0; } /* GPF相关寄存器: GPFCON 0x56000050 R/W Configures the pins of port F 0x0 GPFDAT 0x56000054 R/W The data register for port F Undef. GPFUP 0x56000058 R/W Pull-up disable register for port F 0x000 K1: GPF1 -EINT1: GPF1 [3:2] 00 = Input 01 = Output 10 = EINT[1] 11 = Reserved K2: GPF4 -EINT4: GPF4 [9:8] 00 = Input 01 = Output 10 = EINT[4] 11 = Reserved K3: GPF2 -EINT2: GPF2 [5:4] 00 = Input 01 = Output 10 = EINT2] 11 = Reserved K4: GPF0 -EINT0: GPF0 [1:0] 00 = Input 01 = Output 10 = EINT[0] 11 = Reserved */ gpioRes key_gpio_res[4] = { {IRQ_EINT1, S3C2410_GPFCON, 2, S3C2410_EXTINT0, 5, NULL, "key1", 1}, /* key1 */ {IRQ_EINT4, S3C2410_GPFCON, 8, S3C2410_EXTINT0, 17, IRQF_SHARED, "key2", 4}, /* key2 */ {IRQ_EINT2, S3C2410_GPFCON, 4, S3C2410_EXTINT0, 9, NULL, "key3", 2}, /* key3 */ {IRQ_EINT0, S3C2410_GPFCON, 0, S3C2410_EXTINT0, 1, NULL, "key4", 0}, /* key4 */ }; #define KEY_TIMER_DELAY1 (HZ/50) //按键按下去抖延时20毫秒 #define KEY_TIMER_DELAY2 (HZ/10) //按键抬起去抖延时100毫秒 #define KEY_COUNT 4 static void set_gpio_as_eint(void) { int i; unsigned uiVal = 0; for(i=0; i< ARRAY_SIZE(key_gpio_res); i++) { uiVal = readl(key_gpio_res[i].ctrlReg); uiVal &= ~(0x01 << key_gpio_res[i].ctrlBit); uiVal |= (0x01 << (key_gpio_res[i].ctrlBit + 1)); writel(uiVal, key_gpio_res[i].ctrlReg); } return; } static void set_gpio_as_gpio(void) { int i; unsigned uiVal = 0; for(i=0; i< ARRAY_SIZE(key_gpio_res); i++) { uiVal = readl(key_gpio_res[i].ctrlReg); uiVal &= ~(0x01 << key_gpio_res[i].ctrlBit); uiVal &= ~(0x01 << (key_gpio_res[i].ctrlBit + 1)); writel(uiVal, key_gpio_res[i].ctrlReg); } return; } static irqreturn_t kobox_gpio_irq_handle(int irq, void *dev_id) { int key; // disable_irq_nosync(irq); printk("irq = %d\n", irq); if(dev_id) printk("dev_id:%s\n", dev_id); switch(irq) { case IRQ_EINT1: key = 0; break; case IRQ_EINT4: key = 1; break; case IRQ_EINT2: key = 2; break; case IRQ_EINT0: key = 3; break; default: printk("invalid irq:%d\n", irq); return IRQ_HANDLED; } /* 去抖:延时100ms后,在buttons_timer中读取按键状态,如果还是按下的,就说明是被正常按下的 使用timer是一种方式,后面再采用工作队列、tasklet中的方式来处理 */ /* 使用内核共享队列,延时调度 */ // schedule_delayed_work(&gpio_key_work[key], KEY_TIMER_DELAY2);//会OOPS /* 使用内核共享队列,立即调度,延时放到中断函数中 */ schedule_work(&gpio_key_work[key]);//执行正常 /* 使用单独队列,延时调度 */ // queue_delayed_work(&key_wq[key], &gpio_key_work[key], KEY_TIMER_DELAY2);//同样崩掉! /* 使用内核共享队列,立即调度,延时放到中断函数中 */ // queue_work(&key_wq[key], &gpio_key_work[key]);//同样崩掉! return IRQ_RETVAL(IRQ_HANDLED); } /* GPF相关寄存器: GPFCON 0x56000050 R/W Configures the pins of port F 0x0 GPFDAT 0x56000054 R/W The data register for port F Undef. GPFUP 0x56000058 R/W Pull-up disable register for port F 0x000 K1: GPF1 -EINT1: GPF1 [3:2] 00 = Input 01 = Output 10 = EINT[1] 11 = Reserved K2: GPF4 -EINT4: GPF4 [9:8] 00 = Input 01 = Output 10 = EINT[4] 11 = Reserved K3: GPF2 -EINT2: GPF2 [5:4] 00 = Input 01 = Output 10 = EINT2] 11 = Reserved K4: GPF0 -EINT0: GPF0 [1:0] 00 = Input 01 = Output 10 = EINT[0] 11 = Reserved */ /* 该函数返回0表示按键被按下,返回非0表示没有再被按下,认为这是电平毛刺导致的,是噪声信号 所以,该函数返回0,表示有按键被按下,返回非0表示是抖动 */ static int get_gpio_portf_value(unsigned int pin) { int ret; unsigned int uiVal = 0; printk("I AM @ [%s][%d], pin:%d\n", __FUNCTION__,__LINE__, pin); uiVal = readl(S3C2410_GPFDAT); ret = (0x1 << pin) & uiVal; printk("I AM @ [%s][%d], ret:%d\n", __FUNCTION__,__LINE__, ret); return ret; } static void gpio_key_wq0_handler(struct work_struct *work) { int ret; unsigned int pin; /* 中断后100ms才会导致,执行该函数 */ printk("i am at [%s][%d]\n", __FUNCTION__, __LINE__); msleep(100); pin = key_gpio_res[0].gpfPin; /* 将引脚由EINTX设置会GPIO */ set_gpio_as_gpio(); /* 读取对应引脚GPIO的值,返回0表示按键真正被按下,返回1表示抖动 */ ret = get_gpio_portf_value(pin); if(0 == ret) { pressCnt[0]++; printk("key0 pressed: pressCnt[0]:%d\n", pressCnt[0]); } /* 将引脚设置回EINTX */ set_gpio_as_eint(); return; } static void gpio_key_wq1_handler(struct work_struct *work) { int ret; unsigned int pin; /* 中断后100ms才会导致,执行该函数 */ printk("i am at [%s][%d]\n", __FUNCTION__, __LINE__); msleep(100); pin = key_gpio_res[1].gpfPin; /* 将引脚由EINTX设置会GPIO */ set_gpio_as_gpio(); /* 读取对应引脚GPIO的值,返回0表示按键真正被按下,返回1表示抖动 */ ret = get_gpio_portf_value(pin); if(0 == ret) { pressCnt[1]++; printk("key1 pressed: pressCnt[1]:%d\n", pressCnt[1]); } /* 将引脚设置回EINTX */ set_gpio_as_eint(); return; } static void gpio_key_wq2_handler(struct work_struct *work) { int ret; unsigned int pin; /* 中断后100ms才会导致,执行该函数 */ printk("i am at [%s][%d]\n", __FUNCTION__, __LINE__); msleep(100); pin = key_gpio_res[2].gpfPin; /* 将引脚由EINTX设置会GPIO */ set_gpio_as_gpio(); /* 读取对应引脚GPIO的值,返回0表示按键真正被按下,返回1表示抖动 */ ret = get_gpio_portf_value(pin); if(0 == ret) { pressCnt[2]++; printk("key2 pressed: pressCnt[2]:%d\n", pressCnt[2]); } /* 将引脚设置回EINTX */ set_gpio_as_eint(); return; } static void gpio_key_wq3_handler(struct work_struct *work) { int ret; unsigned int pin; /* 中断后100ms才会导致,执行该函数 */ printk("i am at [%s][%d]\n", __FUNCTION__, __LINE__); msleep(100); pin = key_gpio_res[3].gpfPin; /* 将引脚由EINTX设置会GPIO */ set_gpio_as_gpio(); /* 读取对应引脚GPIO的值,返回0表示按键真正被按下,返回1表示抖动 */ ret = get_gpio_portf_value(pin); if(0 == ret) { pressCnt[3]++; printk("key3 pressed: pressCnt[3]:%d\n", pressCnt[3]); } /* 将引脚设置回EINTX */ set_gpio_as_eint(); return; } static int request_irq_for_gpio(void) { int i; int ret; unsigned uiVal; int nouse; for(i=0; i<ARRAY_SIZE(key_gpio_res);i++) { /* 设置中断触发方式:下降沿有效,触发中断,以便根据GPIO的值来判断是否仍在按下 */ uiVal = readl(key_gpio_res[i].trigReg); uiVal |= (0x1 << (key_gpio_res[i].trigBit)); uiVal &= ~(0x1 << (key_gpio_res[i].trigBit + 1)); writel(uiVal, key_gpio_res[i].trigReg); /* 注册中断 */ ret = request_irq(key_gpio_res[i].irqNum, kobox_gpio_irq_handle, key_gpio_res[i].irqFlag, key_gpio_res[i].irqName, (void *)key_gpio_res[i].irqName); if(ret) printk("[func:%s][line:%d] request_irq failed, ret:%d!\n", __FUNCTION__,__LINE__,ret); else printk("[func:%s][line:%d] request_irq ok, irq:%d!\n", __FUNCTION__,__LINE__, key_gpio_res[i].irqNum); key_wq[i] = create_workqueue(key_gpio_res[i].irqName); if (!key_wq[i] ) { printk("create_workqueue key_wq[%d] failed!\n", i); } /* 初始化工作队列,用于响应中断后半部,中断响应后100ms调度以便去抖动 */ INIT_WORK(&gpio_key_work[i], gpio_key_wq_handler[i]); } return 0; } struct file_operations kobox_key_operations = { .owner = THIS_MODULE, .open = kobox_key_open, .read = kobox_key_read, .release = kobox_key_release, .unlocked_ioctl = kobox_key_ioctl, }; //GPB0 int major; int minor; struct cdev cdev; struct class *kobox_key_class; struct device *pstdev = NULL; #define GPIO_KEY_NAME "kobox_key" int __init key_drv_init(void) { int error; dev_t dev; printk("#####enter key_drv_init!\n"); major = register_chrdev(0, GPIO_KEY_NAME, &kobox_key_operations); if (major < 0) { printk(" can't register major number\n"); return major; } /* create class */ kobox_key_class = class_create(THIS_MODULE, GPIO_KEY_NAME); if(IS_ERR(kobox_key_class)) { printk("class_create failed!\n"); goto fail; } /* create /dev/kobox_gpio */ pstdev = device_create(kobox_key_class, NULL, MKDEV(major, 0), NULL, GPIO_KEY_NAME); if(!pstdev) { printk("device_create failed!\n"); goto fail1; } /* set gpf0/1/2/4 as extern interrupt pins */ set_gpio_as_eint(); request_irq_for_gpio(); printk("#####key_drv_init ok!\n"); return 0; fail1: class_destroy(kobox_key_class); fail: unregister_chrdev(major, GPIO_KEY_NAME); return -1; } void __exit key_drv_exit(void) { printk("exit gpio drv!\n"); device_destroy(kobox_key_class, MKDEV(major, 0)); class_destroy(kobox_key_class); unregister_chrdev(major, GPIO_KEY_NAME); return; } module_init(key_drv_init); module_exit(key_drv_exit); MODULE_LICENSE("GPL");
原文地址:http://blog.csdn.net/xiangpingli/article/details/40057373