欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux驱动input子系统学习二(框架)

程序员文章站 2024-02-23 21:56:52
...

input输入子系统如何工作?

例如以一次鼠标按下事件为例子来说明我们的input输入子系统的工作过程:

设备驱动层:当我们按下鼠标左键的时候就会触发中断(中断是早就注册好的),就会去执行中断所绑定的处理函数,在函数中就会去读取硬件寄存器来判断按下的是哪个按键和状态 ---->

将按键信息上报给input core层  ---> input core层处理好了之后就会上报给input event层,input event层会将我们的输入事件封装成一个input_event结构体放入一个缓冲区中 --->  

应用层read就会将缓冲区中的数据读取出去。

此外,还存在一个返回路径 (return path)。返回路径允许给一个键盘设置 LED,给一个 force feedback joystick提供 motion commands。路径的两个方向(指从内核到用户的方向和从用户到内核的方向)使用相同的 event定义和不同的 type identifier。

 

更进一步理解Input 子系统,需要理解以下4个对象:

Input_device , handler,  handle , client

Input_device:  代表着具体的输入设备,它直接从硬件中读取数据,并以事件的形式转发


/**
 * 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.
 *	The method is being called while holding event_lock and thus must
 *	not sleep
 * @getkeycode: optional method to retrieve current keymap. 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
 * @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)
 * @absres: resolution used for events coming form absolute axes
 * @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;        /* z设备在分层系统的路径 */
	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,
			  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;      /* 重复上报键值,比如:键盘一直按着A不松手 */
	struct timer_list timer;      /* 重复上报的时间 */

	int sync;

	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)];
	unsigned long snd[BITS_TO_LONGS(SND_CNT)];
	unsigned long sw[BITS_TO_LONGS(SW_CNT)];

	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);    /* 设备具体的open函数 */
	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;        /* 用来挂接这个struct input_dev和所有struct handler的链表头 */
	struct list_head	node;   /* 作为链表节点挂接到  input_dev_list 链表上  (input_dev_list链表是input核心层维护的一个用来挂接所有input设备的一个链表头) */
};

Hanler:  代表接收某一类事件的上层接口,对应于一类事件设备文件


/**
 * 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
 * @filter: similar to @event; separates normal event handlers from
 *	"filters".
 * @match: called after comparing device's id with handler's id_table
 *	to perform fine-grained matching between device and handler
 * @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
 * @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.
 *
 * The very same structure is used to implement input filters. Input core
 * allows filters to run first and will not pass event to regular handlers
 * if any of the filters indicate that the event should be filtered (by
 * returning %true from their filter() method).
 *
 * 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);            /* 用于向上层上报输入事件的函数 */
	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);        /* 当handler 与 input_dev 匹配成功之后用来连接 */
	void (*disconnect)(struct input_handle *handle);    /* 断开handler 与 input_dev 之间的连接 */
	void (*start)(struct input_handle *handle);

	const struct file_operations *fops;
	int minor;            /* 该handler 的编号 (在input_table 数组中用来计算数组下标) input_table数组就是input子系统用来管理注册的handler的一个数据结构 */
	const char *name;

	const struct input_device_id *id_table;         /* 里面放置着dev和本handler能匹配在一起的信息 */

	struct list_head	h_list;    /* 用来挂接handler 上连接的所有handle 的一个链表头 */
	struct list_head	node;    /* 作为一个链表节点挂接到 input_handler_list 链表上(input_handler_list 链表是一个由上层handler参维护的一个用来挂接所有注册的handler的链表头) */
};

Handle : 用于将input_device 和  handler 连接起来,对应于某1个具体的设备文件。


/**
 * 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;            /* handle  的私有数据 */

	int open;                 /* 这个也是用来做打开计数的 */
	const char *name;

	struct input_dev *dev;    /*  用来指向该handle 绑定的input_dev 结构体 */
	struct input_handler *handler;    /* 用来指向该handle 绑定的 handler 结构体 */

	struct list_head	d_node;    /* 把它对应的dev里面的hlist与该handle绑定上 */
	struct list_head	h_node;    /* 把它对应的handler里面的hlist与该handle绑定 */
};

Client:  对应于用户程序对文件的访问接口,每open一次事件驱动,就创建一个client.

struct evdev_client {
	struct input_event buffer[EVDEV_BUFFER_SIZE];    /* 缓冲数据 */
	int head;                    /* 为buffer做环形队列做标记 */
	int tail;                    /* 当head和tail相等的时候,说明没有事件 */
	spinlock_t buffer_lock;      /* protects access to buffer, head and tail */
	struct fasync_struct *fasync;  /* 异步通知函数 */  
	struct evdev *evdev;           /* 打开的那个evdev设备 */
	struct list_head node;         /*  evdev_client链表项 */
	struct wake_lock wake_lock;
	char name[28];
};

 

input子系统的核心层维护着两条中要的链表

static LIST_HEAD(input_dev_list);        /* 记录所有的输入设备 */
static LIST_HEAD(input_handler_list);    /* 记录所有的事件驱动 */

每当一个新的设备或者一个新的事件驱动被系统加载(调用input_register_device()或 input_register_driver()),都会扫描整个链表,并调用函数input_match_device(struct input_handler *handler, struct input_dev *dev)  尝试配对工作。Input_handler-->id_table   记录了需要匹配的特征。

 

下面我放一张图来表明上面几个的关系,图参考自下面这篇博客,里面也讲的很清晰

https://www.cnblogs.com/deng-tao/p/6094049.html

linux驱动input子系统学习二(框架)

这个图对层次结构已经讲的很清晰了,后面的具体代码分析也可以对照这张图来分析。我主要把注意点说一下。

1.一个dev可以对应不只一个handler,比如一个mouse设备既可以匹配到mousedev里面的handler又可以匹配到evdev里面的handler,对应于上图的dev1,一个dev对应两个handler就必须有两个handle来连接它们。

2.一个dev设备也可以只对应一个handler,比如我们自行定义的输入设备就只让匹配evdev里面的handler

 

 

input的核心层还一个8个数据的数组,里面放着已经注册的某一类的处理接口(如:mouse,event等)

static struct input_handler *input_table[8];

为什么是8?

这是因为目前常用的handler只有三种,evdev,mousedev,joydev,这么三个,而且evdev可以通用,所以定义8个肯定够用了。

定义了8个后,因为次设备号只有256个。所以平均每个设备类的次设备号最多就32个。

 

下面是系统目前为设备定义的次设备号信息。

#define JOYDEV_MINOR_BASE	0        /* 游戏手柄类次设备号开始位置 */
#define JOYDEV_MINORS		16       /* 游戏手柄类次设备号个数 */

#define MOUSEDEV_MINOR_BASE	32       /*鼠标类次设备号开始位置 */
#define MOUSEDEV_MINORS		32       /* 鼠标类次设备号个数 */

#define EVDEV_MINOR_BASE	64       /*通用事件类次设备号开始位置 */
#define EVDEV_MINORS		32       /*通用事件类次设备号个数 */

 

可以看到每个设备类的次涉笔号的基址都是32的倍数,后面我们会用到这个32为倍数的基址。