{
1 struct evdev *evdev; //定义一个evdev结构体指针 struct class_device *cdev; int minor;
2 for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); if (minor == EVDEV_MINORS) {
printk(KERN_ERR \devices\\n\ return NULL; }
3 if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL))) return NULL;
INIT_LIST_HEAD(&evdev->list);
init_waitqueue_head(&evdev->wait);
evdev->exist = 1;
evdev->minor = minor; evdev->handle.dev = dev;
evdev->handle.name = evdev->name; evdev->handle.handler = handler; evdev->handle.private = evdev;
sprintf(evdev->name, \
evdev_table[minor] = evdev;
cdev = class_device_create(&input_class, &dev->cdev,
MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), dev->cdev.dev, evdev->name);
/* temporary symlink to keep userspace happy */
sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj, evdev->name);
return &evdev->handle; }
先看1处,这里有个定义在evdev.c里边的新面孔 struct evdev { int exist; int open; int minor;
char name[16];
struct input_handle handle; //关联input_handler和input_dev的input_handle
wait_queue_head_t wait; struct evdev_list *grab; struct list_head list; };
evdev这个结构体就是拿来应用开发操作的,在这里就是触摸屏对应的设备文件实体,结构体前边定义了记录设备的一些信息(设备号,打开状态、设备名字等),这里还定义了一个input_handle的实体handle,没错这个handle就是要用来关联input_dev和input_handler的,后面还有一行加粗的部分后面再做介绍。 再看2处,evdev_table[]是一个全局变量的数组,在evdev.c中有定义 #define EVDEV_MINORS 32
static struct evdev *evdev_table[EVDEV_MINORS];
在前面已经说明了,一个device可以对应多个handler,而一个handler也可处理多个device,这里体现出了后者。既然evdev这个结构体是对应的设备文件实体,因为这个handler可能会处理多个device,因此该handler要处理n个device就会应该有n个evdev实体,而这些实体的地址存放在evdev_table[]这个指针数组中,也就是说该handler最多只能处理EVDEV_MINORS个device,而2处的这几句代码就是要遍历evdev_table[]这个数组看还有没有空着的位置,有的话才会继续进行下面的程序。
再看3处,开辟一个evdev结构体的内存空间,前面有说明过kzalloc函数,这里不再说明。
后面的代码就是为evdev结构体变量赋初始值了,其中
init_waitqueue_head(&evdev->wait)初始化等待队列,具体介绍结合/linux/wait.h和查看相关资料(本人不懂)。
函数最后得到evdev结构体内的hanlde地址并返回,此时返回到我们的int input_register_device(struct input_dev *dev)函数里面,最后一句 input_link_handle(handle),进入到该函数中发现
static void input_link_handle(struct input_handle *handle) {
list_add_tail(&handle->d_node, &handle->dev->h_list); list_add_tail(&handle->h_node, &handle->handler->h_list); }
在该函数中也只是将handle中的d_node和h_node分别接入到input_dev和input_handler的h_list中,此时input_dev、input_handler、input_handle三者形成了如图2所示的关系。
至此设备注册过程算是全部完成了,但是貌似还有点乱。在整个设备的注册过程中函数的嵌套一个接着一个,不仅函数嵌套,结构体也使用了结构体和函数嵌套等,在各个函数内也用了大量的结构体指针等等。但是在整个过程中
input_dev、input_handler、input_handle这三个结构体变量的实体也都只有一个。其中input_dev结构体实体在xxx_ts.c中直接定义了一个input_dev的指针全局变量:
struct input_dev *tsdev;
并在初始化函数中开辟了一个input_dev的内存空间并让tsdev指针指向它:
w55fa95_dev = input_allocate_device();
input_handler结构体在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 = \ .id_table = evdev_ids, };
而关联input_dev和input_handler的input_handle则是在调用链接连接函数static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)的时候,在该函数的内部调用evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)开辟了一个evdev结构体的内存空间时创建了,input_handle就是使用了evdev结构体内部定义的input_handle。
一个完整input设备系统不仅要有设备,还需要有处理程序
input_handler,而上文中主要介绍的是设备注册、生成、以及和handler搭配的一个过程,而handler在何时生成的?
第三章、input_handler的注册。
Input_handler是要和用户层打交道的,在evdev.c中直接定义了一个input_handler结构体并初始化了一些内部成员变量。 static struct input_handler evdev_handler = { .event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops, //用户对设备操作的函数指针 .minor = EVDEV_MINOR_BASE, .name = \
.id_table = evdev_ids, //指向一个evedev的指针数组 };
先看第一行加粗的代码,evedev_fops结构体的定义如下 static 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 };
相信做过linux设备驱动编程的对这都很熟悉了,就是一大堆的用户接口函数,包括对设备的open、close、read、write、ioctl等函数。
再看第二行代码加粗的部分.id_table = evdev_ids, id.table就是前面所说过要和input_dev的id匹配的这么一个结构体,这里让它初始化为evdev_ids,在看evdev_ids的定义:
static struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */ { }, /* Terminating zero entry */ };
MODULE_DEVICE_TABLE(input, evdev_ids);
这里是一个结构体数组,令数组中第一个结构体的该成员变量driver_info的值为1,其他成员变量均未定义,说明这个handler对所有device的id都能匹配得上。数组中的第二个结构体为空表示结束,用来标识结束配合下面的MODULE_DEVICE_TABLE(input, evdev_ids)使用,关于
MODULE_DEVICE_TABLE宏定义介绍自行查看相关文献(我也不懂)。
接下来进入正题,input_handler的注册!
Input_handler的注册和input_dev的注册很相似,大同小异罢了,在evdev.c源码中显示定义并初始化了一个input_handler结构体并直接给相关的成员变量赋值了,就是本章开始所将的部分,然后再初始化函数中注册一个input_handler:
static int __init evdev_init(void) {
input_register_handler(&evdev_handler); return 0; }
这里只调用了一个input_register_handler()函数,看起来应该是很简单的样子(相比input_dev的注册),继续跟踪进入到该函数中:
void input_register_handler(struct input_handler *handler) {
struct input_dev *dev;
struct input_handle *handle; struct input_device_id *id;
if (!handler) return;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL)
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node) if (!handler->blacklist
|| !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev))) if ((handle = handler->connect(handler, dev, id))) input_link_handle(handle);
input_wakeup_procfs_readers(); }
该函数中代码加粗的部分,与input_register_device()函数里的如出一辙,这里不再做说明。
至此input_handler的注册已经结束。
第四章、input子系统数据结构
下图3是以触摸屏设备为例子的input子系统数据结构图。