ARM Linux内核Input输入子系统浅解
发布日期:2021-06-30 21:52:10 浏览次数:2 分类:技术文章

本文共 35943 字,大约阅读时间需要 119 分钟。

--触摸屏驱动为例

第一章、了解
linux input子系统
 
      L
inux输入
设备
总类
繁杂
,常见
的包括有
按键
、键盘、触摸屏、
鼠标
、摇杆
等等,他们
本身就是
字符设备
linux内核将
这些
设备
共同性抽象出来
简化驱动开发
建立
了一个input子系统。
子系统共分为
三层,如图
1所示
图1
  input
输入
子系统
 
      
驱动
层和
硬件
相关,直接
捕捉和
获取硬件设备
数据信息等
(包括
被按下、按下位置、
鼠标移动
键盘
按下等等),
然后
数据
信息报告到核心层
。核心层负责
连接
驱动层
事件处理层
设备驱动(
device driver
)
和处理程序
(
handler
)的
注册需要通过核心层来完成,核心层接收来自驱动层的数据
信息,
并将数据信息选择对应
handler去处理,最终handler
数据复制到用户
空间。
      
先了解
三个
定义
在/linux/input.h
重要的结构体
input_dev
、input_handler、input_handle。
struct input_dev {
      void *private;
 
      const char *name;
      const char *phys;
      const char *uniq;
      
struct input_id id;      
//与
input_handler匹配
id
 
      
unsigned long evbit[NBITS(EV_MAX)];            
//设备
支持的
事件
类型
      unsigned long keybit[NBITS(KEY_MAX)];      
//按键
事件支持的子
事件
类型
      unsigned long relbit[NBITS(REL_MAX)];
      
unsigned long absbit[NBITS(ABS_MAX)];      
//绝对坐标
事件支持的子事件类型
      unsigned long mscbit[NBITS(MSC_MAX)];
      unsigned long ledbit[NBITS(LED_MAX)];
      unsigned long sndbit[NBITS(SND_MAX)];
      unsigned long ffbit[NBITS(FF_MAX)];
      unsigned  swbit[NBITS(SW_MAX)];
      int ff_effects_max;
 
      unsigned int keycodemax;
      unsigned int keycodesize;
      void *keycode;
 
      unsigned int repeat_key;
      struct timer_list timer;
 
      struct pt_regs *regs;
      int state;
      int sync;
 
      int abs[ABS_MAX + 1];
      int rep[REP_MAX + 1];
 
      unsigned long key[NBITS(KEY_MAX)];
      unsigned long led[NBITS(LED_MAX)];
      unsigned long snd[NBITS(SND_MAX)];
      unsigned  sw[NBITS(SW_MAX)];
 
      
int absmax[ABS_MAX + 1];      
//绝对
坐标
事件
最大
键值
      int absmin[ABS_MAX + 1];      
//绝对
坐标
事件
的最小键值
      int absfuzz[ABS_MAX + 1];
      int absflat[ABS_MAX + 1];
 
      int (*)(struct input_dev *dev);
      void (*close)(struct input_dev *dev);
      int (*accept)(struct input_dev *dev, struct file *file);
      int (*flush)(struct input_dev *dev, struct file *file);
      int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
      int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
      int (*erase_effect)(struct input_dev *dev, int effect_id);
 
      
struct input_handle *grab;      
//当前
占有该设备的handle
 
      struct mutex mutex;      /* serializes  and close operations */
      unsigned int users;            //
打开
该设备的用户量
 
      struct class_device cdev;
      struct device *dev;      /* will be removed soon */
 
      int dynalloc;      /* temporarily */
 
      struct list_head      h_list;      //
链表头用于
链接该
设备
所关联
的input_handle
      struct list_head      node;      //
链表头用于
设备
链接到input_dev_list
};
      Input_dev
一个很强大的结构体,
把所有的
input
设备
(触摸屏
、键盘、鼠标等
)的
信息都考虑到了,
对于
来说只用到
里面的
一部分
而已,
尤其
加粗
的部分
,注意
该结构体中
最后
两行定义的
两个
list_head
结构体
list_head
在/linux/list.h
有定义,深入跟踪
struct list_head {
      struct list_head *, *prev;
};
结构体内部
没有
定义
数据而
定义了两个指向本身
结构体
的指针
,预先说明
一下,
所有
input 
device
在注册后
加入一个input_dev_list(
输入
设备链表)
所有的e
vent
handler在注册后会加入一个input_handler_list(
输入
处理程序链表)
,这里
的list_
主要
作用
是作为
input_dev_list
和input_handler_list
一个节点来保存地址。I
nput_dev_list
和input_handler_list之间的对应关系由input_handle结构体桥接,具体后面说明。
 
struct input_handler {
 
      void *private;
 
      void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
      struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);
      void (*disconnect)(struct input_handle *handle);
 
      
const struct file_operations *fops;      //
提供给
用户对设备
操作
的函数指针
      int minor;
      char *name;
 
      
struct input_device_id *id_table;      
//与
input_dev匹配
id
      struct input_device_id *blacklist;      //
标记
的黑名单
 
      
struct list_head      h_list;            
//用于
链接和该
handler相关
handle
      struct list_head      node;            //
用于
将该
handler链入
input_handler_list
};
i
nput_handler顾名思义,
它是用来处理
input_dev的
一个结构体,
相关
的处理在结构里内部都有定义,
最后
两行定义的list_结构体作用
input_dev所定义的一样,这里不再说明。
:input_device_id
结构体
/
linux/mod_devicetable.h
有定义
 
struct input_handle {
 
      void *private;
 
      int ;      //
记录
设备打开次数
      char *name;
 
      struct input_dev *dev;      //
指向
所属的input_dev
      struct input_handler *handler;      //
指向所属的
input_handler
 
