linux input系统简析

来源:互联网 发布:js中的eval优缺点 编辑:程序博客网 时间:2024/06/16 09:08

     输入子系统由驱动层(Drivers),输入子系统核心层(Input Core)和事件处理层(EventHandler)三部份组成;


(1)从设备驱动开始分析,以kernel/driver/touchscreen/ft5x06_ts.c的tp驱动为例,在tp驱动probe的过程当中,创建input_dev;

struct input_dev {        ......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)];//绝对设备,tp的坐标x,y就用这表示unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];//杂项设备unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];//led灯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 hint_events_per_packet;        ......struct device dev;struct list_headh_list;struct list_headnode;};

然后分配大小input = input_allocate_device();原型在/kernel/driver/input/input.c里面,很简单通过kmalloc分配大小,并初始话后面会用到的两个链表;

下面为tp驱动当中设置input事件的类型和初始值和范围,

        __set_bit(EV_KEY, input->evbit);__set_bit(EV_ABS, input->evbit);__set_bit(EV_SYN, input->evbit);__set_bit(BTN_TOUCH, input->keybit);__set_bit(ABS_MT_TOUCH_MAJOR, input->absbit);//tp按下去压力值__set_bit(ABS_MT_POSITION_X, input->absbit);//x坐标值__set_bit(ABS_MT_POSITION_Y, input->absbit);//y坐标值__set_bit(ABS_MT_WIDTH_MAJOR, input->absbit);//按键的范围值__set_bit(KEY_MENU,  input->keybit);__set_bit(KEY_BACK,  input->keybit);__set_bit(KEY_HOME,  input->keybit);__set_bit(KEY_SEARCH,  input->keybit);input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);input_set_abs_params(input, ABS_MT_POSITION_X, 0, X_MAX, 0, 0);input_set_abs_params(input, ABS_MT_POSITION_Y, 0, Y_MAX, 0, 0);input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);

然后注册中断 和初始化工作队列,中端函数里面定时去读取取tp所报的值,然后调度队列当中的工作,通过input_report_key和input_report_abs上报键值core层处理;

       input_report_abs(data->input, ABS_MT_TOUCH_MAJOR, event->pressure);       input_report_abs(data->input, ABS_MT_POSITION_X, event->x);       input_report_abs(data->input, ABS_MT_POSITION_Y, event->y);       input_report_abs(data->input, ABS_MT_WIDTH_MAJOR, 1);       input_mt_sync(data->input);//report结束


input_report_abs在input.h实现最后调用input_event,这里就到了input的重点;

probe后面就进行input_register_device;int input_register_device(struct input_dev *dev){    .....    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;    }    .....    list_add_tail(&dev->node, &input_dev_list);    list_for_each_entry(handler, &input_handler_list, node)        input_attach_handler(dev, handler);    ....}EXPORT_SYMBOL(input_register_device);最后去遍历hanlderlist的链表根据设备去配对相应的hanlder就此驱动这边已经走完;

(2)先分析先看看/kernel/driver/input/evdev,evdev输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。

先看evdev_init函数,简单的调用input_register_handler函数并传递一个input_handler的一个结构体;


int input_register_handler(struct input_handler *handler){......list_add_tail(&handler->node, &input_handler_list);list_for_each_entry(dev, &input_dev_list, node)input_attach_handler(dev, handler);        ......}
通过list_add_tail把传递进来的这个handler加入到input_handler_list;然后通过遍历整个input_dev_list通过input_attach_handler函数去一个一个的比较

input_attach_handler --> input_match_device ;注册handler的结构体里有.id_table    = evdev_ids

static const struct input_device_id evdev_ids[] = {{ .driver_info = 1 },/* Matches all devices */{ },/* Terminating zero entry */};

inputdevice都挂在input_dev_list链上,所有的handler都挂在input_handler_list,通过input_attach_handler来进行匹配,并将其挂以input_dev_list.与挂载在input_handler_list中的handler相匹配。如果匹配成功,就会调用handlerconnect函数.

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,             const struct input_device_id *id){    struct evdev *evdev;    ......    //创建并初始化一个evdev设备;    error = input_register_handle(&evdev->handle);    ......}

创建并初始化一个evdev设备,最终去调用input_register_handle函数

int input_register_handle(struct input_handle *handle){struct input_handler *handler = handle->handler;struct input_dev *dev = handle->dev;int error;        ......if (handler->filter)list_add_rcu(&handle->d_node, &dev->h_list);elselist_add_tail_rcu(&handle->d_node, &dev->h_list);mutex_unlock(&dev->mutex);        ........list_add_tail_rcu(&handle->h_node, &handler->h_list);if (handler->start)handler->start(handle);//start 为NULLreturn 0;}EXPORT_SYMBOL(input_register_handle);

handle就注册成功,这里注册成功的handle最后添加到handler和input dev这两个list上面,handle就代表一个成功配对;

    struct input_handle {          void *private;   //每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里。           int open;        //打开标志,每个input_handle 打开后才能操作,这个一般通过事件处理器的open方法间接设置           const char *name;           struct input_dev *dev;  //关联的input_dev结构           struct input_handler *handler; //关联的input_handler结构           struct list_head    d_node;  //input_handle通过d_node连接到了input_dev上的h_list链表上           struct list_head    h_node;  //input_handle通过h_node连接到了input_handler的h_list链表上       };  

(3)进入重要的环节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:

   这个函数主要是根据事件类型的不同,做相应的处理。这里之关心EV_KEY类型,其他函数和事件传递关系不大,只要关心,disposition这个是事件处理的方式,默认的是INPUT_IGNORE_EVENT,忽略这个事件,如果是INPUT_PASS_TO_HANDLERS则是传递给事件处理器,如果是INPUT_PASS_TO_DEVICE,则是传递给设备处理,触摸屏驱动没有定义这个。input_handle_abs_event -> input_pass_event:

static void input_pass_event(struct input_dev *dev,                   unsigned int type, unsigned int code, int value)  {      struct input_handle *handle;       rcu_read_lock();      handle = rcu_dereference(dev->grab);  //如果是绑定的handle,则调用绑定的handler->event函数       if (handle)          handle->handler->event(handle, type, code, value);      else          //如果没有绑定,则遍历dev的h_list链表,寻找handle,如果handle已经打开,说明有进程读取设备关联的evdev。           list_for_each_entry_rcu(handle, &dev->h_list, d_node)              if (handle->open)                  handle->handler->event(handle,                              type, code, value);          // 调用相关的事件处理器的event函数,进行事件的处理       rcu_read_unlock();  }
最后调用evdev的evnet函数
static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value){.....list_for_each_entry_rcu(client, &evdev->client_list, node)evdev_pass_event(client, &event);.....}最后调用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);    wake_lock_timeout(&client->wake_lock, 5 * HZ);    client->buffer[client->head++] = *event;//将事件复制给input_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 (event->type == EV_SYN && event->code == SYN_REPORT) {        client->packet_head = client->head;        kill_fasync(&client->fasync, SIGIO, POLL_IN);    }    spin_unlock(&client->buffer_lock);}

evdev_pass_event函数最终将事件传递给了用户端的client结构中的input_event数组中,只需将这个input_event数组复制给用户空间,进程就能收到触摸屏按下的信息了。具体处理由具体的应用程序来完成。



0 0
原创粉丝点击