一、前言
   前面我们分析了android的input子系统的android部分的代码,下面我们继续来分析kernel部分的,对于这个系统kernel部分和标准linux差别不大,
   google在原linux基础上增加了一些代码以使其更适合手持设备,比如支持多点触控设备,支持android特有的4个按键等等。我们会一步一步的分析
   内核代码,来分析出input系统的工作原理。
二、input设备类的注册
    在input系统android部分我们知道了,在android层的eventhub中,建立了一个epoll,来监控/dev/input目录下有效的input设备所有事件,如果有
    事件输入的话就会读出当前事件,然后交给android上层处理。所使用的方法也是 open(),read(),close()的标准方法。因此我们要分析这个input
    系统的话,要从/dev/input下的设备文件分的实现析其,他的实现代码在android\kernel\drivers\input\input.c文件中,input也是内核的一个模块
    我们从模块的初始化看起subsys_initcall(input_init);

    static int __init input_init(void)    {        int err;            err = class_register(&input_class);    //注册input设备类--------        if (err) {                                                  |            pr_err("unable to register input_dev class\n");         |            return err;                                             |            }                                                           V        /****************************************************************        struct class input_class = {            .name        = "input",            .devnode    = input_devnode,        };        ********************************************************************/        err = input_proc_init();    //建立input类proc文件系统   见下面        if (err)            goto fail1;            err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备驱动,此中包含了设备的具体操作函数 见下面        if (err) {            pr_err("unable to register char major %d", INPUT_MAJOR);            goto fail2;        }            return 0;         fail2:    input_proc_exit();     fail1:    class_unregister(&input_class);        return err;    }

1.proc文件系统建立

   我们首先对proc文件系统做一个介绍:/proc 文件系统是一个特殊的软件创建的文件系统, 内核用来输出消息到外界./proc 下的每个
   文件都绑到一个内核函数上, 当文件被读的时候即时产生文件内容. 我们已经见到一些这样的文件起作用; 例如, /proc/modules, 常
   常返回当前已加载的模块列表./proc 在 Linux 系统中非常多地应用. 很多现代 Linux 发布中的工具, 例如ps, top, 以及 uptime, 
   从 /proc 中获取它们的信息. 一些设备驱动也通过/proc 输出信息, 你的也可以这样做. /proc 文件系统是动态的, 因此你的模块
   可以在任何时候添加或去除条目.-------Linux 设备驱动 第三版
   input_proc_init()代码在android\kernel\drivers\input\input.c中

      static int __init input_proc_init(void)    {        struct proc_dir_entry *entry;            proc_bus_input_dir = proc_mkdir("bus/input", NULL);//为input类创建/proc/bus/input目录        if (!proc_bus_input_dir)        |            return -ENOMEM;             V    /*********************************************************************************    proc_mkdir()函数定义在android\kernel_imx\fs\proc\generic.c中,这个函数调用    调用proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);    这个函数定义也在这个文件中    struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,    struct proc_dir_entry *parent)       {           struct proc_dir_entry *ent;                  ent = __proc_create(&parent, name, S_IFDIR | mode, 2);           if (ent) {               if (proc_register(parent, ent) < 0) {                   kfree(ent);                   ent = NULL;               }           }           return ent;       }    函数调用__proc_create()定义在本文件中            static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,                          const char *name,                          mode_t mode,                          nlink_t nlink)    {        ..................        if (xlate_proc_name(name, parent, &fn) != 0)//将name整理成/proc/name            goto out;    ...................        //分配内存        ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);        if (!ent) goto out;        //初始化这个这个proc子系统        memset(ent, 0, sizeof(struct proc_dir_entry));        memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);        ent->name = ((char *) ent) + sizeof(*ent);        ent->namelen = len;        ent->mode = mode;        ent->nlink = nlink;        atomic_set(&ent->count, 1);        ent->pde_users = 0;        spin_lock_init(&ent->pde_unload_lock);        ent->pde_unload_completion = NULL;        INIT_LIST_HEAD(&ent->pde_openers);//得到队列回首地址     out:        return ent;    }    到了这里我们就创建好了这个proc文件系统的子目录,下面继续创建设备文件    **************************************************************************************/        entry = proc_create("devices", 0, proc_bus_input_dir,   //创建devices这个设备文件                    &input_devices_fileops);        if (!entry)            goto fail1;            entry = proc_create("handlers", 0, proc_bus_input_dir,//创建handlers这个设备文件                    &input_handlers_fileops);        if (!entry)            goto fail2;            return 0;         fail2:    remove_proc_entry("devices", proc_bus_input_dir);     fail1: remove_proc_entry("bus/input", NULL);        return -ENOMEM;    }    
    这里创建了两个proc文件

a)  我们先看entry = proc_create("devices", 0, proc_bus_input_dir,&input_devices_fileops);
    proc_create()是在proc_bus_input_dir创建名字为"devices"的proc文件,这个文件支持的操作由
    input_devices_fileops来定义。当我们对这个文件读、写、查找时就是调用了他里面相应的函数,因
    此我们重点来看这个参数
    static const struct file_operations input_devices_fileops = {
    .owner        = THIS_MODULE,
    .open        = input_proc_devices_open,
    .poll        = input_proc_devices_poll,
    .read        = seq_read,
    .llseek        = seq_lseek,
    .release    = seq_release,
    };
   从这里看出,这个文件支持open poll read  seek release这5个操作,这里还涉及到seq_file这个内核文件接口
   我们再来介绍一下它:由于特殊性, 在/proc 下的大文件的实现有点麻烦.一直以来, /proc 方法因为当输出数量
   变大时的错误实现变得声名狼藉. 作为一种清理 /proc 代码以及使内核开发者活得轻松些的方法, 添加了 seq_file
    接口. 这个接口提供了简单的一套函数来实现大内核虚拟文件.set_file 接口假定你在创建一个虚拟文件, 它涉及
    一系列的必须返回给用户空间的项. 为使用 seq_file, 你必须创建一个简单的 "iterator" 对象, 它能在
    序列里建立一个位置, 向前进, 并且输出序列里的一个项.---------摘自LDD3
    总的来讲seq_file是小的的proc文件以链表的形式合成一个大的文件,而方便用户使用。
   下面我们一一来分析这几个函数

 .open   = input_proc_devices_open 打开文件    static int input_proc_devices_open(struct inode *inode, struct file *file)    {return seq_open(file, &input_devices_seq_ops);//根据input_devices_seq_ops 初始化一个seq_file    }                                   input_devices_seq_ops的定义    static const struct seq_operations input_devices_seq_ops = {.start= input_devices_seq_start,  //起始位输入设备链表input_dev_list的首地址. input_dev_list是系统维护的所有输入设备的一个链表,                                    //通过这个链表可以访问系统所有的input设备.next= input_devices_seq_next,   //根据input_dev_list链表查找下个设备.stop= input_seq_stop,       .show= input_devices_seq_show,   //从设备连表里提取出哪些内容到seq_file中    };                  |                        V    static int input_devices_seq_show(struct seq_file *seq, void *v)    {    。。。。。。    seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",  //首先会是设备厂家ID ,产品ID,驱动版本       dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);        seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : "");    //设备名字    seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : "");    //设备物理设备文件路径    seq_printf(seq, "S: Sysfs=%s\n", path ? path : "");             //设备sysfs中的路径    seq_printf(seq, "U: Uniq=%s\n", dev->uniq ? dev->uniq : "");    //啥东西不认识    seq_printf(seq, "H: Handlers=");                                    // input链表中的路径        list_for_each_entry(handle, &dev->h_list, d_node)    seq_printf(seq, "%s ", handle->name);    seq_putc(seq, '\n');        input_seq_print_bitmap(seq, "PROP", dev->propbit, INPUT_PROP_MAX);  //设备的各种信息        input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX);    if (test_bit(EV_KEY, dev->evbit))    input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX);    if (test_bit(EV_REL, dev->evbit))    input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX);    if (test_bit(EV_ABS, dev->evbit))    input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX);    if (test_bit(EV_MSC, dev->evbit))    input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX);    if (test_bit(EV_LED, dev->evbit))    input_seq_print_bitmap(seq, "LED", dev->ledbit, LED_MAX);    if (test_bit(EV_SND, dev->evbit))    input_seq_print_bitmap(seq, "SND", dev->sndbit, SND_MAX);    if (test_bit(EV_FF, dev->evbit))    input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX);    if (test_bit(EV_SW, dev->evbit))    input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX);        seq_putc(seq, '\n');        kfree(path);    return 0;    }

    也就是说当我们打开devices这个文件的时候,应该可以读到所有设备的上面这些配置信息。

     .poll        = input_proc_devices_poll,//poll查询等待
  
  static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)    {poll_wait(file, &input_devices_poll_wait, wait);if (file->f_version != input_devices_state) {file->f_version = input_devices_state;return POLLIN | POLLRDNORM;}return 0;    }

    .read        = seq_read,
    .llseek        = seq_lseek,
    .release    = seq_release,
    这三个都是seq_file的标准操作函数,实现文件的读取查找和关闭,不多做说明。
    
    总结来看,这个device这个文件就是input设备的描述文件,他从input_dev_list中读取设备信息,然后放到seq_file中
    供使用者方便的查看设备。
b)  entry = proc_create("handlers", 0, proc_bus_input_dir,&input_handlers_fileops);
    这个proc文件还是看参数input_handlers_fileops
   
  static const struct file_operations input_handlers_fileops = {.owner= THIS_MODULE,.open= input_proc_handlers_open,.read= seq_read,.llseek= seq_lseek,.release= seq_release,    };


    同样的
    .read = seq_read,