      struct list_head      d_node;            
//用于
链入
指向的
input_dev的handle
链表
      struct list_head      h_node;            
//用于
链入
指向的
input_
handler
的handle
链表
};
可以
看到input_handle中拥有指向input_dev和input_handler的指针,
input_handle是用来关联
input_dev
和input_handler
。为什么
用input_handle来关联input_dev和input_handler
input_dev和input_handler直接对应呢?因为
一个
device
可以
对应多个
handler
一个handler也可处理多个
device。就
如一个
设备可以
对应
event handler
可以对应tseve handler
      input_dev
、input_
handler
、input_handle
的关系如下图
2所示
图2  input_dev,input_handlerinput_handle关系图
 
第二章、input
 device
注册
      I
nput 
device
的注册
实际上
仅仅
只有几行代码,因为在input.c中已经将大量的代码封装好了,主需要调用几个关键的函数就能完成对input device的注册。
      
xxx_ts.c中预先
定义
全局变量struct input_dev  tsdev
;然后
进入到初始化
static int __init xxx_probe(struct platform_device *pdev)
{
      …
 
      if (!(tsdev = input_allocate_device()))
      {
            printk(KERN_ERR "tsdev: not enough memory\n");
            err = -ENOMEM;
            goto fail;
      }
 
      …
 
      tsdev->name = "xxx TouchScreen";            //xxx
芯片型号
      tsdev ->phys = "xxx/event0";
      tsdev ->id.bustype = BUS_HOST;            
//设备
id,用于匹配handler的id
      tsdev ->id.vendor  = 0x0005;
      tsdev ->id.product = 0x0001;
      tsdev ->id.version = 0x0100;
 
      tsdev ->    = xxx_open;
      tsdev ->close   =xxx_close;
 
      tsdev ->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_SYN);            //
设置
支持的
事件
类型
tsdev ->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);           
      input_set_abs_params
(
tsdev, ABS_X, 0, 0x400, 0, 0);            //
限定
绝对坐标X的取值范围
      input_set_abs_params(tsdev, ABS_Y, 0, 0x400, 0, 0);            //
同上
      input_set_abs_params(tsdev, ABS_PRESSURE, 0, 1000, 0, 0);      //
压力
范围
 
      …
     
      If(input_register_device(tsdev) == error)      
//注册
设备
            goto fail;
     
      …
     
fail:
      input_free_device(tsdev);
      printk(“ts probe failed\n”);
      return err;
}
先看
该函数中的tsdev = input_allocate_device()
深入最终,该在input.c中有定义
struct input_dev *input_allocate_device(void)
{
      struct input_dev *dev;
 
      
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
      if (dev) {
            dev->dynalloc = 1;
            dev->cdev.class = &input_class;
            class_device_initialize(&dev->cdev);
            INIT_LIST_HEAD(&dev->h_list);
            INIT_LIST_HEAD(&dev->node);
      }
 
      return dev;
}
学过
C语言应该都知道malloc函数
(开辟
内存
空间)
这里的kzalloc也
类似
于C语言中的malloc一样,是linux内核
空间
分配内存函数,
后面
的GFP_KERNEL
标志意为常规
的内存分配,更多的分配标志
参阅先关资料
(我
也不懂
)。再
回到前面的函数中来,接着后面的代码为对tsdev结构体中的成员进行
赋值初始化
,赋值完成后就要开始进入主题:
注册
设备了
进入到函数input_register_device(tsdev)
在input.c中有定义。
int input_register_device(struct input_dev *dev)
{
      static atomic_t input_no = ATOMIC_INIT(0);      //
定义
原子变量,
禁止线程
并发访问
      struct input_handle *handle;            //
定义
一些变量
后文使用
      struct input_handler *handler;
      struct input_device_id *id;
      const char *path;
      int error;
 
      if (!dev->dynalloc) {
            printk(KERN_WARNING "input: device %s is statically allocated, will not register\n"
                  "Please convert to input_allocate_device() or contact \n",
                  dev->name ? dev->name : "<Unknown>");
            return -EINVAL;
      }
 
      
mutex_init(&dev->mutex);            //
互斥锁
初始化,
防止
临界区代码
被并发访问
      
set_bit(EV_SYN, dev->evbit);            //
设置
支持同步事件,input设备
全部
默认支持同步事件
 
      /*
      * If delay and period are pre-set by the driver, then autorepeating
      * is handled by the driver itself and we don't do it in input.c.
      */
 
      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;
      }
 
      INIT_LIST_HEAD(&dev->h_list);      //
初始化需要关联
的handle链表头
      list_add_tail(&dev->node, &input_dev_list);      //
设备添加到input_dev_list中
 
      dev->cdev.class = &input_class;
      snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
            "input%ld", (unsigned ) atomic_inc_return(&input_no) - 1);
 
      error = class_device_add(&dev->cdev);
      if (error)
            return error;
 
      error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group);
      if (error)
            goto fail1;
 
      error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
      if (error)
            goto fail2;
 
      error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
      if (error)
            goto fail3;
 
      __module_get(THIS_MODULE);
 
      path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
      printk(KERN_INFO "input: %s as %s\n",
            dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
      kfree(path);
 
 
/*
**
 遍历input_handler_list
全部
的handler,寻找与该设备匹配的handler  ***/
      list_for_each_entry(handler, &input_handler_list, node)
            if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
                  if ((id = input_match_device(handler->id_table, dev)))
                        if ((handle = handler->connect(handler, dev, id)))
                              input_link_handle(handle);
 
 
 
      input_wakeup_procfs_readers();
 
      return 0;
 
 fail3:      sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
 fail2:      sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
 fail1:      class_device_del(&dev->cdev);
      return error;
}
先看
中前面代码加粗的部分
mutex_init(&dev->mutex)
与互斥锁相关的东西
(我也
不太懂
),
set_bit(EV_SYN, dev->evbit)
设置
支持同步事件,linux的input子系统默认要支持同步事件
 
