输入子系统(四)

好记性不如烂笔头,整理一下笔记~ Linux驱动之输入子系统框架


输入子系统将该类驱动划分为3部分
    1、核心层 input.c
    2、设备层 Gpio_keys.c ...
    3、事件处理层 Evdev.c

    事件处理层为纯软件的东西,设备层涉及底层硬件,它们通过核心层建立联系,对外提供open write等接口。


1、我们首先来看,核心层 input.c如何向外界提供接口

    在 input_init 中注册了字符设备驱动
    register_chrdev(INPUT_MAJOR, "input", &input_fops);

    static const struct file_operations input_fops = {
        .owner = THIS_MODULE,
        .open = input_open_file,
    };

    在注册的fops中,仅仅只有1个open函数,下面我们来看这个open函数
    static int input_open_file(struct inode *inode, struct file *file)
    {
        // 根据次设备号,从 input_table 数组中取出对应的 handler
        struct input_handler *handler = input_table[iminor(inode) >> 5];
        const struct file_operations *old_fops, *new_fops = NULL;

        // 将 handler 的fops赋值给file->f_op,并用调用新的open函数
        old_fops = file->f_op;
        file->f_op = fops_get(handler->fops);
        err = file->f_op->open(inode, file);
    }
    那么,我们应该可以猜到,必定有个地方创建了handler并对它进行一定的设置,提供fops函数,将它放入input_table。
    就这样,Input.c 实现了一个通用对外接口。

2、事件处理层,注册input_handler 
    2.1 放入链表、数组(input_register_handler)
    input.c input_register_handler 函数中 创建了handler并对它进行一定的设置,提供fops函数,将它放入input_table,

    int input_register_handler(struct input_handler *handler)
    {
        // 将 handler 放入 input_table
        input_table[handler->minor >> 5] = handler;

        // 将 handler 放入 input_handler_list 链表
        list_add_tail(&handler->node, &input_handler_list);

        // 取出 input_dev_list 链表中的每一个 dev 与 该 handler 进行 比对
        list_for_each_entry(dev, &input_dev_list, node)
        input_attach_handler(dev, handler);
    }

    以 evdev.c 为例

    static struct input_handler evdev_handler = {
        .event = evdev_event,

        .connect = evdev_connect,

        .disconnect = evdev_disconnect,
        .fops  = &evdev_fops,
        .minor = EVDEV_MINOR_BASE,
        .name  = "evdev",
        .id_table= evdev_ids,
    };
    static int __init evdev_init(void)
    {
        return input_register_handler(&evdev_handler);
    }

    // 读函数中 如果没有事件上报休眠,等待上报事件 唤醒休眠,将事件传送到用户空间
    static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
    {
        //如果无数据可读,且为非阻塞方式 立刻返回
        if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
        return -EAGAIN;
        //否则,进入休眠
        retval = wait_event_interruptible(evdev->wait,
        client->head != client->tail || !evdev->exist);
        //将内核空间数据拷贝到用户空间,略
        return retval;
    }
    2.2 匹配 (input_attach_handler)
    static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
    {
        // 看 dev.id 是否存在于 handler->id_table 中
        id = input_match_device(handler->id_table, dev);
        if (!id)
        return -ENODEV;
        // 在的话,调用 handler->connect
        error = handler->connect(handler, dev, id);
    }
    2.3 建立连接
    我们以 Evdev.c 为例,看一下connect函数
    static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
    const struct input_device_id *id)
    {
        // 不要关心 evdev ,只看 evdev->handle 即可,这里构建了一个 handle ,注意不是handler
        // handle 就是个 中间件,可以理解成胶带,它把 hander 与 dev 连在一起
        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

        // 第一次建立联系,在 handle 中记录 dev 与 handle 的信息,这样通过handle就可以找到dev与handler
        // 即是 实现 handle -> dev   handle -> hander 的联系
        evdev->handle.dev = dev;
        evdev->handle.handler = handler;

        // 申请设备号,创建设备节点
        devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),

        dev_set_name(&evdev->dev, "event%d", minor);

        // 在input 类下面创建设备,文件夹的名字是 evdev->name ->inputn ,设备名是 dev->cdev.dev.name -> eventn
        cdev = class_device_create(&input_class, &dev->cdev, devt,
        dev->cdev.dev, evdev->name);
        // 注册 handle
        error = input_register_handle(&evdev->handle);
    }
    2.4 注册handle,第二次建立联系
    int input_register_handle(struct input_handle *handle)
    {
        struct input_handler *handler = handle->handler;
        // 将handle 记录在 dev->h_list 中
        list_add_tail(&handle->d_node, &handle->dev->h_list);
        // 将handle 记录在 handler->h_list 中
        list_add_tail(&handle->h_node, &handler->h_list);
        // 至此,dev 与 hander 也可以找到handle了,dev <-> handle <-> handler 之间畅通无阻
    }
