input子系统 事件流程浅析

事件(struct input_event)从设备驱动层 –> 核心层—>事件处理层的通过java

struct input_event {
    struct timeval time; //事件发生的时间
    __u16 type;          //事件的类型
    __u16 code;          //事件的代码
    __s32 value;         //事件的值
};

这里写图片描述

在input.h中有定义:
/** type: 事件的类型 */
#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  //LED灯设备
#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)  //不知道这个设置是为何了,下面is_event_supported()中会判断事件类型值 <= EV_MAX

/** 当事件类型是EV_KEY的时候,code为设备键盘码 0~127为键盘上的按键 */
#define KEY_Q           16
#define KEY_W           17
#define KEY_E           18
#define KEY_R           19
#define KEY_T           20
#define KEY_Y           21
#define KEY_U           22
#define KEY_I           23
#define KEY_O           24
#define KEY_P           25

/* code : 事件的代码 若是事件的类型是EV_KEY 该code为设备键盘码, 0 - 127 : 键盘上的按键代码 若是事件的类型是EV_REL 该code : REL_X REL_Y value : 若是事件的类型是EV_KEY value : 0 松开 1 按下 若是事件的类型是EV_REL value正负值表示 两个方向上的值 详细请参考input.h */
在中断处理函数中,调用input_report_key( )向输入子系统报告发生的事件
/** 向输入子系统发生事件。 dev :发生事件的设备 code : 事件代码 value:事件的值 在中断函数中不须要考虑重复按键的点击状况,这个函数能够检查这个问题,并报告一次事件 */
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
    // !!value ? 
    input_event(dev, EV_KEY, code, !!value);
}
/** input子系统中 任何向核心层报告事件的都会通过这个函数。 */
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
    unsigned long flags; //为何不初始化就传递过去了?
     /** 判断设备是否支持该事件 is_event_supported(){ 这里的code是上面传递过来的type EV_KEY : 1 EV_CNT : 32 max : 31 既然位图是0~31位,为何还要定义EV_CNT?这里还要判断? code <= max : 事件类型不是 EV_CNT test_bit(code, bm) : 判断evbit的第code位是否被置1了,被置1了,表示支持,为0,表示不支持 return code <= max && test_bit(code, bm); } */   
    if (is_event_supported(type, dev->evbit, EV_MAX)) {
        /* 避免竞态的一种方法:屏蔽本地cpu中断 那么 若是临界区执行完成须要的时间长,那么这里屏蔽中断有点危险 保存本地中断状态, 关闭本地中断, 获取自旋锁 */
        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);
    }
}
static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
   /* 这个disposition变量表示使用什么样的方式处理事件, #define INPUT_IGNORE_EVENT 0 //忽略该事件 #define INPUT_PASS_TO_HANDLERS 1 //将该事件交给handler处理 #define INPUT_PASS_TO_DEVICE 2 //交给input_dev处理 #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) //handler、input_dev共同处理 */
   int disposition = INPUT_IGNORE_EVENT;

   //下面是一个大的switch结构,截取EV_KEY部分。
   case EV_KEY:
                //is_event_supported 判断是否支持该事件。test_bit 测试按键的状态是否改变
        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);
            }
            //设置成INPUT_PASS_TO_HANDLERS,表示由handler来处理这个事件
            disposition = INPUT_PASS_TO_HANDLERS;
        }
        break; 
             .......
   if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
        dev->event(dev, type, code, value);

   // 事件交给handler处理,调用input_pass_event函数 
   if (disposition & INPUT_PASS_TO_HANDLERS)
    input_pass_event(dev, type, code, value);         
}
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);
    if (handle)
        handle->handler->event(handle, type, code, value);
    else   
     /* 遍历input_handle链表,取出input_handle指向的input_handler. 调用这个input_handler的event函数。 */
        list_for_each_entry_rcu(handle, &dev->h_list, d_node)
            if (handle->open)  //若是已经被打开
                /** 事件由核心层流转到事件处理层。 若是input_handler是evdev的话,那么evdev_event将会被调用 */
                handle->handler->event(handle,type, code, value);
    rcu_read_unlock();
}
/** 到这里事件处理到达了 事件处理层,咱们知道了事件发生后,input_handler的event函数会被调用。 下面咱们看看 input_handler的event函数 */
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, //这个handler所可以支持的设备列表
};
static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{
        /*取出evdev 在evdev_connect()中设置 把evdev放入到input_handle的私有数据中, evdev->handle.private = evdev; */
    struct evdev *evdev = handle->private;
    struct evdev_client *client;
    struct input_event event;

    do_gettimeofday(&event.time);
    //根据传入的值 为input_event赋值
    event.type = type;
    event.code = code;
    event.value = value;

    rcu_read_lock();
    client = rcu_dereference(evdev->grab);
    if (client)
        evdev_pass_event(client, &event);
    else
            //遍历client链表,调用evdev_pass_event函数 
        list_for_each_entry_rcu(client, &evdev->client_list, node)
                //调用这个函数来发送
            evdev_pass_event(client, &event);

    rcu_read_unlock();

    //唤醒等待的进程。 那么是在何时被阻塞的呢? 
    wake_up_interruptible(&evdev->wait);
    /** static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos) { /** 若是没有数据&&以非阻塞的方式打开的话,就返回 */  
            if (client->head == client->tail && evdev->exist &&(file->f_flags & O_NONBLOCK))
            {
                return -EAGAIN;
            }
            /** 否则,就休眠。input_event()函数会唤醒的 */  
            retval = wait_event_interruptible(evdev->wait,client->head != client->tail ||!evdev->exist);
            if (retval)
            {
                return retval;
            }   
        }     
    */ 
}
static void evdev_pass_event(struct evdev_client *client,
                 struct input_event *event)
{
    /** 获取自旋锁 --- 访问临界区----释放自旋锁 */    
    spin_lock(&client->buffer_lock);
    //将事件赋值给客户端的input_event 数组
    client->buffer[client->head++] = *event;
    client->head &= EVDEV_BUFFER_SIZE - 1;
    spin_unlock(&client->buffer_lock);
    /** 向应用层发送消息,应用层会执行对应的消息处理函数。 */  
    kill_fasync(&client->fasync, SIGIO, POLL_IN);
}

从上面能够看出  事件最终被放入到了客户端的input_event[]数组中了,
只须要将这个input_event[]数组复制给用户空间便可。

看看用户空间把,用户空间调用read,evdev_read会被调用,
static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
{
    ....
    while (retval + input_event_size() <= count &&evdev_fetch_next_event(client, &event)) {
                //调用了这个函数来获取事件
        if (input_event_to_user(buffer + retval, &event))
            return -EFAULT;
        retval += input_event_size();
    }

    return retval;
}
int input_event_to_user(char __user *buffer,const struct input_event *event)
{
        //哈哈,copy_to_user 将input_event拷贝到用户空间中
    if (copy_to_user(buffer, event, sizeof(struct input_event)))
    {
        return -EFAULT;
    }    
    return 0;
}