[Linux 基础] -- 韦东山第二期视频 input输入子系统总结

概述:

    输入子系统有三个核心结构体:input_dev,  input_handle,  input_handler:

  • input_dev 表示一个输入设备,包含输入设备的一些相关信息;
  • input_handler 表示对输入事件的具体处理,它为输入设备的功能实现了一个接口;
  • input_handle 用来连接输入设备和输入事件。

    输入子系统的主要任务就是把这三个结构体连接在一起。

在input_register_device 与 input_register_handler 两个注册函数中会两两比较 input_handler_list 中的每一项input_handler 和 input_dev 中的每一项input_dev。input_handler中的blacklist和 id_table 判断这个设备是否支持这个handler ,如果支持则调用input_handler的connect函数建立连接。

 

1、app程序并不一定会去指定驱动设备,而是直接调用c库函数接口
   如scanf,printf,gets进行操作,为了实现这样的操作驱动程序
   需要用到input子系统。
   
   输入设备包括按键,键盘,触摸屏,鼠标,摇杆等等,
   它们本身都是字符设备,不过内核为了能将这些设备的共性抽象出来,
   简化驱动的开发,建立了一个Input子系统。
   
   Input子系统分为三层,设备层、核心层、事件驱动层.
   核心层、事件层都是内核已经完成的,需要我们完成是设备层。
   
   输入设备主要的工作过程都是 动作产生(按键,触屏……)-->产生中断-->读取数值(键值,坐标……)-->将数值传递给应用程序。
   
   事件的处理:input device向上层报告-->input core接收报告,并根据在注册input device时建立
   好的连接选择哪一类handler来处理事件-->通过handler将数据存放在相应的dev(evdev,mousedev…)实例的缓冲区中,等待应用程序来读取。
   
   有时候也需要从应用层向设备层逆向传递,比如控制一些和设备相关的LED,蜂鸣器等。

2、回忆字符驱动框架的写法
    a、编写drv_open,drv_write,drv_read等驱动接口函数
       并声明一个major主设备号变量,(或自己指定)。
    b、创建一个struct file_operation fops结构,
       并用drv_open,drv_write,drv_read等进行填充。
    c、用register_chrdev()进行注册fops结构。
    d、用入口函数调用执行第c步操作,并用calss_create和class_device_create
       在sysfs下写一个class类和设备相关信息,利用udev(mdev)机制进行自动生成设备节点。
    e、出口函数,unregister_chrdev,class_device_unregister,class_destroy等释放资源。

3、input子系统的架构
input_init    > register_chrdev(INPUT_MAJOR, "input", &input_fops); //注册file_operation结构

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

问:input子系统怎么read按键?            
input_open_file
    struct input_handler *handler = input_table[iminor(inode) >> 5];
    new_fops = fops_get(handler->fops)
    ......
    file->f_op = new_fops;   
    err = new_fops->open(inode, file);
    ......
app:open
kernel:>...> input_open_file > file->new_fops->open     
app:read
kernel: >...>file->new_fops->read

问:由谁来构造input_table[]结构?

注册一个input_handler结构
input_register_handler(struct input_handler *handler)
    struct input_dev *dev;
    
    input_table[handler->minor >> 5] = handler;          //把handler添加到input_table中
    list_add_tail(&handler->node, &input_handler_list);  //把handler添加到input_handler_list链表中
    
    list_for_each_entry(dev, &input_dev_list, node)      //遍历input_dev_list链表,
        input_attach_handler(dev, handler);
            //匹配handler中不支持的dev(设备),存在返回-ENODEV
            if (handler->blacklist && input_match_device(handler->blacklist, dev))
                return -ENODEV;
            //匹配handler中支持的dev,如果存在就进行connect
            id = input_match_device(handler->id_table, dev);
            if (!id)
                return -ENODEV;
            handler->connect(handler, dev, id);          

注册输入设备
int input_register_device(struct input_dev *dev)
    struct input_handler *handler;
    list_add_tail(&dev->node, &input_dev_list);          //把dev添加到input_dev_list链表中
    ......
    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);
        //匹配handler中不支持的dev(设备),存在返回-ENODEV
        if (handler->blacklist && input_match_device(handler->blacklist, dev))
            return -ENODEV;
        //匹配handler中支持的dev,如果存在就进行connect
        id = input_match_device(handler->id_table, dev);
        if (!id)
            return -ENODEV;
        handler->connect(handler, dev, id);  

