INPUT输入子系统——按键
一、什么是input输入子系统?
1.1. Linux系统支持的输入设备繁多,例如键盘、鼠标、触摸屏、手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型、不同原理、不同的输入信息的输入设备的呢?其实就是通过input输入子系统这套软件体系来完成的。从整体上来说,input输入子系统分为3层:上层(输入事件驱动层)、中层(输入核心层)、下层(输入设备驱动层),如下图所示:
1.2. 图中Drivers对应的就是下层设备驱动层,对应各种各样不同的输入设备,Input Core对应的就是中层核心层,Handlers对应的就是上层输入事件驱动层,最右边的代表的是用户空间
1.3. 上层中的各个handler(Keyboard/Mouse/Joystick/Event)是属于平行关系。由于历史原因,一种设备可能连接到多个handler层中,由于event是后出的。所有event实现大一统,所有输入设备都可以连接到event handler中
1.4. 官方查看文档
文档1:kernel\Documentation\input\input.txt
文档2:kernel\Documentation\input\input-programming.txt
二. 输入子系统框架分析
2.1. 重要结构体
2.1.1. input_dev结构体
a. 该结构体是所有输入设备的抽象,只要是输入设备都可以用此结构体描述
struct input_dev { const char *name; // input设备的名字 const char *phys; // const char *uniq; // struct input_id id; // // 这些是用来表示该input设备能够上报的事件类型有哪些 是用位的方式来表示的 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); // 设备的open函数 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; // 内置的device结构体变量 struct list_head h_list; // 用来挂接input_dev 设备连接的所有handle 的一个链表头 struct list_head node; // 作为链表节点挂接到 input_dev_list 链表上 (input_dev_list链表是input核心层维护的一个用来挂接所有input设备的一个链表头) };
2.1.2. input_handler结构体
struct input_handler { void *private; // 私有数据 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); // handler用于向上层上报输入事件的函数 bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value); bool (*match)(struct input_handler *handler, struct input_dev *dev); // match 函数用来匹配handler 与 input_dev 设备 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); // 当handler 与 input_dev 匹配成功之后用来连接 void (*disconnect)(struct input_handle *handle); // 断开handler 与 input_dev 之间的连接 void (*start)(struct input_handle *handle); const struct file_operations *fops; // 一个file_operations 指针 int minor; // 该handler 的编号 (在input_table 数组中用来计算数组下标) input_table数组就是input子系统用来管理注册的handler的一个数据结构 const char *name; // handler的名字 const struct input_device_id *id_table; // 指向一个 input_device_id 类型的数组,用来进行与input设备匹配时用到的信息 struct list_head h_list; // 用来挂接handler 上连接的所有handle 的一个链表头 struct list_head node; // 作为一个链表节点挂接到 input_handler_list 链表上(input_handler_list 链表是一个由上层handler参维护的一个用来挂接所有注册的handler的链表头) };
2.1.3. input_device_id结构体
struct input_device_id { kernel_ulong_t flags; // 这个flag 表示我们的这个 input_device_id 是用来匹配下面的4个情况的哪一项 // flag == 1表示匹配总线 2表示匹配供应商 4表示匹配产品 8表示匹配版本 __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; };
2.2. input框架中重要函数
2.2.1、核心模块注册input_init
a. 函数位于drivers\input\input.c
b. class_register
c. input_proc_init
d. register_chrdev
static int __init input_init(void) { int err; input_init_abs_bypass(); err = class_register(&input_class); // 创建设备类 /sys/class/input if (err) { printk(KERN_ERR "input: unable to register input_dev class\n"); return err; } err = input_proc_init(); // proc文件系统相关的初始化 if (err) goto fail1; err = register_chrdev(INPUT_MAJOR, "input", &input_fops); // 注册字符设备驱动 主设备号13 input_fops 中只实现了open函数,所以他的原理其实和misc其实是一样的 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; }
2.2.2. 核心层提供给设备驱动层的接口函数
2.2.2.1. input设备驱动框架留给设备驱动层的接口函数主要有3个:
a. input_allocate_device。分配一块input_dev结构体类型大小的内存
struct input_dev *input_allocate_device(void) { struct input_dev *dev; // 定义一个 input_dev 指针 dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); // 申请分配内存 if (dev) { dev->dev.type = &input_dev_type; // 确定input设备的 设备类型 input_dev_type dev->dev.class = &input_class; // 确定input设备所属的设备类 class device_initialize(&dev->dev); // input设备的初始化 mutex_init(&dev->mutex); // 互斥锁初始化 spin_lock_init(&dev->event_lock); // 自旋锁初始化 INIT_LIST_HEAD(&dev->h_list); // input_dev -> h_list 链表初始化 INIT_LIST_HEAD(&dev->node); // input_dev -> node 链表初始化 __module_get(THIS_MODULE); } return dev; }
b. input_set_capability。设置输入设备可以上报哪些输入事件
函数原型:input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
参数:dev就是设备的input_dev结构体变量
type表示设备可以上报的事件类型
code表示上报这类事件中的那个事件
注意:input_set_capability函数一次只能设置一个具体事件,如果设备可以上报多个事件,则需要重复调用这个函数来进行设置,
例如: input_set_capability(dev, EV_KEY, KEY_Q); // 至于函数内部是怎么设置的,将会在后面进行分析。
input_set_capability(dev, EV_KEY, KEY_W);
input_set_capability(dev, EV_KEY, KEY_E);
c. input_register_device。向input核心层注册设备
int input_register_device(struct input_dev *dev) // 注册input输入设备 { static atomic_t input_no = ATOMIC_INIT(0); struct input_handler *handler; // 定义一个 input_handler 结构体指针 const char *path; int error; /* Every input device generates EV_SYN/SYN_REPORT events. */ __set_bit(EV_SYN, dev->evbit); // 每一个input输入设备都会发生这个事件 /* KEY_RESERVED is not supposed to be transmitted to userspace. */ __clear_bit(KEY_RESERVED, dev->keybit); // 清除KEY_RESERVED 事件对应的bit位,也就是不传输这种类型的事件 /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ input_cleanse_bitmasks(dev); // 确保input_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", // 设置input设备对象的名字 input+数字 (unsigned long) atomic_inc_return(&input_no) - 1); error = device_add(&dev->dev); // 添加设备 例如: /sys/devices/virtual/input/input0 if (error) return error; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); // 获取input设备对象所在的路径 /sys/devices/virtual/input/input_xxx 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); // 链表挂接: 将 input_dev->node 作为节点挂接到 input_dev_list 链表上 list_for_each_entry(handler, &input_handler_list, node) // 遍历input_handler_list 链表上的所有handler input_attach_handler(dev, handler); // 将handler与input设备进行匹配 input_wakeup_procfs_readers(); // 更新proc 文件系统 mutex_unlock(&input_mutex); return 0; }
d. input_attach_handler函数:
input_attach_handler就是input_register_device函数中用来对下层的设备驱动和上层的handler进行匹配的一个函数,只有匹配成功之后就会调用上层handler中的connect函数
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { const struct input_device_id *id; // 定义一个input_device_id 的指针 int error; id = input_match_device(handler, dev); // 通过这个函数进行handler与input设备的匹配工作 if (!id) return -ENODEV; error = handler->connect(handler, dev, id); // 匹配成功则调用 handler 中的 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; } static const struct input_device_id *input_match_device(struct input_handler *handler, struct input_dev *dev) { const struct input_device_id *id; // 定义一个 input_device_id 指针 int i; for (id = handler->id_table; id->flags || id->driver_info; id++) { // 依次遍历handler->id_table 所指向的input_device_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); if (!handler->match || handler->match(handler, dev)) return id; // 如果数组中的某个匹配成功了就返回他的地址 } return NULL; }
三. 驱动开发者开发流程
3.1. 内核提供的函数
3.1.1. input_allocate_device
3.1.2. input_register_device
3.1.3. input_register_device
3.2. 开发代码
3.2.1. button_device.c
#include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module_exit #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <mach/gpio.h> #include <linux/leds.h> #include <asm/string.h> #include <linux/platform_device.h> #include <linux/input.h> #include "button_device_driver.h" #define BUTTON_GPIO_CONFIG 0xFF /* KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT * LEFT -> EINT2 -> GPH0_2 * DOWN -> EINT3 -> GPH0_3 * UP -> KP_COL0 -> GPH2_0 * RIGHT -> KP_COL1 -> GPH2_1 * MENU -> KP_COL3 -> GPH2_3 (KEY_A) * BACK -> KP_COL2 -> GPH2_2 (KEY_B) */ void s5pv210_button_release(struct device *dev); struct gpioIRQ button_irq[] = { { "button_Left_IRQ", BUTTON_LEFT_IRQ, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, }, { "button_down_IRQ", BUTTON_DOWN_IRQ, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, } }; static struct s5pv210_button_platdata x210_button_pdata[] = { [0]={ .name = "button_Left", .gpio = S5PV210_GPH0(2), .gpio_cfg = BUTTON_GPIO_CONFIG, .button_code= KEY_LEFT, .button_irq = &button_irq[0], }, [1]={ .name = "button_down", .gpio = S5PV210_GPH0(3), .gpio_cfg = BUTTON_GPIO_CONFIG, .button_code= KEY_DOWN, .button_irq = &button_irq[1], } }; static struct platform_device s5pv210_device_button = { .name = "s5pv210-button", .id = -1, .dev = { .platform_data = &x210_button_pdata, .release = s5pv210_button_release, }, }; void s5pv210_button_release(struct device *dev) { printk(KERN_WARNING "s5pv210_button_release successful \n"); } static int __init s5pv210_button_init(void) { int ret = -1; printk(KERN_INFO "device.c : s5pv210_button_init successful \n"); ret = platform_device_register(&s5pv210_device_button); if (ret < 0) { printk(KERN_WARNING "platform_add_devices fail \n"); } return ret; } static void __exit s5pv210_button_exit(void) { printk(KERN_INFO "device.c : s5pv210_button_exit successful \n"); platform_device_unregister(&s5pv210_device_button); } module_init(s5pv210_button_init); module_exit(s5pv210_button_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息 MODULE_LICENSE("GPL"); // 描述模块的许可证 MODULE_AUTHOR("musk"); // 描述模块的作者 MODULE_DESCRIPTION("x210 button device"); // 描述模块的介绍信息 MODULE_ALIAS("button_device"); // 描述模块的别名信息 3.2.2. button_driver.c #include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <asm/irq.h> #include <asm/io.h> #include <mach/irqs.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include "button_device_driver.h" #include <linux/kernel.h> /* * POWER -> EINT1 -> GPH0_1 * LEFT -> EINT2 -> GPH0_2 * DOWN -> EINT3 -> GPH0_3 * UP -> KP_COL0 -> GPH2_0 * RIGHT -> KP_COL1 -> GPH2_1 * MENU -> KP_COL3 -> GPH2_3 (KEY_A) * BACK -> KP_COL2 -> GPH2_2 (KEY_B) */ static struct input_dev *button_dev; static irqreturn_t button_interrupt(int irq, void *dummy) { struct platform_device *pdev = (struct platform_device *)dummy; struct s5pv210_button_platdata *pdata = pdev->dev.platform_data; if(dummy == NULL) { printk("\n button_interrupt fail;irq = %d\n",irq); return -1; } printk("\n irq = %d\n",irq); if(irq == pdata[0].button_irq->irq) { s3c_gpio_cfgpin(pdata[0].gpio, S3C_GPIO_SFN(0x00)); input_report_key(button_dev, pdata[0].button_code,!gpio_get_value(pdata[0].gpio)); s3c_gpio_cfgpin(pdata[0].gpio, S3C_GPIO_SFN(0x0f)); } if(irq == pdata[1].button_irq->irq) { s3c_gpio_cfgpin(pdata[1].gpio, S3C_GPIO_SFN(0x00)); input_report_key(button_dev, pdata[1].button_code,!gpio_get_value(pdata[1].gpio)); s3c_gpio_cfgpin(pdata[1].gpio, S3C_GPIO_SFN(0x0f)); } input_sync(button_dev); return IRQ_HANDLED; } static int s5pv210_button_probe(struct platform_device *pdev) { int error,ret; struct s5pv210_button_platdata *pdata = pdev->dev.platform_data; printk(KERN_INFO "button_driver.c: s5pv210_button_probe successful\n"); if (request_irq(pdata[0].button_irq->irq, button_interrupt, pdata[0].button_irq->irq_trigger, pdata[0].button_irq->name, pdev)) { printk(KERN_ERR "key-s5pv210.c: Can't allocate irq %d\n", pdata[0].button_irq->irq); return -EBUSY; } ret = gpio_request(pdata[0].gpio, pdata[0].name); if(ret) printk("button-driver: request %s fail", pdata[0].name); s3c_gpio_cfgpin(pdata[0].gpio, S3C_GPIO_SFN(pdata[0].gpio_cfg)); if (request_irq(pdata[1].button_irq->irq, button_interrupt, pdata[1].button_irq->irq_trigger, pdata[1].button_irq->name, pdev)) { printk(KERN_ERR "key-s5pv210.c: Can't allocate irq %d\n", pdata[1].button_irq->irq); return -EBUSY; } ret = gpio_request(pdata[1].gpio, pdata[1].name); if(ret) printk(KERN_ERR "button-driver: request %s fail", pdata[1].name); s3c_gpio_cfgpin(pdata[1].gpio, S3C_GPIO_SFN(pdata[1].gpio_cfg)); button_dev = input_allocate_device(); if (!button_dev) { printk(KERN_ERR "button.c: Not enough memory\n"); error = -ENOMEM; goto err_free_irq; } set_bit(EV_KEY, button_dev->evbit); set_bit(pdata[0].button_code, button_dev->keybit); set_bit(pdata[1].button_code, button_dev->keybit); error = input_register_device(button_dev); if (error) { printk(KERN_ERR "button_driver.c: Failed to register device\n"); goto err_free_dev; } return 0; err_free_dev: input_free_device(button_dev); err_free_irq: free_irq(pdata[0].button_irq->irq, pdev); free_irq(pdata[1].button_irq->irq, pdev); gpio_free(pdata[0].gpio); gpio_free(pdata[1].gpio); return error; } static int s5pv210_button_remove(struct platform_device *pdev) { struct s5pv210_button_platdata *pdata = pdev->dev.platform_data; printk(KERN_INFO "button_driver.c: s5pv210_button_remove successful\n"); free_irq(IRQ_EINT2, pdev); free_irq(IRQ_EINT3, pdev); gpio_free(pdata[0].gpio); gpio_free(pdata[1].gpio); input_unregister_device(button_dev); return 0; } static struct platform_driver s5pv210_button_driver = { .probe = s5pv210_button_probe, .remove = s5pv210_button_remove, .driver = { .name = "s5pv210-button", .owner = THIS_MODULE, } }; static int __init s5pv210_button_init(void) { return platform_driver_register(&s5pv210_button_driver); } static void __exit s5pv210_button_exit(void) { platform_driver_unregister(&s5pv210_button_driver); } module_init(s5pv210_button_init); module_exit(s5pv210_button_exit); MODULE_AUTHOR("musk"); MODULE_DESCRIPTION("s5pv210 button driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("button-s5pv210"); 3.2.3. button_device_driver.h #ifndef __BUTTON_DEVICE_DRIVER_H #define __BUTTON_DEVICE_DRIVER_H #define BUTTON_LEFT_IRQ IRQ_EINT2 #define BUTTON_DOWN_IRQ IRQ_EINT3 #include <mach/irqs.h> #include <linux/interrupt.h> struct gpioIRQ { char *name; unsigned int irq; unsigned long irq_trigger; }; struct s5pv210_button_platdata { char *name; unsigned int gpio; unsigned int gpio_cfg; unsigned int button_code; struct gpioIRQ *button_irq; }; #endif /* __ASM_ARCH_LEDSGPIO_H */