接着看
中间代码加粗的部分,
有关
list操作
在/linux/list.h
有定义
static inline void INIT_LIST_HEAD(struct list_head *list)
{
      list->next = list;
      list->prev = list;
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
      __list_add(new, ->prev, head);
}
static inline void __list_add(struct list_head *new,
                        struct list_head *prev,
                        struct list_head *next)
{
      next->prev = new;
      new->next = next;
      new->prev = prev;
      prev-> = new;
}
可以
看得出INIT_LIST_HEAD(struct list_head *list)
就是让
list指向结构体的成员再指向其本身完成初始化操作,list_add_tail(struct list_head *new, struct list_head *head)
将new
指向的
结构体
作为一
节点
插入到所指向链表节点之前。
因此
中的
list_add_tail(&dev->node, &input_dev_list)
就是将该
设备
添加
input_dev_list
中。
input_dev_list这个双向链表在什么时候被定义了呢,且看input.c源代码开始部分
全局
部分有定义:
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
LIST_HEAD的宏定义:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
      struct list_head name = LIST_HEAD_INIT(name)
显然
这在最开始就已经定义了input_dev_list和input_handler_list
两个链表,
且这
两个链表都只有一个节点,
device
和handler注册的时候会在这两条链表中加入节点,
list_add_tail(&dev->node, &input_dev_list)
就是将该
设备
添加
input_dev_list
 
注意
最后代码加粗的部分,该部分完成了input_dev和input_handler的桥接。先看
list_for_each_entry(handler, &input_handler_list, node)
list_for_each_entry
list.h中有定义,
作用相当于一个for循环。其
等价于:(
个人
理解)
for(handler = input_handler_list
表头所属
input_
handler
结构体
地址;
 handler != input_handler_list
表尾所属
input_
handler
结构体
地址
;
 
handler++)
handler第一次
指向
input_handler_list
头部
所在
的input_handler地址。每循环一次handler就沿着input_handler_list移动到下一个节点,得到下一节点
所属
的handler地址,直到
input_handler_list
的结尾。
而每次
的循环需要做什么
做的就是
input_handler_list上的每一个handler,看有
哪些
handler能与该设备匹配的上
。匹配过程
 
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
//判断
该handler没有被列入黑名单或者
黑名单匹配不成功
的话则
继续
      
if ((id = input_match_device(handler->id_table, dev)))      
//
设备id与handler的id进行匹配,成功则继续往下
if ((handle = handler->connect(handler, dev, id)))
      
//
链接
device与handler
成功则继续往下
      input_link_handle(handle);      
//将
handle链入input_handler_list和input_dev_list
 
继续
跟踪进这些函数
static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
{
      int i;
 
      for (; id->flags || id->driver_info; id++) {
 
            if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)      //
匹配
handler和device  id
flag标志位
                  if (id->bustype != dev->id.bustype)
                        continue;
 
            if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
                  if (id->vendor != dev->id.vendor)
                        continue;
 
            if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
                  if (id->product != dev->id.product)
                        continue;
 
            if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
                  if (id->version != dev->id.version)
                        continue;
 
            MATCH_BIT(evbit,  EV_MAX);            //
匹配
id
相关
标志位
            MATCH_BIT(keybit, KEY_MAX);
            MATCH_BIT(relbit, REL_MAX);
            MATCH_BIT(absbit, ABS_MAX);
            MATCH_BIT(mscbit, MSC_MAX);
            MATCH_BIT(ledbit, LED_MAX);
            MATCH_BIT(sndbit, SND_MAX);
            MATCH_BIT(ffbit,  FF_MAX);
            MATCH_BIT(swbit,  SW_MAX);
 
            return id;
      }
 
      return NULL;
}
用于匹配input_dev
结构体
和input_handler
结构
体里面
定义
的input_device_id,
两者里面
任何
相关
变量不一样则匹配失败
(条件
真苛刻
)。
 
再看
handle = handler->connect(handler, dev, id)
connect函数即为static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
为什么呢会是这个呢,我们先看
evdev.c中
的input_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_handler
结构体
里边的connect即为evdev_connect函数
 
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
1      struct evdev *evdev;            
//定义
一个evdev结构体指针
      struct class_device *cdev;
      int minor;
 
2      for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
      if (minor == EVDEV_MINORS) {
            printk(KERN_ERR "evdev: no more free evdev devices\n");
            return NULL;
      }
 
3      
if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)))
            return NULL;
 
      INIT_LIST_HEAD(&evdev->list);
      init_waitqueue_(&evdev->wait);
 
      evdev->exist = 1;
      evdev->minor = minor;
      evdev->handle.dev = dev;
      evdev->handle.name = evdev->name;
      evdev->handle.handler = handler;
      evdev->handle.private = evdev;
      sprintf(evdev->name, "event%d", minor);
 
      evdev_table[minor] = evdev;
 
      cdev = class_device_create(&input_class, &dev->cdev,
                  MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
                  dev->cdev.dev, evdev->name);
 
      /* temporary symlink to keep userspace happy */
      sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
                    evdev->name);
 
      return &evdev->handle;
}
1处
,这里有个定义在evdev.c里边的新面孔
struct evdev {
      int exist;
      int ;
      int minor;
      char name[16];
      
struct input_handle handle;      //
关联
input_handler和input_dev的input_handle
      wait_queue_head_t wait;
      
struct evdev_list *grab;
      struct list_ list;
};
evdev
这个
结构体就是
拿来应用
开发操作的
,在
这里就是
对应
的设备文件实体,
结构体
前边定义了记录设备的一些信息
(设备号
,打开状态、设备名字等
),
这里还定义了一个input_handle的实体handle,没错这个handle就是要用来
关联
input_dev和input_handler的
后面还有一
加粗的部分后面再做介绍。
2处
evdev_table
[]
一个全局变量的数组
在evdev.c中有定义
#define EVDEV_MINORS            32
static struct evdev *evdev_table[EVDEV_MINORS];
前面
已经
说明了,
一个
device
可以
对应多个
handler
一个handler也可处理多个
device,
这里体现
出了
后者
。既然
evdev这个结构体是
对应
的设备文件实体,
因为这个
handler可能会处理多个device,因此
handler要处理
n
个device就会应该有
n
evdev
实体,而这些实体的地址存放在evdev_table[]
这个指针
数组中,也就是说该handler最多只能处理EVDEV_MINORS
device
2处
的这几句代码就是要evdev_table[]
这个
数组
还有没有
空着
的位置,有
话才会继续
进行
下面的程序。
3处
,开辟
一个evdev结构体的内存空间,前面有说明过kzalloc
函数
,这里不再说明。
后面
的代码就是为
evdev
结构体变量赋
初始值
了,
其中
init_waitqueue_(&evdev->wait)
初始化
等待队列,具体介绍
结合/linux/wait.h和查看
相关资料
(本人
不懂
)。
函数
最后
得到
evdev结构体内的hanlde地址并返回,
此时返回到
我们的int input_register_device(struct input_dev *dev)
函数
里面,最后
一句
input_link_handle(handle)
,进入
到该函数中发现
static void input_link_handle(struct input_handle *handle)
{
      list_add_tail(&handle->d_node, &handle->dev->h_list);
      list_add_tail(&handle->h_node, &handle->handler->h_list);
}
中也
只是
将handle中的d_node和h_node
分别
接入到input_dev和input_handler的h_list中
此时input_dev、input_handler、input_handle
三者
形成了如图
2所示
的关系。
 
