Chinaunix首页 | 论坛 | 博客
  • 博客访问: 829016
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: LINUX

2015-11-09 17:00:38

转载: http://blog.sina.com.cn/s/articlelist_2034446750_0_1.html

linux输入子系统—架构分析  

1、   input输入子系统体系结构里,其核心是input core,其实input core对应的具体文件就是input.c。那么我们就来分析一下吧:

分析驱动,首先从xx_initvoid)函数开始,如下:

 

static int __init input_init(void)

{

 int err;

 

 err = class_register(&input_class);

 if (err) {

  printk(KERN_ERR "input: unable to register input_dev class\n");

  return err;

 }

 

 err = input_proc_init();

 if (err)

  goto fail1;

 

 err = register_chrdev(INPUT_MAJOR, "input", &input_fops);   //注册字符设备

 if (err) {

  printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);

  goto fail2;

 }

 

 return 0;

 

 fail2: input_proc_exit();

 fail1: class_unregister(&input_class);

 return err;

}

 

 


static const struct file_operations input_fops = {

 .owner = THIS_MODULE,

 .open = input_open_file,                            //只有一个open函数,看来open函数肩负重任啊

};


注释1:从上面我们可以看出,input.c里面完成的字符设备的注册,以及file_operations结构体的设置。看来input子系统设计的驱动程序也属于字符设备驱动程序,与一般的字符设备驱动程序的不同之处在于,input子系统字符设备的注册由内核自动完成,不需要程序员来完成,程序员要做的就是,将底层的硬件输入转化为统一事件形式,向输入核心层(input core)汇报。

2、我们看到在file_operations结构体里只有一个open函数,如果要想读设备或则写设备怎么办呢?这说明pen函数肩负着艰巨的使命,下面就来分析这一函数

static int input_open_file(struct inode *inode, struct file *file)

{

 struct input_handler *handler;

 const struct file_operations *old_fops, *new_fops = NULL;

 int err;

 

 lock_kernel();

 

 handler = input_table[iminor(inode) >> 5];   //根据打开的文件的次设备号,来得到一个input_handler结构体,关于这个结构体请见注释1

 if (!handler || !(new_fops = fops_get(handler->fops)))          //通过handler得到新的 file_operations结构体

  {

  err = -ENODEV;

  goto out;

 }

 

 

 if (!new_fops->open) {

  fops_put(new_fops);

  err = -ENODEV;

  goto out;

 }

 old_fops = file->f_op;    // 保存文件之前的f_op

 file->f_op = new_fops;  // 将新的f_op赋给当前文件的f_op

 

 err = new_fops->open(inode, file);//调用open函数,当应用程序读的时候,最终会调用这个函数

 

 if (err) {

  fops_put(file->f_op);

  file->f_op = fops_get(old_fops);

 }

 fops_put(old_fops);

out:

 unlock_kernel();

 return err;

}

注释1struct input_handler {

 

 void *private;

 

 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);

 void (*disconnect)(struct input_handle *handle);

 void (*start)(struct input_handle *handle);

 

 const struct file_operations *fops;

 int minor;

 const char *name;

 

 const struct input_device_id *id_table;

 const struct input_device_id *blacklist;

 

 struct list_head h_list;

 struct list_head node;

};

这个结构体可以通过 input_table来获得,那么 input_table是何许人也呢?查了一下发现input_tableinput_register_handler里被构造了:

input_table[handler->minor >> 5] = handler;

input_register_handler函数由诸如下面一些文件调用:

keyboard.cmousedev.cevdev.c等等

我们进入edev.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 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,//用来表示是否支持该设备,这里表示可以支持任何设备

};

 

static int __init evdev_init(void)

{

 return input_register_handler(&evdev_handler);

}

有点乱对吧!先来理一下:

edev.c中构造了input_handler结构体(当然在这个结构体里包含了file_operation结构体),并向input.c注册这个结构体。在input.c的函数input_register_handler中根据该结构体构造了input_tableinput_table[handler->minor >> 5] = handler,而在 input_open_file函数中,又通过input_table构造了一个input_handler结构体:handler = input_table[iminor(inode) >> 5];这样看input_table起到一个中转作用。然后通过handler构造了新的file_operation结构体:new_fops = fops_get(handler->fops)。(即evdev_handler结构体变量中的.fops  = &evdev_fops

3上面分析了input core的情况,下面我们从最底层的驱动来一步一步分析:

1)在入口函数中注册input设备:input_register_device(button_dev);

2)注册函数定义在input.c中:

int input_register_device(struct input_dev *dev)