简单梳理一下:
    事件处理层,构建 handler , 通过 input_register_handler 进行注册,注册时
    1、将 handler 放入 input_handler_list 链表
    2、将 handler 放入 input_table
    3、取出 input_dev_list链表中的每一个dev 调用 input_attach_handler 进行id匹配
    4、如果匹配成功,则调用 handler->connect 第一次建立连接
    5、创建 handle 在 handle 中记录 dev 与 handler 的信息,这样通过handle就可以找到dev与handler
    6、在dev hander 中记录 handle的信息,实现 dev <-> handle <-> handler


3、设备层,注册input_dev
    int input_register_device(struct input_dev *dev)
    {
        // 将 dev 放入 input_dev_list
        list_add_tail(&dev->node, &input_dev_list);
        // 设置 设备名?所谓的input0 input1 由此而来吧
        snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
        error = device_add(&dev->cdev);
        // 匹配 handler ,参考 2.2-2.4
        list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);
    }


4、辛辛苦苦建立联系,是干嘛的
    在设备层,我们写驱动的时候,比如鼠标按了一下,我们要上报event 到Handler层进行处理,然后提交给用户程序。
    例如:Gpio_keys.c 中断处理函数中
    static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
    {
        input_event(input, type, button->code, !!state);
        input_sync(input);
        return IRQ_HANDLED;
    }
    又得回到input.c void input_event函数
    void input_event

        input_handle_event(dev, type, code, value);

            if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
    dev->event(dev, type, code, value);

            if (disposition & INPUT_PASS_TO_HANDLERS)
    input_pass_event(dev, type, code, value);
                        list_for_each_entry(handle, &dev->h_list, d_node)
                            if (handle->open)
                                handle->handler->event(handle, type, code, value);
    例如LED类事件,dev中定义了event 会先调用dev->event
    最终调用 handler->event(handle, type, code, value);
    好吧,Evdev.c 中的 event 函数看不懂。
    static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
    {
        ......看不懂
        // 唤醒 休眠
        wake_up_interruptible(&evdev->wait);
    }

5、写一个Input子系统 设备驱动
    事件处理层不用我们管了,- -是暂时能力有限管不了。写写设备层的程序就好了。
    软件设计流程:
    /* 1. 分配一个Input_dev结构体 */
    /* 2. 设置 支持哪一类事件,该类事件里的那些事件*/
    /* 3.注册 */
    /* 4.硬件相关操作 */


