九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 4705|回复: 0
打印 上一主题 下一主题

x4412&ibox项目实战44-Linux中断编程实验(三)

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-14 09:23:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
上一小节通过tasklet机制实现了中断底半部,本小节将通过工作队列的机制实现。事实上,工作队列和编程方法与tasklet非常的相似,因此我们只需在上一小节的基础上做些简单的修改就可以了。
在kernel/drivers/char/x4412目录下新建x4412-eint-work.c文件,编辑内容如下:
  1. #include <linux/irq.h>  
  2. #include <linux/interrupt.h>
  3. #include <linux/module.h>
  4. #include <linux/types.h>
  5. #include <linux/fs.h>
  6. #include <linux/errno.h>
  7. #include <linux/sched.h>
  8. #include <linux/init.h>
  9. #include <linux/cdev.h>
  10. #include <asm/uaccess.h>
  11. #include <linux/slab.h>
  12. #include <linux/device.h>
  13. #include <mach/gpio.h>

  14. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
  15. static struct class *cdrv_class;
  16. static struct device *cdrv_device;
  17. static struct work_struct x4412_wq;
  18. static void x4412_eint_do_work(struct work_struct *work);
  19. typedef struct pin_desc
  20. {
  21.          unsigned int pin;
  22.          unsigned int key_val;
  23. }pin_desc;
  24. static struct pin_desc pins_desc[6] =
  25. {
  26.         {EXYNOS4_GPX1(0),0x01},//LEFT
  27.         {EXYNOS4_GPX1(3),0x02},//RIGHT
  28.         {EXYNOS4_GPX1(2),0x03},//UP
  29.             {EXYNOS4_GPX1(1),0x04},//DOWN
  30.             {EXYNOS4_GPX1(5),0x05},//MENU
  31.             {EXYNOS4_GPX1(4),0x06},//BACK
  32. };
  33. struct pin_desc *pin_desc_p;
  34. static unsigned char key_val;
  35. static int ev_press = 0;
  36. int major;
  37. static void x4412_eint_do_work(struct work_struct *work)
  38. {
  39.          unsigned int pinval;
  40.          pinval = gpio_get_value(pin_desc_p->pin);
  41.          if(pinval)
  42.          {
  43.                    key_val = 0x80 | (pin_desc_p->key_val);//松开
  44.          }
  45.          else
  46.          {
  47.                    key_val = pin_desc_p->key_val;// 按下
  48.          }
  49.          ev_press = 1;
  50.          printk("eint occured.\r\n");
  51.          wake_up_interruptible(&button_waitq);
  52. }
  53. /* 用户中断处理函数 */
  54. static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
  55. {
  56.          pin_desc_p = (struct pin_desc *)dev_id;
  57.          schedule_work(&x4412_wq);
  58.          return IRQ_HANDLED;
  59. }
  60. static int x4412_eint_open(struct inode * inode, struct file * filp)
  61. {
  62.          return 0;
  63. }
  64. static ssize_t x4412_eint_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
  65. {
  66.          if (count != 1)
  67.                    return -EINVAL;     
  68.          wait_event_interruptible(button_waitq, ev_press);
  69.          if(copy_to_user(buf, &key_val, 1))
  70.                    return -EFAULT;
  71.          ev_press = 0;
  72.          return 1;
  73. }
  74. static int x4412_eint_release(struct inode *inode, struct file *file)
  75. {
  76.          int irq;
  77.          irq = gpio_to_irq(pins_desc[0].pin);
  78.          free_irq(irq, &pins_desc[0]);
  79.          irq = gpio_to_irq(pins_desc[1].pin);
  80.          free_irq(irq,&pins_desc[1]);
  81.          irq = gpio_to_irq(pins_desc[2].pin);
  82.          free_irq(irq,&pins_desc[2]);
  83.        irq = gpio_to_irq(pins_desc[3].pin);
  84.          free_irq(irq, &pins_desc[3]);
  85.          irq = gpio_to_irq(pins_desc[4].pin);
  86.          free_irq(irq,&pins_desc[4]);
  87.          irq = gpio_to_irq(pins_desc[5].pin);
  88.          free_irq(irq,&pins_desc[5]);
  89.          return 0;
  90. }
  91. static const struct file_operations x4412_eint_fops =
  92. {
  93.          .owner = THIS_MODULE,
  94.          .read = x4412_eint_read,
  95.          .open = x4412_eint_open,
  96.          .release = x4412_eint_release,
  97. };
  98. /* 驱动入口函数 */
  99. static int x4412_eint_init(void)
  100. {
  101.          int ret,irq;
  102.          major = register_chrdev(0, "x4412-key", &x4412_eint_fops);
  103.          cdrv_class = class_create(THIS_MODULE, "x4412-eint");
  104.          cdrv_device = device_create(cdrv_class, NULL, MKDEV(major, 0), NULL, "x4412-eint");
  105.          irq = gpio_to_irq(pins_desc[0].pin);
  106.          ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH,  "LEFT", &pins_desc[0]);
  107.          if(ret)
  108.                    printk("request irq_eint8 error!\r\n");
  109.          irq = gpio_to_irq(pins_desc[1].pin);
  110.          ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "RIGHT",&pins_desc[1]);
  111.          if(ret)
  112.                    printk("request irq_eint11 error!\r\n");
  113.          irq = gpio_to_irq(pins_desc[2].pin);
  114.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "UP",   &pins_desc[2]);
  115.          if(ret)
  116.                    printk("request irq_eint10 error!\r\n");
  117.          irq = gpio_to_irq(pins_desc[3].pin);
  118.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "DOWN", &pins_desc[3]);
  119.          if(ret)
  120.                    printk("request irq_eint9 error!\r\n");
  121.          irq = gpio_to_irq(pins_desc[4].pin);
  122.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "MENU", &pins_desc[4]);
  123.          if(ret)
  124.                    printk("request irq_eint13 error!\r\n");
  125.          irq = gpio_to_irq(pins_desc[5].pin);
  126.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "BACK", &pins_desc[5]);
  127.          if(ret)
  128.                    printk("request irq_eint12 error!\r\n");
  129.          INIT_WORK(&x4412_wq,x4412_eint_do_work);
  130.          pin_desc_p = kmalloc(sizeof(struct pin_desc), GFP_KERNEL);
  131.          if(!pin_desc_p)
  132.          {
  133.                    return -ENOMEM;
  134.          }
  135.          return 0;
  136. }
  137. /* 驱动出口函数 */
  138. static void x4412_eint_exit(void)
  139. {
  140.          unregister_chrdev(major, "x4412-key");
  141.          device_unregister(cdrv_device);  //卸载类下的设备
  142.          class_destroy(cdrv_class);       //卸载类
  143. }
  144. module_init(x4412_eint_init);  //用于修饰入口函数
  145. module_exit(x4412_eint_exit);  //用于修饰出口函数     
  146. MODULE_AUTHOR("www.9tripod.com");
  147. MODULE_LICENSE("GPL");