至此
设备注册
过程
算是全部完成了,但是
貌似
还有点乱
整个
设备的注册过程中函数的
嵌套一个
接着一个,
不仅
函数嵌套,结构体也使用了结构体和函数嵌套等,在
各个
函数内也用了大量的结构体指针等等
在整个
过程中
input_dev、input_handler、input_handle
三个结构体变量的实体也
只有一个。其中
input_dev
结构体实体在xxx_ts.c中直接定义了一个
input_dev
指针
全局变量
struct input_dev *tsdev;
在初始化
函数
开辟
了一个input_dev的内存空间并让tsdev指针指向它:
w55fa95_dev = input_allocate_device()
input_handler结构体
在evdev.c中也直接被定义了
初始化了
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_dev和input_handler的input_handle则是在调用
链接连接
函数static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
时候,在该函数的内部
调用
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)
开辟
了一个evdev结构体的内存空间
创建了
input_handle就是使用了evdev结构体内部定义的input_handle
      一个完整input设备系统不仅要有设备,还需要有处理程序input_handler,
上文中
主要
介绍的是设备注册、生成
以及和handler搭配的一个过程,而handler在
何时生成
 
第三章
、input_handler
注册。
      Input_handler是要和用户层打交道的
在evdev.c中直接定义了一个input_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,            //
指向
一个evedev的指针数组
};
先看
第一
加粗的代码,evedev_fops结构体的定义
如下
static 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
};
相信
做过
linux
设备驱动编程的对这
都很熟悉
了,就是一大堆的用户接口函数,包括对设备的
、close、read、write、ioctl等
看第二行代码加粗的部分
.id_table =      evdev_ids, 
id.table
就是
前面所说过要和input_dev的id匹配的这么一个结构体,这里让它初始化为evdev_ids,在看evdev_ids的定义
static struct input_device_id evdev_ids[] = {
      { .driver_info = 1 },      /* Matches all devices */
      { },                  /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
这里是
一个结构体数组,
数组中
第一个
结构体的该成员变量driver_info的值为
1,
其他成员变量均
定义,说明
这个
handler对
所有
device的id都能匹配得上
。数组
中的第二个结构体为空
表示
结束
,用来
标识结束配合下面的MODULE_DEVICE_TABLE(input, evdev_ids)
使用
,关于MODULE_DEVICE_TABLE
宏定义
介绍自行查看
相关
文献
(我也
不懂
)。
     
      
接下来
进入正题,input_handler的注册
      Input_handler的注册和input_dev的注册很相似,大同小异罢了,在evdev.c源码中
显示
定义并初始化了一个input_handler结构体并直接给相关的
成员
变量赋值了,就是本章开始所将的部分,然后再初始化中注册一个input_handler:
static int __init evdev_init(void)
{
      input_register_handler(&evdev_handler);
      return 0;
}
这里
只调用了一个input_register_handler()
函数,
看起来应该是很简单的样子
(相比
input_dev的注册
),继续
跟踪进入到该函数中:
void input_register_handler(struct input_handler *handler)
{
      struct input_dev *dev;
      struct input_handle *handle;
      struct input_device_id *id;
 
      if (!handler) return;
 
      
INIT_LIST_HEAD(&handler->h_list);
 
      if (handler->fops != NULL)
            input_table[handler->minor >> 5] = handler;
 
      
list_add_tail(&handler->node, &input_handler_list);
 
      list_for_each_entry(dev, &input_dev_list, node)
            if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
                  if ((id = input_match_device(handler->id_table, dev)))
                        if ((handle = handler->connect(handler, dev, id)))
                              input_link_handle(handle);
 
      input_wakeup_procfs_readers();
}
该函数
中代码加粗的部分,与input_register_device()
里的如出一辙,这里不再做说明。
 
至此input_handler
的注册已经
结束
 
第四章
、input
子系统数据
结构
      
3是以
触摸屏设备为例子的input子系统
数据
结构图。
图3  input子系统数据结构图
     
进行
到这里,又多冒出来了一个struct evdev_list的结构体,
在之前并没有提到过,因为在注册input_dev和input_handler过程中并没有用到过,
查看
该结构体:
struct evdev_list {
      
struct input_event buffer[EVDEV_BUFFER_SIZE];      //
存放
设备数据信息
      int head;      //
buffer
下标
,标识从设备中过来
存放到buffer的
数据
的位置
      int tail;            //buffer的下标,标识用户读取
-
-
触摸屏驱动为例
第一章、了解
linux input子系统
 