事件类型:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. struct input_dev {  
  2.   
  3.         void *private;                           //输入设备私有指针,一般指向用于描述设备驱动层的设备结构  
  4.   
  5.         const char *name;                        //提供给用户的输入设备的名称  
  6.         const char *phys;                        //提供给编程者的设备节点的名称  
  7.         const char *uniq;                        //指定唯一的ID号,就像MAC地址一样  
  8.         struct input_id id;                      //输入设备标识ID,用于和事件处理层进行匹配  
  9.   
  10.         unsigned long evbit[NBITS(EV_MAX)];      //位图,记录设备支持的事件类型  
  11.         /* 
  12.          *  #define EV_SYN          0x00    //同步事件 
  13.          *  #define EV_KEY          0x01    //按键事件 
  14.          *  #define EV_REL          0x02    //相对坐标 
  15.          *  #define EV_ABS          0x03    //绝对坐标 
  16.          *  #define EV_MSC          0x04    //其它 
  17.          *  #define EV_SW           0x05    //开关事件 
  18.          *  #define EV_LED          0x11    //LED事件 
  19.          *  #define EV_SND          0x12 
  20.          *  #define EV_REP          0x14<span style="white-space:pre">    </span>//重复上报 
  21.          *  #define EV_FF           0x15 
  22.          *  #define EV_PWR          0x16 
  23.          *  #define EV_FF_STATUS    0x17 
  24.          *  #define EV_MAX          0x1f 
  25.          */  
  26.         unsigned long keybit[NBITS(KEY_MAX)];    //位图,记录设备支持的按键类型  
  27.         unsigned long relbit[NBITS(REL_MAX)];    //位图,记录设备支持的相对坐标  
  28.         unsigned long absbit[NBITS(ABS_MAX)];    //位图,记录设备支持的绝对坐标  
  29.         unsigned long mscbit[NBITS(MSC_MAX)];    //位图,记录设备支持的其他功能  
  30.         unsigned long ledbit[NBITS(LED_MAX)];    //位图,记录设备支持的指示灯  
  31.         unsigned long sndbit[NBITS(SND_MAX)];    //位图,记录设备支持的声音或警报  
  32.         unsigned long ffbit[NBITS(FF_MAX)];      //位图,记录设备支持的作用力功能  
  33.         unsigned long swbit[NBITS(SW_MAX)];      //位图,记录设备支持的开关功能  
  34.   
  35.         unsigned int keycodemax;                //设备支持的最大按键值个数  
  36.         unsigned int keycodesize;               //每个按键的字节大小  
  37.         void *keycode;                          //指向按键池,即指向按键值数组首地址  
  38.         int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);        //修改按键值  
  39.         int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);       //获取按键值  
  40.   
  41.         struct ff_device *ff;                          
  42.   
  43.         unsigned int repeat_key;                //支持重复按键  
  44.         struct timer_list timer;                //设置当有连击时的延时定时器  
  45.   
  46.         int state;                  
  47.   
  48.         int sync;       //同步事件完成标识,为1说明事件同步完成  
  49.   
  50.         int abs[ABS_MAX + 1];                //记录坐标的值  
  51.         int rep[REP_MAX + 1];                //记录重复按键的参数值  
  52.   
  53.         unsigned long key[NBITS(KEY_MAX)];   //位图,按键的状态  
  54.         unsigned long led[NBITS(LED_MAX)];   //位图,led的状态  
  55.         unsigned long snd[NBITS(SND_MAX)];   //位图,声音的状态  
  56.         unsigned long sw[NBITS(SW_MAX)];     //位图,开关的状态  
  57.   
  58.         int absmax[ABS_MAX + 1];             //位图,记录坐标的最大值  
  59.         int absmin[ABS_MAX + 1];             //位图,记录坐标的最小值  
  60.         int absfuzz[ABS_MAX + 1];            //位图,记录坐标的分辨率  
  61.         int absflat[ABS_MAX + 1];            //位图,记录坐标的基准值  
  62.   
  63.         int (*open)(struct input_dev *dev);                         //输入设备打开函数  
  64.         void (*close)(struct input_dev *dev);                       //输入设备关闭函数  
  65.         int (*flush)(struct input_dev *dev, struct file *file);     //输入设备断开后刷新函数  
  66.         int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);        //事件处理  
  67.   
  68.         struct input_handle *grab;              
  69.   
  70.         struct mutex mutex;                     //用于open、close函数的连续访问互斥  
  71.         unsigned int users;                  
  72.   
  73.         struct class_device cdev;               //输入设备的类信息  
  74.         union {                                 //设备结构体  
  75.                 struct device *parent;  
  76.         } dev;  
  77.   
  78.         struct list_head        h_list;         //handle链表  
  79.         struct list_head        node;           //input_dev链表  
  80. };  



