linux驱动input子系统学习二(框架)
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
这个图对层次结构已经讲的很清晰了,后面的具体代码分析也可以对照这张图来分析。我主要把注意点说一下。
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为倍数的基址。
上一篇: 使用Python写CUDA程序的方法
推荐阅读
-
linux驱动input子系统学习二(框架)
-
Linux 字符设备驱动基本框架(二)
-
Linux设备驱动之IIO子系统——IIO框架及IIO数据结构
-
Linux设备驱动之IIO子系统——IIO框架数据读取
-
11-S3C2440驱动学习(七)嵌入式linux-字符设备的另一种写法及RTC驱动程序分析和字符设备驱动框架总结
-
手把手教你写Linux设备驱动---input子系统(四)--电容屏驱动ft5x06编写(一)(基于友善之臂4412开发板)...
-
11-S3C2440驱动学习(五)嵌入式linux-网络设备驱动(二)移植DM9000C网卡驱动程序
-
linux 驱动开发之平台设备驱动设备树 input子系统的使用:按键中断驱动
-
第二期驱动篇——1.1 LCD驱动编写——Linux内核中LCD驱动框架分析
-
Linux音频设备驱动_OSS驱动框架(二)————OSS驱动架构代码vmsnd.c