.llseek = seq_lseek,
.release = seq_release
这三个都是seq_file的标准操作函数不做说明,重点来看
.open = input_proc_handlers_open,
  
  static int input_proc_handlers_open(struct inode *inode, struct file *file)    {    return seq_open(file, &input_handlers_seq_ops);    }    参数input_handlers_seq_ops的定义       static const struct seq_operations input_handlers_seq_ops = {.start= input_handlers_seq_start, //返回input_handler_list的首地址。每个input设备的输入事件,都可以附加它的处理程序,这个处理程序可以通过input_handler连接到设备上                                    //同一时间可以有不同的程序连接到不同的输入设备上,这个链表就是维护不同设备和其处理事件的连接结构体的链表    .next= input_handlers_seq_next,  //链表下一个节点.stop= input_seq_stop,.show= input_handlers_seq_show, //打开这个文件所显示的信息    };                      |                            V    static int input_handlers_seq_show(struct seq_file *seq, void *v)    {    struct input_handler *handler = container_of(v, struct input_handler, node);    union input_seq_state *state = (union input_seq_state *)&seq->private;        seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name);//编号 和 handler的名字    if (handler->filter)    seq_puts(seq, " (filter)");             //有没有filter    if (handler->fops)    seq_printf(seq, " Minor=%d", handler->minor);//表示 handler 实现的文件操作集,这里不是很重要。      seq_putc(seq, '\n');        return 0;    }


到了这里input设备类的proc文件系统已经建立完成了,总结一下,这个文件系统,提供了设备查询,设备事件处理程序关联方法,就是为了方便用户使用的一个接口。


2.注册字符设备
    err = register_chrdev(INPUT_MAJOR, "input", &input_fops);注册一个字符设备,其主设备号为INPUT_MAJOR input设备,此设备号在0~256之间,由系统自动指定
    这个字符设备的操作函数集input_fops:
    static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
    };
    input设备支持open 和 seek两种操作。
    
