Chinaunix首页 | 论坛 | 博客
  • 博客访问: 150937
  • 博文数量: 44
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 407
  • 用 户 组: 普通用户
  • 注册时间: 2015-11-10 13:28
个人简介

仰望星空

文章分类
文章存档

2016年(22)

2015年(22)

我的朋友

分类: 嵌入式

2015-12-20 21:45:51

    学完了普通中断的按键驱动和输入子系统的按键驱动,还是有点迷糊,写这篇文章梳理下
普通按键驱动参考了这篇文章:http://blog.csdn.net/lwj103862095/article/details/17511867

一、普通中断按键驱动实现
    1、内核中断体系
    ARM架构linux内核中,有5种常见的异常,其中中断异常是其一,Linux内核将所有中断统一编号,使用一个irq_desc结构体来描述这些中断,里面记录了中断名称、中断状态、中断标记、并提供了中断的底层硬件访问函数(如:清除、屏蔽、使能中断),提供了这个中断的处理函数入口,通过它还可以调用用户注册的的中断处理函数。

点击(此处)折叠或打开

  1. struct irq_desc {
  2.     unsigned int irq;
  3.     ......
  4.     irq_flow_handler_t handle_irq;
  5.     struct irq_chip *chip;
  6.     ......
  7.     struct irqaction *action; /* IRQ action list */
  8.     unsigned int status; /* IRQ status */
  9.     ......
  10.     unsigned int irq_count; /* For detecting broken IRQs */
  11.     ......
  12.     const char *name;
  13. } ____cacheline_internodealigned_in_smp;
2、使用request_irq 向内核注册中断,其原型:

点击(此处)折叠或打开

  1. request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
  2.      const char *name, void *dev)
  3. {
  4.     return request_threaded_irq(irq, handler, NULL, flags, name, dev);
  5. }
    具体使用方式:

