Chinaunix首页 | 论坛 | 博客
  • 博客访问: 523924
  • 博文数量: 235
  • 博客积分: 1209
  • 博客等级: 少尉
  • 技术积分: 1417
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-19 19:59
文章分类

全部博文(235)

文章存档

2012年(107)

2011年(128)

分类:

2011-12-20 23:57:11

Linux驱动子系统之输入子系统

[内容概要]

介绍了input-subsystem的三个组成部分,并对主要结构体和函数进行了分析以及它们如何关联。

[概述]

内核的输入子系统是对分散的、多种不同类别的输入设备(如键盘、鼠标、跟踪球、操作杆、触摸屏、加速计和手写板)进行统一处理的驱动程序。输入子系统带来的好处:

统一了物理形态各异的相似的输入设备的处理功能。例如,各种鼠标,不论是PS/2、USB,还是蓝牙,都做同样的处理;

提供了用于分发输入报告给用户应用程序的简单的事件接口;

抽取出了输入驱动程序的通用部分,简化了驱动程序,并引入了一致性。

[input-subsystem架构图]

input

[input-core]

核心层对下提供了设备驱动层的编程接口,对上有提供了事件处理层的编程接口。

[相关文件]

Driver/input/input.c

Include/linux/input.h

[关键的数据结构]

Struct input_dev     物理输入设备的基本数据结构,包含了设备相关的一些信息;

  1. struct input_dev { 
  2. const char *name; 
  3.          constchar *phys; 
  4.          constchar *uniq; 
  5. struct input_id id; 
  6.          unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; 
  7.          unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; 
  8.          unsignedlong relbit[BITS_TO_LONGS(REL_CNT)]; 
  9.          unsignedlong absbit[BITS_TO_LONGS(ABS_CNT)]; 
  10.          unsignedlong mscbit[BITS_TO_LONGS(MSC_CNT)]; 
  11.          unsignedlong ledbit[BITS_TO_LONGS(LED_CNT)]; 
  12.          unsignedlong sndbit[BITS_TO_LONGS(SND_CNT)]; 
  13.          unsignedlong ffbit[BITS_TO_LONGS(FF_CNT)]; 
  14.          unsignedlong swbit[BITS_TO_LONGS(SW_CNT)]; 
  15.         …….. 
  16. struct device dev; 
  17. struct list_head        h_list; 
  18. struct list_head        node; 
  19. }; 

Name 输入设备名字;

Evbit 事件支持的位图;

Kerbit 按键支持的位图;

Dev 内嵌的设备结构体;

h_list 链表 表示与这个input_dev相联系的前后一个handler;

node链表 将此Input_dev连接到全局的input_dev_list链表中,内核中所有的Input_dev都连接在其上;

Struct input_handler  事件处理结构体,定义处理事件的方法;

  1. struct input_handler { 
  2. void*private
  3. void (*event)(struct input_handle *handle, unsigned inttype, unsigned int code, int value); 
  4. int (*connect)(struct input_handler *handler, structinput_dev *dev, const struct input_device_id *id); 
  5. void(*disconnect)(struct input_handle *handle); 
  6. void(*start)(struct input_handle *handle); 
  7.          conststruct file_operations *fops; 
  8.          intminor; 
  9.          constchar *name; 
  10.          conststruct input_device_id *id_table; 
  11.          conststruct input_device_id *blacklist; 
  12. struct list_head        h_list; 
  13. struct list_head        node; 
  14. }; 

Event()函数被输入子系统用于处理发送给设备的事件;

Connet()用来连接handler和Input_dev;

h_list链表 表示与这个Input_handler相联系的前后一个handler;

node链表 将此Input_handler连接到全局的input_handler_list链表中,内核中所有的Input_handler都连接在其上;

Struct input_handle   用来创建input_dev和Input_handler之间关系的结构体;

  1. struct input_handle { 
  2. void*private
  3.          intopen; 
  4.          constchar *name; 
  5.          structinput_dev *dev; 
  6.          structinput_handler *handler; 
  7. struct list_head        d_node; 
  8. struct list_head        h_node; 
  9. }; 

d_node 将handle放到设备相关的链表中,也就是放到input_dev->h_list表示的链表中;

h_node 将handle放到input_handler相关的链表中,也就是放到input_handler->h_list表示的链表中;