参考程序:基于mini2440 linux2.6.32内核
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/kernel.h>  
  3. #include <linux/fs.h>  
  4. #include <linux/init.h>  
  5. #include <linux/device.h>  
  6. #include <linux/interrupt.h>  
  7. #include <linux/sched.h>   
  8. #include <linux/irq.h>  
  9. #include <asm/mach/irq.h>  
  10. #include <asm/irq.h>  
  11. #include <asm/uaccess.h>  
  12. #include <mach/gpio-fns.h>  //s3c2410_gpio_getpin  
  13. #include <mach/gpio-nrs.h>    //S3C2410_GPG(x)  
  14. #include <mach/regs-gpio.h>  
  15. #include <linux/poll.h>  
  16. #include <asm/atomic.h>  
  17. #include <linux/spinlock.h>  
  18. #include <linux/input.h>  
  19.   
  20. //中断触发方式的 一些宏定义  
  21. #define __IRQT_FALEDGE  IRQ_TYPE_EDGE_FALLING  
  22. #define __IRQT_RISEDGE  IRQ_TYPE_EDGE_RISING  
  23. #define __IRQT_LOWLVL   IRQ_TYPE_LEVEL_LOW  
  24. #define __IRQT_HIGHLVL  IRQ_TYPE_LEVEL_HIGH  
  25.   
  26. #define IRQT_NOEDGE (0)  
  27. #define IRQT_RISING (__IRQT_RISEDGE)  
  28. #define IRQT_FALLING    (__IRQT_FALEDGE)  
  29. #define IRQT_BOTHEDGE   (__IRQT_RISEDGE|__IRQT_FALEDGE)  
  30. #define IRQT_LOW    (__IRQT_LOWLVL)  
  31. #define IRQT_HIGH   (__IRQT_HIGHLVL)  
  32. #define IRQT_PROBE  IRQ_TYPE_PROBE  
  33.   
  34. #define DEBUG printk(KERN_ERR "%d\n",__LINE__)  
  35.   
  36. static struct input_dev *buttons_dev = NULL;    /* 创建input_dev结构体指针 */  
  37.   
  38. static struct timer_list buttons_timer; //定时器去抖动  
  39.   
  40. struct keys_desc{  
  41.     unsigned int irq;  
  42.     unsigned char *name;  
  43.     unsigned int key_addr;  
  44.     unsigned char key_value;  
  45.     int pin_state;  
  46. };  
  47.   
  48. static struct keys_desc *key_desc = NULL;  
  49.   
  50. struct keys_desc keys_desc[6]={  
  51.     {IRQ_EINT8,  "S1", S3C2410_GPG(0)  ,KEY_L ,S3C2410_GPG0_EINT8},  
  52.     {IRQ_EINT11, "S2", S3C2410_GPG(3)  ,KEY_S ,S3C2410_GPG3_EINT11},  
  53.     {IRQ_EINT13, "S3", S3C2410_GPG(5)  ,KEY_ENTER ,S3C2410_GPG5_EINT13},  
  54.     {IRQ_EINT14, "S4", S3C2410_GPG(6)  ,KEY_1 ,S3C2410_GPG6_EINT14},  
  55.     {IRQ_EINT15, "S5", S3C2410_GPG(7)  ,KEY_2 ,S3C2410_GPG7_EINT15},  
  56.     {IRQ_EINT19, "S6", S3C2410_GPG(11) ,KEY_3 ,S3C2410_GPG11_EINT19},  
  57. };  
  58.   
  59.   
  60. static irqreturn_t buttons_irq(int irq, void *dev_id)  
  61. {  
  62.     key_desc = (struct keys_desc *)dev_id;  
  63.     mod_timer(&buttons_timer, jiffies+HZ/100);  
  64.   
  65.     return IRQ_RETVAL(IRQ_HANDLED);  
  66. }  
  67.   
  68. static void buttons_timer_function(unsigned long data){  
  69.     DEBUG;    
  70.     unsigned int keyval;  
  71.   
  72.     if(key_desc == NULL) return;      
  73.     DEBUG;  
  74.     //定时器启动的时候会首先中断一次,因为没设置时间默认为0  
  75.     keyval = s3c2410_gpio_getpin(key_desc->key_addr);  
  76.     DEBUG;  
  77.     printk("keyval: %d\n", keyval);  
  78.     if (keyval)  
  79.     {  
  80.         DEBUG;  
  81.           
  82.         input_event(buttons_dev, EV_KEY ,key_desc->key_value ,0);  
  83.         input_sync(buttons_dev);  
  84.     }  
  85.     else  
  86.     {  
  87.         DEBUG;  
  88.           
  89.         input_event(buttons_dev, EV_KEY ,key_desc->key_value ,1);  
  90.         input_sync(buttons_dev);  
  91.     }  
  92. }  
  93.   
  94. static int __init input_drv_init(void){  
  95.     int error,i;  
  96.       
  97.     DEBUG;  
  98.       
  99. /* 1. 分配一个Input_dev结构体 */  
  100.     buttons_dev = input_allocate_device();  
  101.     if(buttons_dev == NULL){  
  102.         printk(KERN_ERR "Unable to allocate input device\n");  
  103.     }  
  104.       
  105. /* 2. 设置 */  
  106.     set_bit(EV_KEY, buttons_dev->evbit);     //设置设备支持的事件类型为按键类型  
  107.     set_bit(KEY_L, buttons_dev->keybit); //设置支持哪些 按键类型   
  108.     set_bit(KEY_S, buttons_dev->keybit); //设置支持哪些 按键类型   
  109.     set_bit(KEY_ENTER, buttons_dev->keybit);//设置支持哪些 按键类型   
  110.     set_bit(KEY_1, buttons_dev->keybit); //设置支持哪些 按键类型   
  111.     set_bit(KEY_2, buttons_dev->keybit); //设置支持哪些 按键类型   
  112.     set_bit(KEY_3, buttons_dev->keybit); //设置支持哪些 按键类型   
  113.   
  114. /* 3.注册 */  
  115.     error = input_register_device(buttons_dev);  
  116.     if (error) {  
  117.         printk(KERN_ERR "Unable to register gpio-keys input device\n");  
  118.     }  
  119.       
  120. /* 4.硬件相关操作 */  
  121.     /* 定时器 */  
  122.     init_timer(&buttons_timer); //初始化定时器  
  123.     buttons_timer.function = buttons_timer_function;//绑定定时器处理函数   
  124.     add_timer(&buttons_timer);//将定时器加到timer列表中去,启动定时器  
  125.     /* 注册中断 */  
  126.     for(i = 0; i < 6; i++){  
  127.         s3c2410_gpio_cfgpin(keys_desc[i].key_addr, keys_desc[i].pin_state);//新增  
  128.         request_irq(keys_desc[i].irq,  buttons_irq, IRQT_BOTHEDGE,   
  129.             keys_desc[i].name, &keys_desc[i]);  
  130.     }  
  131.     DEBUG;  
  132.     return 0;  
  133. }  
  134.   
  135. static void __exit input_drv_exit(void){  
  136.     int i;  
  137.     for(i=0; i<6; i++){  
  138.         free_irq(keys_desc[i].irq, &keys_desc[i]);  
  139.     }  
  140.     del_timer(&buttons_timer);  
  141.     input_unregister_device(buttons_dev);  
  142.     input_free_device(buttons_dev);//新增  
  143.     DEBUG;  
  144. }  
  145.   
  146. module_init(input_drv_init);  
  147. module_exit(input_drv_exit);  
  148. MODULE_LICENSE("GPL");  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值