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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-13 15:39:59 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在Linux设备驱动中,使用中断的设备需要申请和释放对应的中断,分别使用内核提供的request_irq()和free_irq()函数。
  1. int request_irq(unsigned int irq,void (*handler)(int irq, void *dev_id, struct pt_regs *regs),unsigned long irqflags,const char * devname,void *dev_id);
复制代码
       irq:要申请的硬件中断号。
       handler:向系统登记的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
irqflags:中断处理的属性,若设置了SA_INTERRUPT,则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了SA_SHIRQ,则表示多个设备共享中断,dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
devname:中断描述文件,每一个中断可指定一种中断描述名。
dev_id:传递给handler或设置为NULL。
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY 表示中断已经被占用且不能共享。
与request_irq()向对应的函数为free_irq(),它的原型如下:
  1. void free_irq(unsigned int irq,void *dev_id);
复制代码
free_irq()中参数的定义与request_irq()相同。
编写一个驱动模块,通过request_irq函数实现按键中断,卸载驱动时通过free_irq函数释放中断资源。
在kernel/drivers/char/x4412目录下新建x4412-eint.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. /* 定义并初始化等待队列头 */
  15. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
  16. static struct class *cdrv_class;
  17. static struct device *cdrv_device;
  18. typedef struct pin_desc
  19. {
  20.          unsigned int pin;
  21.          unsigned int key_val;
  22. }pin_desc;

  23. static struct pin_desc pins_desc[6] =
  24. {
  25.         {EXYNOS4_GPX1(0),0x01},//LEFT
  26.         {EXYNOS4_GPX1(3),0x02},//RIGHT
  27.         {EXYNOS4_GPX1(2),0x03},//UP
  28.             {EXYNOS4_GPX1(1),0x04},//DOWN
  29.             {EXYNOS4_GPX1(5),0x05},//MENU
  30.             {EXYNOS4_GPX1(4),0x06},//BACK
  31. };
  32. /* 键值: 按下时, 0x01,0x02,0x03,0x04,0x05,0x06 */
  33. /* 键值: 松开时, 0x81,0x82,0x83,0x84,0x05,0x06 */
  34. static unsigned char key_val;
  35. static int ev_press = 0;
  36. int major;
  37. /* 用户中断处理函数 */
  38. static irqreturn_t x4412_buttons_irq(int irq, void *dev_id)
  39. {
  40.          struct pin_desc *pindesc = (struct pin_desc *)dev_id;
  41.          unsigned int pinval;
  42.          printk("eint occured..\r\n");
  43.          pinval = gpio_get_value(pindesc->pin);
  44.          if(pinval)
  45.          {
  46.                    key_val = 0x80 | (pindesc->key_val);/* 松开 */  
  47.          }
  48.          else
  49.          {
  50.                    key_val = pindesc->key_val;/* 按下 */
  51.          }
  52.          ev_press = 1;                           /* 表示中断已经发生 */
  53.          wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
  54.          return IRQ_HANDLED;
  55. }

  56. static int x4412_eint_open(struct inode * inode, struct file * filp)
  57. {
  58.          return 0;
  59. }
  60. static ssize_t x4412_eint_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
  61. {
  62.          if (count != 1)
  63.                    return -EINVAL;     
  64.          wait_event_interruptible(button_waitq, ev_press);
  65.          if(copy_to_user(buf, &key_val, 1))
  66.                    return -EFAULT;
  67.          ev_press = 0;
  68.          return 1;
  69. }
  70. static int x4412_eint_release(struct inode *inode, struct file *file)
  71. {
  72.          int irq;
  73.          irq = gpio_to_irq(pins_desc[0].pin);
  74.          free_irq(irq, &pins_desc[0]);
  75.          irq = gpio_to_irq(pins_desc[1].pin);
  76.          free_irq(irq,&pins_desc[1]);
  77.          irq = gpio_to_irq(pins_desc[2].pin);
  78.          free_irq(irq,&pins_desc[2]);
  79.        irq = gpio_to_irq(pins_desc[3].pin);
  80.          free_irq(irq, &pins_desc[3]);
  81.          irq = gpio_to_irq(pins_desc[4].pin);
  82.          free_irq(irq,&pins_desc[4]);
  83.          irq = gpio_to_irq(pins_desc[5].pin);
  84.          free_irq(irq,&pins_desc[5]);
  85.          return 0;
  86. }
  87. static const struct file_operations x4412_eint_fops =
  88. {
  89.          .owner = THIS_MODULE,
  90.          .read = x4412_eint_read,
  91.          .open = x4412_eint_open,
  92.          .release = x4412_eint_release,
  93. };
  94. /* 驱动入口函数 */
  95. static int x4412_eint_init(void)
  96. {
  97.          int ret,irq;
  98.          major = register_chrdev(0, "x4412-key", &x4412_eint_fops);
  99.          cdrv_class = class_create(THIS_MODULE, "x4412-eint");
  100.          cdrv_device = device_create(cdrv_class, NULL, MKDEV(major, 0), NULL, "x4412-eint");
  101.          irq = gpio_to_irq(pins_desc[0].pin);
  102.          ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH,  "LEFT", &pins_desc[0]);
  103.          if(ret)
  104.                    printk("request irq_eint8 error!\r\n");
  105.          irq = gpio_to_irq(pins_desc[1].pin);
  106.          ret = request_irq(irq, x4412_buttons_irq,IRQ_TYPE_EDGE_BOTH, "RIGHT",&pins_desc[1]);
  107.          if(ret)
  108.                    printk("request irq_eint11 error!\r\n");
  109.          irq = gpio_to_irq(pins_desc[2].pin);
  110.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "UP",   &pins_desc[2]);
  111.          if(ret)
  112.                    printk("request irq_eint10 error!\r\n");
  113.          irq = gpio_to_irq(pins_desc[3].pin);
  114.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "DOWN", &pins_desc[3]);
  115.          if(ret)
  116.                    printk("request irq_eint9 error!\r\n");
  117.          irq = gpio_to_irq(pins_desc[4].pin);
  118.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "MENU", &pins_desc[4]);
  119.          if(ret)
  120.                    printk("request irq_eint13 error!\r\n");
  121.          irq = gpio_to_irq(pins_desc[5].pin);
  122.          ret = request_irq(irq, x4412_buttons_irq, IRQ_TYPE_EDGE_BOTH, "BACK", &pins_desc[5]);
  123.          if(ret)
  124.                    printk("request irq_eint12 error!\r\n");
  125.          return 0;
  126. }
  127. /* 驱动出口函数 */
  128. static void x4412_eint_exit(void)
  129. {
  130.          unregister_chrdev(major, "x4412-key");
  131.          device_unregister(cdrv_device);  //卸载类下的设备
  132.          class_destroy(cdrv_class);       //卸载类
  133. }
  134. module_init(x4412_eint_init);  //用于修饰入口函数
  135. module_exit(x4412_eint_exit);  //用于修饰出口函数     
  136. MODULE_AUTHOR("www.9tripod.com");
  137. MODULE_LICENSE("GPL");