      L
inux输入
设备
总类
繁杂
,常见
的包括有
按键
、键盘、触摸屏、
鼠标
、摇杆
等等,他们
本身就是
字符设备
linux内核将
这些
设备
共同性抽象出来
简化驱动开发
建立
了一个input子系统。
子系统共分为
三层,如图
1所示
图1
  input
输入
子系统
 
      
驱动
层和
硬件
相关,直接
捕捉和
获取硬件设备
数据信息等
(包括
被按下、按下位置、
鼠标移动
键盘
按下等等),
然后
数据
信息报告到核心层
。核心层负责
连接
驱动层
事件处理层
设备驱动(
device driver
)
和处理程序
(
handler
)的
注册需要通过核心层来完成,核心层接收来自驱动层的数据
信息,
并将数据信息选择对应
handler去处理,最终handler
数据复制到用户
空间。
      
先了解
三个
定义
在/linux/input.h
重要的结构体
input_dev
、input_handler、input_handle。
struct input_dev {
      void *private;
 
      const char *name;
      const char *phys;
      const char *uniq;
      
struct input_id id;      
//与
input_handler匹配
id
 
      
unsigned long evbit[NBITS(EV_MAX)];            
//设备
支持的
事件
类型
      unsigned long keybit[NBITS(KEY_MAX)];      
//按键
事件支持的子
事件
类型
      unsigned long relbit[NBITS(REL_MAX)];
      
unsigned long absbit[NBITS(ABS_MAX)];      
//绝对坐标
事件支持的子事件类型
      unsigned long mscbit[NBITS(MSC_MAX)];
      unsigned long ledbit[NBITS(LED_MAX)];
      unsigned long sndbit[NBITS(SND_MAX)];
      unsigned long ffbit[NBITS(FF_MAX)];
      unsigned  swbit[NBITS(SW_MAX)];
      int ff_effects_max;
 
      unsigned int keycodemax;
      unsigned int keycodesize;
      void *keycode;
 
      unsigned int repeat_key;
      struct timer_list timer;
 
      struct pt_regs *regs;
      int state;
      int sync;
 
      int abs[ABS_MAX + 1];
      int rep[REP_MAX + 1];
 
      unsigned long key[NBITS(KEY_MAX)];
      unsigned long led[NBITS(LED_MAX)];
      unsigned long snd[NBITS(SND_MAX)];
      unsigned  sw[NBITS(SW_MAX)];
 
      
int absmax[ABS_MAX + 1];      
//绝对
坐标
事件
最大
键值
      int absmin[ABS_MAX + 1];      
//绝对
坐标
事件
的最小键值
      int absfuzz[ABS_MAX + 1];
      int absflat[ABS_MAX + 1];
 
      int (*)(struct input_dev *dev);
      void (*close)(struct input_dev *dev);
      int (*accept)(struct input_dev *dev, struct file *file);
      int (*flush)(struct input_dev *dev, struct file *file);
      int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
      int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
      int (*erase_effect)(struct input_dev *dev, int effect_id);
 
      
struct input_handle *grab;      
//当前
占有该设备的handle
 
      struct mutex mutex;      /* serializes  and close operations */
      unsigned int users;            //
打开
该设备的用户量
 
      struct class_device cdev;
      struct device *dev;      /* will be removed soon */
 
      int dynalloc;      /* temporarily */
 
      struct list_head      h_list;      //
链表头用于
链接该
设备
所关联
的input_handle
      struct list_head      node;      //
链表头用于
设备
链接到input_dev_list
};
      Input_dev
一个很强大的结构体,
把所有的
input
设备
(触摸屏
、键盘、鼠标等
)的
信息都考虑到了,
对于
来说只用到
里面的
一部分
而已,
尤其
加粗
的部分
,注意
该结构体中
最后
两行定义的
两个
list_head
结构体
list_head
在/linux/list.h
有定义,深入跟踪
struct list_head {
      struct list_head *, *prev;
};
结构体内部
没有
定义
数据而
定义了两个指向本身
结构体
的指针
,预先说明
一下,
所有
input 
device
在注册后
加入一个input_dev_list(
输入
设备链表)
所有的e
vent
handler在注册后会加入一个input_handler_list(
输入
处理程序链表)
,这里
的list_
主要
作用
是作为
input_dev_list
和input_handler_list
一个节点来保存地址。I
nput_dev_list
和input_handler_list之间的对应关系由input_handle结构体桥接,具体后面说明。
 
struct input_handler {
 
      void *private;
 
      void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
      struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);
      void (*disconnect)(struct input_handle *handle);
 
      
const struct file_operations *fops;      //
提供给
用户对设备
操作
的函数指针
      int minor;
      char *name;
 
      
struct input_device_id *id_table;      
//与
input_dev匹配
id
      struct input_device_id *blacklist;      //
标记
的黑名单
 
      
struct list_head      h_list;            
//用于
链接和该
handler相关
handle
      struct list_head      node;            //
用于
将该
handler链入
input_handler_list
};
i
nput_handler顾名思义,
它是用来处理
input_dev的
一个结构体,
相关
的处理在结构里内部都有定义,
最后
两行定义的list_结构体作用
input_dev所定义的一样,这里不再说明。
:input_device_id
结构体
/
linux/mod_devicetable.h
有定义
 
struct input_handle {
 
      void *private;
 
      int ;      //
记录
设备打开次数
      char *name;
 
      struct input_dev *dev;      //
指向
所属的input_dev
      struct input_handler *handler;      //
指向所属的
input_handler
 
      struct list_head      d_node;            
//用于
链入
指向的
input_dev的handle
链表
      struct list_head      h_node;            
//用于
链入
指向的
input_
handler
的handle
链表
};
可以
看到input_handle中拥有指向input_dev和input_handler的指针,
input_handle是用来关联
input_dev
和input_handler
。为什么
用input_handle来关联input_dev和input_handler
input_dev和input_handler直接对应呢?因为
一个
device
可以
对应多个
handler
一个handler也可处理多个
device。就
如一个
设备可以
对应
event handler
可以对应tseve handler
      input_dev
、input_
handler
、input_handle
的关系如下图
2所示
图2  input_dev,input_handlerinput_handle关系图
 
第二章、input
 device
注册
      I
nput 
device
的注册
实际上
仅仅
只有几行代码,因为在input.c中已经将大量的代码封装好了,主需要调用几个关键的函数就能完成对input device的注册。
      
xxx_ts.c中预先
定义
全局变量struct input_dev  tsdev
;然后
进入到初始化
static int __init xxx_probe(struct platform_device *pdev)
{
      …
 
      if (!(tsdev = input_allocate_device()))
      {
            printk(KERN_ERR "tsdev: not enough memory\n");
            err = -ENOMEM;
            goto fail;
      }
 
      …
 
      tsdev->name = "xxx TouchScreen";            //xxx
芯片型号
      tsdev ->phys = "xxx/event0";
      tsdev ->id.bustype = BUS_HOST;            
//设备
id,用于匹配handler的id
      tsdev ->id.vendor  = 0x0005;
      tsdev ->id.product = 0x0001;
      tsdev ->id.version = 0x0100;
 
      tsdev ->    = xxx_open;
      tsdev ->close   =xxx_close;
 
      tsdev ->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_SYN);            //
设置
支持的
事件
类型
tsdev ->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);           
      input_set_abs_params
(
tsdev, ABS_X, 0, 0x400, 0, 0);            //
限定
绝对坐标X的取值范围
      input_set_abs_params(tsdev, ABS_Y, 0, 0x400, 0, 0);            //
同上
      input_set_abs_params(tsdev, ABS_PRESSURE, 0, 1000, 0, 0);      //
压力
范围
 
      …
     
      If(input_register_device(tsdev) == error)      
//注册
设备
            goto fail;
     
      …
     
fail:
      input_free_device(tsdev);
      printk(“ts probe failed\n”);
      return err;
}
先看
该函数中的tsdev = input_allocate_device()
深入最终,该在input.c中有定义
struct input_dev *input_allocate_device(void)
{
      struct input_dev *dev;
 
      
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
      if (dev) {
            dev->dynalloc = 1;
            dev->cdev.class = &input_class;
            class_device_initialize(&dev->cdev);
            INIT_LIST_HEAD(&dev->h_list);
            INIT_LIST_HEAD(&dev->node);
      }
 
      return dev;
}
学过
C语言应该都知道malloc函数
(开辟
内存
空间)
这里的kzalloc也
类似
于C语言中的malloc一样,是linux内核
空间
分配内存函数,
后面
的GFP_KERNEL
标志意为常规
的内存分配,更多的分配标志
参阅先关资料
(我
也不懂
)。再
回到前面的函数中来,接着后面的代码为对tsdev结构体中的成员进行
赋值初始化
,赋值完成后就要开始进入主题:
注册
设备了
进入到函数input_register_device(tsdev)
在input.c中有定义。
int input_register_device(struct input_dev *dev)
{
      static atomic_t input_no = ATOMIC_INIT(0);      //
定义
原子变量,
禁止线程
并发访问
      struct input_handle *handle;            //
定义
一些变量
后文使用
      struct input_handler *handler;
      struct input_device_id *id;
      const char *path;
      int error;
 
      if (!dev->dynalloc) {
            printk(KERN_WARNING "input: device %s is statically allocated, will not register\n"
                  "Please convert to input_allocate_device() or contact \n",
                  dev->name ? dev->name : "<Unknown>");
            return -EINVAL;
      }
 
      
mutex_init(&dev->mutex);            //
互斥锁
初始化,
防止
临界区代码
被并发访问
      
set_bit(EV_SYN, dev->evbit);            //
设置
支持同步事件,input设备
全部
默认支持同步事件
 
      /*
      * If delay and period are pre-set by the driver, then autorepeating
      * is handled by the driver itself and we don't do it in input.c.
      */
 
      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;
      }
 
      INIT_LIST_HEAD(&dev->h_list);      //
初始化需要关联
的handle链表头
      list_add_tail(&dev->node, &input_dev_list);      //
设备添加到input_dev_list中
 
      dev->cdev.class = &input_class;
      snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
            "input%ld", (unsigned ) atomic_inc_return(&input_no) - 1);
 
      error = class_device_add(&dev->cdev);
      if (error)
            return error;
 
      error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group);
      if (error)
            goto fail1;
 
      error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
      if (error)
            goto fail2;
 
      error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
      if (error)
            goto fail3;
 
      __module_get(THIS_MODULE);
 
      path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
      printk(KERN_INFO "input: %s as %s\n",
            dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
      kfree(path);
 
 
/*
**
 遍历input_handler_list
全部
的handler,寻找与该设备匹配的handler  ***/
      list_for_each_entry(handler, &input_handler_list, node)
            if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
                  if ((id = input_match_device(handler->id_table, dev)))
                        if ((handle = handler->connect(handler, dev, id)))
                              input_link_handle(handle);
 
 
 
      input_wakeup_procfs_readers();
 
      return 0;
 
 fail3:      sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
 fail2:      sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
 fail1:      class_device_del(&dev->cdev);
      return error;
}
先看
中前面代码加粗的部分
mutex_init(&dev->mutex)
与互斥锁相关的东西
(我也
不太懂
),
set_bit(EV_SYN, dev->evbit)
设置
支持同步事件,linux的input子系统默认要支持同步事件
 
接着看
中间代码加粗的部分,
有关
list操作
在/linux/list.h
有定义
static inline void INIT_LIST_HEAD(struct list_head *list)
{
      list->next = list;
      list->prev = list;
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
      __list_add(new, ->prev, head);
}
static inline void __list_add(struct list_head *new,
                        struct list_head *prev,
                        struct list_head *next)
{
      next->prev = new;
      new->next = next;
      new->prev = prev;
      prev-> = new;
}
可以
看得出INIT_LIST_HEAD(struct list_head *list)
就是让
list指向结构体的成员再指向其本身完成初始化操作,list_add_tail(struct list_head *new, struct list_head *head)
将new
指向的
结构体
作为一
节点
插入到所指向链表节点之前。
因此
中的
list_add_tail(&dev->node, &input_dev_list)
就是将该
设备
添加
input_dev_list
中。
input_dev_list这个双向链表在什么时候被定义了呢,且看input.c源代码开始部分
全局
部分有定义:
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
LIST_HEAD的宏定义:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
      struct list_head name = LIST_HEAD_INIT(name)
显然
这在最开始就已经定义了input_dev_list和input_handler_list
两个链表,
且这
两个链表都只有一个节点,
device
和handler注册的时候会在这两条链表中加入节点,
list_add_tail(&dev->node, &input_dev_list)
就是将该
设备
添加
input_dev_list
 
注意
最后代码加粗的部分,该部分完成了input_dev和input_handler的桥接。先看
list_for_each_entry(handler, &input_handler_list, node)
list_for_each_entry
list.h中有定义,
作用相当于一个for循环。其
等价于:(
个人
理解)
for(handler = input_handler_list
表头所属
input_
handler
结构体
地址;
 handler != input_handler_list
表尾所属
input_
handler
结构体
地址
;
 
handler++)
handler第一次
指向
input_handler_list
头部
所在
的input_handler地址。每循环一次handler就沿着input_handler_list移动到下一个节点,得到下一节点
所属
的handler地址,直到
input_handler_list
的结尾。
而每次
的循环需要做什么
做的就是
input_handler_list上的每一个handler,看有
哪些
handler能与该设备匹配的上
。匹配过程
 
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
//判断
该handler没有被列入黑名单或者
黑名单匹配不成功
的话则
继续
      
if ((id = input_match_device(handler->id_table, dev)))      
//
设备id与handler的id进行匹配,成功则继续往下
if ((handle = handler->connect(handler, dev, id)))
      
//
链接
device与handler
成功则继续往下
      input_link_handle(handle);      
//将
handle链入input_handler_list和input_dev_list
 
继续
跟踪进这些函数
static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
{
      int i;
 
      for (; id->flags || id->driver_info; id++) {
 
            if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)      //
匹配
handler和device  id
flag标志位
                  if (id->bustype != dev->id.bustype)
                        continue;
 
            if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
                  if (id->vendor != dev->id.vendor)
                        continue;
 
            if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
                  if (id->product != dev->id.product)
                        continue;
 
            if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
                  if (id->version != dev->id.version)
                        continue;
 
            MATCH_BIT(evbit,  EV_MAX);            //
匹配
id
相关
标志位
            MATCH_BIT(keybit, KEY_MAX);
            MATCH_BIT(relbit, REL_MAX);
            MATCH_BIT(absbit, ABS_MAX);
            MATCH_BIT(mscbit, MSC_MAX);
            MATCH_BIT(ledbit, LED_MAX);
            MATCH_BIT(sndbit, SND_MAX);
            MATCH_BIT(ffbit,  FF_MAX);
            MATCH_BIT(swbit,  SW_MAX);
 
            return id;
      }
 
      return NULL;
}
用于匹配input_dev
结构体
和input_handler
结构
体里面
定义
的input_device_id,
两者里面
任何
相关
变量不一样则匹配失败
(条件
真苛刻
)。
 
再看
handle = handler->connect(handler, dev, id)
connect函数即为static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
为什么呢会是这个呢,我们先看
evdev.c中
的input_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_handler
结构体
里边的connect即为evdev_connect函数
 
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
1      struct evdev *evdev;            
//定义
一个evdev结构体指针
      struct class_device *cdev;
      int minor;
 
2      for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
      if (minor == EVDEV_MINORS) {
            printk(KERN_ERR "evdev: no more free evdev devices\n");
            return NULL;
      }
 
3      
if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)))
            return NULL;
 
      INIT_LIST_HEAD(&evdev->list);
      init_waitqueue_(&evdev->wait);
 
      evdev->exist = 1;
      evdev->minor = minor;
      evdev->handle.dev = dev;
      evdev->handle.name = evdev->name;
      evdev->handle.handler = handler;
      evdev->handle.private = evdev;
      sprintf(evdev->name, "event%d", minor);
 
      evdev_table[minor] = evdev;
 
      cdev = class_device_create(&input_class, &dev->cdev,
                  MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
                  dev->cdev.dev, evdev->name);
 
      /* temporary symlink to keep userspace happy */
      sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
                    evdev->name);
 
      return &evdev->handle;
}
1处
,这里有个定义在evdev.c里边的新面孔
struct evdev {
      int exist;
      int ;
      int minor;
      char name[16];
      
struct input_handle handle;      //
关联
input_handler和input_dev的input_handle
      wait_queue_head_t wait;
      
struct evdev_list *grab;
      struct list_ list;
};
evdev
这个
结构体就是
拿来应用
开发操作的
,在
这里就是
对应
的设备文件实体,
结构体
前边定义了记录设备的一些信息
(设备号
,打开状态、设备名字等
),
这里还定义了一个input_handle的实体handle,没错这个handle就是要用来
关联
input_dev和input_handler的
后面还有一
加粗的部分后面再做介绍。
2处
evdev_table
[]
一个全局变量的数组
在evdev.c中有定义
#define EVDEV_MINORS            32
static struct evdev *evdev_table[EVDEV_MINORS];
前面
已经
说明了,
一个
device
可以
对应多个
handler
一个handler也可处理多个
device,
这里体现
出了
后者
。既然
evdev这个结构体是
对应
的设备文件实体,
因为这个
handler可能会处理多个device,因此
handler要处理
n
个device就会应该有
n
evdev
实体,而这些实体的地址存放在evdev_table[]
这个指针
数组中,也就是说该handler最多只能处理EVDEV_MINORS
device
2处
的这几句代码就是要evdev_table[]
这个
数组
还有没有
空着
的位置,有
话才会继续
进行
下面的程序。
3处
,开辟
一个evdev结构体的内存空间,前面有说明过kzalloc
函数
,这里不再说明。
后面
的代码就是为
evdev
结构体变量赋
初始值
了,
其中
init_waitqueue_(&evdev->wait)
初始化
等待队列,具体介绍
结合/linux/wait.h和查看
相关资料
(本人
不懂
)。
函数
最后
得到
evdev结构体内的hanlde地址并返回,
此时返回到
我们的int input_register_device(struct input_dev *dev)
函数
里面,最后
一句
input_link_handle(handle)
,进入
到该函数中发现
static void input_link_handle(struct input_handle *handle)
{
      list_add_tail(&handle->d_node, &handle->dev->h_list);
      list_add_tail(&handle->h_node, &handle->handler->h_list);
}
中也
只是
将handle中的d_node和h_node
分别
接入到input_dev和input_handler的h_list中
此时input_dev、input_handler、input_handle
三者
形成了如图
2所示
的关系。
 
