input子系统学习进行简单的总结。先看driver/input/input.c
入口函数input_init
- static int __init input_init(void)
- { //此函数主要代码
- input_init_abs_bypass();
- err = class_register(&input_class);
- err = input_proc_init();
- err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
- }
register_chrdev注册了一个字符设备,此设备号INPUT_MAJOR被定义成13。
- static const struct file_operations input_fops = {
- .owner = THIS_MODULE,
- .open = input_open_file,
- };
注册的此设备的file_operations中只有open函数。这样当有程序调用此驱动的open函数时,input_open_file函数将执行。
- static int input_open_file(struct inode *inode, struct file *file)
- {
- struct input_handler *handler;
- const struct file_operations *old_fops, *new_fops = NULL;
- lock_kernel();
- handler = input_table[iminor(inode) >> 5]; //(1)
- if (!handler || !(new_fops = fops_get(handler->fops))) {
- err = -ENODEV;
- goto out;
- }
- if (!new_fops->open) {
- fops_put(new_fops);
- err = -ENODEV;
- goto out;
- }
- old_fops = file->f_op;
- file->f_op = new_fops; //(2)
- err = new_fops->open(inode, file); //(3)
- fops_put(old_fops); //减少旧file计数引用
- out:
- unlock_kernel();
- return err;
- }
(1)input_table是一个全局的input_handler结构体指针数组,定义如static struct input_handler *input_table[8],iminor(inode) >> 5 将inode设备文件节点的次设备号除以32。取出input_table中相应handler。
input_handler结构体如下,
- /**
- * struct input_handler - implements one of interfaces for input devices
- * @private: driver-specific data
- * @event: event handler. This method is being called by input core with
- * interrupts disabled and dev->event_lock spinlock held and so
- * it may not sleep
- * @connect: called when attaching a handler to an input device
- * @disconnect: disconnects a handler from input device
- * @start: starts handler for given handle. This function is called by
- * input core right after connect() method and also when a process
- * that "grabbed" a device releases it
- * @fops: file operations this driver implements
- * @minor: beginning of range of 32 minors for devices this driver
- * can provide
- * @name: name of the handler, to be shown in /proc/bus/input/handlers
- * @id_table: pointer to a table of input_device_ids this driver can
- * handle
- * @blacklist: pointer to a table of input_device_ids this driver should
- * ignore even if they match @id_table
- * @h_list: list of input handles associated with the handler
- * @node: for placing the driver onto input_handler_list
- *
- * Input handlers attach to input devices and create input handles. There
- * are likely several handlers attached to any given input device at the
- * same time. All of them will get their copy of input event generated by
- * the device.
- *
- * Note that input core serializes calls to connect() and disconnect()
- * methods.
- */
- struct input_handler {
- void *private;
- void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
- int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
- 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;
- const struct input_device_id *blacklist;
- struct list_head h_list;
- struct list_head node;
- };
(2)file是打开的相关设备文件的一个表示,它由内核在open时创建并会传递在该文件上操作的所有函数,直到close一个文件。在这里是用从input_table[]中新取出的new_fops代替file文件中原file_operations.
(3)使用new_fops中的open函数重新打开此设备文件.
input_table[]数组是由什么来初始化的?input_register_handler函数将注册一个新的input handler。函数如下
- int input_register_handler(struct input_handler *handler)
- { //主要代码
- struct input_dev *dev;
- int retval;
- retval = mutex_lock_interruptible(&input_mutex);
- INIT_LIST_HEAD(&handler->h_list); //(1)
- if (handler->fops != NULL) { //(2)
- if (input_table[handler->minor >> 5]) {
- retval = -EBUSY;
- goto out;
- }
- input_table[handler->minor >> 5] = handler;
- }
- list_add_tail(&handler->node, &input_handler_list); //(3)
- list_for_each_entry(dev, &input_dev_list, node) //(4)
- input_attach_handler(dev, handler);
- input_wakeup_procfs_readers();
- }
(1)初始化新的handler的h_list成员,也就是list->next = list;list->prev = list; h_list是保存了handle的链表,它是设备和handler联系的结构,后面会分析到。
(2)如果新hanlder的file_operations不为空,则判断这个次设备号除32对于的input_table是否已经被占用,如果是则返回以占用(-EBUSY),否则添加到input_table中。也说明input子系统类型中最多有8中不同的设备hanlder。
(3)将新hanlder的node节点添加到全局的input_handler_list链表中。
(4)list_for_each_entry是定义的一个宏,
- #define list_for_each_entry(pos, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member); \
- prefetch(pos->member.next), &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
功能是遍历head中的member成员。pos是容器作用的指针。在这里既是遍历input_dev_list链表中的所有node节点,这个node节点是一个list_head类型,而pos是input_dev类型,list_entry这个宏展开为container_of,它会求得包含了这个node的结构变量。
input_attach_handler(dev, handler)则是匹配这个dev和要注册的这个handler。
- static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
- {
- const struct input_device_id *id;
- int error;
- if (handler->blacklist && input_match_device(handler->blacklist, dev)) //(1)
- return -ENODEV;
- id = input_match_device(handler->id_table, dev); //(2)
- if (!id)
- return -ENODEV;
- error = handler->connect(handler, dev, id); //(3)
- 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;
- }
(1)handler中blacklist变量表示此handler应该忽略的设备,所以如果应该忽略的设备是参数传来的设备则返回-ENODEV错误。
(2)返回input_match_device(handler->id_table, dev)匹配的id,类型是struct input_device_id。
(3)调用handler的connect函数链接设备和处理函数。
- 看一下input_device_id 结构体
- struct input_device_id {
- kernel_ulong_t flags; //标志信息
- __u16 bustype; //总线类型
- __u16 vendor; //制造商
- __u16 product; //产品ID
- __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_device_id类型和input_dev类型设备的
- 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) //(1)
- if (id->bustype != dev->id.bustype) //(2)
- continue;
- if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) //匹配生产商
- if (id->vendor != dev->id.vendor)
- continue;
- if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) //匹配产品ID
- 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); //(3)
- 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;
- }
(1)设置了总线匹配标志,则进入匹配判断
(2)判断总线是否匹配,如果不匹配则continue 匹配id数组中下一个设备,如果匹配则接着执行下面的代码继续匹配其他项目。
(3)匹配id的evbit和input_dev中evbit的各个位,如果不匹配则continue,数组中下一个设备。MATCH_BIT宏定义如下
- #define MATCH_BIT(bit, max) \
- for (i = 0; i < BITS_TO_LONGS(max); i++) \ //(1)
- if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \ //(2)
- break; \
- if (i != BITS_TO_LONGS(max)) \ //(3)
- continue;
(1)
循环所有位(2)id的bit[i]中置1则dev的bit[i]也应置1 否则break
(3)如果不是max,即是break出来的而不是循环完的,则continue
阶段小结:
(1)首先在input_init入口函数中调用register_chrdev,注册了一个主设备号13的input字符设备
(2)此驱动的只有open函数,所以在调用open时input_open_file函数会在input_table[]全局数组中找到对应的handler,然后替换
处理函数结构,再用新open打开设备文件。以后所有的操作就会使用input_table中新的操作函数。
(3)input_table[]中结构可以通过input_register_handler函数来注册,且将node添加到input_handler_list中。
(4)在input_register_handler函数注册中会去匹配所有和这个handler相关的在input_dev_list中的设备。
(5)在匹配成功后会调用handler中的connect函数,做链接设备和handler的操作。
以上分析都是分析input核心和handler的注册部分。handler是函数处理部分,那下面则要分析设备部分。分析完设备部分,
最后在看它们之间的联系,之后则就可大致了解input子系统。
-------------------------------------------------------------------------------
上面我们已经知道input类型的设备是用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, int scancode, int keycode);
- int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
- struct ff_device *ff;
- unsigned int repeat_key;
- struct timer_list timer;
- int sync;
- int abs[ABS_MAX + 1];
- 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_MAX + 1];
- int absmin[ABS_MAX + 1];
- int absfuzz[ABS_MAX + 1];
- int absflat[ABS_MAX + 1];
- int absres[ABS_MAX + 1];
- 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;
- };
分配一个input_dev结构体需要调用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_type类型
- dev->dev.class = &input_class; //struct class类型
- device_initialize(&dev->dev); //初始化input_dev中的device类型
- mutex_init(&dev->mutex); //初始化互斥量
- spin_lock_init(&dev->event_lock);
- INIT_LIST_HEAD(&dev->h_list); //list_head类型,初始化h_list
- INIT_LIST_HEAD(&dev->node); //list_head类型初始化node
- __module_get(THIS_MODULE);
- }
- return dev;
- }
注册一个input_dev类型的设备需要调用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;
- __set_bit(EV_SYN, dev->evbit); //(1)
- /*
- * 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); //(2)
- 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) //(3)
- dev->getkeycode = input_default_getkeycode;
- if (!dev->setkeycode) //(4)
- dev->setkeycode = input_default_setkeycode;
- dev_set_name(&dev->dev, "input%ld", //(5)
- (unsigned long) atomic_inc_return(&input_no) - 1);
- error = device_add(&dev->dev); //(6)
- if (error)
- return error;
- path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); //(7)
- 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); //(8)
- list_for_each_entry(handler, &input_handler_list, node) //(9)
- input_attach_handler(dev, handler);
- input_wakeup_procfs_readers(); //(10)
- mutex_unlock(&input_mutex);
- return 0;
- }
(1)设置evbit成员,EN_SYN位,evbit代表设备可以支持的事件类型。?
(2)初始化dev->timer定时器,此定时器用于重复按键,下面6行也是设置重复按键相关
(3)检查是否有getkeycode函数,没有则使用默认函数input_default_getkeycode。此函数功能为获取按键的code值。(?)
(4)逻辑同上,setkeycode,设置按键code值
(5)设置input_dev中的device的名字,形如input1,input2,...这些名字可在内核导出的sysfs文件系统中看到
(6)将input_dev中的device添加到内核设备模型中,可在sysfs中体现。
(7)获得设备路径,在后面要kfree掉返回的字符串,它是kobject_get_path中kzalloc出的空间。 (?)
(8)将此input_dev添加到input_dev_list链表中,此表保存了所有的input设备。
(9)匹配相应的handler,这和之前分析的input_register_handler函数,是一样的。
(10)proc目录信息相关。
顺便看一下evbit可以设置的设备事件类型,
- #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 //LED
- #define EV_SND 0x12 //蜂鸣器
- #define EV_REP 0x14 //重复按键类型
- #define EV_FF 0x15
- #define EV_PWR 0x16 //电源管理
- #define EV_FF_STATUS 0x17
阅读(1120) | 评论(0) | 转发(0) |