JZ2440开发板学习------中级(二十四:中)

2019-07-12 14:54发布

输入事件的传递过程  


当一个事件被触发设,备将向上层报告发生了什么事。为表述这个事件的传递过程我们 以触摸屏为例。触摸屏的源程序在前面博文中已有详述,这里就不赘言了。 当在触摸屏上按下时会发出这样的报告:     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()来实现罢了。这里就不详述了。