Linux输入子系统分析

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值