input输入子系统框架分析(汇顶GT911)

input输入子系统框架分析(汇顶GT911)_第1张图片

1.框架,文件路径和内核选择:

 

event handler层:为应用层提供编程接口,接受设备驱动层的上报的数据,属于应用空间和设备驱动之间的桥梁。

input core层:负责管理输入设备,为输入设备驱动层提供各种接口,比如注册注销,上报数据方式等等。

driver层:主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。

基本逻辑过程:

input设备产生一个input event,但是只是产生并没有处理,而是交给handler进行处理。

各个源码路径:

event handler层:

drivers/input/input.c  :用于管理输入设备,提供接口代码

input-compat.c:用于与应用层进行数据交互的代码。

input-mt.c:force-feedbac力感反馈。

以上多个文件共同编译成input-core

输入设备驱动层:

drivers/input/keyboard/

drivers/input/touchscreen/

drivers/input/joystick/

内核驱动选择:

input输入子系统框架分析(汇顶GT911)_第2张图片

 

 

 

2.各个层分析:

 

Evdev.c (drivers\input)    


module_init(evdev_init);
module_exit(evdev_exit);

static int __init evdev_init(void)
{
    return input_register_handler(&evdev_handler);
}

input.c层(核心层)
 input_register_handler(struct input_handler *handler)
    |
    // 初始化handler链表
    INIT_LIST_HEAD(&handler->h_list);
    //现将handler保存到input_table数组中,minor一般都是32的倍数
    // 注意,这个在input.c核心代码中有用到
    input_table[handler->minor >> 5] = handler;

    //并将handler->node加入到input_handler_list链表中
    list_add_tail(&handler->node, &input_handler_list);
    
    // 通过handler找到对应的输入设备
    list_for_each_entry(dev, &input_dev_list, node)
        input_attach_handler(dev, handler);-----------------------------------
                                             |
    //更新//proc中的数据                                                         |
    input_wakeup_procfs_readers();                                               |
                                                     |
                                                                                     |
input_attach_handler(dev, handler); <-------------------------------------------------
    |
    //比对设备id列表
    list_for_each_entry(dev, &input_dev_list, node)
        id = input_match_device(handler, dev);------------------------
    // 如果比对成功,那么执行handler的connect函数                        |
    handler->connect(handler, dev, id);                                  |
                                                                             |
//比对方法                                     |
static const struct input_device_id evdev_ids[] = {                          |
    { .driver_info = 1 },    /* Matches all devices */                    |
    { },            /* Terminating zero entry */                 |
};                                         |
                                         |
input_match_device(handler, dev);<-------------------------------------------|
    |  //id表示的是hander中的id_table,那这里面一般有什么呢
    // 在evdev_ids中兼容所有的设备
    for (id = handler->id_table; id->flags || id->driver_info; id++)
    /*以下有几种比对方式:
        1, 比较id->flags: 前提就是设置了flags,如果没有设置,就不比较
            总线INPUT_DEVICE_ID_MATCH_BUS,id->bustype != dev->id.bustype
            厂商:INPUT_DEVICE_ID_MATCH_VENDOR,id->vendor != dev->id.vendor
            产品id:INPUT_DEVICE_ID_MATCH_PRODUCT,id->product != dev->id.product
            版本:INPUT_DEVICE_ID_MATCH_VERSION,id->version != dev->id.version
        2,比较设置产生的事件类型:比如有evbit,keybit,absbit
            //就是将dev->id和handler->id中evbit/keybit/absbit数组进行位与预算
            MATCH_BIT(evbit,  EV_MAX);        
        
        3,如果hander有match函数,那么就执行match函数,以下这种写法很神奇
            if (!handler->match || handler->match(handler, dev))
                return id; // 代码能执行到这里就表示找到了
    */
    总结:1,将构建的handler加入到input_handler_list中
          2,并匹配input_dev_list中的device,device的类型为struct input_dev
          3, 匹配的依据是各自对象中的id结构体,匹配成功后会执行handler中的connect函数
          4,对于input设备对象的驱动代码中,需要设置evbit/keybit/absbit等数组中的位,用于进行比对

