Android之input系统流程

转载评论:这篇文章着重讲述了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)


你可能感兴趣的:(转发)