i.MX6ULL驱动开发 | 20 - Linux input 子系统

Posted Mculover666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了i.MX6ULL驱动开发 | 20 - Linux input 子系统相关的知识,希望对你有一定的参考价值。

一、input子系统概述

1. 什么是input子系统

input子系统是管理输入的子系统,和pinctrl、gpio子系统一样,都是Linux内核针对某一类设备创建的框架。

2. input子系统的作用

输入设备都是典型的字符设备,比如按键、鼠标、键盘、触摸屏等。

这些设备的工作机理是底层在按键、触摸等动作发生时产生一个中断,或者通过软件定时器去查询时间,然后CPU通过SPI、I2C等总线读取键值、坐标等数据,并将它们放入一个缓冲区,由驱动模块管理这些缓冲区,通过驱动的read接口就可以让用户读取键值、坐标等数据。

显然 ,在这些工作中,只有中断、读取键值/坐标这些操作是与硬件相关的,而输入事件的管理则对输入设备是通用的,因此,Linux内核设计了input子系统,由核心层处理公共的工作。

有了input子系统后,驱动编写时就不需要考虑应用层的事情,只需要按照input子系统的要求,上报输入事件即可

3. input子系统的框架

input子系统分为input驱动层、input核心层、input事件处理层。

  • input驱动层:输入设备的具体驱动程序,比如按键驱动,向内核层报告输入事件
  • input核心层:对下提供设备注册和操作接口,对上通知input事件层对输入事件进行处理
  • input事件层:和用户空间进行交互

二、input子系统使用方法

1. 输入设备

input子系统的核心层会向Linux内核注册一个字符设备,主设备号为INPUT_MAJOR,

#define INPUT_MAJOR		13

因此,input子系统所有设备主设备号都为13,在使用input子系统时不需要再注册字符设备,只注册一个输入设备即可。

1.1. input_dev结构体

Linux内核用 input_dev 结构体描述一个输入设备,定义在文件 include/linux/input.h 文件中,如下:

/**
 * 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)
 * @propbit: bitmap of device properties and quirks
 * @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
 * @hint_events_per_packet: average number of events generated by the
 *	device in a packet (between EV_SYN/SYN_REPORT events). Used by
 *	event handlers to estimate size of the buffer needed to hold
 *	events.
 * @keycodemax: size of keycode table
 * @keycodesize: size of elements in keycode table
 * @keycode: map of scancodes to keycodes for this device
 * @getkeycode: optional legacy method to retrieve current keymap.
 * @setkeycode: optional method to alter current keymap, used to implement
 *	sparse keymaps. If not supplied default mechanism will be used.
 *	The method is being called while holding event_lock and thus must
 *	not sleep
 * @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
 * @rep: current values for autorepeat parameters (delay, rate)
 * @mt: pointer to multitouch state
 * @absinfo: array of &struct input_absinfo elements holding information
 *	about absolute axes (current value, min, max, flat, fuzz,
 *	resolution)
 * @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
 * @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
 * @num_vals: number of values queued in the current frame
 * @max_vals: maximum number of values queued in a frame
 * @vals: array of values queued in the current frame
 * @devres_managed: indicates that devices is managed with devres framework
 *	and needs not be explicitly unregistered or freed.
 */
