转载评论:这篇文章着重讲述了kernel部分的输入系统结构
============================================
键或者触摸屏输入设备是最常用不过的设备,那么如果一个按键信息是如何从内核传递到android的呢,首先我们得先清楚Linux的input子系统框架,下面是我在公司自己写的一篇文档,现在先粘帖过来
Linux之Input子系统分析
目前Android、QT等众多应用对于linux系统中键盘、鼠标、触摸屏等输入设备的支持都通过标准的input输入子系统。因为input子系统已经完成了字符驱动的文件操作接口,所以编写驱动的核心工作是完成input系统留出的接口,工作量相对小很多,下面我们将从input输入子系统框架以及基于3202中的触摸屏代码为实例进行分析。
一、input输入子系统框架
下图是input输入子系统框架,输入子系统由输入子系统核心层(input core),驱动层和事件处理层(Event Handler)三部分组成。一个输入事件,比如滑动触摸屏都是通过input driver -> input core -> event handler -> user space 到达用户空间传给应用程序。
Input输入子系统框架
二、Input driver编写要点
1. 分配、注册、注销input设备
参见触摸屏驱动ctp_it7250.c
struct input_dev *input_allocate_device(void)
int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
2. 设置input设备支持的事件类型、事件码、事件值的范围等信息
参见触摸屏驱动ctp_it7250.c
__set_bit(EV_ABS, Ctp_it7250->input_dev->evbit);
__set_bit(ABS_X, Ctp_it7250->input_dev->absbit);
__set_bit(ABS_Y, Ctp_it7250->input_dev->absbit);
__set_bit(EV_SYN, Ctp_it7250->input_dev->evbit);
__set_bit(EV_KEY, Ctp_it7250->input_dev->evbit);
__set_bit(BTN_TOUCH, Ctp_it7250->input_dev->keybit);
Include/linux/input.h中定义了支持的类型
#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)
一个设备可以支持一个或多个事件类型。每个事件类型下面还需要设置具体的触发事件码。比如:EV_KEY事件,需要定义其支持哪些按键事件码。
3. 如果需要,设置input设备的打开、关闭的处理方法
参见触摸屏驱动ctp_it7250.c
Ctp_it7250->input_dev->open = Ctp_it7250_touch_open;
Ctp_it7250->input_dev->close = Ctp_it7250_touch_close;
4. 在发生输入事件时,向子系统报告事件
参见触摸屏驱动ctp_it7250.c
input_report_key(Ctp_it7250->input_dev,ABS_MT_TRACKING_ID,0);
input_report_abs(Ctp_it7250->input_dev, ABS_MT_POSITION_X, gpdwSampleX[0]);
input_report_abs(Ctp_it7250->input_dev, ABS_MT_POSITION_Y, gpdwSampleY[0]);
input_report_abs(Ctp_it7250->input_dev, ABS_MT_TOUCH_MAJOR, 1);
input_mt_sync(Ctp_it7250->input_dev);
input_sync(Ctp_it7250->input_dev);
而上述函数其实都是通过下面这个函数实现的
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
而此函数最终会调用handle->handler->event,也就是evdev_event函数
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
三、Event Handler层解析
1. Input输入子系统数据结构关系图
2. Input_handler结构体
以evdev.c中的evdev_handler为例:
static struct input_handler evdev_handler = {
.event = evdev_event, //向系统报告input事件,系统通过read方法读取
.connect = evdev_connect, //和input_dev匹配后调用connect构建
.disconnect = evdev_disconnect,
.fops = &evdev_fops, //event设备文件的操作方法
.minor = EVDEV_MINOR_BASE, //次设备号基准值
.name = "evdev",
.id_table = evdev_ids, //匹配规则
};
3. Input字符设备注册过程
drivers/input/input.c中:
static int __init input_init(void)
{
int err;
……
err = class_register(&input_class);
……
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
……
}
Input_fops定义:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
input_dev和input_handler匹配后调用input_handler的connect。以evdev_handler为例:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
for (minor = 0; minor < EVDEV_MINORS; minor++) //找到字符设备event对应的次设备号
if (!evdev_table[minor])
break;
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //为每个匹配evdev_handler的设备创建一个evdev
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); //创建event字符设备节点
……
4. Input字符设备的打开过程
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]; //得到对应的input_handler
if (!handler || !(new_fops = fops_get(handler->fops))) { //取出对应input_handler的file_operations
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;
}
5. Input字符设备的事件处理
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event; //构造一个input_event结构体
struct timespec ts;
ktime_get_ts(&ts);
event.time.tv_sec = ts.tv_sec; //设置事件的相关属性
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event); //把事件加入到client链表中
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
rcu_read_unlock();
wake_up_interruptible(&evdev->wait); //解锁中断唤醒
}
6. Input字符设备的读操作
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
int retval;
if (count < input_event_size())
return -EINVAL;
if (client->head == client->tail && evdev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist); //等待中断唤醒,并判断是否有数据,如果没有进入休眠
if (retval)
return retval;
if (!evdev->exist)
return -ENODEV;
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
//从buffer中取出数据传给user space,内部是通过copy_to_user函数实现的
if (input_event_to_user(buffer + retval, &event))
return -EFAULT;
retval += input_event_size();
}
return retval;
}
如果上面的您看明白了,那么好我们继续往上走,以Android2.1为例,input上报的流程如下,蓝色为文件以及路径,红色为用到的函数
frameworks/base/services/java/com/android/server/KeyInputQueue.java->:Thread mThread = new Thread("InputDeviceReader")
frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp->:static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, jobject event)
frameworks/base/libs/ui/EventHub.cpp->:bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
其中EventHub.cpp为HAL层代码、com_android_server_KeyInputQueue.cpp为jni本地调用,KeyInputQueue.java为framework的server代码,具体细节可以自己去研究其代码。在Android2.3里多了几层的封装
frameworks/base/services/java/com/android/server/InputManager.java->:public void start()
frameworks/base/services/jni/com_android_server_InputManager.cpp->:static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz)
frameworks/base/libs/ui/InputManager.cpp->:status_t InputManager::start()
frameworks/base/libs/ui/InputReader.cpp->:bool InputReaderThread::threadLoop()
frameworks/base/libs/ui/EventHub.cpp->:bool EventHub::getEvent(RawEvent* outEvent)