复制代码
       通过以上源码,可以总结出使用工作队列实现中断底半部的步骤如下:
       第一步:定义工作队列和工作函数:
  1. static struct work_struct x4412_wq;
  2. static void x4412_eint_do_work(struct work_struct *work);
复制代码
       第二步:编写中断底半部处理函数:
  1. static void x4412_eint_do_work(struct work_struct *work)
  2. {
  3.          ……
  4. }
复制代码
       第三步:在中断顶半部处理函数中引用schedule_work函数触发底半部工作:
  1. static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
  2. {
  3.          ……
  4.          schedule_work(&x4412_wq);
  5.          ……
  6. }
复制代码
       第四步:在驱动模块加载函数中申请中断和初始化工作队列:
  1. static int x4412_eint_init(void)
  2. {
  3.          int ret,irq;
  4.      ……
  5.          ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH,  "LEFT", &pins_desc[0]);
  6.          if(ret)
  7.                    printk("request irq_eint8 error!\r\n");
  8.          INIT_WORK(&x4412_wq,x4412_eint_do_work);
  9.          ……
  10. }
复制代码
       第五步:在release函数或模块卸载函数中释放中断。
       编辑kernel/drivers/char/x4412/Kconfig文件,添加如下内容:
  1. config X4412_EINT_WORK_DRIVER
  2.          tristate "x4412 eint work driver"
  3.          default m
  4.          help
  5.          compile for x4412 eint work driver,y for kernel,m for module.
