输入事件的传递过程

来源:互联网 发布:好用的网络云盘 编辑:程序博客网 时间:2024/06/08 00:36

接上一篇《【转】输入子系统设备模型分析 (有修改)

 

      当一个事件被触发设,备将向上层报告发生了什么事。为表述这个事件的传递过程我们以触摸屏为例。触摸屏的源程序在前面博文中已有详述,这里就不赘言了。

当在触摸屏上按下时会发出这样的报告:

    input_report_abs(dev, ABS_X, xp);  //报告x坐标值
    input_report_abs(dev, ABS_Y, yp);  //报告y坐标值

    input_report_key(dev, BTN_TOUCH, 1); //报告触摸屏被按下
    input_report_abs(dev, ABS_PRESSURE, 1); //报告触摸屏被按下
    input_sync(dev); //报告结束。

/**********************************************************************************************************************/

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
 input_event(dev, EV_ABS, code, value);
}

/************************************************************************************************************************/

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);
 }
}

/************************************************************************************************************************/

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:  //常用的是该类同步事件报告函数input_sync(dev);
   if (!dev->sync) {
    dev->sync = 1;
    disposition = INPUT_PASS_TO_HANDLERS; //对这一按键事件交给handler处理。

   }
   break;
  case SYN_MT_REPORT: 
   dev->sync = 0;
   disposition = INPUT_PASS_TO_HANDLERS; //对这一按键事件交给handler处理。
   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) //如果按键被按下

//如果支持重复按键,就开启定时器dev->timer进行重复按键检测,直到按键抬起。
     input_start_autorepeat(dev, code);  
    else
     input_stop_autorepeat(dev); //如果按键抬起就停止定时器
   }

   disposition = INPUT_PASS_TO_HANDLERS; //对这一按键事件交给handler处理。
  }
  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; //对这一按键事件交给handler处理。
  }
  break;

 case EV_ABS: //绝对坐标事件处理
  if (is_event_supported(code, dev->absbit, ABS_MAX)) {

//绝对坐标事件类型有很多子事件,这些子事件分为两类,一类是包含在数组 input_abs_bypass中

//另一类当然就是数组 input_abs_bypass之外了。包含在数组input_abs_bypass中的子事件直接

//break;交给handler处理。数组 input_abs_bypass之外的子事件要经过滤除干扰等处理后再交给handler处理

   if (test_bit(code, input_abs_bypass)) {
    disposition = INPUT_PASS_TO_HANDLERS;
    break;
   }

//滤除干扰取出一个合理的值。

   value = input_defuzz_abs_event(value,dev->abs[code], dev->absfuzz[code]);

   if (dev->abs[code] != value) {
    dev->abs[code] = value;
    disposition = INPUT_PASS_TO_HANDLERS;
   }
  }
  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: //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; //设定与重复按键检测相关的时间。REP_DELAY和REP_PERIOD。
   disposition = INPUT_PASS_TO_ALL; //这个事件既要发向input_dev又要发向handler。
  }
  break;

 case EV_FF: //受力事件处理
  if (value >= 0)
   disposition = INPUT_PASS_TO_ALL; //这个事件既要发向input_dev又要发向handler。
  break;

 case EV_PWR: //电源相关的事件
  disposition = INPUT_PASS_TO_ALL; //这个事件既要发向input_dev又要发向handler。
  break;
 }

 if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
  dev->sync = 0; //如果该事件不应被忽略也不是同步事件就清零dev->sync,这一举动与上面同步时间有关。

 if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
  dev->event(dev, type, code, value); //如果设一事件要传递给设备就调用该函数。

 if (disposition & INPUT_PASS_TO_HANDLERS)
  input_pass_event(dev, type, code, value); //如果该事件要传递给handler就调用该函数。
}

/************************************************************************************************************************/

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); 

//如果input_dev得dev->grab指向了一个当前使用的handle ,就将该事件交给这个handle 对应的handler处理,

//否则就将该事件交给挂在dev->h_list上的所有handle 对应的handler处理。
 if (handle) 
  handle->handler->event(handle, type, code, value);
 else
  list_for_each_entry_rcu(handle, &dev->h_list, d_node)
   if (handle->open)
    handle->handler->event(handle,type, code, value);
 rcu_read_unlock();
}

假如此时的input_dev匹配的handler是evdev_handler,那么函数

 handle->handler->event(handle, type, code, value);的调用就是调用的函数

 evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)。

在讨论这个函数之前先看两个结构体:

(1)

每一个事件都会被包装成这样一个结构体。

struct input_event {
 struct timeval time;  //事件发生的时间
 __u16 type;    //事件类型
 __u16 code;  //子事件
 __s32 value; //事件发生的相关value
};

(2)