到了这里整个input设备类的注册已经完成了。总结一下,input设备类注册过程,首先注册一个类,然后注册proc文件系统,最后注册input的字符设备。


二、input设备注册
    一个输入设备要加载到系统中,需要调用int input_register_device(struct input_dev *dev)
        
  
  int input_register_device(struct input_dev *dev)    {    static atomic_t input_no = ATOMIC_INIT(0);    struct input_handler *handler;    const char *path;    int error;        /* Every input device generates EV_SYN/SYN_REPORT events. */    __set_bit(EV_SYN, dev->evbit);  //强制加入 EV_SYN 事件        /* KEY_RESERVED is not supposed to be transmitted to userspace. */    __clear_bit(KEY_RESERVED, dev->keybit); //用户空间不支持KEY_RESERVED        /* Make sure that bitmasks not mentioned in dev->evbit are clean. */    input_cleanse_bitmasks(dev);        if (!dev->hint_events_per_packet)    dev->hint_events_per_packet =    input_estimate_events_per_packet(dev);        /*     * If delay and period are pre-set by the driver, then autorepeating     * is handled by the driver itself and we don't do it in input.c.     *//初始化设备连击计时器,如果驱动没有填写连击参数就使用默认值    init_timer(&dev->timer);    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {    dev->timer.data = (long) dev;    dev->timer.function = input_repeat_key;    dev->rep[REP_DELAY] = 250;    dev->rep[REP_PERIOD] = 33;    }        if (!dev->getkeycode)    dev->getkeycode = input_default_getkeycode;//得到键编码        if (!dev->setkeycode)    dev->setkeycode = input_default_setkeycode;        dev_set_name(&dev->dev, "input%ld",             //input设备的名字叫 input0 input1 input2.。。。。         (unsigned long) atomic_inc_return(&input_no) - 1);        error = device_add(&dev->dev);  //使用device_add()函数将input_dev包含的device结构注册到Linux设备模型中,并可以在sysfs文件系统中表现出来。    if (error)    return error;        path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);    pr_info("%s as %s\n",    dev->name ? dev->name : "Unspecified device",   //打印调试信息    path ? path : "N/A");    kfree(path);        error = mutex_lock_interruptible(&input_mutex);    if (error) {    device_del(&dev->dev);    return error;    }        list_add_tail(&dev->node, &input_dev_list); //将设备加入到device_add中        list_for_each_entry(handler, &input_handler_list, node) //将设备和handler连接起来    input_attach_handler(dev, handler);        input_wakeup_procfs_readers();        mutex_unlock(&input_mutex);        return 0;    }


到了这里input设备的注册已经完成了。设备注册就是生成设备文件,建立好各个目录中的设备连接,然后将handler 与之相连接


三、事件处理
    在驱动中我们报告事件调用的是input_event()函数
    
    
void input_event(struct input_dev *dev,     unsigned int type, unsigned int code, int value)    {    unsigned long flags;        if (is_event_supported(type, dev->evbit, EV_MAX)) {        spin_lock_irqsave(&dev->event_lock, flags);    add_input_randomness(type, code, value);//函数对事件发送没有一点用处,只是用来对随机数熵池增加一些贡献,因为按键输入是一种随机事件,                                              //所以对熵池是有贡献的。熵池是内核产生随机数用的一个框架,它是收集各种随机事件,然后尽量保证返回数据随机性。    input_handle_event(dev, type, code, value);    spin_unlock_irqrestore(&dev->event_lock, flags);    }    }


    主要来看input_handle_event这个函数是,上报事件的进一步传递
   
    
static void input_handle_event(struct input_dev *dev,           unsigned int type, unsigned int code, int value)    {    int disposition = INPUT_IGNORE_EVENT;        switch (type) {        case EV_SYN:    switch (code) {    case SYN_CONFIG:    disposition = INPUT_PASS_TO_ALL;    break;        case SYN_REPORT:    if (!dev->sync) {    dev->sync = true;    disposition = INPUT_PASS_TO_HANDLERS;    }    break;    case SYN_MT_REPORT:    dev->sync = false;    disposition = INPUT_PASS_TO_HANDLERS;    break;    }    break;        case EV_KEY:    if (is_event_supported(code, dev->keybit, KEY_MAX) &&        !!test_bit(code, dev->key) != value) {        if (value != 2) {    __change_bit(code, dev->key);    if (value)    input_start_autorepeat(dev, code);//重复按键处理    else    input_stop_autorepeat(dev);    }        disposition = INPUT_PASS_TO_HANDLERS;    }    break;        case EV_SW:    if (is_event_supported(code, dev->swbit, SW_MAX) &&        !!test_bit(code, dev->sw) != value) {        __change_bit(code, dev->sw);    disposition = INPUT_PASS_TO_HANDLERS;    }    break;        case EV_ABS:    if (is_event_supported(code, dev->absbit, ABS_MAX))    disposition = input_handle_abs_event(dev, code, &value);        break;        case EV_REL:    if (is_event_supported(code, dev->relbit, REL_MAX) && value)    disposition = INPUT_PASS_TO_HANDLERS;        break;        case EV_MSC:    if (is_event_supported(code, dev->mscbit, MSC_MAX))    disposition = INPUT_PASS_TO_ALL;        break;        case EV_LED:    if (is_event_supported(code, dev->ledbit, LED_MAX) &&        !!test_bit(code, dev->led) != value) {        __change_bit(code, dev->led);    disposition = INPUT_PASS_TO_ALL;    }    break;        case EV_SND:    if (is_event_supported(code, dev->sndbit, SND_MAX)) {        if (!!test_bit(code, dev->snd) != !!value)    __change_bit(code, dev->snd);    disposition = INPUT_PASS_TO_ALL;    }    break;        case EV_REP:    if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {    dev->rep[code] = value;    disposition = INPUT_PASS_TO_ALL;    }    break;        case EV_FF:    if (value >= 0)    disposition = INPUT_PASS_TO_ALL;    break;        case EV_PWR:    disposition = INPUT_PASS_TO_ALL;    break;    }           if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)    dev->sync = false;             //上面是检测上报事件的有效性             if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)//如果需要设备处理参与的话    dev->event(dev, type, code, value);     //设备的事件处理函数是用户自定义的一个处理事件的函数。比如摁下caps lock键  键盘上会有一个灯打开和关闭 用的就是这个原理        if (disposition & INPUT_PASS_TO_HANDLERS)//如果需要handler参与    input_pass_event(dev, type, code, value);   //进行下一步的传递        }           我们看input_pass_event    static void input_pass_event(struct input_dev *dev,         unsigned int type, unsigned int code, int value)    {    struct input_handler *handler;    struct input_handle *handle;        rcu_read_lock();        handle = rcu_dereference(dev->grab);    if (handle)    handle->handler->event(handle, type, code, value);    else {    bool filtered = false;        list_for_each_entry_rcu(handle, &dev->h_list, d_node) {    if (!handle->open)    continue;        handler = handle->handler;    if (!handler->filter) {    if (filtered)    break;        handler->event(handle, type, code, value);        } else if (handler->filter(handle, type, code, value))    filtered = true;    }    }        rcu_read_unlock();    }



    input_pass_event这个函数如果dev指定了handler的话就执行这个handler->event 如果没有的话就遍历所有handler_list 来查找合适handler->event
    从这里可以看出如果我们要处理input_event的话,必须制定一个handler->event,事实上,我们从/dev/input目录下的设备文件读取事件也是这么做的,
    我们接下来继续看。
    首先我们必须找到设备 handler注册地方
    我们知道读取事件都是在/dev/input/event0 ...设备上读去的,我们找到event设备的 创建函数在
    android\kernel_imx\drivers\input\evdev.c中
     
static int __init evdev_init(void)    {    return input_register_handler(&evdev_handler);    }    我们看到这里调用input_register_handler注册了handler,重点来看这个参数    static struct input_handler evdev_handler = {    .event= evdev_event,    .connect= evdev_connect,    .disconnect= evdev_disconnect,    .fops= &evdev_fops,    .minor= EVDEV_MINOR_BASE,    .name= "evdev",    .id_table= evdev_ids,    };    这里event=evdev_event,这就是处理事件的函数具体的            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;    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);  //得到一个client     if (client)    evdev_pass_event(client, &event);   //把事件传递过去    else    list_for_each_entry_rcu(client, &evdev->client_list, node)    evdev_pass_event(client, &event);        rcu_read_unlock();        if (type == EV_SYN && code == SYN_REPORT)    wake_up_interruptible(&evdev->wait);    }



    我们继续看evdev_pass_event
        
    
static void evdev_pass_event(struct evdev_client *client,         struct input_event *event)    {    /* Interrupts are disabled, just acquire the lock. */    spin_lock(&client->buffer_lock);        client->buffer[client->head++] = *event;    client->head &= client->bufsize - 1;        if (unlikely(client->head == client->tail)) {    /*     * This effectively "drops" all unconsumed events, leaving     * EV_SYN/SYN_DROPPED plus the newest event in the queue.     */    client->tail = (client->head - 2) & (client->bufsize - 1);        client->buffer[client->tail].time = event->time;    client->buffer[client->tail].type = EV_SYN;    client->buffer[client->tail].code = SYN_DROPPED;    client->buffer[client->tail].value = 0;        client->packet_head = client->tail;    if (client->use_wake_lock)    wake_unlock(&client->wake_lock);    }        if (event->type == EV_SYN && event->code == SYN_REPORT) {    client->packet_head = client->head;    if (client->use_wake_lock)    wake_lock(&client->wake_lock);    kill_fasync(&client->fasync, SIGIO, POLL_IN);    }        spin_unlock(&client->buffer_lock);    }


    这个函数就是把相应的input_event放入到 client->buffer 的队列中。到了这里,我们看到输入设备的事件处理,被放到一个队列里,那么然后呢?
    我从event中读事件,是调用的 evdev的read函数,我们来看
    evdev的操作集.fops = &evdev_fops,
   
 static const struct file_operations evdev_fops = {    .owner= THIS_MODULE,    .read= evdev_read,    .write= evdev_write,    .poll= evdev_poll,    .open= evdev_open,    .release= evdev_release,    .unlocked_ioctl= evdev_ioctl,    #ifdef CONFIG_COMPAT    .compat_ioctl= evdev_ioctl_compat,    #endif    .fasync= evdev_fasync,    .flush= evdev_flush,    .llseek= no_llseek,    };


     可以看到读取函数为evdev_read 我们继续看
     
   
 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 = 0;        if (count < input_event_size())    return -EINVAL;        if (!(file->f_flags & O_NONBLOCK)) {    retval = wait_event_interruptible(evdev->wait,     client->packet_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)) {        if (input_event_to_user(buffer + retval, &event))//把事件拷贝到 usr的buff中    return -EFAULT;        retval += input_event_size();    }        if (retval == 0 && file->f_flags & O_NONBLOCK)    retval = -EAGAIN;    return retval;    }


    
到了这里所有的事件处理已经完成了。总结一下,事件读取设备文件evdev 是独立于input_device的一个内核模块,两者通过input_handler联系起来
当input_device收到驱动发送的event以后,会回调evdev的处理函数,把event复制到一个缓存中,当我们从evdev读取数据时,相应的处理函数会把
event复制到buff中,因此我们就读取到了事件。android读取到事件以后,就会进行android层面的input_event处理
        

更多相关文章

  1. 关于蓝牙设备之间共享网络的问题(android4.2)
  2. 批量处理ios破解后的资源文件为android所用
  3. Android中的R.java文件你知多少
  4. 如何给你的Android 安装文件(APK)瘦身(一)
  5. 在Android使用XML文件控制按钮文字在各种状态下的颜色
  6. Android设备检测

随机推荐

  1. c语言“或”符号
  2. extern c作用
  3. c语言加减乘除代码
  4. c语言必背入门代码
  5. c语句以句号结束对吗
  6. c++学习路线
  7. c语言数组中以列优先对吗
  8. c语言函数调用例子
  9. c语言的文件存取方式只能是顺序存取?
  10. c语言打印菱形