{

 static atomic_t input_no = ATOMIC_INIT(0);

 struct input_handler *handler;

 const char *path;

 int error;

 

 __set_bit(EV_SYN, dev->evbit);

 

 

 

 init_timer(&dev->timer);

 if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {

  dev->timer.data = (long) dev;

  dev->timer.function = input_repeat_key;

  dev->rep[REP_DELAY] = 250;

  dev->rep[REP_PERIOD] = 33;

 }

 

 if (!dev->getkeycode)

  dev->getkeycode = input_default_getkeycode;

 

 if (!dev->setkeycode)

  dev->setkeycode = input_default_setkeycode;

 

 snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),

   "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

 

 error = device_add(&dev->dev);

 if (error)

  return error;

 

 path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);

 printk(KERN_INFO "input: %s as %s\n",

  dev->name ? dev->name : "Unspecified device", path ? path : "N/A");

 kfree(path);

 

 error = mutex_lock_interruptible(&input_mutex);

 if (error) {

  device_del(&dev->dev);

  return error;

 }

 

 list_add_tail(&dev->node, &input_dev_list);//input设备放入一个链表input_dev_list

 

 list_for_each_entry(handler, &input_handler_list, node)//对于一个input设备,使其与input_handler_list链表中的每一个handler都比较一下,在注册input_handler结构体时,会将该input_handler结构体放入input_handler_list链表中,见注释

 

input_attach_handler(dev, handler);//根据id.table比较来判断handler是否支持设备

 

 input_wakeup_procfs_readers();

 

 mutex_unlock(&input_mutex);

 

 return 0;

}

EXPORT_SYMBOL(input_register_device);

注释:注册input_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;

 }

 

 list_add_tail(&handler->node, &input_handler_list);//handler加入链表input_handler_list

 list_for_each_entry(dev, &input_dev_list, node)//与每一个input设备进行比较

  input_attach_handler(dev, handler);//判断是否匹配

 

 input_wakeup_procfs_readers();

 

 out:

 mutex_unlock(&input_mutex);

 return retval;

}

综上:注册input_devinput_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handlerid_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handlerconnect函数建立"连接"

3)那么这connect函数是怎么一回事儿呢? 让源码说话:

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

  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);//分配一个struct evdev结构体,然后设置

 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);//input设备放进去

 evdev->handle.name = evdev->name;

 evdev->handle.handler = handler;//handler处理函数放进去

 evdev->handle.private = evdev;

 

 strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));

 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);//注册handle,我们可以走进去,见注释1

 if (error)

  goto err_free_evdev;

 

 error = evdev_install_chrdev(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;

}

注释1

int input_register_handle(struct input_handle *handle)

{

 struct input_handler *handler = handle->handler;//handler处理函数拿出来

 struct input_dev *dev = handle->dev;//input设备拿出来

 int error;

 

 

 error = mutex_lock_interruptible(&dev->mutex);

 if (error)

  return error;

 list_add_tail_rcu(&handle->d_node, &dev->h_list);//handle加入到dev->h_list链表中

 mutex_unlock(&dev->mutex);

 synchronize_rcu();

 

 

 list_add_tail(&handle->h_node, &handler->h_list);//handle加入到handler->h_list链表中

 

 if (handler->start)

  handler->start(handle);

 

 return 0;

}

 

现在我们就清楚了,如果handler支持input设备,通过connect的连接,input_device可以根据其dev->h_list来找到handle,然后根据handle找到相应的处理函数。反之,处理函数也一样。

我们可以整理出来框架:

evdev.c文件中:

input_register_handler(&evdev_handler);//evdev_handler里面定义了eventconnectfops等一些东东

        input_table[handler->minor >> 5] = handler;//将根据次设备号将handler放入数组中

        list_add_tail(&handler->node, &input_handler_list);//handler加入链表input_handler_list

          list_for_each_entry(dev, &input_dev_list, node)//遍历input_ dev _list链表中的所有dev

        input_attach_handler(dev, handler);//判断设备与处理函数是否匹配

          id = input_match_device(handler->id_table, dev);//根据handler->id_table判断设备与处理函数是否匹配

          if (!id)

          return -ENODEV;

      handler->connect(handler, dev, id);//如果匹配就调用handler->connect函数

        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

        evdev->handle.dev = dev;//dev放进去

        evdev->handle.handler = handler;//handler放进去

        input_register_handle(&evdev->handle);

        struct input_handler *handler = handle->handler;

      list_add_tail(&handle->d_node, &handle->dev->h_list);//handle加入到dev->h_list链表中

        list_add_tail(&handle->h_node, &handler->h_list);//handle加入到handler->h_list链表中

 

input_register_device(input);

        list_add_tail(&dev->node, &input_dev_list);//dev将加入input_dev_list列表中

        list_for_each_entry(handler, &input_handler_list, node)//遍历input_handler_list链表中所有的handler判断是否支持设备

        input_attach_handler(dev, handler);//判断设备与处理函数是否匹配

       接下来的分析跟上面完全一致,我们不再分析

这样处理函数和设备就联系起来了。我们再来啰嗦一下:我们在注册设备的时候会有次设备号,而注册处理函数的时候又会根据这个次设备号来将处理函数放入数组中,然后在对设备进行操作的时候,根据次设备号来找到操作函数。这样我们就对应起来了。

               

input.c文件中:

 register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备

input_fops里面只有open函数,在open函数中会完成取出操作函数的工作。


阅读(573) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~