[子系统初始化函数]

  1. static int __init input_init(void
  2.          interr; 
  3.          input_init_abs_bypass(); 
  4.          err= class_register(&input_class); 
  5. if(err) { 
  6.                    printk(KERN_ERR"input: unable to register input_dev class\n"); 
  7.                    returnerr; 
  8.          } 
  9.          err= input_proc_init(); 
  10. if(err) 
  11.                    gotofail1; 
  12.          err= register_chrdev(INPUT_MAJOR, "input", &input_fops); 
  13. if(err) { 
  14.                    printk(KERN_ERR"input: unable to register char major %d", INPUT_MAJOR); 
  15.                    gotofail2; 
  16.          } 
  17.          return0; 
  18. fail2:       input_proc_exit(); 
  19. fail1:       class_unregister(&input_class); 
  20. return err; 

初始化函数将完成三个工作:

1、  class_register(&input_class) 注册一个Input类;

2、  input_proc_init() 在proc下建立相关的交互文件;

3、  register_chrdev(INPUT_MAJOR, "input", &input_fops) 注册为字符设备。

[file_operations]

  1. static const struct file_operationsinput_fops = { 
  2.          .owner= THIS_MODULE, 
  3.          .open= input_open_file, 
  4. }; 

你会感到会奇怪为什么file_operations中只实现了open方法,OK,我们来看下input_open_file的源代码

  1. static int input_open_file(struct inode*inode, struct file *file) 
  2.          structinput_handler *handler; 
  3.          conststruct file_operations *old_fops, *new_fops = NULL; 
  4.          interr; 
  5.          lock_kernel(); 
  6. /*No load-on-demand here? */
  7.          handler= input_table[iminor(inode) >> 5]; 
  8. if(!handler || !(new_fops = fops_get(handler->fops))) { 
  9.                    err= -ENODEV; 
  10.                    gotoout; 
  11.          } 
  12. /*
  13.           * That's _really_ odd. Usually NULL ->openmeans "nothing special",
  14.           * not "no device". Oh, well...
  15.           */
  16. if(!new_fops->open) { 
  17.                    fops_put(new_fops); 
  18.                    err= -ENODEV; 
  19.                    gotoout; 
  20.          } 
  21.          old_fops= file->f_op; 
  22.          file->f_op= new_fops; 
  23.          err= new_fops->open(inode, file); 
  24. if(err) { 
  25.                    fops_put(file->f_op); 
  26.                    file->f_op= fops_get(old_fops); 
  27.          } 
  28.          fops_put(old_fops); 
  29. out: 
  30.          unlock_kernel(); 
  31.          returnerr; 

这个open方法完成了两个工作:

1、  将file->fops重定向到Input_handler->fops,因为input_handler->fops定义了对具体的事件的操作方法;通过file->fops我们就能对不同的设备进行操作;

2、  用input_handler->fops的open方法开打设备。

[inputdevice drivers]

驱动层提供对硬件各寄存器的读写访问和讲底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层。

[相关文件]

Driver/input/mouse

Drvier/input/touchscreen

Driver/input/joystick

……

[input_dev]

Struct input_dev 代表一个输入设备,驱动层主要的结构体,我们所做的工作就是填充此结构体。

[分配一个输入设备]

struct input_dev*input_allocate_device(void)

[注册和注销]

int input_register_device(struct input_dev*dev)

void input_unregister_device(structinput_dev *dev)

[input_register_device分析]

  1. int input_register_device(struct input_dev*dev) 
  2.          staticatomic_t input_no = ATOMIC_INIT(0); 
  3.          structinput_handler *handler; 
  4.          constchar *path; 
  5.          interror; 
  6.          __set_bit(EV_SYN, dev->evbit);    /* support all event */
  7. /*
  8.           * If delay and period are pre-set by thedriver, then autorepeating
  9.           * is handled by the driver itself and we don'tdo it in input.c.
  10.           */
  11.          init_timer(&dev->timer); 
  12. if(!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { 
  13.                    dev->timer.data= (long) dev; 
  14.                   dev->timer.function =input_repeat_key; 
  15.                    dev->rep[REP_DELAY]= 250; 
  16.                    dev->rep[REP_PERIOD]= 33; 
  17.          } 
  18. if(!dev->getkeycode) 
  19.                    dev->getkeycode= input_default_getkeycode; 
  20. if(!dev->setkeycode) 
  21.                    dev->setkeycode= input_default_setkeycode; 
  22.          dev_set_name(&dev->dev,"input%ld", 
  23.                         (unsigned long)atomic_inc_return(&input_no) - 1); 
  24.          error = device_add(&dev->dev); 
  25. if(error) 
  26.                    returnerror; 
  27.          path= kobject_get_path(&dev->dev.kobj, GFP_KERNEL); 
  28.          printk(KERN_INFO"input: %s as %s\n", 
  29.                    dev->name? dev->name : "Unspecified device", path ? path :"N/A"); 
  30.          kfree(path); 
  31.          error= mutex_lock_interruptible(&input_mutex); 
  32. if(error) { 
  33.                    device_del(&dev->dev); 
  34.                    returnerror; 
  35.          } 
  36.          list_add_tail(&dev->node,&input_dev_list); 
  37.          list_for_each_entry(handler,&input_handler_list, node) 
  38.                    input_attach_handler(dev,handler); 
  39.          input_wakeup_procfs_readers(); 
  40.          mutex_unlock(&input_mutex); 
  41.          return0; 

Input_register_device函数做了两个工作:

1、  把input_dev包含的device结构注册到linux设备模型中;

device_add(&dev->dev);

2、  调用list_add_tail函数将input_dev加入到Input_dev_list链表中,input_dev_list链表包含了系统中所有的input_dev设备。

List_for_each_entry函数用来遍历input_handler_list;

Input_attch_handler函数用来匹配input_dev和handler,只有匹配成功了,才会调用connect函数,使input_dev和handler处理器关联起来;

[inputevent driver]

Input event driver 也就是eventhandler,事件处理层;

为用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。

[相关文件]

Drivers/input/evdev.c

Drivers/input/joydev.c

Drivers/input/mousedev.c

……

[注册input_handler]

  1. int input_register_handler(structinput_handler *handler) 
  2.          structinput_dev *dev; 
  3.          intretval; 
  4.          retval= mutex_lock_interruptible(&input_mutex); 
  5. if(retval) 
  6.                    returnretval; 
  7.          INIT_LIST_HEAD(&handler->h_list); 
  8. if(handler->fops != NULL) { 
  9. if(input_table[handler->minor >> 5]) { 
  10.                             retval= -EBUSY; 
  11.                             gotoout; 
  12.                    } 
  13.                    input_table[handler->minor>> 5] = handler; 
  14.          } 
  15.          list_add_tail(&handler->node,&input_handler_list); 
  16.          list_for_each_entry(dev,&input_dev_list, node) 
  17.                    input_attach_handler(dev,handler); 
  18.          input_wakeup_procfs_readers(); 
  19. out: 
  20.          mutex_unlock(&input_mutex); 
  21.          returnretval; 

注册一个新的input_handler处理器

list_add_tail(&handler->node, &input_handler_list)把handler加入到全局链表input_handler_list中;

List_for_each_entry函数用来遍历input_dev_list;

Input_attch_handler函数用来匹配input_dev和handler,只有匹配成功了,才会调用connect函数,使input_dev和handler处理器关联起来;

[input_register_handle]

  1. int input_register_handle(struct input_handle*handle) 
  2.          structinput_handler *handler = handle->handler; 
  3.          structinput_dev *dev = handle->dev; 
  4.          interror; 
  5. /*
  6.           * We take dev->mutex here to prevent racewith
  7.           * input_release_device().
  8.           */
  9.          error= mutex_lock_interruptible(&dev->mutex); 
  10. if(error) 
  11.                    returnerror; 
  12.          list_add_tail_rcu(&handle->d_node,&dev->h_list); 
  13.          mutex_unlock(&dev->mutex); 
  14.          list_add_tail(&handle->h_node,&handler->h_list); 
  15. if(handler->start) 
  16.                    handler->start(handle); 
  17.          return0; 

Input_register_handle函数主要工作是将input_dev和Input_handler关联起来,和设备、驱动、总线的驱动模型非常相似。  

[input_dev、handler和handle三者的关系]

Input_register_device、input_register_handler和input_register_handle的关系在下图有很好的体现:

input1

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