打印
[S3C2440]

学了一段时间Linux内核驱动现整理一下之一 input输入子系统...

[复制链接]
1737|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
nello|  楼主 | 2015-12-18 17:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 nello 于 2015-12-18 17:17 编辑

所用内核是 linux-2.6.22.6


Ⅰ ---->对于硬件驱动层
设备驱动程序首先分配一个input_dev结构体                                                                                                                           

1 struct input_dev *input =input_allocate_device();                                                                                                               
2 设置这个结构体
3 注册这个结构体
注册函数为:
input_register_device(input)
   list_add_tail(&dev->node, &input_dev_list); //将注册的这个输入设备挂接到input_dev_list链表上
   list_for_each_entry(handler, &input_handler_list, node)//遍历整个input_handler_list链表  
             input_attach_handler(dev, handler);
           input_match_device(handler->id_table, dev)
                 handler->connect(handler, dev, id)//如果匹配成功那么就调用 handler里面注册的connect函数


Ⅱ----->对于input核心层
input核心层 input.c
     input_init
         ① class_register(&input_class);//注册一个class
         ② register_chrdev(INPUT_MAJOR, "input", &input_fops); //注册一个字符型设备  但是还缺少一个步骤 那就是 class_device_create


Ⅲ----->对于事件处理层(以event事件处理机制为例)
1 首先分配设置注册一个handler

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,
};

input_register_handler(&evdev_handler);
    input_table[handler->minor >> 5] = evdev_handler;//将分配设置的evdev_handler结构体放入  input_table数组中 这个数组是以minor为索引的;这里向右移动5为的原因是 每一个事件处理器能够支持 32中输入设备 这些输入设备以次设备好来索引
    list_add_tail(&handler->node, &input_handler_list);//将注册的这个事件处理器 挂接到input_handler_list链表上
    list_for_each_entry(dev, &input_dev_list, node)//遍历整个input_dev_list链表 寻找匹配项
        input_attach_handler(dev, handler); //寻找匹配
               handler->connect(handler, dev, id)//如果匹配成功那么就调用 handler里面注册的connect函数

鉴于input_register_device  input_register_handler最终都会调用到 handler->connect函数 下面分析一下 connect函数都做了什么

evdev_connect(handler, dev, id)
     for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);//这个for循环就是为了找到一个空的evdev_table 并且 minor < EVDEV_MINORS
     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//分配一个evdev结构体
     init_waitqueue_head(&evdev->wait);//初始化 wait

     evdev->exist = 1;  //设置这个evdev结构体  并且注意到 evdev里面有一个handle 将已经匹配成功的 input_dev跟input_handler链接在了一起
     evdev->minor = minor;
     evdev->handle.dev = dev;
     evdev->handle.name = evdev->name;
     evdev->handle.handler = handler;
     evdev->handle.private = evdev;
      
     evdev_table[minor] = evdev; //将 这个evdev放入 evdev_table
     devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
③class_device_create(&input_class, &dev->cdev, devt, dev->cdev.dev, evdev->name); //说明这个event(0~n)是供应用程序调用的
     input_register_handle(&evdev->handle)//////这个函数也是挺重要的
根据①②③ 就完成了 一个输入子系统字符设备驱动程序的设置过程
由于内核中事先已经加载好了 input_init 函数那么①②率先完成  
evdev_handler这个结构体内核中也已经加载好了,那么当驱动程序被加载好以后 执行input_register_device(input)就会调用evdev_connect 函数完成第三步③ 至此整个字符设备就已经注册好了

接下来看一下input_init函数的file_operaions 结构体
static const struct file_operations input_fops = { //当应用程序打开event0时就会调用这个input_open_file
        .owner = THIS_MODULE,
        .open = input_open_file,
};

根据以上的分析 当应用程序执行open("dev/input/event0",O_RDWR);;;;那么就会调用
这里的input_open_file
        new_fops = fops_get(handler->fops) //根据注册的那个handler获得一个新的file_operations结构体 也就是handler里面的结构体 即evdev_fops
然后调用  new_fops->open(inode, file);//
观察     handler->fops
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
};

对于evdev_open
        i = iminor(inode) - EVDEV_MINOR_BASE;
        evdev = evdev_table; //根据次设备号来获得evdev结构体----这个结构体能够索引到能够匹配的input_dev跟input_handler
        client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);//分配一个客户端结构
        client->evdev = evdev;//将这个endev放入客户端结构体中
        list_add_tail(&client->node, &evdev->client_list);
        input_open_device(&evdev->handle);
                   dev->open(dev);

当底层有数据产生的时候就会就会将数据传递给事件处理器
利用函数
input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)//上报事件
    list_for_each_entry(handle, &dev->h_list, d_node)//遍历input_dev上因input_dev与input_handler配对成功而生成的handler
        if (handle->open) //首先判断这个设备是不是已经被打开了
                handle->handler->event(handle, type, code, value); 如果被打开了那么就调用相应handler的event函数
                evdev_event
                      struct evdev *evdev = handle->private; //根据connect函数中设置的私有数据 得到evdev
                      list_for_each_entry(client, &evdev->client_list, node)
                          -
                        -
                        -
                        填充client->buffer
                       kill_fasync(&client->fasync, SIGIO, POLL_IN);上报事件
                       wake_up_interruptible(&evdev->wait);//唤醒进程  

事件上报完成或者进程被唤醒 那么就要读取数据了
当应用程序调用read函数时就会调用
evdev_read
     retval = wait_event_interruptible(evdev->wait,//(这个wait在connect函数被调用的时候就已经被初始化了)
            client->head != client->tail || !evdev->exist); // 没有事件产生的时候进程就会睡在endev的等待队列上了,,等待条件是有事件产生 或者设备不存在了
                evdev_event_to_user-----至此就将事件上报给了应用程序中

相关帖子

沙发
阿南| | 2015-12-19 10:02 | 只看该作者
多谢分享

使用特权

评论回复
板凳
eternity1120| | 2016-1-18 14:13 | 只看该作者

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

5

帖子

2

粉丝