您的位置:首页 > 其它

输入子系统

2015-12-01 17:09 381 查看
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(),打开和读写输入设备

对主设备号为INPUT_MAJOR的设备节点进行操作,会将操作集转换成handler的操作集:

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_open()函数:

用户可以这样调用open("/dev/input/event1",O_RDONLY)函数打开设备节点时,会调用evdev_fops中的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; 得到在evdev_table[]中的序号

int error;

if (i >= EVDEV_MINORS)

return -ENODEV;

error = mutex_lock_interruptible(&evdev_table_mutex);

if (error)

return error;

evdev = evdev_table[i];

if (evdev)

get_device(&evdev->dev);

mutex_unlock(&evdev_table_mutex);

if (!evdev)

return -ENODEV;

client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); 分配并初始化一个client结构体,并将它和evdev关联起来

if (!client) {

error = -ENOMEM;

goto err_put_evdev;

}

spin_lock_init(&client->buffer_lock);

client->evdev = evdev;

evdev_attach_client(evdev, client); 将client挂到evdev->client_list上

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_open_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据:

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;

else if (!evdev->open++) { 如果是第一次打开,就会调用input_open_device()打开evdev对应的handle

retval = input_open_device(&evdev->handle);

if (retval)

evdev->open--;

}

mutex_unlock(&evdev->mutex);

return retval;

}

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++;

if (!dev->users++ && dev->open)

retval = dev->open(dev);

if (retval) {

dev->users--;

if (!--handle->open) {

/*

* Make sure we are not delivering any more events

* through this handle

*/

synchronize_rcu();

}

}

out:

mutex_unlock(&dev->mutex);

return retval;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: