Chinaunix首页 | 论坛 | 博客
  • 博客访问: 52715
  • 博文数量: 17
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 70
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-09 22:49
文章分类
文章存档

2015年(9)

2014年(8)

我的朋友

分类: LINUX

2015-02-02 10:53:07

原文地址:input子系统(2) 作者:steven_miao

input子系统
 
谨以此文纪念过往的岁月。
三.input handler
花开两朵,各表一枝,话说前文讲述了input设备的添加,那现在就来看一下input设备的"驱动"。
在linux系统启动时,会添加一个万能的event handler,其初始化在 /drivers/input/evdev.c中。
static int __init evdev_init(void)
{
 return input_register_handler(&evdev_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,
};
那来看input_register_handler这个函数。
int input_register_handler(struct input_handler *handler)
{
 struct input_dev *dev;
 int retval;
 retval = mutex_lock_interruptible(&input_mutex);
 if (retval)
  return retval;
 INIT_LIST_HEAD(&handler->h_list);
 if (handler->fops != NULL) {
  if (input_table[handler->minor >> 5]) {
   retval = -EBUSY;
   goto out;
  }
  input_table[handler->minor >> 5] = handler; --保存handler
 }
 list_add_tail(&handler->node, &input_handler_list); --这个才是最最主要的。
 list_for_each_entry(dev, &input_dev_list, node)
  input_attach_handler(dev, handler);
 input_wakeup_procfs_readers();
 out:
 mutex_unlock(&input_mutex);
 return retval;
}
在系统刚启动时,没有任何input_dev注册。input_register_handler先注册一个适合于所有input设备的handler注册。此时input_dev_list为空,input_handler_list
上有一个节点,就是万能input handler(evdev_handler)。这样我们在回过头去看input_attach_handler就很好懂了。其实在没有额外的handler添加时,每一个input dev
注册时均会调用evdev_connect。那来看看connect是怎么运行的。
3.1 evdev_connect
将handler和dev连接起来。这里的handler为evdev_handler
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
 struct evdev *evdev;
 int minor;
 int error;
 for (minor = 0; minor < EVDEV_MINORS; minor++)  --EVDEV_MINORS = 32 ,其采用了全局变量的手段来管理evdev_table.
  if (!evdev_table[minor])
   break;
 if (minor == EVDEV_MINORS) {
  printk(KERN_ERR "evdev: no more free evdev devices\n");
  return -ENFILE;
 }
 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
 if (!evdev)
  return -ENOMEM;
 INIT_LIST_HEAD(&evdev->client_list); 
 spin_lock_init(&evdev->client_lock);
 mutex_init(&evdev->mutex);
 init_waitqueue_head(&evdev->wait);
 snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
 evdev->exist = 1;
 evdev->minor = minor;
 evdev->handle.dev = input_get_device(dev);
 evdev->handle.name = evdev->name;
 evdev->handle.handler = handler;
 evdev->handle.private = evdev;
 dev_set_name(&evdev->dev, evdev->name);
 evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
 evdev->dev.class = &input_class;
 evdev->dev.parent = &dev->dev;
 evdev->dev.release = evdev_free;
 device_initialize(&evdev->dev);
 error = input_register_handle(&evdev->handle);
 if (error)
  goto err_free_evdev;
 error = evdev_install_chrdev(evdev);    --  evdev_table[evdev->minor] = evdev
 if (error)
  goto err_unregister_handle;
 error = device_add(&evdev->dev);
 if (error)
  goto err_cleanup_evdev;
 return 0;
 err_cleanup_evdev:
 evdev_cleanup(evdev);
 err_unregister_handle:
 input_unregister_handle(&evdev->handle);
 err_free_evdev:
 put_device(&evdev->dev);
 return error;
}
上面的函数就是赋值注册。可以看出每一个event都会有一个evevt device来对应。其中最主要的一个函数是input_register_handle这个函数和前面
input_register_handler是不同的的。
int input_register_handle(struct input_handle *handle)
{
 struct input_handler *handler = handle->handler;
 struct input_dev *dev = handle->dev;
 int error;
 error = mutex_lock_interruptible(&dev->mutex);
 if (error)
  return error;
 list_add_tail_rcu(&handle->d_node, &dev->h_list);
 mutex_unlock(&dev->mutex);
 synchronize_rcu();
 list_add_tail(&handle->h_node, &handler->h_list);  --添加到handler链表。
 if (handler->start)
  handler->start(handle);
 return 0;
}
为什么会有上面的函数呢,因为同一个handler可以去对应n个input dev这个就需要对应了,其实handle类似于platfrom中的device_drvier,起到一个连接作用。
通过该结构可以将handler和input_dev 一起管理起来。
到此为止,input_dev的注册应该了解了。类似于platform中的设备已经注册并且probe,那下面来看设备是怎样运行的。
四.input运行
在input子系统中通过input_event向event层提交信息。那来看这个函数的具体实现。
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_KEY:
  if (is_event_supported(code, dev->keybit, KEY_MAX) &&!!test_bit(code, dev->key) != value) {  --检测数据
   if (value != 2) {     --这段程序理解简单,但是其为什么会这么做啊???有可能是用于ctrl+x类型函数的处理。
    __change_bit(code, dev->key);
    if (value)
     input_start_autorepeat(dev, code);
   }
   disposition = INPUT_PASS_TO_HANDLERS;
  }
  break;
  ...
 }
 if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
  dev->sync = 0;
 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);
}
下面的函数是重新启动定时器。
static void input_start_autorepeat(struct input_dev *dev, int code)
{
 if (test_bit(EV_REP, dev->evbit) &&dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&dev->timer.data) {
  dev->repeat_key = code;
  mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
 }
}
input_pass_event函数中会调用handler->event 在没有添加特别的handler时,会调用evdev_handler.evdev_event,那来看evdev_event
这个函数。在这个函数中一个是对event进行时间赋值,再找这里面有两个很重要,一个是evdev_pass_event另一个则是wake_up_interruptible
关于client在下面讲打开设备以及调用时再说。
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;
 struct timespec ts;
 ktime_get_ts(&ts);
 event.time.tv_sec = ts.tv_sec;
 event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
 event.type = type;
 event.code = code;
 event.value = value;
 rcu_read_lock();
 client = rcu_dereference(evdev->grab);  --关于这些东东,在以后学习中在说。
 if (client)
  evdev_pass_event(lient, &eventc);
 else
  list_for_each_entry_rcu(client, &evdev->client_list, node)
   evdev_pass_event(client, &event);
 rcu_read_unlock();
 wake_up_interruptible(&evdev->wait);   --这个其实是唤醒阻塞,这个在read的时候被阻塞。
}
 
