input子系统 事件流程浅析

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

 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;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值