注意:该输入子系统框架是基于linux3.4内核之前的版本来进行说明的,现在的linux内核版本4.4这一部分变动很大。
输入子系统框架:
drivers/input/input.c
input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
问:input_fops没有读函数,怎样读按键呢?
input_open_file
handler = input_table[iminor(inode) >> 5];
new_fops = fops_get(handler->fops);
file->f_op = new_fops;
err = new_fops->open(inode, file)
app: read > .......>file->f_op->read
input_table[]数组由谁构造?
input_table数组是在input_register_handler函数中被构造的。那么input_register_handler函数又是被谁调用的?
搜索一下,发现是由evdev.c,keyboard.c, mousedev.c等这几个文件调用,它们通过调用input_register_handler函数向上注册handler。
注册input_handler
input_register_handler
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list); //放入链表
list_for_each_entry(dev, &input_dev_list, node)//对于每一个input_dev,调用input_attach_handler
input_attach_handler(dev, handler);//根据input_handler中的id_table,判断能否支持这个设备。
注册输入设备:
input_register_device
//放入链表
list_add_tail(&dev->node, &input_dev_list);
//对于每一个handler,都调用input_attach_handler函数。
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);//根据input_handler的id_table判断能够支持这个input_dev
从上面可以看出,不管是先注册input_dev还是input_handler,都调用input_attach_handler进行两两匹配。
注册input_dev或Input_handler时,会两两比较input_dev和input_handler
根据input_handler中的id_table判断input_handler能否支持这个input_dev,如果能支持,则调用input_handler中的connect函数建立连接。
怎么建立连接?
1、分配一个input_handle结构体,
2、input_handle.dev = input_dev //指向input_dev
input_handle.handler = input_handler;// 指向input_handler
3、注册
input_register_handle
input_handler->h_list =&input_handle
input_dev->h_list =&input_handle
1 static const struct file_operations input_fops = {
2 .owner = THIS_MODULE,
3 .open = input_open_file, //在该结构体中,只有一个input_open_file这个函数,这个函数只是起一个中转作用,在里面肯定还做了其他事情。
4 .llseek = noop_llseek,
5 };
6
7 static int __init input_init(void)
8 {
9 int err;
10
11 err = class_register(&input_class);
12 if (err) {
13 pr_err("unable to register input_dev class
");
14 return err;
15 }
16
17 err = input_proc_init();
18 if (err)
19 goto fail1;
20
21 err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
22 if (err) {
23 pr_err("unable to register char major %d", INPUT_MAJOR);
24 goto fail2;
25 }
26
27 return 0;
1 static int input_open_file(struct inode *inode, struct file *file)
2 {
3 struct input_handler *handler;
4 const struct file_operations *old_fops, *new_fops = NULL;
5 int err;
6
7 err = mutex_lock_interruptible(&input_mutex);
8 if (err)
9 return err;
10
11 /* No load-on-demand here? */
/*次设备号右移5位,即相当于除以32,以这个数为下标,从input_table数组中取出一项,赋给handler.*/
12 handler = input_table[iminor(inode) >> 5];
13 if (handler)/*handler中有一个file_operation结构体,把handler中的file_operation结构体取出来赋给new_fops.*/
14 new_fops = fops_get(handler->fops);
15
16 mutex_unlock(&input_mutex);
17
18 /*
19 * That's _really_ odd. Usually NULL ->open means "nothing special",
20 * not "no device". Oh, well...
21 */
22 if (!new_fops || !new_fops->open) {
23 fops_put(new_fops);
24 err = -ENODEV;
25 goto out;
26 }
27
28 old_fops = file->f_op;
29 file->f_op = new_fops; /*打开的这个文件的f_op= 新的fops*/
31 err = new_fops->open(inode, file);/*调用新的fops中的open函数,以后如果读的话,就是用到了handler里面的新的fops*/
32 if (err) {
33 fops_put(file->f_op);
34 file->f_op = fops_get(old_fops);
35 }
36 fops_put(old_fops);
37 out:
38 return err;
1 struct input_handler {
3 void *private;
5 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
6 bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
7 bool (*match)(struct input_handler *handler, struct input_dev *dev);
8 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
9 void (*disconnect)(struct input_handle *handle);
10 void (*start)(struct input_handle *handle);
12 const struct file_operations *fops;
13 int minor;
14 const char *name;
15
16 const struct input_device_id *id_table;
17
18 struct list_head h_list;
19 struct list_head node;
20 };
input_table[]数组是在input_register_handler这个函数中构造的。
1 int input_register_handler(struct input_handler *handler)
2 {
3 struct input_dev *dev;
4 int retval;
5
6 retval = mutex_lock_interruptible(&input_mutex);
7 if (retval)
8 return retval;
9
10 INIT_LIST_HEAD(&handler->h_list);
11
12 if (handler->fops != NULL) {
13 if (input_table[handler->minor >> 5]) {
14 retval = -EBUSY;
15 goto out;
16 }
17 input_table[handler->minor >> 5] = handler;
18 }
19
20 list_add_tail(&handler->node, &input_handler_list);
21
22 list_for_each_entry(dev, &input_dev_list, node)
23 input_attach_handler(dev, handler);
24
25 input_wakeup_procfs_readers();
26
27 out:
28 mutex_unlock(&input_mutex);
29 return retval;
input_register_handler这个函数又是被谁调用的呢?选取evdev.c进行分析。
1 static struct input_handler evdev_handler = {
2 .event = evdev_event,
3 .connect = evdev_connect,
4 .disconnect = evdev_disconnect,
5 .fops = &evdev_fops, /*这个fops以前是由我们自己构造,现在内核已经帮我们做好了。*/
6 .minor = EVDEV_MINOR_BASE,
7 .name = "evdev",
8 .id_table = evdev_ids,//表示该handler能够支持哪些输入设备。如果能够支持的话,就调用connect函数。
9 };
10
11 static int __init evdev_init(void)
12 {
/*input_register_handler会将evdev_handler向上注册,通过上面的分析可知,就是把这个evdev_handler放入Input_table数组
中,以次设备号右移5位为下标。这样的话,在input_open_file中获得的handler就是evdev_handler这个结构体,new_fops就是evdev_handler
结构体中的fops
*/ 13 return input_register_handler(&evdev_handler); 14 }
1 static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
2 {
3 const struct input_device_id *id;
4 int error;
5
6 id = input_match_device(handler, dev);
7 if (!id)
8 return -ENODEV;
9
10 error = handler->connect(handler, dev, id);
11 if (error && error != -ENODEV)
12 pr_err("failed to attach handler %s to device %s, error: %d
",
13 handler->name, kobject_name(&dev->dev.kobj), error);
14
15 return error;
16 }
1 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
2 const struct input_device_id *id)
3 {
4 struct evdev *evdev;
17 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); 分配
18 if (!evdev)
19 return -ENOMEM;
20
21 INIT_LIST_HEAD(&evdev->client_list);
22 spin_lock_init(&evdev->client_lock);
23 mutex_init(&evdev->mutex);
24 init_waitqueue_head(&evdev->wait);
25
26 dev_set_name(&evdev->dev, "event%d", minor);
27 evdev->exist = true;
28 evdev->minor = minor;
29 设置
30 evdev->handle.dev = input_get_device(dev); //指向input_dev结构体
31 evdev->handle.name = dev_name(&evdev->dev);
32 evdev->handle.handler = handler; //指向input_handler结构体
33 evdev->handle.private = evdev;
注册
41 error = input_register_handle(&evdev->handle);
}
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
/*
* Filters go to the head of the list, normal handlers
* to the tail.
*/
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
list_add_tail_rcu(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
return 0;
}
怎么读按键?
app:read------------>evdev_read(还是以evdev.c为例)
1 static const struct file_operations evdev_fops = {
2 .owner = THIS_MODULE,
3 .read = evdev_read,
4 .write = evdev_write,
5 .poll = evdev_poll,
6 .open = evdev_open,
7 .release = evdev_release,
8 .unlocked_ioctl = evdev_ioctl,
9 #ifdef CONFIG_COMPAT
10 .compat_ioctl = evdev_ioctl_compat,
11 #endif
12 .fasync = evdev_fasync,
13 .flush = evdev_flush,
14 .llseek = no_llseek,
15 };
1 static ssize_t evdev_read(struct file *file, char __user *buffer,
2 size_t count, loff_t *ppos)
3 {
4 struct evdev_client *client = file->private_data;
5 struct evdev *evdev = client->evdev;
6 struct input_event event;
7 int retval = 0;
8
9 if (count < input_event_size())
10 return -EINVAL;
11
12 if (!(file->f_flags & O_NONBLOCK)) { //以阻塞的方式打开,没有数据可读,则进行休眠。那在什么地方被唤醒呢?搜evdev->wait即可。
13 retval = wait_event_interruptible(evdev->wait,
14 client->packet_head != client->tail ||
15 !evdev->exist);
16 if (retval)
17 return retval;
18 }
19
20 if (!evdev->exist)
21 return -ENODEV;
22
23 while (retval + input_event_size() <= count &&
24 evdev_fetch_next_event(client, &event)) {
25
26 if (input_event_to_user(buffer + retval, &event))
27 return -EFAULT;
28
29 retval += input_event_size();
30 }
31
32 if (retval == 0 && (file->f_flags & O_NONBLOCK))//以非阻塞的方式打开,没有数据可读,立即返回。
33 return -EAGAIN;
34
35 return retval;
1 static void evdev_event(struct input_handle *handle,
2 unsigned int type, unsigned int code, int value)
3 {
5 wake_up_interruptible(&evdev->wait);
6 }
evdev_event是被谁调用的呢?
猜测:应该是硬件相关的代码,input_dev那层调用的
在设备的中断服务程序里面,确定事件是什么,然后调用相应的input_handler的event处理函数。
看一个例子:
gpio_keys_irq_isr
上报事件
input_event(input, EV_KEY, button->code, 1);
input_sync(input);
input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
input_handle_event(dev, type, code, value);
input_pass_event(dev, type, code, value);
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)
continue;
handler = handle->handler;
handler->event(handle, type, code, value)
}
如何写符合输入子系统框架的驱动程序?
1、分配一个input_dev结构体;
2、设置
3、注册
4、硬件相关的代码,比如在中断服务程序里上报事件
以上就是输入子系统的框架及调用关系,不过这是基于较老的linux内核版本,在工作中使用的内核是linux4.4.对于Input.c里面的内容变动很大,具体还没有分析,以后会补上。