在Linux中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。其中设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层;而核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口;而事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,而是要关心对各硬件寄存器的操作和提交的输入事件。
各层之间通信的基本单位就是事件,任何一个输入设备的动作都可以抽象成一种事件,如键盘的按下,触摸屏的按下,鼠标的移动等。事件有三种属性:类型(type),编码(code),值(value),Input子系统支持的所有事件都定义在input.h中,包括所有支持的类型,所属类型支持的编码等。事件传送的方向是 硬件驱动层-->子系统核心-->事件处理层-->用户空间
驱动层:
1、request_irq()
2、input_allocate_device()并初始化struct input_dev{}
我们先来看看 struct input_dev {} 这个结构体
struct input_dev { const char *name; const char *phys; const char *uniq; struct input_id id; unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //设备支持的事件类型 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//设备支持的按键类型 unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; unsigned int keycodemax; unsigned int keycodesize; void *keycode; int (*setkeycode)(struct input_dev *dev, unsigned int scancode, unsigned int keycode); int (*getkeycode)(struct input_dev *dev, unsigned int scancode, unsigned int *keycode); struct ff_device *ff; unsigned int repeat_key; struct timer_list timer; int sync; int abs[ABS_CNT]; int rep[REP_MAX + 1]; unsigned long key[BITS_TO_LONGS(KEY_CNT)]; unsigned long led[BITS_TO_LONGS(LED_CNT)]; unsigned long snd[BITS_TO_LONGS(SND_CNT)]; unsigned long sw[BITS_TO_LONGS(SW_CNT)]; int absmax[ABS_CNT]; int absmin[ABS_CNT]; int absfuzz[ABS_CNT]; int absflat[ABS_CNT]; int absres[ABS_CNT]; int (*open)(struct input_dev *dev); void (*close)(struct input_dev *dev); int (*flush)(struct input_dev *dev, struct file *file); int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); struct input_handle *grab; spinlock_t event_lock; struct mutex mutex; unsigned int users; bool going_away; struct device dev; struct list_head h_list; struct list_head node; };
/* * Event types */ #define EV_SYN 0x00 #define EV_KEY 0x01 #define EV_REL 0x02 #define EV_ABS 0x03 #define EV_MSC 0x04 #define EV_SW 0x05 #define EV_LED 0x11 #define EV_SND 0x12 #define EV_REP 0x14 #define EV_FF 0x15 #define EV_PWR 0x16 #define EV_FF_STATUS 0x17 #define EV_MAX 0x1f #defineEV_CNT (EV_MAX+1)
/* * Keys and buttons * * Most of the keys/buttons are modeled after USB HUT 1.12 * (see http://www.usb.org/developers/hidpage). * Abbreviations in the comments: * AC - Application Control * AL - Application Launch Button * SC - System Control */ #define KEY_RESERVED 0 #define KEY_ESC 1 #define KEY_1 2 #define KEY_2 3 #define KEY_3 4 #define KEY_4 5 #define KEY_5 6 #define KEY_6 7 #define KEY_7 8 #define KEY_8 9 #define KEY_9 10 /*......*/ /*keybit太多了,在input.h中定义*/
3、input_register_device()
我们下面来看input子系统跟我们写的程序是怎么联系起来的:
我们首先来看在前面的按键输入驱动中的 input_allocate_device()
struct input_dev *input_allocate_device(void) { struct input_dev *dev; dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); if (dev) { dev->dev.type = &input_dev_type;//device dev->dev.class = &input_class; //class device_initialize(&dev->dev); mutex_init(&dev->mutex); spin_lock_init(&dev->event_lock); INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->node); __module_get(THIS_MODULE); } return dev; }
接下来,在按键驱动程序中要做的就是指定好evbit、keybit 这两个成员变量,接着就是input_register_device()
int input_register_device(struct input_dev *dev) { static atomic_t input_no = ATOMIC_INIT(0); struct input_handler *handler; const char *path; int error; /* Every input device generates EV_SYN/SYN_REPORT events. */ __set_bit(EV_SYN, dev->evbit); /* KEY_RESERVED is not supposed to be transmitted to userspace. */ __clear_bit(KEY_RESERVED, dev->keybit); /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ input_cleanse_bitmasks(dev); /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. */ init_timer(&dev->timer); //内核的动态定时器,用来处理重复击键 if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { //如果处理重复击键的位都没有设,就赋上默认值 dev->timer.data = (long) dev; dev->timer.function = input_repeat_key; dev->rep[REP_DELAY] = 250; dev->rep[REP_PERIOD] = 33; } if (!dev->getkeycode) dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode) dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);//设置input_dev中device的名字,这个名字会在/class/input中出现 error = device_add(&dev->dev);//将device加入到linux设备模型中去 if (error) return error; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); error = mutex_lock_interruptible(&input_mutex); if (error) { device_del(&dev->dev); return error; } list_add_tail(&dev->node, &input_dev_list); //这个变量在static LIST_HEAD(input_handler_list)处定义
把自己的设备节点加入到input_dev_list中去 list_for_each_entry(handler, &input_handler_list, node) {//遍历input_handler_list链表,配对 input_dev 和 input_handler
input_attach_handler(dev, handler); ////input_attach_handler 这个函数是配对的关键,下面将详细分析
}
input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); return 0; }
/*下面看input_attach_handler()*/ static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { const struct input_device_id *id; //这个结构体主要包含设备的标志信息,如产品制造商、bustype、产品ID、位的设置等 int error; id = input_match_device(handler, dev);////这个是主要的配对函数,主要比较id中的各项,下面详细分析 if (!id) return -ENODEV; error = handler->connect(handler, dev, id);//配对成功调用handler的connect函数,这个函数在事件处理器中(比如evdev.c)定义,主要生成一个input_handle结构,并初始化,还生成一个事件处理器相关的设备结构,后面input_register_handler同样调用evdev_connect函数,后面分析 if (error && error != -ENODEV) printk(KERN_ERR "input: failed to attach handler %s to device %s, " "error: %d\n", handler->name, kobject_name(&dev->dev.kobj), error); return error; }
/*
这个是事件处理器所支持的input设备
*/ struct input_device_id { kernel_ulong_t flags;//mach时用来让程序选择比较哪项,如:busytype,vendor还是其他 __u16 bustype; __u16 vendor; __u16 product; __u16 version; 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; };
/*
input_match_device()
此函数主要是比较input_dev中的id和handler支持的id,这个存放在handler的id_table中。首先看id->driver_info有没有设置,如果设置了说明它匹配所有的id,evdev就是这个样的handler
*/
static const struct input_device_id *input_match_device(struct input_handler *handler,struct input_dev *dev)
{ const struct input_device_id *id; //这个是事件处理器所支持的input设备 int i; //函数传入的参数是所要配对handler的id_table,下面遍历这个id_table寻找合适的id进行配对 for (id = handler->id_table; id->flags || id->driver_info; id++) { #ifdef CONFIG_NKERNEL_DDI /* We hide normal device if a back-end virtualize it */ if ((id->bustype != BUS_VIRTUAL) && (dev->id.bustype != BUS_VIRTUAL)) { NkPhAddr plink = 0; NkDevVlink* vlink = NULL; char* vname = NULL; int vdriver_match = 0; while ((plink = nkops.nk_vlink_lookup("vevent", plink))) { vlink = nkops.nk_ptov(plink); if (!vlink->s_info) continue; vname = nkops.nk_ptov(vlink->s_info); if (strcmp(vname, dev->name) == 0) { vdriver_match = 1; break; } } if (vdriver_match == 1) continue; } #endif 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); if (!handler->match || handler->match(handler, dev)) return id; } return NULL;
在按键驱动程序的input_allocate_device --> 设置相应的位 --> input_register_device 这个过程中,其实就是为自己的struct input_dev 分配内存并初始化一些通用的位 -->设置一些与自己输入设备相关的特殊位-->
在全局的input_handler_list中,找到相匹配的handler,并调用这个handler的connect函数。
在我们的按键输入驱动中,当按键按下时,在中断处理程序中,主要就是向input子系统报告输入事件:
input_report_key();
input_sync();
看定义
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_ABS, code, value); } static inline void input_sync(struct input_dev *dev) { input_event(dev, EV_SYN, SYN_REPORT, 0); }
都调用了input_event(),继续
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { unsigned long flags; if (is_event_supported(type, dev->evbit, EV_MAX)) { spin_lock_irqsave(&dev->event_lock, flags); add_input_randomness(type, code, value); input_handle_event(dev, type, code, value); spin_unlock_irqrestore(&dev->event_lock, flags); } }
调用 input_handle_event()
这个函数主要是根据事件类型的不同,做相应的处理。这里之关心EV_KEY类型,其他函数和事件传递关系不大,只要关心,disposition这个是事件处理的方式,默认的是INPUT_IGNORE_EVENT,忽略这个事件,如果是INPUT_PASS_TO_HANDLERS则是传递给事件处理器,如果是INPUT_PASS_TO_DEVICE,则是传递给设备处理。
static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { int disposition = INPUT_IGNORE_EVENT; switch (type) { case EV_SYN: switch (code) { case SYN_CONFIG: disposition = INPUT_PASS_TO_ALL; break; case SYN_REPORT: if (!dev->sync) { dev->sync = 1; disposition = INPUT_PASS_TO_HANDLERS; } break; case SYN_MT_REPORT: dev->sync = 0; disposition = INPUT_PASS_TO_HANDLERS; break; } break; case EV_KEY: if (is_event_supported(code, dev->keybit, KEY_MAX) && !!test_bit(code, dev->key) != value) { if (value != 2) { __change_bit(code, dev->key); if (value) input_start_autorepeat(dev, code); else input_stop_autorepeat(dev); } disposition = INPUT_PASS_TO_HANDLERS; } break; case EV_SW: if (is_event_supported(code, dev->swbit, SW_MAX) && !!test_bit(code, dev->sw) != value) { __change_bit(code, dev->sw); disposition = INPUT_PASS_TO_HANDLERS; } break; case EV_ABS: if (is_event_supported(code, dev->absbit, ABS_MAX)) { if (test_bit(code, input_abs_bypass)) { disposition = INPUT_PASS_TO_HANDLERS; break; } value = input_defuzz_abs_event(value, dev->abs[code], dev->absfuzz[code]); if (dev->abs[code] != value) { dev->abs[code] = value; disposition = INPUT_PASS_TO_HANDLERS; } } break; case EV_REL: if (is_event_supported(code, dev->relbit, REL_MAX) && value) disposition = INPUT_PASS_TO_HANDLERS; break; case EV_MSC: if (is_event_supported(code, dev->mscbit, MSC_MAX)) disposition = INPUT_PASS_TO_ALL; break; case EV_LED: if (is_event_supported(code, dev->ledbit, LED_MAX) && !!test_bit(code, dev->led) != value) { __change_bit(code, dev->led); disposition = INPUT_PASS_TO_ALL; } break; case EV_SND: if (is_event_supported(code, dev->sndbit, SND_MAX)) { if (!!test_bit(code, dev->snd) != !!value) __change_bit(code, dev->snd); disposition = INPUT_PASS_TO_ALL; } break; case EV_REP: if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) { dev->rep[code] = value; disposition = INPUT_PASS_TO_ALL; } break; case EV_FF: if (value >= 0) disposition = INPUT_PASS_TO_ALL; break; case EV_PWR: disposition = INPUT_PASS_TO_ALL; break; } if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) dev->sync = 0; if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) dev->event(dev, type, code, value); if (disposition & INPUT_PASS_TO_HANDLERS) input_pass_event(dev, type, code, value); //最后调用此函数 }
/*input_pass_event*/ static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct input_handler *handler; struct input_handle *handle; rcu_read_lock(); handle = rcu_dereference(dev->grab);//如果是绑定的handle,则调用绑定的handler->event函数 if (handle) handle->handler->event(handle, type, code, value); else { bool filtered = false; //如果没有绑定,则遍历dev的h_list链表,寻找handle,如果handle已经打开,说明有进程读取设备关联的evdev list_for_each_entry_rcu(handle, &dev->h_list, d_node) { if (!handle->open) continue; handler = handle->handler; if (!handler->filter) { if (filtered) break; handler->event(handle, type, code, value); } else if (handler->filter(handle, type, code, value)) filtered = true; } } rcu_read_unlock();
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; do_gettimeofday(&event.time); event.type = type; event.code = code; event.value = value; //将传过来的事件,赋值给input_event结构 rcu_read_lock(); client = rcu_dereference(evdev->grab); //如果evdev绑定了client那么,处理这个客户端,触摸屏驱动没有绑定 if (client) evdev_pass_event(client, &event); else //遍历client链表,调用evdev_pass_event函数 list_for_each_entry_rcu(client, &evdev->client_list, node) evdev_pass_event(client, &event); rcu_read_unlock(); wake_up_interruptible(&evdev->wait); //唤醒等待的进程 }
static void evdev_pass_event(struct evdev_client *client, struct input_event *event) { /* * Interrupts are disabled, just acquire the lock */ spin_lock(&client->buffer_lock); client->buffer[client->head++] = *event; //将事件赋值给客户端的input_event 数组 client->head &= EVDEV_BUFFER_SIZE - 1; spin_unlock(&client->buffer_lock); kill_fasync(&client->fasync, SIGIO, POLL_IN); }
我们接下来要看的是evdev输入事件驱动,它为input子系统提供了一套默认的事件处理方法。在/drivers/input/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 = "evdev", .id_table = evdev_ids, }; /*
驱动层只是把输入设备注册到输入子系统中,在驱动层的代码中本身并不创建设备结点。应用程序用来与设备打交道的设备结点的创建由Event Handler层调用Input core中的函数来实现。而在创建具体的设备节点之前,Event Handler层需要先注册一类设备的输入事件处理函数及相关接口
*/ static int __init evdev_init(void) { return input_register_handler(&evdev_handler); }
/*
evdev结构体
*/
struct evdev { int exist; int open; int minor; struct input_handle handle; wait_queue_head_t wait; struct evdev_client *grab;//强制绑定的evdev_client结构 struct list_head client_list;//evdev_client 链表,这说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问evdev设备 spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev;//device结构,说明这是一个设备结构 };
evdev结构体在配对成功的时候生成,由handler->connect生成,对应设备文件为/class/input/event(n),如触摸屏驱动的event0,这个设备是用户空间要访问的设备,可以理解它是一个虚拟设备,因为没有对应的硬件,但是通过handle->dev 就可以找到input_dev结构,而它对应着触摸屏,设备文件为/class/input/input0。这个设备结构生成之后保存在evdev_table中
/*
evdev用户端结构
这个结构在进程打开event0设备的时候调用evdev的open方法,在open中创建这个结构,并初始化。在关闭设备文件的时候释放这个结构
*/
struct evdev_client { struct input_event buffer[EVDEV_BUFFER_SIZE];//这个是一个input_event数据结构的数组,input_event代表一个事件,基本成员:类型(type),编码(code),值(value) int head;//针对buffer数组的索引 int tail;//针对buffer数组的索引,当head与tail相等的时候,说明没有事件 spinlock_t buffer_lock; /* protects access to buffer, head and tail */ struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; struct wake_lock wake_lock; char name[28]; };
上面列出了evdev里定义的结构体,接下来具体看看input_register_handler(&evdev_handler)这个函数,注册一个handler事件处理器
/*事件处理结构体input_handler,
其中最重要的就是:
even函数:具体处理输入事件的函数。
connect函数:用来连接dev与handler(在注册input_dev的时候,在input_handler_list中寻找匹配的handler时就遇到过它)
*/ struct input_handler { void *private; void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//event 函数是当事件处理器接收到了来自input设备传来的事件时调用的处理函数,负责处理事件,非常重要,在事件传递过程中会详细分析 bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value); bool (*match)(struct input_handler *handler, struct input_dev *dev); int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);//connect 函数是当一个input设备模块注册到内核的时候调用的,将事件处理器与输入设备联系起来的函数,也就是将input_dev和input_handler配对的函数 void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle); const struct file_operations *fops; int minor; const char *name; const struct input_device_id *id_table; struct list_head h_list; struct list_head node; };
int input_register_handler(struct input_handler *handler) { struct input_dev *dev; int retval; retval = mutex_lock_interruptible(&input_mutex); if (retval) return retval; INIT_LIST_HEAD(&handler->h_list); //初始化h_list链表 if (handler->fops != NULL) { if (input_table[handler->minor >> 5]) {//把要注册的handler的指针存到全局数组input_table的对应元素中 retval = -EBUSY; goto out; } input_table[handler->minor >> 5] = handler; }
//input_table,每个注册的handler都会将自己保存到这里,索引值为handler->minor右移5为,也就是除以32
//为什么会这样呢,因为每个handler都会处理最大32个input_dev,所以要以minor的32为倍数对齐,这个minor是传进来的handler的MINOR_BASE
//每一个handler都有一个这一个MINOR_BASE,以evdev为例,EVDEV_MINOR_BASE = 64,可以看出系统总共可以注册8个handler
/*将handler加入到input_handler_list当中,然后再遍历input_dev_list链表,寻找匹配的input_dev,如果匹配成功,调用handler的connect函数。*/ list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler); //上面input_register_device()那里有列出 input_wakeup_procfs_readers(); out: mutex_unlock(&input_mutex); return retval; }
我们之前一直不清楚在connect(input_attach_handler()里调用)的时候究竟做了什么事情,我们下面就来看evdev提供的connect函数:
/*主要就是分配一个evdev结构体,做一些初始化,并将handle结构体注册进去,再把evdev存放到全局数组中evdev_table中 * Create new evdev device. Note that input core serializes calls * to connect and disconnect so we don't need to lock evdev_table here. */ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; int minor; int error;
//EVDEV_MINORS为32,说明evdev这个handler可以同时有32个输入设备和他配对,evdev_table中以minor(非次设备号,但是有一个换算关系)存放evdev结构体,后面要详细分析这个结构体
for (minor = 0; minor < EVDEV_MINORS; minor++) if (!evdev_table[minor]) break; if (minor == EVDEV_MINORS) { printk(KERN_ERR "evdev: no more free evdev devices\n"); return -ENFILE; } evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); if (!evdev) return -ENOMEM; INIT_LIST_HEAD(&evdev->client_list); spin_lock_init(&evdev->client_lock); mutex_init(&evdev->mutex); init_waitqueue_head(&evdev->wait); dev_set_name(&evdev->dev, "event%d", minor); ////这个是设置evdev中device的名字,他将出现在/class/input中。 evdev->exist = 1; evdev->minor = minor;
//因为evdev中包含handle了,所以初始化它就可以了,这样就连接了input_handler与input_dev evdev->handle.dev = input_get_device(dev); evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev; evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);//注意:这个minor不是真正的次设备号,还要加上EVDEV_MINOR_BASE evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev;//配对生成的device,父设备是与他相关连的input_dev evdev->dev.release = evdev_free; device_initialize(&evdev->dev); error = input_register_handle(&evdev->handle); //注册handle if (error) goto err_free_evdev; error = evdev_install_chrdev(evdev);//这个函数只做了一件事,就是把evdev结构保存到evdev_table中,这个数组也minor为索引 if (error) goto err_unregister_handle; error = device_add(&evdev->dev); if (error) goto err_cleanup_evdev; return 0; err_cleanup_evdev: evdev_cleanup(evdev); err_unregister_handle: input_unregister_handle(&evdev->handle); err_free_evdev: put_device(&evdev->dev); return error; }
我们来看input_register_handle这个函数:
/*input_handle 结构体代表一个成功配对的input_dev和input_handler*/
struct input_handle { void *private;//每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里 int open;//打开标志,每个input_handle 打开后才能操作,这个一般通过事件处理器的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链表 };
/*
*主要就是将handle分别加入到dev、handler的h_list当总中,然后调用handler的start函数,但是start函数未定义,所以不调用
*这个函数基本没做什么事,就是把一个handle结构体通过d_node链表项,分别链接到input_dev的h_list,input_handler的h_list上。以后通过这个h_list就可以遍历相关的input_handle了。 * input_register_handle - register a new input handle * @handle: handle to register * * This function puts a new input handle onto device's * and handler's lists so that events can flow through * it once it is opened using input_open_device(). * * This function is supposed to be called from handler's * connect() method. */ 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);//将handle的d_node,链接到其相关的input_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);//将handle的h_node,链接到其相关的input_handler的h_list链表中 if (handler->start) handler->start(handle); return 0; }
这里说下三个数据结构之间的关系:
input_dev 是硬件驱动层,代表一个input设备
input_handler 是事件处理层,代表一个事件处理器
input_handle 个人认为属于核心层,代表一个配对的input设备与input事件处理器
input_dev 通过全局的input_dev_list链接在一起。设备注册的时候实现这个操作。
input_handler 通过全局的input_handler_list链接在一起。事件处理器注册的时候实现这个操作(事件处理器一般内核自带,一般不需要我们来写)
input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和 input_handler 的h_list上了。通过input_dev 和input_handler就可以找到input_handle 在设备注册和事件处理器, 注册的时候都要进行配对工作,配对后就会实现链接。通过input_handle也可以找到input_dev和input_handler。
所以
从上图我们知道了三个层次是怎么联系在一起的。
open函数,从上图可以看出,在中间层init函数中,注册了一个字符设备,并把input_fops注册了进去,而input_fops里面只定义了一个open函数,这个open函数的用处就是把相应的handler提供的fops存放到file->fops里面 ,并跳转到相应handler的提供的 fops 里面的open函数。以后用户再打开这个设备文件,就直接进去的handler的fops函数集里面了。
输入子系统核心分析:
输入子系统核心对应与/drivers/input/input.c文件,这个也是作为一个模块注册到内核的。所以首先分析模块初始化函数,输入子系统的核心其他部分都是提供的接口,向上连接事件处理层,向下连接驱动层。
向下对驱动层的接口主要有:
input_allocate_device 这个函数主要是分配一个input_dev接口,并初始化一些基本的成员,这就是我们不能简单用kmalloc分配input_dev结构的原因,因为缺少了一些初始化。
input_unregister_device 注册一个input设备
input_event 这个函数很重要,是驱动层向input子系统核心报告事件的函数,在事件传递过程中再分析。
input_allocate_device 分配并初始化一个input_dev结构
向上对事件处理层接口主要有:
input_register_handler 注册一个事件处理器
input_register_handle 注册一个input_handle结构
先看初始化函数:
static int __init input_init(void) { int err; input_init_abs_bypass(); err = class_register(&input_class);//向内核注册一个类,用于linux设备模型。注册后会在/sys/class下面出现input目录 if (err) { printk(KERN_ERR "input: unable to register input_dev class\n"); return err; } err = input_proc_init(); if (err) goto fail1; err = register_chrdev(INPUT_MAJOR, "input", &input_fops); if (err) { printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); goto fail2; } return 0; fail2: input_proc_exit(); fail1: class_unregister(&input_class); return err; }
static const struct file_operations input_fops = { .owner = THIS_MODULE, .open = input_open_file,//只定义了一个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; int err; err = mutex_lock_interruptible(&input_mutex); if (err) return err; /* No load-on-demand here? */ handler = input_table[iminor(inode) >> 5]; if (handler) new_fops = fops_get(handler->fops);//new_fops == handler的fops mutex_unlock(&input_mutex); /* * That's _really_ odd. Usually NULL ->open means "nothing special", * not "no device". Oh, well... */ if (!new_fops || !new_fops->open) { fops_put(new_fops); err = -ENODEV; goto out; } old_fops = file->f_op; file->f_op = new_fops; err = new_fops->open(inode, file); if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); } fops_put(old_fops); out: return err; }
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.c中定义的input_handler fops*/ static struct input_handler evdev_handler = { .event = evdev_event, .connect = evdev_connect, .disconnect = evdev_disconnect, .fops = &evdev_fops, //指向上面的file_operations .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, };
下面是用户空间调用open打开device的过程:
/*evdev open函数*/ static int evdev_open(struct inode *inode, struct file *file) { struct evdev *evdev; struct evdev_client *client; int i = iminor(inode) - EVDEV_MINOR_BASE; int error; if (i >= EVDEV_MINORS) return -ENODEV; error = mutex_lock_interruptible(&evdev_table_mutex); if (error) return error; evdev = evdev_table[i]; //得到evdev设备结构,每次调用evdev_connect配对成功后都会把分配的evdev结构以minor为索引,保存在evdev_table数组中 if (evdev) get_device(&evdev->dev); //增加device引用计数 mutex_unlock(&evdev_table_mutex); if (!evdev) return -ENODEV; client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); //分配用户端结构 if (!client) { error = -ENOMEM; goto err_put_evdev; } spin_lock_init(&client->buffer_lock); client->evdev = evdev; //使用户端与evdev设备结构联系起来 evdev_attach_client(evdev, client); //这个函数所做的就是把client连接到evdev的client链表中 error = evdev_open_device(evdev); //这个函数打开设备,有很多层调用,后面详细分析 if (error) goto err_free_client; file->private_data = client; return 0; err_free_client: evdev_detach_client(evdev, client); kfree(client); err_put_evdev: put_device(&evdev->dev); return error; }
/*evdev设备打开函数evdev_open_device,由evdev_open调用*/ static int evdev_open_device(struct evdev *evdev) { int retval; retval = mutex_lock_interruptible(&evdev->mutex); if (retval) return retval; if (!evdev->exist) retval = -ENODEV; //判断设备结构是否存在,在evdev_connect中初始话此成员为1 else if (!evdev->open++) { retval = input_open_device(&evdev->handle); if (retval) evdev->open--; } //evdev->open分配结构的时候没有初始化,默认为0,也就是没有打开,每次打开都会加1 mutex_unlock(&evdev->mutex); return retval; }
此函数在判断结构存在与否后,主要调用了input_open_device,这个函数是子系统核心函数,定义在input.c中,下面分析这个函数:
int input_open_device(struct input_handle *handle) { struct input_dev *dev = handle->dev; int retval; retval = mutex_lock_interruptible(&dev->mutex); if (retval) return retval; if (dev->going_away) { retval = -ENODEV; goto out; } handle->open++; //将handle的打开计数加1,注意和evdev的open的区别 if (!dev->users++ && dev->open) retval = dev->open(dev); //如果此input_dev没有进程在引用,并且定义了open方法,就调用open方法 if (retval) { //retval = 1 说明没有打开成功 dev->users--; if (!--handle->open) { //说明有其他的进程已经打开了这个handle /* * Make sure we are not delivering any more events * through this handle */ synchronize_rcu(); } } out: mutex_unlock(&dev->mutex); return retval; }
此文多参考了 http://blog.csdn.net/daisy_chenting/article/details/6947387