------------------------------------------------------------------------------------------------------------
hander对象中的connect函数:
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,
};
//参数1,handler对象本身 2,匹配成功后的input设备对象 3,id列表
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
    |
    struct evdev *evdev;
    
    // 在evdev_table数组找到一个没有使用的位置
    for (minor = 0; minor < EVDEV_MINORS; minor++)
        if (!evdev_table[minor]);

    //分配一个evdev 对象
    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
    
    //  初始化列表,用于记录????
    INIT_LIST_HEAD(&evdev->client_list);
    //初始化等待队列
    init_waitqueue_head(&evdev->wait);

    //设置evdev的名字,用于用户空间的设备节点
    dev_set_name(&evdev->dev, "event%d", minor);

    evdev->exist = true;
    evdev->minor = minor;
    // 此时出现了struct input_handle
    // struct input_handle会记录input设备是哪个
    evdev->handle.dev = input_get_device(dev);
    evdev->handle.name = dev_name(&evdev->dev);
    //同时handle也会记录handler是哪个
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;
    
    //设置input设备的设备号,次设备号从64开始
    evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
    evdev->dev.class = &input_class;
    evdev->dev.parent = &dev->dev;
    evdev->dev.release = evdev_free;
    device_initialize(&evdev->dev);

    // 注册handle,实际就是将input设备对象加入到handle中的handle->d_node链表中
    // 将handler加入到handle中的handle->h_node链表中
    input_register_handle(&evdev->handle);
            |
            list_add_tail_rcu(&handle->d_node, &dev->h_list);
            list_add_tail_rcu(&handle->h_node, &handler->h_list);
            // 如果handler中有start,那么就执行start
            if (handler->start)
                handler->start(handle);
    // 将evdev存放到evdev_table数组中,下标为次设备号
    evdev_install_chrdev(evdev);
    //注册字符设备,创建相应的设备节点
    device_add(&evdev->dev);


总结:
    1, struct evdev是用于与应用程序进行交互的媒介,用于创建字符设备
    2, struct evdev是在handler和input设备匹配成功后产生的
    3,  struct  evdev中包含了struct input_handle,该handle中记录了handler和input设备
    4, 每一个input设备对应一个struct evdev,一个handler可以对应多个struct evdev和input设备
    5, evdev中的input_handle两个链表:handle->d_node记录对应的input设备对象的链表
                handle->d_node记录对应的handler对象
-----------------------------------------------------------------------------------

input核心层代码分析:
Input.c (drivers\input)    

subsys_initcall(input_init);
module_exit(input_exit);

struct class input_class = {
    .name        = "input",
    .devnode    = input_devnode,
};

input_init(void)
    |
    // 创建/sys/class/input
    class_register(&input_class);
    // 创建/proc/bus/input/: devices   handlers,你可以理解成是总线
    err = input_proc_init();

    //注册字符设备
    register_chrdev(INPUT_MAJOR, "input", &input_fops);-------------
                                    |
                                    |
                                    |
                                    |
static const struct file_operations input_fops = {<----------------------
    .owner = THIS_MODULE,
    .open = input_open_file,----------------------------------------
    .llseek = noop_llseek,                        |
                                    |
};                                    |
                                    |
应用open()                                |
------------------------------------------------------------------------|
sys_open                                    |
------------------------------------------------------------------------|
                                    |
static int input_open_file(struct inode *inode, struct file *file)<-----|
    |

    struct input_handler *handler;
    //根据设备此设备号召到对应的input_table
    // 该数组在input_register_handler函数中初始化了
    handler = input_table[iminor(inode) >> 5];
    if (handler)//如果有当前次设备对应的handler,那么就获取hander对应的fops
        new_fops = fops_get(handler->fops);
    // 保存用户空间的f_op
    old_fops = file->f_op;
    // 将handler的f_op赋值给用户空间的f_op    
    file->f_op = new_fops;
    // 执行handler的open函数
    err = new_fops->open(inode, file);  -----------------------------
                                     |    |
此时要切换到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 const struct file_operations evdev_fops = {            |
    .owner        = THIS_MODULE,                    |
    .read        = evdev_read,                    |
    .write        = evdev_write,                    |
    .poll        = evdev_poll,                    |
    .open        = evdev_open,<-----------------------------------
    .release    = evdev_release,
    .unlocked_ioctl    = evdev_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl    = evdev_ioctl_compat,
#endif
    .fasync        = evdev_fasync,
    .flush        = evdev_flush,
    .llseek        = no_llseek,
};