struct input_dev 
	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;

	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

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

	unsigned int keycodemax;
	unsigned int keycodesize;
	void *keycode;

	int (*setkeycode)(struct input_dev *dev,
			  const struct input_keymap_entry *ke,
			  unsigned int *old_keycode);
	int (*getkeycode)(struct input_dev *dev,
			  struct input_keymap_entry *ke);

	struct ff_device *ff;

	unsigned int repeat_key;
	struct timer_list timer;

	int rep[REP_CNT];

	struct input_mt *mt;

	struct input_absinfo *absinfo;

	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 (*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 __rcu *grab;

	spinlock_t event_lock;
	struct mutex mutex;

	unsigned int users;
	bool going_away;

	struct device dev;

	struct list_head	h_list;
	struct list_head	node;

	unsigned int num_vals;
	unsigned int max_vals;
	struct input_value *vals;

	bool devres_managed;
;

1.2. 分配/释放一个输入设备

(1)申请一个输入设备

/**
 * input_allocate_device - allocate memory for new input device
 *
 * Returns prepared struct input_dev or %NULL.
 *
 * NOTE: Use input_free_device() to free devices that have not been
 * registered; input_unregister_device() should be used for already
 * registered devices.
 */
struct input_dev *input_allocate_device(void);

(2)释放输入设备

/**
 * input_free_device - free memory occupied by input_dev structure
 * @dev: input device to free
 *
 * This function should only be used if input_register_device()
 * was not called yet or if it failed. Once device was registered
 * use input_unregister_device() and memory will be freed once last
 * reference to the device is dropped.
 *
 * Device should be allocated by input_allocate_device().
 *
 * NOTE: If there are references to the input device then memory
 * will not be freed until last reference is dropped.
 */
void input_free_device(struct input_dev *dev)

1.3. 注册/注销输入设备

(1)向内核注册输入设备

/**
 * input_register_device - register device with input core
 * @dev: device to be registered
 *
 * This function registers device with input core. The device must be
 * allocated with input_allocate_device() and all it's capabilities
 * set up before registering.
 * If function fails the device must be freed with input_free_device().
 * Once device has been successfully registered it can be unregistered
 * with input_unregister_device(); input_free_device() should not be
 * called in this case.
 *
 * Note that this function is also used to register managed input devices
 * (ones allocated with devm_input_allocate_device()). Such managed input
 * devices need not be explicitly unregistered or freed, their tear down
 * is controlled by the devres infrastructure. It is also worth noting
 * that tear down of managed input devices is internally a 2-step process:
 * registered managed input device is first unregistered, but stays in
 * memory and can still handle input_event() calls (although events will
 * not be delivered anywhere). The freeing of managed input device will
 * happen later, when devres stack is unwound to the point where device
 * allocation was made.
 */
int input_register_device(struct input_dev *dev);

(2)从内核注销输入设备

/**
 * input_unregister_device - unregister previously registered device
 * @dev: device to be unregistered
 *
 * This function unregisters an input device. Once device is unregistered
 * the caller should not try to access it as it may get freed at any moment.
 */
void input_unregister_device(struct input_dev *dev);

2. 上报输入事件

2.1. 上报指定的事件以及对应的值

/**
 * 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 to report input events. See also input_inject_event().
 *
 * NOTE: input_event() may be safely used right after input device was
 * allocated with input_allocate_device(), even before it is registered
 * with input_register_device(), but the event will not reach any of the
 * input handlers. Such early invocation of input_event() may be used
 * to 'seed' initial state of a switch or initial position of absolute
 * axis, etc.
 */
void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)

参数意义如下:

  • dev:需要上报的输入设备
  • type:上报的事件类型
  • code:事件码
  • value:事件值

2.2. 针对特定事件的上报函数

(1)上报按键

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)

	input_event(dev, EV_KEY, code, !!value);

(2)上报相对坐标

static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)

	input_event(dev, EV_REL, code, value);

(3)上报绝对坐标

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)

	input_event(dev, EV_ABS, code, value);

2.3. 告知内核上报结束

static inline void input_sync(struct input_dev *dev)

	input_event(dev, EV_SYN, SYN_REPORT, 0);

三、应用获取事件

Linux内核使用 input_event 结构体来描述所有的输入事件,定义在 include/uapi/linux/input.h 头文件中:

/*
 * The event structure itself
 */

struct input_event 
	struct timeval time;
	__u16 type;
	__u16 code;
	__s32 value;
;

其中每个成员的意义如下:

  • time:事件发生的时间
  • type:事件类型
  • code:事件码
  • value:事件值

所有的输入设备最终都是按照input_event结构体呈现给用户,用户应用程序可以通过 input_event来获取到具体的输入事件或相关的值,比如按键值等。

以上是关于i.MX6ULL驱动开发 | 20 - Linux input 子系统的主要内容,如果未能解决你的问题,请参考以下文章

i.MX6ULL驱动开发 | 30 - 使用EC20 4G网卡(移植移远GobiNet驱动)

i.MX6ULL驱动开发 | 30 - 使用EC20 4G网卡(移植移远GobiNet驱动)

i.MX6ULL驱动开发1——字符设备开发模板

i.MX6ULL驱动开发 | 15 - Linux UART 驱动框架

i.MX6ULL驱动开发 | 09 -基于Linux自带的LED驱动点亮LED

i.MX6ULL驱动开发 | 08 -基于pinctrl子系统和gpio子系统点亮LED