点击(此处)折叠或打开

  1. request_irq(IRQ_EINT4, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
  2. request_irq(IRQ_EINT5, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
    (1)第一个参数是硬件中断号
    (2)第二个参数是中断服务函数,当发生中断时调用
    (3)第三个参数是中断标准,规定中断出发的方式,上升沿/下降沿。。
    (4)第四个是名字,以通过cat proc/interrupts查看
    (5)第五个是传递参数,可传递给中断服务函数 buttons_irq() ,如传递给buttons_irq里的dev_id.
这个是中断服务函数原型static irqreturn_t buttons_irq(int irq, void *dev_id)
3、request_irq()的作用

    (1)irq_desc[irq]结构体中的action链表中已经链入了用户注册的中断处理函数。

    (2)中断触发方式已经被设置好。

    (3)中断已经被使能。
4、linux内核怎么样休眠
    
使用wait_event函数,其扩展型常用的函数为wait_event_interruptible(wq, condition),即可被中断打断的休眠。

wq是一个等待队列,condition是条件,如果condition = 0,则将会进行休眠,直到condition = 1,并且有唤醒函数唤醒它。

5、linux如何进行唤醒
    使用wait_up函数,其扩展型常用的函数为wake_up_interruptible(wq),wq与wait_event_interruptible的wq是一致的。

6、注意不能直接使用wait_event_interruptible和wake_up_interruptible函数必须先事先使用static DECLARE_WAIT_QUEUE_HEAD(wq)定义并初始化一个等待队列头,并设置condition条件变量。
7、注意驱动代码,主要报告如下函数
(1)驱动入口函 static int third_drv_init(void) ,用于主设备号的获取,累的创建、设备的创建
(2)驱动出口函数 static int third_drv_exit(void),用于卸载类和设备
(3)
module_init(third_drv_init); //用于修饰入口函数   module_exit(third_drv_exit); //用于修饰出口函数 
(4)
static int third_drv_open(struct inode * inode, struct file * filp) 设备打开函数,内涵request_irq(),用于进行中断的注册和初始化。
(5)
static ssize_t third_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos) 设备的读取函数,copy_to_user()
(6)最后是中断处理函数,通过request_irq()传入的参数判断是哪个引脚发生了中断,并将不同的值返回给 设备的读函数, 最后copy_to_user。

点击(此处)折叠或打开

  1. /* 定义并初始化等待队列头 */
  2. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
  3.   
  4.   
  5. static struct class *thirddrv_class;
  6. static struct device *thirddrv_device;
  7.   
  8. static struct pin_desc{
  9.     unsigned int pin;
  10.     unsigned int key_val;
  11. };
  12.   
  13. static struct pin_desc pins_desc[4] = {
  14.         {S3C2410_GPF1,0x01},
  15.         {S3C2410_GPF4,0x02},
  16.         {S3C2410_GPF2,0x03},
  17.         {S3C2410_GPF0,0x04},
  18. };
  19.   
  20. static int ev_press = 0;
  21.   
  22. /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
  23. /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
  24. static unsigned char key_val;
  25. int major;
  26.   
  27. /* 用户中断处理函数 */
  28. static irqreturn_t buttons_irq(int irq, void *dev_id)
  29. {
  30.     struct pin_desc *pindesc = (struct pin_desc *)dev_id;
  31.     unsigned int pinval;
  32.     pinval = s3c2410_gpio_getpin(pindesc->pin);
  33.   
  34.     if(pinval)
  35.     {
  36.         /* 松开 */
  37.         key_val = 0x80 | (pindesc->key_val);
  38.     }
  39.     else
  40.     {
  41.         /* 按下 */
  42.         key_val = pindesc->key_val;
  43.     }
  44.   
  45.     ev_press = 1; /* 表示中断已经发生 */
  46.      wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
  47.     return IRQ_HANDLED;
  48. }
  49. static int third_drv_open(struct inode * inode, struct file * filp)
  50. {
  51.     /* K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0
  52.      * 配置GPF1、GPF4、GPF2、GPF0为相应的外部中断引脚
  53.      * IRQT_BOTHEDGE应该改为IRQ_TYPE_EDGE_BOTH
  54.      */
  55.     request_irq(IRQ_EINT1, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1",&pins_desc[0]);
  56.     request_irq(IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2",&pins_desc[1]);
  57.     request_irq(IRQ_EINT2, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3",&pins_desc[2]);
  58.     request_irq(IRQ_EINT0, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4",&pins_desc[3]);
  59.     return 0;
  60. }
  61.   
  62. static ssize_t third_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
  63. {
  64.     if (size != 1)
  65.             return -EINVAL;
  66.       
  67.     /* 当没有按键按下时,休眠。
  68.      * 即ev_press = 0;
  69.      * 当有按键按下时,发生中断,在中断处理函数会唤醒
  70.      * 即ev_press = 1;
  71.      * 唤醒后,接着继续将数据通过copy_to_user函数传递给应用程序
  72.      */
  73.     wait_event_interruptible(button_waitq, ev_press);
  74.     copy_to_user(user, &key_val, 1);
  75.       
  76.     /* 将ev_press清零 */
  77.     ev_press = 0;
  78.     return 1;
  79. }
  80.   
  81. static int third_drv_close(struct inode *inode, struct file *file)
  82. {
  83.     free_irq(IRQ_EINT1,&pins_desc[0]);
  84.     free_irq(IRQ_EINT4,&pins_desc[1]);
  85.     free_irq(IRQ_EINT2,&pins_desc[2]);
  86.     free_irq(IRQ_EINT0,&pins_desc[3]);
  87.     return 0;
  88. }
  89.   
  90. /* File operations struct for character device */
  91. static const struct file_operations third_drv_fops = {
  92.     .owner = THIS_MODULE,
  93.     .open = third_drv_open,
  94.     .read = third_drv_read,
  95.     .release = third_drv_close,
  96. };
  97.   
  98.   
  99. /* 驱动入口函数 */
  100. static int third_drv_init(void)
  101. {
  102.     /* 主设备号设置为0表示由系统自动分配主设备号 */
  103.     major = register_chrdev(0, "third_drv", &third_drv_fops);
  104.   
  105.     /* 创建thirddrv类 */
  106.     thirddrv_class = class_create(THIS_MODULE, "thirddrv");
  107.   
  108.     /* 在thirddrv类下创建buttons设备,供应用程序打开设备*/
  109.     thirddrv_device = device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons");
  110.   
  111.     return 0;
  112. }
  113.   
  114. /* 驱动出口函数 */
  115. static void third_drv_exit(void)
  116. {
  117.     unregister_chrdev(major, "third_drv");
  118.     device_unregister(thirddrv_device); //卸载类下的设备
  119.     class_destroy(thirddrv_class); //卸载类
  120. }
  121.   
  122. module_init(third_drv_init); //用于修饰入口函数
  123. module_exit(third_drv_exit); //用于修饰出口函数
  124.   
  125. MODULE_AUTHOR("LWJ");
  126. MODULE_DESCRIPTION("Just for Demon");
  127. MODULE_LICENSE("GPL"); //遵循GPL协议
二、输入子系统 input_dev
    参考这两篇文章和视频:http://blog.csdn.net/lwj103862095/article/details/17734625
                                                   http://blog.chinaunix.net/uid-20746260-id-3065868.html
1、Input子系统框架    
    (1)分配一个输入子系统
    (2)设置能产生哪类事件
    (3)设置能产生这类事件的哪些事件
    (4)注册
    (5)硬件相关的操作,定时器相关操作,申请中断等
2、使用
input_allocate_device函数进行分配
      
input_dev结构体的重要成员

点击(此处)折叠或打开

  1. struct input_dev {
  2.     const char *name;
  3.     const char *phys;
  4.     const char *uniq;
  5.     struct input_id id;
  6.   
  7.     unsigned long evbit[NBITS(EV_MAX)]; // 表示能产生哪类事件
  8.     unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键
  9.     unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮
  10.     unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y
  11.     unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
  12.     unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
  13.     unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
  14.     unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
  15.     ...
  16. }
3、设置产生哪类事件和这类事件的哪些操作,在下面的代码中有的
4、有如下这些个类

点击(此处)折叠或打开

  1. #define EV_SYN 0x00 //同步类
  2. #define EV_KEY 0x01 //按键类
  3. #define EV_REL 0x02 //相对位移类
  4. #define EV_ABS 0x03 //绝对位移类
  5. #define EV_MSC 0x04
  6. #define EV_SW 0x05
  7. #define EV_LED 0x11
  8. #define EV_SND 0x12 //声音类
  9. #define EV_REP 0x14 //重复类
  10. #define EV_FF 0x15
  11. #define EV_PWR 0x16
5、最后使用
input_register_device(struct input_dev *dev)函数来注册
6、驱动源码:
包括如下主要的函数
(1)最主要的是驱动入口函数static int buttons_input_init(void),功能包括以上四步,申请Input子系统、设置、注册、硬件等,还包括定时器的初始化和初始化中断request_irq()
(2)驱动出口函数包括:释放中断、删除定时器、删除设备等
(3)定时器处理函数:
static void buttons_timer_function(unsigned long data) ,可根据传入的参数对功能进行判断。
(4)用户中断处理函数
static irqreturn_t buttons_irq(int irq, void *dev_id) ,仅仅是增加定时器的时间。具体的处理在void_timer_function()函数中实现。


点击(此处)折叠或打开

  1. static struct pin_desc{
  2.     int irq;
  3.     unsigned char *name;
  4.     unsigned int pin;
  5.     unsigned int key_val;
  6. };
  7.   
  8. static struct pin_desc pins_desc[4] = {
  9.         {IRQ_EINT1,"K1",S3C2410_GPF1,KEY_L},
  10.         {IRQ_EINT4,"K2",S3C2410_GPF4,KEY_S},
  11.         {IRQ_EINT2,"K3",S3C2410_GPF2,KEY_ENTER},
  12.         {IRQ_EINT0,"K4",S3C2410_GPF0,KEY_LEFTSHIFT},
  13. };
  14.   
  15. static struct pin_desc *irq_pd;
  16. static struct input_dev *buttons_dev;
  17. static struct timer_list buttons_timer;
  18.   
  19. /* 用户中断处理函数 */
  20. static irqreturn_t buttons_irq(int irq, void *dev_id)
  21. {
  22.     irq_pd = (struct pin_desc *)dev_id;
  23.       
  24.     /* 修改定时器定时时间,定时10ms,即10秒后启动定时器
  25.      * HZ 表示100个jiffies,jiffies的单位为10ms,即HZ = 100*10ms = 1s
  26.      * 这里HZ/100即定时10ms
  27.      */
  28.     mod_timer(&buttons_timer, jiffies + (HZ /100));
  29.     return IRQ_HANDLED;
  30. }
  31.   
  32.   
  33. /* 定时器处理函数 */
  34. static void buttons_timer_function(unsigned long data)
  35. {
  36.     struct pin_desc *pindesc = irq_pd;
  37.     unsigned int pinval;
  38.     pinval = s3c2410_gpio_getpin(pindesc->pin);
  39.   
  40.     if(pinval)
  41.     {
  42.         /* 松开 最后一个参数: 0-松开, 1-按下 */
  43.         input_event(buttons_dev,EV_KEY,pindesc->key_val,0);
  44.         input_sync(buttons_dev);
  45.     }
  46.     else
  47.     {
  48.         /* 按下 */
  49.         input_event(buttons_dev,EV_KEY,pindesc->key_val,1);
  50.         input_sync(buttons_dev);
  51.     }
  52. }
  53.   
  54. /* 驱动入口函数 */
  55. static int buttons_input_init(void)
  56. {
  57.     int i;
  58.       
  59.     /* 1.分配一个input_dev结构体 */
  60.     buttons_dev = input_allocate_device();
  61.   
  62.     /* 2.设置 */
  63.     /* 2.1 设置按键能产生哪类事件 */
  64.     set_bit(EV_KEY,buttons_dev->evbit);
  65.     set_bit(EV_REP,buttons_dev->evbit);
  66.   
  67.     /* 2.2 设置能产生这类操作的哪些事件 */
  68.     set_bit(KEY_L,buttons_dev->keybit);
  69.     set_bit(KEY_S,buttons_dev->keybit);
  70.     set_bit(KEY_ENTER,buttons_dev->keybit);
  71.     set_bit(KEY_LEFTSHIFT,buttons_dev->keybit);
  72.       
  73.     /* 3.注册 */
  74.     input_register_device(buttons_dev);
  75.   
  76.       
  77.     /* 4.硬件相关的设置 */
  78.     /* 4.1 定时器相关的操作 */
  79.     init_timer(&buttons_timer);
  80.     buttons_timer.function = buttons_timer_function;
  81.     add_timer(&buttons_timer);
  82.   
  83.     /* 4.2 申请中断 */
  84.     for(i = 0;i < sizeof(pins_desc)/sizeof(pins_desc[0]);i++)
  85.     {
  86.         request_irq(pins_desc[i].irq, buttons_irq, IRQ_TYPE_EDGE_BOTH, pins_desc[i].name, &pins_desc[i]);
  87.     }
  88.       
  89.     return 0;
  90. }
  91.   
  92. /* 驱动出口函数 */
  93. static void buttons_input_exit(void)
  94. {
  95.     int i;
  96.     for(i = 0;i < sizeof(pins_desc)/sizeof(pins_desc[0]);i++)
  97.     {
  98.         free_irq(pins_desc[i].irq, &pins_desc[i]);
  99.     }
  100.     del_timer(&buttons_timer);
  101.     input_unregister_device(buttons_dev);
  102.     input_free_device(buttons_dev);
  103. }
  104.   
  105. module_init(buttons_input_init); //用于修饰入口函数
  106. module_exit(buttons_input_exit); //用于修饰出口函数
  107.   
  108. MODULE_AUTHOR("LWJ");
  109. MODULE_DESCRIPTION("Just for Demon");
  110. MODULE_LICENSE("GPL"); //遵循GPL协议

阅读(1735) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~