复制代码
       编辑kernel/drivers/char/x4412/Makefile文件,添加如下内容:
  1. 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文件,编辑内容如下:
  1. #include <linux/irq.h>  
  2. #include <linux/interrupt.h>
  3. #include <linux/module.h>
  4. #include <linux/types.h>
  5. #include <linux/fs.h>
  6. #include <linux/errno.h>
  7. #include <linux/sched.h>
  8. #include <linux/init.h>
  9. #include <linux/cdev.h>
  10. #include <asm/uaccess.h>
  11. #include <linux/slab.h>
  12. #include <linux/device.h>
  13. #include <mach/gpio.h>
  14. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
  15. static struct class *cdrv_class;
  16. static struct device *cdrv_device;
  17. static struct work_struct x4412_wq;
  18. static struct workqueue_struct *queue=NULL;
  19. static void x4412_eint_do_work(struct work_struct *work);
  20. typedef struct pin_desc
  21. {
  22.          unsigned int pin;
  23.          unsigned int key_val;
  24. }pin_desc;
  25. static struct pin_desc pins_desc[6] =
  26. {
  27.         {EXYNOS4_GPX1(0),0x01},//LEFT
  28.         {EXYNOS4_GPX1(3),0x02},//RIGHT
  29.         {EXYNOS4_GPX1(2),0x03},//UP
  30.             {EXYNOS4_GPX1(1),0x04},//DOWN
  31.             {EXYNOS4_GPX1(5),0x05},//MENU
  32.             {EXYNOS4_GPX1(4),0x06},//BACK
  33. };
  34. struct pin_desc *pin_desc_p;
  35. static unsigned char key_val;
  36. static int ev_press = 0;
  37. int major;
  38. static void x4412_eint_do_work(struct work_struct *work)
  39. {
  40.          unsigned int pinval;
  41.          pinval = gpio_get_value(pin_desc_p->pin);
  42.          if(pinval)
  43.          {
  44.                    key_val = 0x80 | (pin_desc_p->key_val);//松开
  45.          }
  46.          else
  47.          {
  48.                    key_val = pin_desc_p->key_val;// 按下
  49.          }
  50.          ev_press = 1;
  51.          printk("eint occured.\r\n");
  52.          wake_up_interruptible(&button_waitq);
  53. }
  54. /* 用户中断处理函数 */
  55. static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
  56. {
  57.          pin_desc_p = (struct pin_desc *)dev_id;
  58.          queue_work(queue, &x4412_wq);
  59.          return IRQ_HANDLED;
  60. }

  61. static int x4412_eint_open(struct inode * inode, struct file * filp)
  62. {
  63.          return 0;
  64. }
  65. static ssize_t x4412_eint_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
  66. {
  67.          if (count != 1)
  68.                    return -EINVAL;     
  69.          wait_event_interruptible(button_waitq, ev_press);
  70.          if(copy_to_user(buf, &key_val, 1))
  71.                    return -EFAULT;
  72.          ev_press = 0;
  73.          return 1;
  74. }
  75. static int x4412_eint_release(struct inode *inode, struct file *file)
  76. {
  77.          int irq;
  78.          irq = gpio_to_irq(pins_desc[0].pin);
  79.          free_irq(irq, &pins_desc[0]);
  80.          irq = gpio_to_irq(pins_desc[1].pin);
  81.          free_irq(irq,&pins_desc[1]);
  82.          irq = gpio_to_irq(pins_desc[2].pin);
  83.          free_irq(irq,&pins_desc[2]);
  84.        irq = gpio_to_irq(pins_desc[3].pin);
  85.          free_irq(irq, &pins_desc[3]);
  86.          irq = gpio_to_irq(pins_desc[4].pin);
  87.          free_irq(irq,&pins_desc[4]);
  88.          irq = gpio_to_irq(pins_desc[5].pin);
  89.          free_irq(irq,&pins_desc[5]);
  90.          return 0;
  91. }
  92. static const struct file_operations x4412_eint_fops =
  93. {
  94.          .owner = THIS_MODULE,
  95.          .read = x4412_eint_read,
  96.          .open = x4412_eint_open,
  97.          .release = x4412_eint_release,
  98. };
  99. /* 驱动入口函数 */
  100. static int x4412_eint_init(void)
  101. {
  102.          int ret,irq;
  103.          major = register_chrdev(0, "x4412-key", &x4412_eint_fops);
  104.          cdrv_class = class_create(THIS_MODULE, "x4412-eint");
  105.          cdrv_device = device_create(cdrv_class, NULL, MKDEV(major, 0), NULL, "x4412-eint");
  106.          irq = gpio_to_irq(pins_desc[0].pin);
  107.          ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH,  "LEFT", &pins_desc[0]);
  108.          if(ret)
  109.                    printk("request irq_eint8 error!\r\n");
  110.          irq = gpio_to_irq(pins_desc[1].pin);
  111.          ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "RIGHT",&pins_desc[1]);
  112.          if(ret)
  113.                    printk("request irq_eint11 error!\r\n");
  114.          irq = gpio_to_irq(pins_desc[2].pin);
  115.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "UP",   &pins_desc[2]);  
  116.          if(ret)
  117.                    printk("request irq_eint10 error!\r\n");
  118.          irq = gpio_to_irq(pins_desc[3].pin);
  119.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "DOWN", &pins_desc[3]);
  120.          if(ret)
  121.                    printk("request irq_eint9 error!\r\n");
  122.          irq = gpio_to_irq(pins_desc[4].pin);
  123.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "MENU", &pins_desc[4]);
  124.          if(ret)
  125.                    printk("request irq_eint13 error!\r\n");
  126.          irq = gpio_to_irq(pins_desc[5].pin);
  127.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "BACK", &pins_desc[5]);
  128.          if(ret)
  129.                    printk("request irq_eint12 error!\r\n");
  130.          INIT_WORK(&x4412_wq,x4412_eint_do_work);
  131.          queue=create_singlethread_workqueue("x4412_work");
  132.          if(!queue)
  133.          {
  134.                    printk("create x4412_work queue error!\r\n");
  135.                    return -1;
  136.          }
  137.          pin_desc_p = kmalloc(sizeof(struct pin_desc), GFP_KERNEL);
  138.          if(!pin_desc_p)
  139.          {
  140.                    return -ENOMEM;
  141.          }
  142.          return 0;
  143. }
  144. /* 驱动出口函数 */
  145. static void x4412_eint_exit(void)
  146. {
  147.          destroy_workqueue(queue);
  148.          unregister_chrdev(major, "x4412-key");
  149.          device_unregister(cdrv_device);  //卸载类下的设备
  150.          class_destroy(cdrv_class);       //卸载类
  151. }
  152. module_init(x4412_eint_init);  //用于修饰入口函数
  153. module_exit(x4412_eint_exit);  //用于修饰出口函数     
  154. MODULE_AUTHOR("www.9tripod.com");
  155. MODULE_LICENSE("GPL");
