➤简介
Linux输入设备总类繁杂,常见的包括有按键、键盘、触摸屏、鼠标、摇杆等等,他们本身就是字符设备,而linux内核将这些设备的共同性抽象出来,简化驱动开发建立了一个input子系统。子系统共分为三层,如图1所示。
图1 input输入子系统
➣驱动层
驱动层和硬件相关,直接捕捉和获取硬件设备的数据信息等(包括触摸屏被按下、按下位置、鼠标移动、键盘按下等等),然后将数据信息报告到核心层。
➣核心层
核心层负责连接驱动层和事件处理层,设备驱动(device driver)和处理程序(handler)的注册需要通过核心层来完成,核心层接收来自驱动层的数据信息,并将数据信息选择对应的handler去处理,最终handler将数据复制到用户空间。
➤重要的结构体input_dev、input_handler、input_handle。
➣input_dev
structinput_dev {void *private;const char *name;const char *phys;const char *uniq;struct inBITS(KEY_MAX)]; //按键事件支持的子事件类型
unsigned longrelbit[NBITS(REL_MAX)];
unsignedlong absbit[NBITS(ABS_Mput_id id; //与input_handler匹配用的id
unsigned long evbit[NBITS(EV_MAX)]; //设备支持的事件类型
unsigned long keybit[NAX)]; //绝对坐标事件支持的子事件类型
unsigned longmscbit[NBITS(MSC_MAX)];
unsignedlongledbit[NBITS(LED_MAX)];
unsignedlongsndbit[NBITS(SND_MAX)];
unsignedlongffbit[NBITS(FF_MAX)];
unsignedlongswbit[NBITS(SW_MAX)];intff_effects_max;
unsignedintkeycodemax;
unsignedintkeycodesize;void *keycode;
unsignedintrepeat_key;structtimer_list timer;struct pt_regs *regs;intstate;intsync;int abs[ABS_MAX + 1];int rep[REP_MAX + 1];
unsignedlongkey[NBITS(KEY_MAX)];
unsignedlongled[NBITS(LED_MAX)];
unsignedlongsnd[NBITS(SND_MAX)];
unsignedlongsw[NBITS(SW_MAX)];int absmax[ABS_MAX + 1]; //绝对坐标事件的最大键值
int absmin[ABS_MAX + 1]; //绝对坐标事件的最小键值
int absfuzz[ABS_MAX + 1];int absflat[ABS_MAX + 1];int (*open)(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, intvalue);int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);int (*erase_effect)(struct input_dev *dev, inteffect_id);struct input_handle *grab; //当前占有该设备的handle
struct mutex mutex; /*serializes open and close operations*/unsignedint users; //打开该设备的用户量
structclass_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中有定义,深入跟踪
structlist_head {struct list_head *next, *prev;
};
该结构体内部并没有定义数据而只定义了两个指向本身结构体的指针,预先说明一下:★所有的input device在注册后会加入一个input_dev_list(输入 设备链表)。★所有的eventhandler在注册后会加入一个input_handler_list(输入处理程序链表),这里的list_head主要的作用是作为input_dev_list和input_handler_list的一个节点来保存地址。Input_dev_list和input_handler_list之间的对应关系由input_handle结构体桥接,具体后面说明。
➣input_handler
structinput_handler {void *private;void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, intvalue);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; //提供给用户对设备操作的函数指针
intminor;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
};
input_handler顾名思义,它是用来处理input_dev的一个结构体获取回来的数据,相关的处理函数在结构里内部都有定义,最后两行定义的list_head结构体作用同input_dev所定义的一样,这里不再说明。
注:input_device_id结构体在/linux/mod_devicetable.h中有定义
➣input_handle
structinput_handle {void *private;int open; //记录设备打开次数
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_handler,input_handle关系图
Input 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;gotofail;
}
…
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.vers