输入子系统
Posted 专注it
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了输入子系统相关的知识,希望对你有一定的参考价值。
Linux的输入子系统不仅支持鼠标、键盘等常规的输入设备,而且还支持蜂鸣器、触摸屏等设备
输入子系统又叫input子系统。其构建非常灵活,只需要调用一些简单的函数,就可以将一个输入设备的功能呈现给应用程序
#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
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
input_handler是输入设备的事件处理接口,为处理事件提供一个统一的函数模板,程序员应该根据具体的需要实现其中的一些函数,并将其注册到输入子系统中。该结构体的定义如下:
- 01 struct input_handler {
- 02 void *private;
- 03 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
- 04 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
- 05 void (*disconnect)(struct input_handle *handle);
- 06 void (*start)(struct input_handle *handle);
- 07 const struct file_operations *fops;
- 08 int minor;
- 09 const char *name;
- 10 const struct input_device_id *id_table;
- 11 const struct input_device_id *blacklist;
- 12 struct list_head h_list;
- 13 struct list_head node;
- 14 };
对该结构体简要分析如下。
第02行,定义了一个private指针,表示驱动特定的数据。这里的驱动指的就是handler处理器。
第03行,定义了一个event()处理函数,这个函数将被输入子系统调用去处理发送给设备的事件。例如将发送一个事件命令LED灯点亮,实际控制硬件的点亮操作就可以放在event()函数中实现。
第04行,定义了一个connect()函数,该函数用来连接handler和input_dev。在input_attach_handler()函数的第10行,就是回调的这个自定义函数。
第05行,定义了一个disconnect()函数,这个函数用来断开handler和input_dev之间的联系。
第07行,表示handler实现的文件操作集,这里不是很重要。
第08行,表示设备的次设备号。
第09行,定义了一个name,表示handler的名字,显示在/proc/bus/input/handlers目录中。
第10行,定义了一个id_table表,表示驱动能够处理的表。
第11行,指向一个input_device_id表,这个表包含handler应该忽略的设备。
第12行,定义了一个链表h_list,表示与这个input_handler相联系的下一个handler。
第13行,定义了一个链表node,将其连接到全局的input_handler_list链表中,所有的input_handler都连接在其上。
struct input_dev {
const char *name;//设备名
const char *phys;
const char *uniq;
struct input_id id;//用于匹配事件处理层handler
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)];//led
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];//beep
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;//最后一次同步后没有新的事件置1
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)];//反映当前led状态的位图
unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反映当前beep状态的位图
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
/*tp驱动代码里一般使用input_set_abs_params函数设置
函数参数从右往左依次代表输入设备指针、坐标轴、最小值、最大值、分辨率、基准值。
最后两个参数也可以填为0,代表设备非常精确并且总能精确的回到中心位置。*/
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;//handle链表
struct list_head node;//input_dev链表
};
struct input_event是事件传送的载体,输入子系统的事件都是包装成struct input_event传给用户空间。各个成员如下所示:
/* include/linux/input.h */
struct input_event {
struct timeval time;//时间戳
__u16 type;//事件类型
__u16 code;//事件代码
__s32 value;//事件值,如坐标的偏移值
};
struct input_dev注册的时候需要跟匹配的hanlder建立连接,匹配的依据就是struct input_dev所包含的struct input_id。
/* include/linux/input.h */
struct input_id {
__u16 bustype;//总线类型
__u16 vendor;//生产商编号
__u16 product;//产品编号
__u16 version;//版本号
};
input_handler这个结构体是事件驱动的主体,每一种处理方式对应一个handler结构体。注册input_handler,其实就是将
input_hangler加入到input_handler_list当中。使用input_register_handler注册。
/* include/linux/input.h */
struct input_handler {
//私有数据指针
void *private;
//事件处理函数指针。设备驱动报告的事件最终由这个函数来处理
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
//连接handler和input_dev的函数指针
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
//断开连接函数指针
void (*disconnect)(struct input_handle *handle);
//为给定的handle启动handler函数指针
void (*start)(struct input_handle *handle);
//文件操作结构体
const struct file_operations *fops;
//这个handler可以使用的32个次设备号的最小值
int minor;
//此handler的名字
const char *name;
//可以处理的input_device_ids列表
const struct input_device_id *id_table;
//需要被忽略的input_device_ids列表
const struct input_device_id *blacklist;
//用来连接handle的链表链表节点。每个与此handler相关的handle都放入此链表
struct list_head h_list;
//用来放入全局handler链表的节点
struct list_head node;
};
input_handle这个结构体用来连接input_dev和input_handler。
/* include/linux/input.h */
struct input_handle {
void *private;//私有数据指针
int open;//记录本设备被打开的次数
const char *name;//创建此handle的handler所赋予的名字
struct input_dev *dev;//指向附着的input_dev
struct input_handler *handler;//指向创建此handle的handler
struct list_head d_node;//链表节点,用来加入附着的input_dev
struct list_head h_node;//链表节点,用来加入附着的input_handler
};
input_dev和input_handler匹配过程中用到了input_device_id
/* include/linux/mod_devicetable.h */
struct input_device_id {
kernel_ulong_t flags;//定义需要匹配input_id的哪些域
__u16 bustype;//对应input_id的四个数据域
__u16 vendor;
__u16 product;
__u16 version;
//存储支持事件的位图,与input_dev中的同名数据成员功能一致
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;//指示结构体中是否含有驱动信息
};
先看一个简单的输入设备驱动程序的例子:
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/io.h>
static struct input_dev *button_dev; //输入设备结构体
static irqreturn_t button_interrupt(int irq, void *dummy, struct pt_regs *fp) //中断处理函数
{
input_report_key(&button_dev, BTN_0, inb(BUTTON_PORT) & 1); //向输入子系统报告产生的按键事件
input_sync(&button_dev); //通知接收者,一个报告发送完毕
return IRQ_HANDLED;
}
static int __init button_init(void)
{
int error;
if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) { //申请中断处理函数
printk(KERN_ERR "button.c: Can\'t allocate irq %d\\n", button_irq); //申请失败
return -EBUSY;
}
button_dev = input_allocate_device(); //分配一个设备结构体
if(!button_dev) //
{
printk(KERN_ERR"button.c:Not enougu memory\\n");
error = -ENOMEM;
goto err_free_irq;
}
button_dev.evbit[0] = BIT(EV_KEY); //设置按键信息
button_dev.keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
error = input_register_device(&button_dev); //注册一个输入设备
if(error)
{
printk("KERN_ERR"button.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(BUTTON_IRQ,button_interrupt);
return error;
}
static void __exit button_exit(void) //卸载函数
{
input_unregister_device(&button_dev); //注销按键设备
free_irq(BUTTON_IRQ, button_interrupt); //释放按键占用的中断线
}
module_init(button_init);
module_exit(button_exit);
input_allocate_device()函数在内存中为输入设备结构体分配一个空间,并对其主要成员进行了初始化。
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); //分配一个input_dev结构体,并且初始化为0
if (dev) {
dev->dev.type = &input_dev_type; //初始化设备的类型
dev->dev.class = &input_class; //设置为输入设备类
device_initialize(&dev->dev); //初始化device结构
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;
}
该函数返回一个指向input_dev类型的指针,该结构体是一个输入设备结构体,包含输入设备的一些相关信息,如设备支持的按键码、设备的名字、设备支持的事件等
input_register_device()函数注册输入子系统设备结构体 调用失败用input_allocate_deivce()函数释放input_allocate_device()分配的空间。卸载用input_unregister_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); //设置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",
(unsigned long) atomic_inc_return(&input_no) - 1); //设置input_dev中的device的名字,出现在sysfs系统中
error =
device_add(&dev->dev);
//将input_dev包含的device结构注册到Linux设备模型中,并可以以sysfs文件系统表现出来
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);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
input_attach_handler()函数用于匹配input_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)) //设备和处理函数之间的匹配
return -ENODEV;
id = input_match_device(handler->id_table, dev);
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
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_match_device()函数用来与input_dev和handler进行匹配。handler的id_table表中定义了其支持的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)
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);
return id;
}
return NULL;
}
#define MATCH_BIT(bit, max) \\
for (i = 0; i < BITS_TO_LONGS(max); i++) \\
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \\
break; \\
if (i != BITS_TO_LONGS(max)) \\
continue;
只有当input_device和input_handler的ID成员在evbit,keybit,...,swbit项相同才会匹配成功。而且匹配顺序是从evbit,keybit,到swbit。只要有一项不同就会循环到ID下一项比较
input_report_key()函数向输入子系统报告发生的事件
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
第二个参数是产生的事件,可以去BTN_0,BTN_1,BTN_LEFT,BTN_RIGHT等值
input_event() 向输入子系统报告输入设备产生的事件:
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value) //type是事件的类型,EV_KEY,EV_REL等等
{
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);
}
}
is_event_supported函数检查输入设备是否支持该事件:
static inline int is_event_supported(unsigned int code,
unsigned long *bm, unsigned int max)
{
return code <= max && test_bit(code, bm);
}
input_handle_event()函数向输入子系统传达事件信息:
参数1,输入设备 参数2,事件类型 参数3,键码 参数4,键值
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;
}
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); //处理重复按键的情况
}
disposition = INPUT_PASS_TO_HANDLERS; //事件需要handler来处理,交给handler处理
}
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)) {
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) //处理EV_SYN事件
dev->sync = 0;
if ((disposition & INPUT_PASS_TO_DEVICE) &&
dev->event)
//交给input_dev处理,并且处理事件存在event()用来想输入子系统报告一个将要发送给设备的事件
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS) //交给handler处理,调用input_pass_event()处理
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_handle *handle; 分配一个handle结构的指针
rcu_read_lock();
handle = rcu_dereference(dev->grab); 得到dev->grab的指针,grab是强制为input device的handler,这时要调用handler的event函数
if (handle)
handle->handler->event(handle, type, code, value);
else 如果没有为input device强制指定handler,即为grap赋值,就遍历input device->h_list上的handle成员,如果该handle被打开,表示该设备已经被一个用户进程使用
list_for_each_entry_rcu(handle, &dev->h_list,
d_node)
就会调用与输入设备对应的handler的event()函数。注意,只有在handle被打开的情况下才会收到事件,这
if
(handle->open)
就是说,只有设备被用户程序使用时,才有必要向用户导出信息。
handle->handler->event(handle,
type, code, value);
rcu_read_unlock();
}
事件处理过程如下:
input_handler是输入子系统的主要数据结构,一般将其称为handler处理器,表示对输入事件的具体处理。input_handler为输入设备的功能实现了一个接口,输入事件最终传递到handler处理器,handler处理器根据一定的规则,然后对事件进行处理。
struct input_dev 物理输入设备的基本数据结构,包含设备相关的一些信息
struct input_handler 事件处理结构体,定义怎么处理事件的逻辑
struct input_handle 用来创建input_dev和input_handler之间的关系的结构体
struct input_handler {
void *private; 表示驱动特定的数据。这里的驱动指的就是handler处理器
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);
连接handler和input_dev在input_attach_handler中调用
void (*disconnect)(struct input_handle *handle); 断开handler和input_dev之间的联系
void (*start)(struct input_handle *handle);
const struct file_operations *fops; handler实现的文件操作集
int minor; 次设备号
const char *name; 表示handler的名字,显示在/proc/bus/input/handlers/目录中
const struct input_device_id *id_table; 表示驱动能够处理的表
const struct input_device_id *blacklist; 应该忽略的设备
struct list_head h_list; 与这个handler相联系的下一个handler
struct list_head node; 将其连接到全局的input_handler_list链表中,所有的input_handler都连接在其上
};
input_register_handler()函数注册一个新的input handler处理器。这个handler将为输入设备使用,一个handler可以添加到多个支持它的设备中,也就是一个handler可以处理多个输入设备的事件:
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);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) { 次设备号右移5位作为索引值插入到input_table[]中
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
list_add_tail(&handler->node, &input_handler_list); 插入到全局链表
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
return retval;
}
struct input_handle {
void *private; handle特定的数据
int open; 表示handle是否正在被使用,当使用时,会将事件分发给设备处理
const char *name; handle的名字
struct input_dev *dev; 表示该handle依附的input_dev设备
struct input_handler *handler; 与设备相关的handler处理器
struct list_head d_node; 将handle放到设备相关的链表中,也就是放到input_dev->h_list表示的链表中
struct list_head h_node; 将handle放到input_handler相关的链表中,也就是handler->h_list表示的链表中
};
input_handle是用来连接input_dev和input_handler的一个中间结构体。事件通过input_handle从input_dev发送到input_handler,或者从input_handler发送到input_dev进行处理。在使用input_handle之前,需要对其进行注册,注册函数input_register_handle():
该函数接受一个注册前初始化的input_handle类型指针来注册一个新的handle到输入子系统
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;
list_add_tail_rcu(&handle->d_node, &dev->h_list); 将handle加入到dev->h_list链表中
mutex_unlock(&dev->mutex);
synchronize_rcu();
/*
* 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(&handle->h_node, &handler->h_list); 将handle加入到handler->h_list链表中
if (handler->start)
handler->start(handle);
return 0;
}
input_dev、input_handler和input_handle的关系:
input子系统:
在Linux中,输入子系统作为一个模板存在,向上,为用户提供接口函数,向下,为驱动层程序提供统一的接口函数。这样,就能够使输入设备的事件通过输入子系统发送给用户层应用程序,用户程序也可以通过输入子系统通知驱动程序完成某项功能
初始化函数input_init():
先看几个结构的定义:
struct class input_class = { 结构体的定义
.name = "input",
};
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
static int __init input_init(void)
{
int err;
err = class_register(&input_class); 注册input类,所有input
device都属于这个类,在sysfs中表现就是,所有input device所代表的目录都位于/dev/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);
注册主设备号为INPUT_MAJOR(13)次设备号为0~255的字符设备。它的操作指针为input_fops,所有主设备号为13的字符设备的操作,都会转入到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;
}
input_open_file()函数,该函数将控制input_handler中定义的fops文件指针的open()函数。该函数在input_handler中实现,这样就使不同的handler处理器对应了不同的文件打开方法:
static struct input_handler *input_table[8];
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;
lock_kernel();
/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5]; 取出handler处理器,原理是前面赋值的时候,也可看出一个handler代表32个设备节点
if (!handler || !(new_fops = fops_get(handler->fops))) {
err = -ENODEV;
goto out;
}
/*
* That\'s _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) { 判断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); 使用新的open()函数,重新打开设备
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
unlock_kernel();
return err;
}
evdev输入事件驱动:
evdev输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。evdev输入事件驱动从底层接收事件信息,将其反映到sys文件系统中,用户程序通过对sys文件系统的操作,就能够达到处理事件的能力
evdev以模块的方式被组织在内核中
初始化函数evdev_init():
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler); 注册evdev_handler事件处理器
}
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,
};
#define EVDEV_MINOR_BASE 64
因为handler可以处理32个设备,所以evdev_handler所能处理的设备文件范围为(13,64)~(13,64+32),其中13是所有输入设备的主设备号
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
evdev_ids没有定义flags,也没有定义匹配属性值。这个evdev_ids的意思就是:evdev_handler可以匹配所有input_dev设备,也就是所有input_dev发出的事件,都可以由evdev_handler来处理。匹配成功后会调用handler->connect()函数
evdev_connect()函数主要用来连接input_dev和input_handler,这样事件的流通链才能建立。流通链建立后,事件才知道被谁处理,或者处理后向谁返回结果:
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;
for (minor = 0; minor < EVDEV_MINORS; minor++) 宏为32,表示32个设备文件。找到evdev_table为空的那一项,这时,for结束时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); 分配一个struct evdev空间
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list); 初始化evdev
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor); event1,event2命名
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
dev_set_name(&evdev->dev, evdev->name);
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle); 注册一个input_handle结构体
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(evdev); 将evdev_table的minor项指向evdev
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev); 将evdev->device注册到sysfs文件系统中
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;
}
用户程序通过输入子系统创建的设备节点函数open(),read()和write(
以上是关于输入子系统的主要内容,如果未能解决你的问题,请参考以下文章