复制代码
       结合源码分析,可以总结出创建自己的队列需要的步骤。
       第一步:创建一个工作结构体变量,声明一个指向工作队列的指针,同时声明一个工作处理函数:
  1. static struct work_struct x4412_wq;
  2. static struct workqueue_struct *queue=NULL;
  3. static void x4412_eint_do_work(struct work_struct *work);
复制代码
       第二步:在init函数或open函数中初始化新建的结构体变量,并给该结构体变量添加处理函数的入口地址,即绑定工作队列入口函数:
  1. INIT_WORK(&x4412_wq,x4412_eint_do_work);
复制代码
       第三步:在init函数或open函数中创建自己的工作队列:
  1. queue=create_singlethread_workqueue("x4412_work");
  2. if(!queue)
  3. {
  4.          printk("create x4412_work queue error!\r\n");
  5.          return -1;
  6. }
复制代码
    第四步:引用queue_work函数,将工作添加到自己创建的工作队列等待执行。如果存在中断底半部,则将它放在中断顶半部中,用于触发中断底半部函数执行。queue_work函数的作用与schedule_work()类似,不同的是将工作添加入queue指针指向的工作队列而不是系统共享的工作队列:
  1. static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
  2. {
  3.          ……
  4.          queue_work(queue, &x4412_wq);
  5.          ……
  6. }
复制代码
       第五步:在release函数或exit函数中删除自己的工作队列:
  1. destroy_workqueue(queue);
复制代码
       编辑kernel/drivers/char/x4412/Kconfig文件,添加如下内容:
  1. config X4412_EINT_MYWORK_DRIVER
  2.          tristate "x4412 eint my work driver"
  3.          default m
  4.          help
  5.          compile for x4412 eint my work driver,y for kernel,m for module.
复制代码
       编辑kernel/drivers/char/x4412/Makefile文件,添加如下内容:
  1. 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)
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-6-1 22:30 , Processed in 0.020379 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表