static int evdev_open(struct inode *inode, struct file *file)
    |
    int i = iminor(inode) - EVDEV_MINOR_BASE;
    // 在evdev_table数组中根据次设备号找到对应的struct evdev
    // 该对象是在evdev_connect()中实现的
    evdev = evdev_table[i];
    
    bufsize = evdev_compute_buffer_size(evdev->handle.dev);
    //产生一个struct evdev_client对象,该对象用于承载用户空间和内核空间的缓冲区
    //里面包含一个struct input_event buffer[];
    client = kzalloc(sizeof(struct evdev_client) +
                bufsize * sizeof(struct input_event),
             GFP_KERNEL);
    client->bufsize = bufsize;
    client->evdev = evdev;
    // 将evdev_client放入到evdev->client_list链表中
    evdev_attach_client(evdev, client);
    error = evdev_open_device(evdev);
            |
            //好像没做什么事情,主要是判断input设备对象是否有open方法,有就打开
            //并且对应handle的使用进行计数
            input_open_device(&evdev->handle);
    //将evdev_client记录到应用空间的file中的私有数据中            
    file->private_data = client;

总结:1,当应用空间通过open函数时, 在evdev层会产生一个evdev_client对象

用户open-->input_handler-->evdev(handle)<----input_dev
                 |
                 |
              evdev_client

==============================================================================================
应用read()
---------------------------------------------------------------------
sys_read
-------------------------------------------------------------------
input.c  file->f_op = new_fops;
    evdev.c:  evdev_handler->evdev_read
            |
            |
static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
        |
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
        // 等待数据
        wait_event_interruptible(evdev->wait,client->packet_head != client->tail || !evdev->exist);
        //如果数据到来后,将数据上传给用户
        input_event_to_user(buffer + retval, &event)
            |
            copy_to_user(buffer, event, sizeof(struct input_event)

总结: 1,read中数据的等待需要有等待队列来完成
       2,在read中的等待队列是谁唤醒的呢:

      
接下来我们来看看input设备驱动层中数据提交:
 input_report_key(button_dev, KEY_LEFT, 0);
    |
    input_event(dev, EV_KEY, code, !!value);
        |
        input_handle_event(dev, type, code, value);
            |
            input_start_autorepeat(dev, code);
            input_pass_event(dev, type, code, value);
                |
                struct input_handler *handler;
                struct input_handle *handle;
                // 在input设备对象中的dev->h_list找到handle对象
                list_for_each_entry_rcu(handle, &dev->h_list, d_node)
                    //再找到handler
                    handler = handle->handler;
                    //执行handler的event函数,并传递type, code, value
                    handler->event(handle, type, code, value);------------
                                                 |
                                                 |                                              
所以此时又切换到evdev.c中的evdev_handler:                             |
static void evdev_event(struct input_handle *handle, <---------------------------------------|
                   unsigned int type, unsigned int code, int value)
        |
        //获取通过handle中找到evdev
        struct evdev *evdev = handle->private;
        //封装input_event
        event.type = type;
        event.code = code;
        event.value = value;
        //通过evdev->client_list找到一个evdev_client对象
        list_for_each_entry_rcu(client, &evdev->client_list, node)
            // 将input设备层传递过来的数据放入到client对象中
            evdev_pass_event(client, &event);
        //如果type为EV_SYN的话,那么就将唤醒等待队列
        if (type == EV_SYN && code == SYN_REPORT)
        wake_up_interruptible(&evdev->wait);
    

总结:
           evdev.c                         
        |
用户open-->input_handler-->evdev(handle)<----input_dev
                 |
                 |
  file->private_data<----evdev_client

用户read-->input_handler--evdev_client                              input_dev
                |                                       |
                |填充evdev_client<----------------------input_report_key
                |                                       |
 <----input_evnt----------evdev->waitqueue<---wake_up_interruptible<----input_sync
                

 

 

3.应用编程:

a.前提编译一个能用的驱动

fd = open("/dev/event0",O_RDWR);

struct input_event *input;

input = malloc(sizeof(struct input_event));

read(fd,input,sizeof(struct intput_event))

switch(input ->code)

               case KEY_UP:

if(input->value)

free(input);

close(fd);

b.驱动编写参考文档:Documention/input/input-programming.txt。

 

你可能感兴趣的:(嵌入式Linux驱动开发,驱动,input输入子系统)