一个结构体input_event 就代表了一个事件,结构体evdev_client则是对这些事件进行存储管理。

struct evdev_client {
 struct input_event buffer[EVDEV_BUFFER_SIZE]; //可以同时管理EVDEV_BUFFER_SIZE(64)个事件
 int head;
//取出事件从head开始
 int tail;  //存储事件从tail开始。
 spinlock_t buffer_lock; /* protects access to buffer, head and tail */
 struct fasync_struct *fasync; //异步通知事件发生
 struct evdev *evdev;   //指向本evdev_client归属的evdev。
 struct list_head node;  //用于挂接到evdev的链表头client_list上。
};

上面调用的函数 handle->handler->event(handle, type, code, value);既是下面函数

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。

 do_gettimeofday(&event.time);
 event.type = type;
 event.code = code;
 event.value = value;

 rcu_read_lock();

 client = rcu_dereference(evdev->grab);

//一个input_dev 对应了一个evdev结构体。

//如果evdev的dev->grab指向了一个当前使用的client  ,就将该事件插入到该client得buffer中,

//否则就将该事件插入到evdev->client_list上的所有client 的buffer中。
 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();

 wake_up_interruptible(&evdev->wait);
}
/**************************************************************************************************************/

static void evdev_pass_event(struct evdev_client *client,  struct input_event *event)
{
 spin_lock(&client->buffer_lock);
 client->buffer[client->head++] = *event; 
 client->head &= EVDEV_BUFFER_SIZE - 1;
 spin_unlock(&client->buffer_lock);

 kill_fasync(&client->fasync, SIGIO, POLL_IN);  //通知相关进程。
}

/**************************************************************************************************************/

让我们来看一看将事件插入了client->buffer[]后有将做何处理。

我们还是讨论输入设备匹配的 input_handler 是evdev_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,
};

该设备对应的文件处理函数集evdev_fops在文件evdev.c中实现。

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
};

先来看看文件打开函数:

static int evdev_open(struct inode *inode, struct file *file)
{
 struct evdev *evdev;
 struct evdev_client *client;
 int i = iminor(inode) - EVDEV_MINOR_BASE;
。。。。。。
 evdev = evdev_table[i];
//根据次设备号取出数组evdev_table[]中对应的evdev 。
 if (evdev)
  get_device(&evdev->dev);  //增加引用计数
 mutex_unlock(&evdev_table_mutex);

。。。。。。

 client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);//创建事件管理结构体evdev_client
。。。。。。
 client->evdev = evdev; //指向client所归属的evdev。

//以下函数主要完成的工作是list_add_tail_rcu(&client->node, &evdev->client_list);

 evdev_attach_client(evdev, client);

//下面打开函数主要是evdev->open++,等等。

 error = evdev_open_device(evdev);
 if (error)
  goto err_free_client;

 file->private_data = client;  //将file->private_data 指向刚创建的client。
。。。。。。
}

client是一个事件管理结构体,当一个事件发生时就会将该事件结构体插入client->buffer[]中。

而事件的读取是通过函数 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;

 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;

//函数evdev_fetch_next_event(client, &event)是取出事件存于event中

 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;
}

取出事件:

static int evdev_fetch_next_event(struct evdev_client *client,
      struct input_event *event)
{
 int have_event;

 spin_lock_irq(&client->buffer_lock);

 have_event = client->head != client->tail;
 if (have_event) {
  *event = client->buffer[client->tail++];  //从client->buffer[]的client->tail处取事件
  client->tail &= EVDEV_BUFFER_SIZE - 1;
 }

 spin_unlock_irq(&client->buffer_lock);

 return have_event;
}

将事件拷到用户空间:

int input_event_to_user(char __user *buffer, const struct input_event *event)
{

//如果设置了标识INPUT_COMPAT_TEST就将事件event包装成结构体compat_event
 if (INPUT_COMPAT_TEST) { 
  struct input_event_compat compat_event;

  compat_event.time.tv_sec = event->time.tv_sec;
  compat_event.time.tv_usec = event->time.tv_usec;
  compat_event.type = event->type;
  compat_event.code = event->code;
  compat_event.value = event->value;

 //将包装成input_event_compat的事件拷到用户空间

  if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat)))
   return -EFAULT;

 } else {
  if (copy_to_user(buffer, event, sizeof(struct input_event))) //将事件拷到用户空间。
   return -EFAULT;
 }

 return 0;
}

这就是一个事件的产生到传递到用户空间的过程。当然传递到用户空间还可以用函数

evdev_ioctl()来完成。还有从用户空间将一个事件传递到输入设备,大体过程也都相仿

不过是调用函数evdev_write()或是evdev_ioctl()来实现罢了。这里就不详述了。

原创粉丝点击