现在的位置: 首页 > 综合 > 正文

input输入子系统

2014年01月12日 ⁄ 综合 ⁄ 共 5171字 ⁄ 字号 评论关闭

input输入子系统

我们看input.c文件,其中首先看输入init函数

static int __init input_init(void)

函数中有注册file_operation的结构体,查看该结构体:

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

里面只有一个input_open_file函数,一般来说,正常的驱动程序会有open,read,write等函数,这边只有一个open函数,那么这个原因必然在open函数中。

我们继续分析这个函数(忽略一些不必要的代码):

static int input_open_file(struct inode *inode, struct file *file)
{
        struct input_handler *handler;
        const struct file_operations *old_fops, *new_fops = NULL;
        。。。。。
        /* No load-on-demand here? */
        handler = input_table[iminor(inode) >> 5];
        if (!handler || !(new_fops = fops_get(handler->fops))) {
               err = -ENODEV;
               goto out;
        }
        。。。。
        

               old_fops = file->f_op;

               file->f_op = new_fops;

               err = new_fops->open(inode,file);

上面的的代码中,定义了一个struct input_handler结构体,handler从input_table中去取值,然后从新的ops中调用open函数

new_fops->open(inode,file);

这个结构体作用是什么?还有input_table是在哪里初始化的,作用是什么?

我们先分析下input_table是在哪里构造的,搜索代码:

      首先是一个全局变量

static structinput_handler *input_table[8];

在函数input_register_handler中有使用,分析函数:

注册input_handler:

int input_register_handler(struct input_handler *handler)
{
        struct input_dev *dev;
        // 放入数组中
        input_table[handler->minor >> 5] = handler;
        // 放入链表
        list_add_tail(&handler->node, &input_handler_list);
        // 对于每个input_dev,调用input_attach_handler
        list_for_each_entry(dev, &input_dev_list, node)
               // 根据input_handler的id_table判断能否支持这个input_dev
               input_attach_handler(dev, handler);   
        。。。。。。
}

 

      函数中主要工作是先把input_handler注册到数组input_table中,索引值是minor值右移5位,相当于除以32,然后将input_handler挂入到全局链表input_handler_list中,最后对于每个input_dev,调用input_attach_handler,根据input_handler的id_table判断能否支持这个input_dev。具体的分析函数input_attach_handler。

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
        。。。。。
        // 根据id_table看是否支持该设备
        id = input_match_device(handler->id_table, dev);
        if (!id)
               return -ENODEV;
        // 支持,则调用connect函数
        error = handler->connect(handler, dev, id);
        。。。。
}

        以上分析我们知道了结构体structinput_handler *handler初始化好后,调用input_register_handler进行注册,注册过程中会从input_dev_list链表中去寻找是否有支持的设备,如果找到匹配的,则会调用handler->connect函数。

      以上是注册handler的过程,让我们看看注册input_dev是怎么实现的?

注册输入设备:

int input_register_device(struct input_dev *dev)
{
        // 放入链表
        list_add_tail(&dev->node, &input_dev_list);
        
        // 对于每一个input_handler,都调用input_attach_handler
        list_for_each_entry(handler, &input_handler_list, node)
               // 根据input_handler的id_table判断能否支持这个input_dev
               input_attach_handler(dev, handler);
}

        我们分析到这可以看到函数input_register_handler和input_register_device是对称的,不管是先注册handler还是dev,都会调用函数input_attach_handler进行匹配,匹配成功调用connect函数。

具体例子分析:

linux-2.6.30.4\drivers\input\evdev.c

初始化函数

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

调用了函数input_register_handler进行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,
};

其中我们主要看下connect函数,id_table。

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
                        const struct input_device_id *id)
{
        // 分配一个设备结构体
        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
        
        evdev->handle.dev = input_get_device(dev);// 设置input_dev
        evdev->handle.name = evdev->name;
        evdev->handle.handler = handler; // 设置input_handler
        evdev->handle.private = evdev;
        // 注册 handle
        error = input_register_handle(&evdev->handle);
        。。。
}

       这里要注意一下handle,

       将input_dev和input_handler链接起来,input_register_handle函数注册handle

int input_register_handle(struct input_handle *handle)
{
        // 将d_node放入到dev的h_list中
        list_add_tail_rcu(&handle->d_node, &dev->h_list);
        
        // 将h_node放入到handler的h_list中
        list_add_tail(&handle->h_node, &handler->h_list);
 
        if (handler->start)
               handler->start(handle);
}

        通过input_register_handle函数,我们就可以通过dev的h_list找到handle结构体,从而找到对应的handler,或者通过input_handler的h_list找到handle结构体,从而找到对应的dev,所以结构体structinput_handle是沟通dev和handler的桥梁。

总结下建立连接的方式:

 

怎么建立连接?

1. 分配一个input_handle结构体

2.

      input_handle.dev = input_dev;  // 指向左边的input_dev

      input_handle.handler = input_handler;  // 指向右边的input_handler

3. 注册:

  input_handler->h_list = &input_handle;

  inpu_dev->h_list      = &input_handle;

 

怎么读呢??

static ssize_t evdev_read(struct file *file, char __user *buffer,
                       size_t count, loff_t *ppos)
{
        // 等待事件发生,等待条件是client->head != client->tail || !evdev->exist
        retval = wait_event_interruptible(evdev->wait,
               client->head != client->tail || !evdev->exist);
        。。。
}

既然这里有等待,那必然会有函数去唤醒,那么唤醒函数在哪里呢?搜索代码,有个函数叫evdev_event,其中

evdev_event    
        evdev_pass_event(client, &event);
               kill_fasync(&client->fasync, SIGIO, POLL_IN); // 发送信号给其他进程
        wake_up_interruptible(&evdev->wait); // 唤醒进程

evdev_event 被谁调用呢???

      evdev_event 在static struct input_handlerevdev_handler中被赋值了。

static structinput_handler evdev_handler = {

      .event           =evdev_event,

      。。。

};

      从上面可以看到调用函数evdev_event是通过evdev_handler的event函数调用的。

抱歉!评论已关闭.