输入事件的传递过程


arm linux设备驱 2010-08-13 19:18:55 阅读219 评论0  字号: 订阅

当一个事件被触发设,备将向上层报告发生了什么事。为表述这个事件的传递过程我们

以触摸屏为例。触摸屏的源程序在前面博文中已有详述,这里就不赘言了。

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

    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_DELAYREP_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_devdev->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匹配的handlerevdev_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;
 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结构体。

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

//否则就将该事件插入到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()来实现罢了。这里就不详述了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值