可以将输入子系统看做由三大部分组成, 体现了一种分离分层思想. 分别为:
核心层
事件处理层
设备驱动层
核心层: 这部分主要由 input.c 来实现, 它为事件处理层和设备驱动层提供统一接口, 这里我们先列出几个重要的函数.
- static int __init input_init(void)
- int input_register_device(struct input_dev *dev)
- int input_register_handler(struct input_handler *handler)
- int input_register_handle(struct input_handle *handle)
这几个函数几乎就实现了整个输入子系统的运作过程.
- input.c
- subsys_initcall(input_init);/* 是整个输入子系统的入口函数, 它是被编译进内核的, 也就是说一开机就会被执行的 */
- input.c
- static int __init input_init(void)
- {
- err = class_register(&input_class); /* 注册类, 放在 / sys/class*/
- err = register_chrdev(INPUT_MAJOR, "input", &input_fops);/* 注册一个设备名为 input 且主设备号为 INPUT_MAJOR(13)的字符设备,
- 可以通过命令 cat /proc/devices 查看 */
- }
接下来把重点放在 input_fops
- input.c
- static const struct file_operations input_fops = {
- .owner = THIS_MODULE,
- .open = input_open_file,
- };
不可能只有. open 函数的, 肯定在 input_open_file 函数里实现了其它应用函数.
- input.c
- static int input_open_file(struct inode *inode, struct file *file)
- {
- struct input_handler *handler = input_table[iminor(inode)>> 5];/* 根据次设备号从 input_table 取出 handler*/
- new_fops = fops_get(handler->fops),file->f_op = new_fops;/* 从 handler 结构中获取一个 fops 结构并将替换原来的 fops.*/
- err = new_fops->open(inode, file); /* 然后调用新的 file_operations 结构体里面的 open 函数 */
- ... ...
- }
也就是说应用程序以后执行读写等操作 (比如读按键) 是调用新的 fops 里面相应的函数, 而新的 fops 是在数组 input_table[]里面, 根据所打开的文件 (或者说设备) 的次设备号找出来的其中一个 handler, 在这个 handler 里面就有个新的 fops, 而 input.c 只起到一个中转作用.
那到底新的 fops 是什么时候注册进 input_table[]数组里面的?
input_open_file 函数一开始就从 input_table 获得一项 handler, 接下来我们会一步步追踪它的由来.
搜索下, 找到 tatic struct input_handler *input_table[8]发现它是一个静态变量
- input.c
- static struct input_handler *input_table[8];
一步步找下来最终发现它在 int input_register_handler 函数中定义, 但 input_register_handler 又是由谁调用呢?
- input.c
- int input_register_handler(struct input_handler *handler)
- {
- input_table[handler->minor>> 5] = handler;/*input_table[]被赋值 */
- }
同样搜索下, 找到了几个 c 文件调用了它, 分别有 evdev.c,joydev.c,mousedev.c 等等.
分析到这里, 就已经体现了一开始说的分离分层思想的分离思想, evdev.c, 游戏手柄 joydev.c, 鼠标 mousedev.c 这些就是事件处理层, 这部分的代码稳定, 一般我们不用修改.
接着上面我们说的 input_table 在 input_register_handler 中定义的, 我们现在要分析的就是 input_register_handler 函数做了哪些具体的事情.
以 evdev.c 为例, 看看 evdev_handler 里面都有哪些内容:
- evdev.c
- static int __init evdev_init(void)
- {
- return input_register_handler(&evdev_handler); /* 调用 input.c--->input_register_handler, 注册 evdev_handler*/
- }
- static struct input_handler evdev_handler = {
- .event = evdev_event,
- .connect = evdev_connect,
- .disconnect = evdev_disconnect,
- .fops = &evdev_fops, /*.fops: 文件操作结构体, 其中 evdev_fops 函数就是自己的写的操作函数, 然后赋到. fops 中 */
- .minor = EVDEV_MINOR_BASE, /*.minor: 用来存放次设备号, EVDEV_MINOR_BASE=64, 然后调用
- input_register_handler(&evdev_handler)后,
- 由于 EVDEV_MINOR_BASE/32=2, 所以存到 input_table[2]中 */
- .name = "evdev",
- .id_table = evdev_ids, /* 一个存放该 handler 所支持的设备 id 的表(其实内部存放的是 EV_xxx 事件, 用于判断 device 是否支持该事件)*/
- };
- 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
- }
我们看 input.c--->input_register_handler, 是如何注册 handler 的
这个函数主要做了三件事情:
1, 将 handler 放进 input_table 表中, 而不同的处理层该 input_handler 的结构不同, 比如 evdev.c 与 joydev.c 的 input_handler 的结构就可能不一样
2, 将 handler 放进 input_handler_list 链表中
3, 从 input_dev_list 链表中找出每个 input_dev 并执行 input_attach_handler(dev, handler);
- input.c
- int input_register_handler(struct input_handler *handler)
- {
- input_table[handler->minor>> 5]; /*1 这里就是将 handler 注册进 input_table[]数组, 而不同的处理层该 input_handler 的结构不同 */
- list_add_tail(&handler->node, &input_handler_list); /*2将 handler 放进 input_handler_list 链表中 */
- list_for_each_entry(dev, &input_dev_list, node) /*3从 input_dev_list 链表中找出每个 input_dev 并执行 input_attach_handler*/
- input_attach_handler(dev, handler);
- }
继续跟踪 input_attach_handler
- input.c
- static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
- {
- id = input_match_device(handler->id_table, dev); /*1对两者的 id 进行匹配, 同样若有不同则匹配不成功.*/
- error = handler->connect(handler, dev, id); /*2当两者匹配成功后执行 handler->connect(handler, dev, id)*/
- }
内核中 input_device_id 的数据结构如下:
路径:\include\Linux\Mod_devicetable.h
- struct input_device_id {
- kernel_ulong_t flags; /* 这个 flag 表示我们的这个 input_device_id 是用来匹配下面的 4 个情况的哪一项 */
- /* flag == 1 表示匹配总线 2 表示匹配供应商 4 表示匹配产品 8 表示匹配版本 */
- __u16 bustype; /* 对应 input_id 的四个数据域 */
- __u16 vendor;
- __u16 product;
- __u16 version;
- /* 存储支持事件的位图, 与 input_dev 中的同名数据成员功能一致 */
- /*(其实内部存放的是 EV_xxx 事件, 用于判断 device 是否支持该事件)*/
- kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t driver_info;
- };
而 evdev_handler 的. id_table = evdev_ids, 如下:
- evdev.c
- static const struct input_device_id evdev_ids[] = {
- { .driver_info = 1 }, /* Matches all devices */ /* 所有设备都支持, 所以就不会存在 id 不匹配的情况 */
- { }, /* Terminating zero entry */ /* 终止 0 头目 */
- };
匹配过程如下:
- input.c
- static const struct input_device_id *input_match_device(const struct input_device_id *id,
- struct input_dev *dev)
- {
- int i;
- for (; id->flags || id->driver_info; id++) {
- if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
- if (id->bustype != dev->id.bustype)
- continue;
- if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
- if (id->vendor != dev->id.vendor)
- continue;
- if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
- if (id->product != dev->id.product)
- continue;
- if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
- if (id->version != dev->id.version)
- continue;
- MATCH_BIT(evbit, EV_MAX);
- MATCH_BIT(keybit, KEY_MAX);
- MATCH_BIT(relbit, REL_MAX);
- MATCH_BIT(absbit, ABS_MAX);
- MATCH_BIT(mscbit, MSC_MAX);
- MATCH_BIT(ledbit, LED_MAX);
- MATCH_BIT(sndbit, SND_MAX);
- MATCH_BIT(ffbit, FF_MAX);
- MATCH_BIT(swbit, SW_MAX);
- return id;
- }
- return NULL;
- }
两者匹配成功后执行 evdev_connect:
- evdev.c
- static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
- {
- evdev->handle.dev = dev;
- evdev->handle.handler = handler; /* 这两句, handle 连接了 dev,handle*/
- class_device_create(&input_class, &dev->cdev, devt, dev->cdev.dev, evdev->name); /* 创建了一个设备节点 */
- input_register_handle(&evdev->handle); /* 注册一个 handle, 注意不是 handler*/
- }
nput_handle 结构体的原型如下, 只有 dev,handler,d_node,h_node 几个成员.
- input.h
- struct input_handle {
- void *private;
- int open;
- const char *name;
- struct input_dev *dev;
- struct input_handler *handler;
- struct list_head d_node;/*input_handle 通过 d_node 连接到了 input_dev 上的 h_list 链表上 */
- struct list_head h_node;/*input_handle 通过 h_node 连接到了 input_handler 的 h_list 链表上 */
- };
- input_register_handle
- input.c
- int input_register_handle(struct input_handle *handle)
- {
- struct input_handler *handler = handle->handler;/* 将 handle 里保存的 handler 拿出来 */
- /*
- 下面两句, 这样就可以将 dev 和 handler 建立起连接了
- */
- list_add_tail(&handle->d_node, &handle->dev->h_list);/* 把 input_handle 的 d_node 加入 input_dev 的 h_list*/
- list_add_tail(&handle->h_node, &handler->h_list); /* 把 input_handle 的 h_node 加入 input_handler 的 h_list*/
- }
由以上可以得出 handler,handle,input_dev 的关系如下:
建立起连接以后:
1就可以通过 input_handler 里面的 h_list 找到 input_handle, 然后通过 input_handle 里面的 dev 找到 input_dev.
2也可以通过 inpu_dev 的 h_list 找到 input_handle, 然后通过 input_handle 的 handler 找到 input_handler
至此我们对事件处理层的分析基本完成了, 我们接下来再分析设备驱动层
那么 input_dev 在哪里注册, input_dev 结构体又有哪些内容?
先看 input_dev 结构体, 有哪些内容:
- input.h
- struct input_dev
- {
- void *private;
- const char *name; /* 这 4 个不用管, 不重要.*/
- const char *phys;
- const char *uniq;
- struct input_id id;
- unsigned long evbit[NBITS(EV_MAX)]; /*evbit: 表示能产生哪类事件看 EV_MAX 这个宏 */
- unsigned long keybit[NBITS(KEY_MAX)]; /* 表示能产生哪些按键 */
- unsigned long relbit[NBITS(REL_MAX)]; /* 表示能产生哪些相对位移事件, x,y, 滚轮(比如鼠标)*/
- unsigned long absbit[NBITS(ABS_MAX)]; /* 表示能产生哪些绝对位移事件, x,y(比如触摸屏)*/
- unsigned long mscbit[NBITS(MSC_MAX)];
- unsigned long ledbit[NBITS(LED_MAX)];
- unsigned long sndbit[NBITS(SND_MAX)];
- unsigned long ffbit[NBITS(FF_MAX)];
- unsigned long swbit[NBITS(SW_MAX)];
- ... ...
- struct list_head h_list;
- struct list_head node;
- }
再看看 input_dev 注册函数 input_register_device():
- input.c
- int input_register_device(struct input_dev *dev)
- {
- list_add_tail(&dev->node, &input_dev_list);
- list_for_each_entry(handler, &input_handler_list, node) /* 主要的事情还是这两件, 这和上面讲的事件处理层很对称.*/
- input_attach_handler(dev, handler);
- }
Linux 内核已经自带有一些事件处理器, 可以支持大部分输入设备, 比如 evdev.c,mousedev.c,joydev.c 等, 这些. c 文件所在的层为 handler 层. Linux 内核自带的 input_handler 中, evdev_handler 是最常见的, 因为它可以匹配任何的 input_dev 设备. 一个事件处理器用 struct input_handler 结构体来表示, 在 evdev.c 文件中的定义如下.
static struct input_handler evdev_handler. 我们在 evdev.c 里面搜索不到 input_register_device 函数, 是因为 evdev.c 是用作事件处理, 并不支持输入设备.
搜索: input_register_device, 发现存在于下面这些文件中, 里面是各种鼠标, 按键, 触摸屏等等驱动, 在这些驱动里面构造:
一般设备驱动层都是由用户根据设备的硬件情况自己编写代码的, 最终要调用 input_register_device 将设备注册进内核.
我们以 \ drivers\input\keyboard\Gpio_keys.c 作为示例, 看看是如何注册 input_dev 的:
- Gpio_keys.c /* 注意 Linux 内核的 Gpio_keys.c 只是一个参考范例, 没实际用途.*/
- static int __devinit gpio_keys_probe(struct platform_device *pdev)
- {
- struct input_dev *input; /* 定义一个 input_dev 结构体指针 input*/
- /*1分配一个 input_dev 结构体 input*/
- input = input_allocate_device();
- /*2设置(其实这里设置的就是 dev 的 id, 跟 handler 里面的. id_table 里各项内容对比要对应才能建立起连接)*/
- /*1. 能产生哪类事件?*/
- input->evbit[0] = BIT(EV_KEY); /* 设置 nput->evbit[0]这个数组中的 0 位为 EV_KEY, 这个驱动就能产生按键类事件 */
- //set_bit(EV_KEY, input->evbit); /* 这样写会更好, 功能是一样的.*/
- //set_bit(EV_REP, input->evbit); /* 使之能产生重复类事件 */
- /*2 能产生这类操作里的哪些事件?*/
- //set_bit(KEY_L, input->keybit);/* 设置这个数组里面的某一位, 能够产生 L 这个键 */
- ... ...
- /* 3注册 input_dev 结构体 buttons_dev */
- error = input_register_device(input);
- /*
- . 注册之后, 它就会把 buttons_dev 这个设备 (结构体) 放到 input_dev_list 链表里面去
- . 把 input_handler_list 链表里面的每一个 input_handler(结构体), 一个个拿出来跟 buttons_dev 这个 input_dev 结构体的 id 作比较
- 如果 input_handler 的 id_table 表示说能匹配这个 buttons_dev 的话, 就会调用 input_handler 的 connect 函数, connect 函数就会建立
- 一个 input_handle.
- . input_handle 结构体有: handler,dev,d_node,h_node 成员
- . input_dev 的 h_list 链表指向 handle->d_node; 同样地, input_handler 的 h_list 也指向了 handle->h_node, 这样三者之间就建立
- 起了连接, handle 在 input_dev 和 input_handler 之前起到了桥梁般的连接作用.
- . 简单点可以理解为: input_handler 里面的 h_list 指向 input_handle 结构体, inpu_dev 里面的 h_list 同样指向 input_handle 结构体
- 建立起连接以后:
- 就可以通过 input_handler 里面的 h_list 找到 input_handle, 然后通过 input_handle 里面的 dev 找到 input_dev.
- 也可以通过 inpu_dev 的 h_list 找到 input_handle, 然后通过 input_handle 的 handler 找到 input_handler
- */
- if (error) {/* 判断返回值 */}
- }
在 Linux 内核中 evdev.c 中的 evdev_handler 的. id_table 内容为: 支持所有输入设备, 所以 connect 函数就会建立一个 input_handle, 然后使 inputv 和 evdev_handler 建立连接.
至此设备处理层也将完了.
接下来讲讲从应用程序的角度如何使用输入子系统.
其中一个重要的函数为 input_event
那如果没有按键按下的话, 你读按键, 最终会调用 evdev.c 的 evdev_handler->.fops->.read 函数, 没有数据产生就进入休眠状态. 在哪里唤醒呢? 看下一部分.
我们分析一下 evdev.c 事件驱动的. read 函数是 evdev_read()函数:
- evdev.c
- static ssize_t evdev_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos)
- {
- ... ...
- /* 判断应用层要读取的数据是否正确 */
- if (count <evdev_event_size()) return -EINVAL;
- /* 无数据并且是非阻塞方式打开, 立刻返回.*/
- 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);
- ... ... // 上传数据
- }
例如有按键中断了, 我们在中断服务函数里通过 input_event 告知内核有哪个事件发生了, 比如(更详细搜索源码: buttons.c):
buttons.c
xxx 中断处理函数
- {
- /* 参考以前的 poll 代码, 一般进入中断会先读出键值, 然后再唤醒. 唤醒之后应用程序就马上可以通过 read 函数读取键值 */
- /* buttons_dev 输入设备, EV_KEY 按钮类事件, pindesc->key_val 哪个引脚 ?,0 是松开(看原理图判断)*/
- input_event(buttons_dev, EV_KEY, pindesc->key_val, 0); /* 告知内核有哪个事件发生了, 同时会唤醒, 往下看就知道 */
- input_sync(buttons_dev); /* 上报完事件之后, 还需要上报一个同步事件, 表示这个事件已经上报完了 */
- }
input_event 这个通知函数最终会调用 handler 的 event 处理函数并唤醒等待事件.
- input.c
- void 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 ->h_list 链表找到 input_handle 驱动处理结构体 */
- if (handle->open) /* 如果 input_handle 之前 open 过, 那么这个就是我们的驱动处理结构体 */
- handle->handler->event(handle, type, code, value); /* 调用. event 的 evdev_event()事件函数 */
- }
evdev_event 函数如下:
- evdev.c
- /*
- evdev_event 为 evdev_handler->.event 函数, 当有事件发生了, 有按键按下时, 就会进入. event 函数中处理事件
- */
- static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
- {
- ... ...
- wake_up_interruptible(&evdev->wait);/* 有事件触发, 便唤醒等待中断, 和之前写的驱动程序一样 */
- }
以上就是整个输入子系统大概的框架.
最后, 如何写一个符合 input 子系统框架的驱动程序?
1, 分配一个 input_dev 结构体
2, 设置
3, 注册
4, 硬件相关的代码, 比如在中断服务程序里上报事件.
具体代码, 看下一节.
来源: http://www.bubuko.com/infodetail-3384528.html