1、LInux事件输入子系统包括三个层次:事件处理层(input_handler)、核心层(input core)、驱动层(inout driver)。
事件处理层:负责于用户程序打交道,将硬件驱动层传来的事件报告给用户程序(实际上就是提供了对应的文件操作函数接口给用户程序)。内核中出现时间=用户打开设备几点文件,在驱动的open函数中找到对应的结构体。
核心层(core):链接事件处理层和驱动层之间的桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
驱动层:负责操作具体的硬件设备,这层代码针对具体的驱动程序。键盘、鼠标、触摸屏等字符设备驱动功能的实现工作。(实际上是对应在一个具体设备的驱动代码中,在驱动代码中获取事件,并且上报给事件处理层,由该层的用户接口提供给用户程序)
2、层次结构如下:
-------------用户程序--------------
-------------用户接口--------------
-------------事件处理层-----------
-------------输入子系统核心层---
-------------设备驱动层------------
-------------硬件驱动---------------
3、三个重要的结构体
1) input_dev结构体表示底层硬件设备,是所有输入设备的抽象。比如按键事件上报,要编写按键(GPIO)的驱动代码,这是具体的硬件,在驱动代码中input_dev负责获取事件,那么这里的input_dev就代表了按键这个事件,成为了代表具体硬件的结构体,上报的事件就是按键驱动的事件。
2) input_handle结构体是连接底层硬件和上层事件处理层
3) input_handler表示事件处理器,是对事件处理的抽象。(纯软件层)
input_dev的内容:
/**
* struct input_dev - represents an input device
* @name: name of the device
* @phys: physical path to the device in the system hierarchy
* @uniq: unique identification code for the device (if device has it)
* @id: id of the device (struct input_id)
* @evbit: bitmap of types of events supported by the device (EV_KEY,
* EV_REL, etc.)
* @keybit: bitmap of keys/buttons this device has
* @relbit: bitmap of relative axes for the device
* @absbit: bitmap of absolute axes for the device
* @mscbit: bitmap of miscellaneous events supported by the device
* @ledbit: bitmap of leds present on the device
* @sndbit: bitmap of sound effects supported by the device
* @ffbit: bitmap of force feedback effects supported by the device
* @swbit: bitmap of switches present on the device
* @keycodemax: size of keycode table
* @keycodesize: size of elements in keycode table
* @keycode: map of scancodes to keycodes for this device
* @setkeycode: optional method to alter current keymap, used to implement
* sparse keymaps. If not supplied default mechanism will be used
* @getkeycode: optional method to retrieve current keymap. If not supplied
* default mechanism will be used
* @ff: force feedback structure associated with the device if device
* supports force feedback effects
* @repeat_key: stores key code of the last key pressed; used to implement
* software autorepeat
* @timer: timer for software autorepeat
* @sync: set to 1 when there were no new events since last EV_SYNC
* @abs: current values for reports from absolute axes
* @rep: current values for autorepeat parameters (delay, rate)
* @key: reflects current state of device's keys/buttons
* @led: reflects current state of device's LEDs
* @snd: reflects current state of sound effects
* @sw: reflects current state of device's switches
* @absmax: maximum values for events coming from absolute axes
* @absmin: minimum values for events coming from absolute axes
* @absfuzz: describes noisiness for axes
* @absflat: size of the center flat position (used by joydev)
* @open: this method is called when the very first user calls
* input_open_device(). The driver must prepare the device
* to start generating events (start polling thread,
* request an IRQ, submit URB, etc.)
* @close: this method is called when the very last user calls
* input_close_device().
* @flush: purges the device. Most commonly used to get rid of force
* feedback effects loaded into the device when disconnecting
* from it
* @event: event handler for events sent _to_ the device, like EV_LED
* or EV_SND. The device is expected to carry out the requested
* action (turn on a LED, play sound, etc.) The call is protected
* by @event_lock and must not sleep
* @grab: input handle that currently has the device grabbed (via
* EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
* recipient for all input events coming from the device
* @event_lock: this spinlock is is taken when input core receives
* and processes a new event for the device (in input_event()).
* Code that accesses and/or modifies parameters of a device
* (such as keymap or absmin, absmax, absfuzz, etc.) after device
* has been registered with input core must take this lock.
* @mutex: serializes calls to open(), close() and flush() methods
* @users: stores number of users (input handlers) that opened this
* device. It is used by input_open_device() and input_close_device()
* to make sure that dev->open() is only called when the first
* user opens device and dev->close() is called when the very
* last user closes the device
* @going_away: marks devices that are in a middle of unregistering and
* causes input_open_device*() fail with -ENODEV.
* @dev: driver model's view of this device
* @h_list: list of input handles associated with the device. When
* accessing the list dev->mutex must be held
* @node: used to place the device onto input_dev_list
*/
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 (*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;
int going_away;
struct device dev;
struct list_head h_list;
struct list_head node;
};
inpuit_handle的内容:
/**
* struct input_handle - links input device with an input handler
* @private: handler-specific data
* @open: counter showing whether the handle is 'open', i.e. should deliver
* events from its device
* @name: name given to the handle by handler that created it
* @dev: input device the handle is attached to
* @handler: handler that works with the device through this handle
* @d_node: used to put the handle on device's list of attached handles
* @h_node: used to put the handle on handler's list of handles from which
* it gets events
*/
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
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;
};
4、核心层提供的注册接口
1) input_dev
//注册input_dev ,把它加载入子系统创建好的链表中,并且查找input_handler链表,两者进行匹配
int input_register_device(struct input_dev *dev);
//取消注册函数
void input_unregister_device(struct input_dev *dev);
2) input_handler
//注册函数,和input_dev一样的过程
int input_register_handler(struct input_handler *handler);
void input_unregister_handler(struct input_handler *handler);
3) input_handle(在input_dev和input_handler匹配之后,input_handler的connect函数就会被调用,构建一个input_handle,把input_dev和input_handler与她建立联系,input_handle存入链表中,提供给底层驱动中input_dev查找到input_handler)
//在input_handler的connect函数中调用
int input_register_handle(struct input_handle *handle);
void input_unregister_handle(struct input_handle *handle);
5、分析事件的处理过程
a、用户空间程序打开一个输入事件处理的设备驱动
b、系统调用该字符设备驱动程序里面的open函数,可以看到input_handler 被handler = input_table[iminor(inode) >> 5];找到,使用的是打开的设备节点的子设备号,在input_handler 的数组中查找。input_register_handler中把input_handler 放到input_table里面。
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];
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) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op;
file->f_op = new_fops;
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
unlock_kernel();
return err;
}
c、在input_open_file函数中,获取input_handler 的fops,这些fops是实际对应具体的输入设备的,fops的实现是根据设备的特点实现的,所以可以看到input_handler 是内核里面已经实现好的,鼠标、键盘等设备的用户空间接口均由input_handler 实现,并挂载在全局链表中,input_dev注册的时候,就通过支持的事件类型(static const struct input_device_id)和input_handler 匹配,建立事件传输的通道(还是id匹配的方式),MODULE_DEVICE_TABLE(input, joydev_ids);为实现了热拔插提供了信息文件。
d、读取事件类型的接口函数read中会发生睡眠wait_event_interruptible(没有事件到达时,就会停止在这里,等待在其它的进程中唤醒这个等待队列),唤醒队列不难,获得这个队列头就可以实现。这个发生在input_handler中,队列头也在这里,所以唤醒这个队列就需要在input_handler中实现(为了可以获得input_handler中队列头,如果在这个域之外,那就不好了),input_handler中的event接口函数就完成了这个功能, 根据事件及其类型,封装好input_event。最后唤醒等待的读取进程。那么这个event接口函数是被谁调用的呢?看下一点内容
static void mousedev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct mousedev *mousedev = handle->private;
switch (type) {
case EV_ABS:
/* Ignore joysticks */
if (test_bit(BTN_TRIGGER, handle->dev->keybit))
return;
if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
mousedev_touchpad_event(handle->dev,
mousedev, code, value);
else
mousedev_abs_event(handle->dev, mousedev, code, value);
break;
case EV_REL:
mousedev_rel_event(mousedev, code, value);
break;
case EV_KEY:
if (value != 2) {
if (code == BTN_TOUCH &&
test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
mousedev_touchpad_touch(mousedev, value);
else
mousedev_key_event(mousedev, code, value);
}
break;
case EV_SYN:
if (code == SYN_REPORT) {
if (mousedev->touch) {
mousedev->pkt_count++;
/*
* Input system eats duplicate events,
* but we need all of them to do correct
* averaging so apply present one forward
*/
fx(0) = fx(1);
fy(0) = fy(1);
}
mousedev_notify_readers(mousedev, &mousedev->packet);
mousedev_notify_readers(mousedev_mix, &mousedev->packet);
mousedev->packet.dx = mousedev->packet.dy =
mousedev->packet.dz = 0;
mousedev->packet.abs_event = 0;
}
break;
}
}
e、input_handler里面的event具有接收事件和整理事件、唤醒等待队列的功能,那么是什么时候调用的呢?怎么调用的呢?a-d的步骤已经实现了一个input_dev、input_handle、input_handler三者之间的联系,input_dev和input_handler一一对应。事件获取实在底层驱动中获得的,因为事件是硬件产生的。事件传输的方向是input_dev----->input_handler。它们之间事件载体是input_event结构体。
/*
* The event structure itself
*/
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input
* devices. See also input_inject_event().
*/
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
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);
}
}
如上:input_event的传递设计到input_event函数,执行的流程为:
input_event
————》input_handle_event
——————》input_pass_event
————————》handle->handler->event(handle,type, code, value);
可以看出,最终使用的就是handler里面的event函数接口,并且表示事件的结构体也被传入了对应的数据填充。
input_event实在底层设备驱动调用,说明写一个事件输入子系统的事件传输驱动,我们只是需要考虑input_dev的实现,input_handler在内核中已经存在。
f、input_dev中open,close的调用
/**
* input_open_device - open input device
* @handle: handle through which device is being accessed
*
* This function should be called by input handlers when they
* want to start receive events from given input device.
*/
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++;
/*
执行input_dev的open函数,在查询匹配的过程中自动执行的
if (!dev->users++ && dev->open)
retval = dev->open(dev);
这个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;
}
close是反向过程。
6、在输入子系统框架下我们如何实现一个事件的传输
(1)流程如下:
----------------分配一个输入设备input_dev---------------
struct input_dev *input_allocate_device(void);
---------------------注册一个输入设备-----------------------
int __must_check input_register_device(struct input_dev *);
---------------------向上提交输入事件-----------------------
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_REL, code, value);
}
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_FF_STATUS, code, value);
}
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_SW, code, !!value);
}
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
static inline void input_mt_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}
---------------------注销一个输入设备-----------------------
void input_unregister_device(struct input_dev *);
---------------------释放一个输入设备-----------------------
void input_free_device(struct input_dev *dev);
7、设置支持的事件类型、事件类型中支持的事件种类
#define set_bit(nr,p) ATOMIC_BITOP_LE(set_bit,nr,p)
事件类型
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
/*
* Event types
*/
#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
#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)
事件种类
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)];
事件具体的种类在input.h头文件中声明,以事件类型为命名的开头。比如
/*
* Relative axes
*/
#define REL_X 0x00
#define REL_Y 0x01
#define REL_Z 0x02
#define REL_RX 0x03
#define REL_RY 0x04
#define REL_RZ 0x05
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX+1)
8、input_event事件结构体的内部成员
/*
* The event structure itself
*/
struct input_event {
struct timeval time; //按键时间
__u16 type; //类型,在下面有定义
__u16 code; //要模拟成什么按键
__s32 value;//是按下还是释放
};
9、模拟的事件子系统使用例子(其它实际的情景中用法流程是一样的,只是引发事件上报的条件不同)
#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)
{
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 enough memory\n");
error = -ENOMEM;
goto err_free_irq;
}
button_dev->evbit[0] = BIT_MASK(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);
10、input_handler的注册
static int __init mousedev_init(void)
——》mousedev_create
——》input_register_handler
——》misc_register
以上三步实现事件设备的初始化和注册,以及注册与之相关联的字符设备。
如果要为设备实现一个事件子系统input handler部分,需要按照上面的三个步骤实现:
static int __init mousedev_init(void)
{
int error;
//实例化代表一个具体器件的事件处理的结构体
//在这个实例结构体中主要是基于handle扩展的,根据实际情况填充
mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX);
if (IS_ERR(mousedev_mix))
return PTR_ERR(mousedev_mix);
//注册事件处理器,包含了所支持设备的id列表
error = input_register_handler(&mousedev_handler);
if (error) {
mousedev_destroy(mousedev_mix);
return error;
}
//生成杂设备
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
error = misc_register(&psaux_mouse);
if (error)
printk(KERN_WARNING "mice: could not register psaux device, "
"error: %d\n", error);
else
psaux_registered = 1;
#endif
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
return 0;
}