复制代码
       驱动模块在x4412_eint_init函数中通过gpio_to_irq函数获取开发板上六个按键对应的GPIO口的中断号,再通过request_irq函数申请中断。在中断服务线程x4412_buttons_irq中通过gpio_get_value函数获取当前按键的按下或抬起状态,并保存在变量key_val中。在x4412_eint_read函数中通过copy_to_user函数向上层上报按键键值及按下或抬起的状态。
       在驱动模块中添加了等待队列机制。程序开始声明并初始化了等待队列头:
  1. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
复制代码
       在读函数中,调用wait_event_interruptible函数定义了一个等待事件,它允许被中断打断。
  1. wait_event_interruptible(button_waitq, ev_press);
复制代码
       在应用程序通过read函数从驱动模块中读取键值时,如果没有中断产生,将一时处于等待状态。每当产生一次中断,ev_press将被清零,应用程序再次调用时,在没有中断产生的情况下又会进入等待状态。
       在中断服务线程中,每当产生一次中断,将通过wake_up_interruptible函数唤醒等待队列,读函数中的等待队列被唤醒,应用程序将会读到按键键值。
       将内核中默认的按键中断驱动取消,通过menuconfig即可配置:
  1. Device Drivers  ---> Input device support  ---> Keyboards  ---> GPIO Buttons
复制代码
       取消选中即可,否则将会与现有驱动冲突。编辑kernel/drivers/char/x4412/Kconfig文件,添加以下内容:
  1. config X4412_EINT_DRIVER
  2.          tristate "x4412 eint driver"
  3.          default m
  4.          help
  5.          compile for x4412 eint driver,y for kernel,m for module.
复制代码
       编辑kernel/drivers/char/x4412/Makefile文件,添加以下内容:
  1. obj-$(CONFIG_X4412_EINT_DRIVER) += x4412-eint.o
复制代码
       编译内核,在kernel/drivers/char/x4412目录下将会生成目标映像x4412-eint.ko
       ubuntu的用户目录或samba目录下新建测试程序x4412-eint-app.c文件,编辑内容如下:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. int main(int argc ,char *argv[])
  7. {
  8.          int fd;
  9.          unsigned char key_val;
  10.          fd = open("/dev/x4412-eint",O_RDWR);
  11.          if (fd < 0)
  12.          {
  13.                    printf("open error\n");
  14.          }
  15.          while(1)
  16.          {
  17.                    read(fd,&key_val,1);
  18.                    printf("key_val = 0x%x\n",key_val);
  19.          }
  20.          return 0;
  21. }
复制代码
       使用如下指令编译应用程序:
  1. arm-none-linux-gnueabi-gcc x4412-eint-app.c -o x4412-eint-app
复制代码
       加载驱动模块,通过cat /proc/ interrupts指令可以查看驱动模块申请的中断信息:
  1. [root@x4412 mnt]# cat /proc/interrupts
  2.            CPU0       CPU3  
  3. ……   
  4. 360:          9          0  exynos-eint  LEFT
  5. 361:         14          0  exynos-eint  DOWN
  6. 362:         13          0  exynos-eint  UP
  7. 363:         10          0  exynos-eint  RIGHT
  8. 364:          8          0  exynos-eint  BACK
  9. 365:          8          0  exynos-eint  MENU
  10. 373:          1          0  exynos-eint  eth0
  11. 383:          1          0  exynos-eint  hpd
  12. ……
  13. [root@x4412 mnt]#
复制代码
       第一列对应中断号,第二列和第三列表示在CPU0CPU3上的中断次数,第四列表示可编程中断控制器,第五列表示设备名称,即request_irqdevname段。
       按下开发板上六个独立按键的任意一个,将会输出打印信息“eintoccured..”,表明中断已经产生。执行应用程序,观察实验现象:
  1. [root@x4412 mnt]# ./x4412-eint-app
  2. [ 3410.952271] eint occured..
  3. key_val = 0x3
  4. [ 3411.187970] eint occured..
  5. key_val = 0x83
  6. [ 3411.552708] eint occured..
  7. key_val = 0x4
  8. [ 3411.775483] eint occured..
  9. [ 3411.776791] eint occured..
  10. key_val = 0x84
复制代码
       当没有按下按键,即没有中断产生时,不会有任何打印输出,这时read函数正在等待队列被唤醒。只有按下中断后,等待队列才会被唤醒,应用程序才能够读出按键键值并打印输出。

可直接在x4412开发板或ibox卡片电脑上测试的驱动模块和应用程序:注意内核也需要更新,否则中断会冲突。
x4412-eint.ko (5.36 KB, 下载次数: 4)
x4412-eint-app (5.97 KB, 下载次数: 4)
zImage.part1.rar (1.9 MB, 下载次数: 5)
zImage.part2.rar (1.74 MB, 下载次数: 5)


回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-9-25 07:25 , Processed in 0.023357 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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