至此
设备注册
过程
算是全部完成了,但是
貌似
还有点乱
整个
设备的注册过程中函数的
嵌套一个
接着一个,
不仅
函数嵌套,结构体也使用了结构体和函数嵌套等,在
各个
函数内也用了大量的结构体指针等等
在整个
过程中
input_dev、input_handler、input_handle
三个结构体变量的实体也
只有一个。其中
input_dev
结构体实体在xxx_ts.c中直接定义了一个
input_dev
指针
全局变量
struct input_dev *tsdev;
在初始化
函数
开辟
了一个input_dev的内存空间并让tsdev指针指向它:
w55fa95_dev = input_allocate_device()
input_handler结构体
在evdev.c中也直接被定义了
初始化了
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_dev和input_handler的input_handle则是在调用
链接连接
函数static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
时候,在该函数的内部
调用
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)
开辟
了一个evdev结构体的内存空间
创建了
input_handle就是使用了evdev结构体内部定义的input_handle
      一个完整input设备系统不仅要有设备,还需要有处理程序input_handler,
上文中
主要
介绍的是设备注册、生成
以及和handler搭配的一个过程,而handler在
何时生成
 
第三章
、input_handler
注册。
      Input_handler是要和用户层打交道的
在evdev.c中直接定义了一个input_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,            //
指向
一个evedev的指针数组
};
先看
第一
加粗的代码,evedev_fops结构体的定义
如下
static 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
};
相信
做过
linux
设备驱动编程的对这
都很熟悉
了,就是一大堆的用户接口函数,包括对设备的
、close、read、write、ioctl等
看第二行代码加粗的部分
.id_table =      evdev_ids, 
id.table
就是
前面所说过要和input_dev的id匹配的这么一个结构体,这里让它初始化为evdev_ids,在看evdev_ids的定义
static struct input_device_id evdev_ids[] = {
      { .driver_info = 1 },      /* Matches all devices */
      { },                  /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
这里是
一个结构体数组,
数组中
第一个
结构体的该成员变量driver_info的值为
1,
其他成员变量均
定义,说明
这个
handler对
所有
device的id都能匹配得上
。数组
中的第二个结构体为空
表示
结束
,用来
标识结束配合下面的MODULE_DEVICE_TABLE(input, evdev_ids)
使用
,关于MODULE_DEVICE_TABLE
宏定义
介绍自行查看
相关
文献
(我也
不懂
)。
     
      
接下来
进入正题,input_handler的注册
      Input_handler的注册和input_dev的注册很相似,大同小异罢了,在evdev.c源码中
显示
定义并初始化了一个input_handler结构体并直接给相关的
成员
变量赋值了,就是本章开始所将的部分,然后再初始化中注册一个input_handler:
static int __init evdev_init(void)
{
      input_register_handler(&evdev_handler);
      return 0;
}
这里
只调用了一个input_register_handler()
函数,
看起来应该是很简单的样子
(相比
input_dev的注册
),继续
跟踪进入到该函数中:
void input_register_handler(struct input_handler *handler)
{
      struct input_dev *dev;
      struct input_handle *handle;
      struct input_device_id *id;
 
      if (!handler) return;
 
      
INIT_LIST_HEAD(&handler->h_list);
 
      if (handler->fops != NULL)
            input_table[handler->minor >> 5] = handler;
 
      
list_add_tail(&handler->node, &input_handler_list);
 
      list_for_each_entry(dev, &input_dev_list, node)
            if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
                  if ((id = input_match_device(handler->id_table, dev)))
                        if ((handle = handler->connect(handler, dev, id)))
                              input_link_handle(handle);
 
      input_wakeup_procfs_readers();
}
该函数
中代码加粗的部分,与input_register_device()
里的如出一辙,这里不再做说明。
 
至此input_handler
的注册已经
结束
 
第四章
、input
子系统数据
结构
      
3是以
设备为例子的input子系统
数据
结构图。
图3  input子系统
     
进行
到这里,又多冒出来了一个struct evdev_list的结构体,
在之前并没有提到过,因为在注册input_dev和input_handler过程中并没有用到过,
查看
该结构体:
struct evdev_list {
      
struct input_event buffer[EVDEV_BUFFER_SIZE];      //
存放
设备数据信息
      int head;      //
buffer
下标
,标识从设备中过来
存放到buffer的
数据
的位置
      int tail;            //buffer的下标,标识用户读取

转载地址:https://loongembedded.blog.csdn.net/article/details/50818165 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:深入理解SELinux SEAndroid
下一篇:Linux and the Device Tree

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年05月02日 11时52分56秒