总结:注册input_dev和input_handler时,会两两比较input_handler_list中的每一项input_handler和
     input_dev_list中的每一项input_dev。 input_handler中的blacklist和id_table判断这个设备是否
     支持这个handler,如果支持则调用input_handler的函数建立连接。
    
问:谁调用input_register_handler进行注册?
猜想:肯定是事件层,也就是input_handler这层。
Evdev.c
Joydev.c
Keyboard.c
Mousedev.c
Tsdev.c
以Evdev.c为例:
evdev_init
    input_register_handler(&evdev_handler);
    
static struct input_handler evdev_handler = {
    .event =    evdev_event,
    .connect =    evdev_connect,   //建立input_handler和input_dev的连接
    .disconnect =    evdev_disconnect,
    .fops =        &evdev_fops,    //file_operation结构,用于input子系统调用的
    .minor =    EVDEV_MINOR_BASE,
    .name =        "evdev",
    .id_table =    evdev_ids,      //input_handler支持的dev
};


static const struct input_device_id evdev_ids[] = {
    { .driver_info = 1 },    
    { },            
};

总结:由事件层进行调用,并且定义一个input_handler结构,指定.fops
     、.conncect、.id_table等,通过input_register_handler进行注册。


问:input_handler和input_dev怎么进行连接?
evdev_connect
    
    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
    ......
    evdev->handle.dev = dev;          //指向左边的input_dev
    evdev->handle.name = evdev->name;
    evdev->handle.handler = handler;  //指向右边的input_handler
    evdev->handle.private = evdev;
    ......
    input_register_handle(&evdev->handle); //注册input_handle结构
        list_add_tail(&handle->d_node, &handle->dev->h_list);   //把input_dev中的h_list指向input_handle
        list_add_tail(&handle->h_node, &handler->h_list);       //把input_handler中的h_list指向input_handle

总结:
1、分配一个input_handle结构
2、input_handle.dev = input_dev   
   input_handle.handler = input_handler
3、input_dev.h_list = input_handle.d_node
   input_handler.h_list = input_handle.h_node        
        


问:怎么读取按键?
以Evdev.c为例:
app:read
-----------------------------
    ......
        evdev_read
            //当前没有数据,并且如果事件为非阻塞方式读,则返回
            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);

问:谁来唤醒?
evdev_event
    wake_up_interruptible(&evdev->wait); //唤醒
    
问:谁来调用evdev_event
猜想:应该有是硬件相关的程序调用,input_dev那层。
在设备相关的中断服务程序里面,确定事件产生,则调用相应的input_handler的event处理函数。
   gpio_keys.c实例;
gpio_keys_isr  //中断服务程序
    //如果有按键按下,触发中断,然后调用input_event
    input_event(input, type, button->code, !!state);
    input_sync(input);
    
input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
    list_for_each_entry(handle, &dev->h_list, d_node)  //遍历设备相关的handle
        if (handle->open)
            handle->handler->event(handle, type, code, value);  //调用与之相关的handler->event函数

问:该怎么写输入子系统的驱动程序?
1、分配一个struct input_dev* input结构,并填充。
2、设置
3、注册input_register_device
4、硬件相关代码。(中断服务程序中调用input_event进行投递信息给input子系统进行事件的响应)

        
语句分析:
总结:   list_for_each_entry(pos, head, member)的作用遍历head指向的结构体成员member所包含的和(*pos)相同类型的结构体变量


#define list_for_each_entry(pos, head, member)                \  
    for (pos = list_entry((head)->next, typeof(*pos), member);    \
         prefetch(pos->member.next), &pos->member != (head);     \
         pos = list_entry(pos->member.next, typeof(*pos), member))

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)

#define container_of(ptr, type, member) ({            \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

list_for_each_entry(dev, &input_dev_list, node)
    for (dev = list_entry((input_dev_list)->next, typeof(*dev), node);    \
        dev = ({            \
            const typeof( ((typeof(*dev) *)0)->node ) *__mptr = ((input_dev_list)->next);    \
            (typeof(*dev) *)( (char *)__mptr - offsetof(typeof(*dev), node) );})
                                             ((size_t) &((typeof(*dev) *)0)->node)
             prefetch(dev->node.next), &dev->node != (input_dev_list);     \
             dev = list_entry(dev->node.next, typeof(*dev), node))
            const typeof( ((typeof(*dev)*)0)->node ) *__mptr = (dev->node.next);    \
            (typeof(*dev)*)( (char *)__mptr - offsetof(typeof(*dev),node) );})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值