static void evdev_pass_event(struct evdev_client *client,struct input_event *event)
{
 spin_lock(&client->buffer_lock);
 wake_lock_timeout(&client->wake_lock, 5 * HZ);
 client->buffer[client->head++] = *event;
 client->head &= EVDEV_BUFFER_SIZE - 1;
 spin_unlock(&client->buffer_lock);
 kill_fasync(&client->fasync, SIGIO, POLL_IN);  --异步通知,这个也是以后再说。
}
五.input应用层
在调用input时,首先会open event设备。由于对于同一个input 设备其对应的应用程序可能会有好几个,那个怎么来区分呢?所以client就诞生了,用
client来标示每一个进程。在见识多了platform种的种种结构体的嵌套,那这儿也就不奇怪了。有client的成员保存evdev的信息。
在这里面会client,针对每一个进程
static int evdev_open(struct inode *inode, struct file *file)
{
 struct evdev *evdev;
 struct evdev_client *client;
 int i = iminor(inode) - EVDEV_MINOR_BASE;
 int error;
 if (i >= EVDEV_MINORS)
  return -ENODEV;
 error = mutex_lock_interruptible(&evdev_table_mutex);
 if (error)
  return error;
 evdev = evdev_table[i];    --获取evdev
 if (evdev)
  get_device(&evdev->dev);
 mutex_unlock(&evdev_table_mutex);
 if (!evdev)
  return -ENODEV;
 client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);   --创建client
 if (!client) {
  error = -ENOMEM;
  goto err_put_evdev;
 }
 spin_lock_init(&client->buffer_lock);
 snprintf(client->name, sizeof(client->name), "%s-%d", evdev->name,task_tgid_vnr(current));
 wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
 client->evdev = evdev;
 evdev_attach_client(evdev, client);
 error = evdev_open_device(evdev);
 if (error)
  goto err_free_client;
 file->private_data = client;
 return 0;
 err_free_client:
 evdev_detach_client(evdev, client);
 kfree(client);
 err_put_evdev:
 put_device(&evdev->dev);
 return error;
}
static void evdev_attach_client(struct evdev *evdev,struct evdev_client *client)
{
 spin_lock(&evdev->client_lock);
 list_add_tail_rcu(&client->node, &evdev->client_list);  --将client 添加入evdev—>client_list链表中。
 spin_unlock(&evdev->client_lock);
 synchronize_rcu();
}
static int evdev_open_device(struct evdev *evdev)
{
 int retval;
 retval = mutex_lock_interruptible(&evdev->mutex);
 if (retval)
  return retval;
 if (!evdev->exist)
  retval = -ENODEV;
 else if (!evdev->open++) {   --如果evdev是首次打开,则调用input_open_device。
  retval = input_open_device(&evdev->handle);
  if (retval)
   evdev->open--;
 }
 mutex_unlock(&evdev->mutex);
 return retval;
}
int input_open_device(struct input_handle *handle)
{
 struct input_dev *dev = handle->dev;
 int retval;
 retval = mutex_lock_interruptible(&dev->mutex);
 if (retval)
  return retval;
 if (dev->going_away) {
  retval = -ENODEV;
  goto out;
 }
 handle->open++;
 if (!dev->users++ && dev->open)
  retval = dev->open(dev);   --这也是为什么input_dev->open 在没有使用的情况下必须要返回0。
 if (retval) {
  dev->users--;
  if (!--handle->open) {
   /*
    * Make sure we are not delivering any more events
    * through this handle
    */
   synchronize_rcu();
  }
 }
 out:
 mutex_unlock(&dev->mutex);
 return retval;
}
以上的几个函数都是设置input_dev以及event设备的一些标志。如果顺利的话,设备就会打开。
应用层调用read的时候,将会调用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;  --这个在open的时候就保存了。
 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))  --如果client没有打开,并且file标识符为非阻塞。直接over
  return -EAGAIN;
 retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist); --阻塞,在input_event中会唤醒。
 if (retval)
  return retval;
 if (!evdev->exist)
  return -ENODEV;
 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;
}
六.总结
在input子系统中,其设备驱动和应用层均比较简单,其很多工作都交给内核去做。在该篇中就是要去理解内核到底做什么了,为什么这么去做。
在linux系统刚启动时,会先注册一个handler用于处理所有的input_dev。在没有注册其余的handler前,所有的驱动均会使用该handler来处理。
阅读(1075) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~