上一小节通过tasklet机制实现了中断底半部,本小节将通过工作队列的机制实现。事实上,工作队列和编程方法与tasklet非常的相似,因此我们只需在上一小节的基础上做些简单的修改就可以了。 在kernel/drivers/char/x4412目录下新建x4412-eint-work.c文件,编辑内容如下: - #include <linux/irq.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/fs.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/init.h>
- #include <linux/cdev.h>
- #include <asm/uaccess.h>
- #include <linux/slab.h>
- #include <linux/device.h>
- #include <mach/gpio.h>
-
- static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
- static struct class *cdrv_class;
- static struct device *cdrv_device;
- static struct work_struct x4412_wq;
- static void x4412_eint_do_work(struct work_struct *work);
- typedef struct pin_desc
- {
- unsigned int pin;
- unsigned int key_val;
- }pin_desc;
- static struct pin_desc pins_desc[6] =
- {
- {EXYNOS4_GPX1(0),0x01},//LEFT
- {EXYNOS4_GPX1(3),0x02},//RIGHT
- {EXYNOS4_GPX1(2),0x03},//UP
- {EXYNOS4_GPX1(1),0x04},//DOWN
- {EXYNOS4_GPX1(5),0x05},//MENU
- {EXYNOS4_GPX1(4),0x06},//BACK
- };
- struct pin_desc *pin_desc_p;
- static unsigned char key_val;
- static int ev_press = 0;
- int major;
- static void x4412_eint_do_work(struct work_struct *work)
- {
- unsigned int pinval;
- pinval = gpio_get_value(pin_desc_p->pin);
- if(pinval)
- {
- key_val = 0x80 | (pin_desc_p->key_val);//松开
- }
- else
- {
- key_val = pin_desc_p->key_val;// 按下
- }
- ev_press = 1;
- printk("eint occured.\r\n");
- wake_up_interruptible(&button_waitq);
- }
- /* 用户中断处理函数 */
- static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
- {
- pin_desc_p = (struct pin_desc *)dev_id;
- schedule_work(&x4412_wq);
- return IRQ_HANDLED;
- }
- static int x4412_eint_open(struct inode * inode, struct file * filp)
- {
- return 0;
- }
- static ssize_t x4412_eint_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
- {
- if (count != 1)
- return -EINVAL;
- wait_event_interruptible(button_waitq, ev_press);
- if(copy_to_user(buf, &key_val, 1))
- return -EFAULT;
- ev_press = 0;
- return 1;
- }
- static int x4412_eint_release(struct inode *inode, struct file *file)
- {
- int irq;
- irq = gpio_to_irq(pins_desc[0].pin);
- free_irq(irq, &pins_desc[0]);
- irq = gpio_to_irq(pins_desc[1].pin);
- free_irq(irq,&pins_desc[1]);
- irq = gpio_to_irq(pins_desc[2].pin);
- free_irq(irq,&pins_desc[2]);
- irq = gpio_to_irq(pins_desc[3].pin);
- free_irq(irq, &pins_desc[3]);
- irq = gpio_to_irq(pins_desc[4].pin);
- free_irq(irq,&pins_desc[4]);
- irq = gpio_to_irq(pins_desc[5].pin);
- free_irq(irq,&pins_desc[5]);
- return 0;
- }
- static const struct file_operations x4412_eint_fops =
- {
- .owner = THIS_MODULE,
- .read = x4412_eint_read,
- .open = x4412_eint_open,
- .release = x4412_eint_release,
- };
- /* 驱动入口函数 */
- static int x4412_eint_init(void)
- {
- int ret,irq;
- major = register_chrdev(0, "x4412-key", &x4412_eint_fops);
- cdrv_class = class_create(THIS_MODULE, "x4412-eint");
- cdrv_device = device_create(cdrv_class, NULL, MKDEV(major, 0), NULL, "x4412-eint");
- irq = gpio_to_irq(pins_desc[0].pin);
- ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "LEFT", &pins_desc[0]);
- if(ret)
- printk("request irq_eint8 error!\r\n");
- irq = gpio_to_irq(pins_desc[1].pin);
- ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "RIGHT",&pins_desc[1]);
- if(ret)
- printk("request irq_eint11 error!\r\n");
- irq = gpio_to_irq(pins_desc[2].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "UP", &pins_desc[2]);
- if(ret)
- printk("request irq_eint10 error!\r\n");
- irq = gpio_to_irq(pins_desc[3].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "DOWN", &pins_desc[3]);
- if(ret)
- printk("request irq_eint9 error!\r\n");
- irq = gpio_to_irq(pins_desc[4].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "MENU", &pins_desc[4]);
- if(ret)
- printk("request irq_eint13 error!\r\n");
- irq = gpio_to_irq(pins_desc[5].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "BACK", &pins_desc[5]);
- if(ret)
- printk("request irq_eint12 error!\r\n");
- INIT_WORK(&x4412_wq,x4412_eint_do_work);
- pin_desc_p = kmalloc(sizeof(struct pin_desc), GFP_KERNEL);
- if(!pin_desc_p)
- {
- return -ENOMEM;
- }
- return 0;
- }
- /* 驱动出口函数 */
- static void x4412_eint_exit(void)
- {
- unregister_chrdev(major, "x4412-key");
- device_unregister(cdrv_device); //卸载类下的设备
- class_destroy(cdrv_class); //卸载类
- }
- module_init(x4412_eint_init); //用于修饰入口函数
- module_exit(x4412_eint_exit); //用于修饰出口函数
- MODULE_AUTHOR("www.9tripod.com");
- MODULE_LICENSE("GPL");
复制代码 通过以上源码,可以总结出使用工作队列实现中断底半部的步骤如下: 第一步:定义工作队列和工作函数: - static struct work_struct x4412_wq;
- static void x4412_eint_do_work(struct work_struct *work);
复制代码 第二步:编写中断底半部处理函数: - static void x4412_eint_do_work(struct work_struct *work)
- {
- ……
- }
复制代码 第三步:在中断顶半部处理函数中引用schedule_work函数触发底半部工作: - static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
- {
- ……
- schedule_work(&x4412_wq);
- ……
- }
复制代码 第四步:在驱动模块加载函数中申请中断和初始化工作队列: - static int x4412_eint_init(void)
- {
- int ret,irq;
- ……
- ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "LEFT", &pins_desc[0]);
- if(ret)
- printk("request irq_eint8 error!\r\n");
- INIT_WORK(&x4412_wq,x4412_eint_do_work);
- ……
- }
复制代码 第五步:在release函数或模块卸载函数中释放中断。 编辑kernel/drivers/char/x4412/Kconfig文件,添加如下内容: - config X4412_EINT_WORK_DRIVER
- tristate "x4412 eint work driver"
- default m
- help
- compile for x4412 eint work driver,y for kernel,m for module.
复制代码 编辑kernel/drivers/char/x4412/Makefile文件,添加如下内容: - obj-$(CONFIG_X4412_EINT_WORK_DRIVER) += x4412-eint-work.o
复制代码 编译内核,在kernel/drivers/char/x4412目录下将会生成目标文件x4412-eint-work.ko文件,加载驱动模块测试,观察是否和前面的现象一致。 需要说明的是,以上工作队列是利用系统中共享的工作队列来添加自己的工作,这种情况下的处理函数不能消耗太多的时间,否则就会影响共享队列中其它任务的处理。还有一种方法是创建自己的工作队列。 在kernel/drivers/char/x4412目录下新建x4412-eint-mywork.c文件,编辑内容如下: - #include <linux/irq.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/fs.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/init.h>
- #include <linux/cdev.h>
- #include <asm/uaccess.h>
- #include <linux/slab.h>
- #include <linux/device.h>
- #include <mach/gpio.h>
- static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
- static struct class *cdrv_class;
- static struct device *cdrv_device;
- static struct work_struct x4412_wq;
- static struct workqueue_struct *queue=NULL;
- static void x4412_eint_do_work(struct work_struct *work);
- typedef struct pin_desc
- {
- unsigned int pin;
- unsigned int key_val;
- }pin_desc;
- static struct pin_desc pins_desc[6] =
- {
- {EXYNOS4_GPX1(0),0x01},//LEFT
- {EXYNOS4_GPX1(3),0x02},//RIGHT
- {EXYNOS4_GPX1(2),0x03},//UP
- {EXYNOS4_GPX1(1),0x04},//DOWN
- {EXYNOS4_GPX1(5),0x05},//MENU
- {EXYNOS4_GPX1(4),0x06},//BACK
- };
- struct pin_desc *pin_desc_p;
- static unsigned char key_val;
- static int ev_press = 0;
- int major;
- static void x4412_eint_do_work(struct work_struct *work)
- {
- unsigned int pinval;
- pinval = gpio_get_value(pin_desc_p->pin);
- if(pinval)
- {
- key_val = 0x80 | (pin_desc_p->key_val);//松开
- }
- else
- {
- key_val = pin_desc_p->key_val;// 按下
- }
- ev_press = 1;
- printk("eint occured.\r\n");
- wake_up_interruptible(&button_waitq);
- }
- /* 用户中断处理函数 */
- static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
- {
- pin_desc_p = (struct pin_desc *)dev_id;
- queue_work(queue, &x4412_wq);
- return IRQ_HANDLED;
- }
-
- static int x4412_eint_open(struct inode * inode, struct file * filp)
- {
- return 0;
- }
- static ssize_t x4412_eint_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
- {
- if (count != 1)
- return -EINVAL;
- wait_event_interruptible(button_waitq, ev_press);
- if(copy_to_user(buf, &key_val, 1))
- return -EFAULT;
- ev_press = 0;
- return 1;
- }
- static int x4412_eint_release(struct inode *inode, struct file *file)
- {
- int irq;
- irq = gpio_to_irq(pins_desc[0].pin);
- free_irq(irq, &pins_desc[0]);
- irq = gpio_to_irq(pins_desc[1].pin);
- free_irq(irq,&pins_desc[1]);
- irq = gpio_to_irq(pins_desc[2].pin);
- free_irq(irq,&pins_desc[2]);
- irq = gpio_to_irq(pins_desc[3].pin);
- free_irq(irq, &pins_desc[3]);
- irq = gpio_to_irq(pins_desc[4].pin);
- free_irq(irq,&pins_desc[4]);
- irq = gpio_to_irq(pins_desc[5].pin);
- free_irq(irq,&pins_desc[5]);
- return 0;
- }
- static const struct file_operations x4412_eint_fops =
- {
- .owner = THIS_MODULE,
- .read = x4412_eint_read,
- .open = x4412_eint_open,
- .release = x4412_eint_release,
- };
- /* 驱动入口函数 */
- static int x4412_eint_init(void)
- {
- int ret,irq;
- major = register_chrdev(0, "x4412-key", &x4412_eint_fops);
- cdrv_class = class_create(THIS_MODULE, "x4412-eint");
- cdrv_device = device_create(cdrv_class, NULL, MKDEV(major, 0), NULL, "x4412-eint");
- irq = gpio_to_irq(pins_desc[0].pin);
- ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "LEFT", &pins_desc[0]);
- if(ret)
- printk("request irq_eint8 error!\r\n");
- irq = gpio_to_irq(pins_desc[1].pin);
- ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "RIGHT",&pins_desc[1]);
- if(ret)
- printk("request irq_eint11 error!\r\n");
- irq = gpio_to_irq(pins_desc[2].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "UP", &pins_desc[2]);
- if(ret)
- printk("request irq_eint10 error!\r\n");
- irq = gpio_to_irq(pins_desc[3].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "DOWN", &pins_desc[3]);
- if(ret)
- printk("request irq_eint9 error!\r\n");
- irq = gpio_to_irq(pins_desc[4].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "MENU", &pins_desc[4]);
- if(ret)
- printk("request irq_eint13 error!\r\n");
- irq = gpio_to_irq(pins_desc[5].pin);
- ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "BACK", &pins_desc[5]);
- if(ret)
- printk("request irq_eint12 error!\r\n");
- INIT_WORK(&x4412_wq,x4412_eint_do_work);
- queue=create_singlethread_workqueue("x4412_work");
- if(!queue)
- {
- printk("create x4412_work queue error!\r\n");
- return -1;
- }
- pin_desc_p = kmalloc(sizeof(struct pin_desc), GFP_KERNEL);
- if(!pin_desc_p)
- {
- return -ENOMEM;
- }
- return 0;
- }
- /* 驱动出口函数 */
- static void x4412_eint_exit(void)
- {
- destroy_workqueue(queue);
- unregister_chrdev(major, "x4412-key");
- device_unregister(cdrv_device); //卸载类下的设备
- class_destroy(cdrv_class); //卸载类
- }
- module_init(x4412_eint_init); //用于修饰入口函数
- module_exit(x4412_eint_exit); //用于修饰出口函数
- MODULE_AUTHOR("www.9tripod.com");
- MODULE_LICENSE("GPL");
复制代码 结合源码分析,可以总结出创建自己的队列需要的步骤。 第一步:创建一个工作结构体变量,声明一个指向工作队列的指针,同时声明一个工作处理函数: - static struct work_struct x4412_wq;
- static struct workqueue_struct *queue=NULL;
- static void x4412_eint_do_work(struct work_struct *work);
复制代码 第二步:在init函数或open函数中初始化新建的结构体变量,并给该结构体变量添加处理函数的入口地址,即绑定工作队列入口函数: - INIT_WORK(&x4412_wq,x4412_eint_do_work);
复制代码 第三步:在init函数或open函数中创建自己的工作队列: - queue=create_singlethread_workqueue("x4412_work");
- if(!queue)
- {
- printk("create x4412_work queue error!\r\n");
- return -1;
- }
复制代码 第四步:引用queue_work函数,将工作添加到自己创建的工作队列等待执行。如果存在中断底半部,则将它放在中断顶半部中,用于触发中断底半部函数执行。queue_work函数的作用与schedule_work()类似,不同的是将工作添加入queue指针指向的工作队列而不是系统共享的工作队列: - static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
- {
- ……
- queue_work(queue, &x4412_wq);
- ……
- }
复制代码 第五步:在release函数或exit函数中删除自己的工作队列: - destroy_workqueue(queue);
复制代码 编辑kernel/drivers/char/x4412/Kconfig文件,添加如下内容: - config X4412_EINT_MYWORK_DRIVER
- tristate "x4412 eint my work driver"
- default m
- help
- compile for x4412 eint my work driver,y for kernel,m for module.
复制代码 编辑kernel/drivers/char/x4412/Makefile文件,添加如下内容: - obj-$(CONFIG_X4412_EINT_MYWORK_DRIVER) += x4412-eint-mywork.o
复制代码 编译内核,将会在kernel/drivers/char/x4412目录下生成目标文件x4412-eint-mywork.ko。加载驱动模块,观察测试结果是否和前面的相同。
编译好的驱动模块:
x4412-eint-work.ko
(5.73 KB, 下载次数: 5)
x4412-eint-mywork.ko
(5.99 KB, 下载次数: 4)
|