一、什么是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设备的一个链表头)
};
View Code
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的链表头)
};
View Code
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;
};
View Code
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;
}
View Code
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;
}
View Code
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;
}
View Code
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;
}
View Code
三. 驱动开发者开发流程
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 */
View Code