http://blog.csdn.net/lmm670/article/details/6081019
http://www.cnitblog.com/luofuchong/archive/2007/08/24/32382.html
闲聊linux中的input设备(1-10)
接口
前面说了,linux内核input子系统中已经实现了input设备的接口函数,这使得我们工作量大大的减轻了。我们以akm8973芯片(用于智能手机指南针的主功能芯片,实际上就一电子罗盘)为例,来简单看一下写一个input设备我们需要做的工作。
首先,在驱动模块加载函数中申请一个input设备,并告知input子系统它支持哪些事件,如下所示:
akm->input_dev = input_allocate_device();
set_bit(EV_ABS, akm->input_dev->evbit);
input_set_abs_params(akm->input_dev, ABS_RX, 0, 23040, 0, 0);
input_set_abs_params(akm->input_dev, ABS_RY, -11520, 11520, 0, 0);
input_set_abs_params(akm->input_dev, ABS_RZ, -5760, 5760, 0, 0);
input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0);
input_set_abs_params(akm->input_dev, ABS_RUDDER, 0, 3, 0, 0);
input_set_abs_params(akm->input_dev, ABS_HAT0X, -2048, 2032, 0, 0);
input_set_abs_params(akm->input_dev, ABS_HAT0Y, -2048, 2032, 0, 0);
input_set_abs_params(akm->input_dev, ABS_BRAKE, -2048, 2032, 0, 0);
以上这些都是为让input子系统支持的某些参数而设置的,EV_ABS表示支持绝对值坐标,后面都是针对这些坐标的一些参数访问范围设置。至于为什么这样设置,我们继续往下走,到后面我们就明白了。
接着,在驱动模块函数中注册输入设备:
err = input_register_device(akm->input_dev);
然后,报告发生的一些事件以及对应的坐标。
input_report_abs(data->input_dev, ABS_RX, rbuf[0]);
input_report_abs(data->input_dev, ABS_RY, rbuf[1]);
input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);
对应的三个方向的坐标值就被驱动记录下来了。
深入里面跟踪一下:
static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
{
dev->absmin[axis] = min;
dev->absmax[axis] = max;
dev->absfuzz[axis] = fuzz;
dev->absflat[axis] = flat;
dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
}
这个函数用来干嘛的呢?这个留到以后讲,不过你得多个心眼,后面用得到的。
添加一个input设备,我们要做的工作就这些了。接下来我们就可以通过input内核子系统提供的接口函数来处理这些坐标值,把把他们传到用户空间。
我们这些比较懒的家伙为了避免去完善那些复杂的设备接口函数集,所以干脆把它注册成一个input设备,所以你就得先申请它,注册它等一系列预备工作,做好这些之后,我们就实行鲁迅先生的拿来主义,直接使用input子系统的的接口函数”。
原理
继续我们的input设备之旅。
从前一节来看,在linux内核中添加一个input设备变得很简单了。我们再也不必须去动手写那些该死的接口函数了。可是你有没有想过,是谁让我们的工作变得这么简单了呢?答案是linux内核中的input core。她总是那么痴情,默默地不求回报地为你做许许多多的事情,在你背后默默的支持你爱着你。是的,你所想到的大多数事情,我们的input core都已经为你做好。除了感动,我们还能说什么呢?(input core对应的实体在linux内核源码目录linux-2.6.29/drivers/input/input.c文件)
在正式接触我们可爱的input core之前,有必要了解一下几个重要的结构体,这几个结构体是我们这个故事的主体。
第一个数据结构 struct input_dev。悟性高的哥们马上就会想到,它就是我们input 设备在linux内核中的模拟,即里面记录了一个input设备的所有信息。定义于linux-2.6.29/include/linux/input.h中
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //表示此input设备支持的事件
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //设置相应的位以支持某一类绝对值坐标
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int sync;
int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
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 (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
int going_away;
struct device dev;
struct list_head h_list; //struct list_head h_list;表示的是和该设备相关的所有input handle的结构体链表
struct list_head node; //struct list_head node;所有input设备组成的链表结构(后面将会知道它对应于input_dev_list)。
};
很强大的一个结构体,因为她把所有的input设备的信息都考虑到了,真的是很无私。不过对于我们的akm驱动来说只需要关注几个小细节,结构中的加粗部分。
unsigned long evbit [BITS_TO_LONGS(EV_CNT)] 表示此input设备支持的事件,
比如前面的第二节中的set_bit(EV_ABS, akm->input_dev->evbit)设置input_dev->evbit中的相应位让它支持绝对值坐标。
类似的还有以下这些事件:EV_KEY -按键, EV_REL -相对坐标 EV_ABS -绝对坐标,EV_LED - LED,EV_FF- 力反馈。
unsigned long absbit [BITS_TO_LONGS(ABS_CNT)];设置相应的位以支持某一类绝对值坐标。
比如第二节中的input_set_abs_params(akm->input_dev, ABS_RX, 0, 23040, 0, 0);它的函数体如下:
static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
{ dev->absmin[axis] = min;
dev->absmax[axis] = max;
dev->absfuzz[axis] = fuzz;
dev->absflat[axis] = flat;
dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
}
表示支持绝对值x坐标,并设置它在坐标系中的最大值和最小值,以及干扰值和平焊位置等。
Ok 马上进入第二个结构体struct input_handler(还是来自linux-2.6.29/include/linux/input.h)
struct 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设备的一个结构体。
struct list_head h_list表示的是和该设备相关的所有input handle的结构体链表和前面那个一样;
struct list_head node所有input_handle组成的结构体连链表(后面将会知道它对应于input_handler_list)。每一个input设备在注册时,他都会遍历input_handler_list链表上的每一个input_handler,去找寻他心中的那个她,同理每一个input_handler在注册时,他也会去input_dev_list上找寻那个属于他的她。有时候事情往往不会那么尽如人意,当input_handler还没出生时,你这个input_dev就一直在那等吧,等到天荒地老,等到海枯石烂。最后来一句,我等到花儿也谢了,你丫到底还来不来啊。注意这里的input_handler和input_dev并不是一一对应的关系,有时一个input_handler对应好几个input_dev。于是乎,作为看代码的我就在想,linux内核开发者的思想怎么这么不单纯呢,这不明摆着教育我们搞一夫多妻制吗,不过管怎样,还是得记住公司的企业文化,本分点。如果你丫说你同时拥有两个马子,我将会无情的向你抛出那句话:“出来混,迟早要还的!”。
前面多次提到那个input_handle(注意区别input_handler),她到底是何方神圣。好吧,就让我们来一层一层揭开她那神秘的面纱,当你第一次看到她完完全全展现在你面前时,那时候你的满足感和兴奋度和她的害羞度是成正比的。
同样来自linux-2.6.29/include/linux/input.h
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
怎么啦?是不是很失望,原来就这么回事啊。嗯,没错,兄弟,就这么回事。人往往都这样,得到了某样东西,想想觉得就那么回事,没得到呢,那叫一个好奇,那叫一个盼望。好了既然看到她的庐山真面目了,就坦然面对她,作为一个负责的男人,我还是来好好研究一下。
Input_Handle其实也好理解,它就是input_dev和 input_handler粘合剂,通过Input_Handle这么一搅和,input_dev就和 input_handler发生了点关系,至于什么样的关系我们后文将会知道的一清二楚。struct input_dev *dev对应的input设备。struct input_handler *handler对应该设备的handler。struct list_head d_node和struct list_head h_node则分别关联上前面两个结构体中的struct list_head h_list。
终于告一段落了,休息,休息一下!
======================================================================================================
2.6内核输入子系统分析 前面对s3c2410的触摸屏驱动进行了分析,现深入一层,对其所在的输入子系统进行刺探。
首先引用一个不错的帖子,对2.6内核的输入子系统进行一个大致的描述:
引:
在做触摸屏?对于输入子系统,相信你也早看了网上一些介绍文章文章了,
读一下就可了解对其基本架构,剩下的只是一些源码细节阅读。
输入子系统的3层间的联系是很简单的,驱动层的核心结构为struct input_dev:
struct input_dev {
...
struct list_head h_list;
...
};
在input_register_device时就会将input_dev与input_handle联系起来;
所谓联系就是将有关的input_handle链入以input_dev中h_list为Hash头的链中;
而事件处理层的核心结构是struct input_handler:
struct input_handler {
...
struct list_head h_list;
...
};
在input_register_handler时同样会将input_handler与input_handle联系起来,
所谓联系就是将有关的input_handle链入以input_handler中h_list为Hash头的链中;
由上可见input_handle即是一个用于关联驱动层input_dev和事件处理
层input_handler的中间结构:
struct input_handle {
...
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
其中d_node用于input_dev链,h_node用于input_handler链,有了input_handle,
就把相关dev与handler联系起来,相互能容易的找到。
注:
原文请看一下网址:
http://bbs.ustc.edu.cn/cgi/bbstcon?board=Kernel&file=M.1179398612.A
看了以上的内容,相信你对2.6内核的输入子系统应该有个大概的了解了,
现在我就input_dev 、 input_handle、input_handler这三者建立联系的过程进行详细的分析:
触摸屏驱动中,s3c2410ts_probe函数的最后一步,调用input_register_device函数开始进入三者建立联系的过程:
void input_register_device(struct input_dev *dev)
{
struct input_handle *handle;
struct input_handler *handler;
struct input_device_id *id;
........................................................................................
INIT_LIST_HEAD(&dev->h_list);
list_add_tail(&dev->node, &input_dev_list);
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);
..........................................................................................
}
注:
我只保留重要的部分,省略号部分不是我关心的,以下同。
list_for_each_entry(handler, &input_handler_list, node)的作用在于:
从input_handler_list的链表中提取input_handler的指针。
##################################################################################
那这个input_handler的指针又是何时存放在input_handler_list链表里面的呢?
答案是像tsdev.c这些接口驱动里面调用input_register_handler 进而调用list_add_tail(&handler->node, &input_handler_list);
把其input_handler指针加进input_handler_list里面,详细请查看源码,在此不做详细分析。
###################################################################################
获取了input_handler指针后通过input_match_device进行匹配选择:
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++) {
................................................................
MATCH_BIT(evbit, EV_MAX); // id为0的选项
MATCH_BIT(keybit, KEY_MAX); // id为1的选项
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_handler指针所拥有的特性表handler->id_table
与我们所注册的input_dev的特性表dev.id进行对照。
仍以触摸屏驱动s3c2410-ts.c与触摸屏接口tsdev.c为例:
s3c2410-ts.c:
ts.dev.evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
ts.dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(&ts.dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(&ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(&ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
ts.dev.id.bustype = BUS_RS232;
ts.dev.id.vendor = 0xDEAD;
ts.dev.id.product = 0xBEEF;
ts.dev.id.version = S3C2410TSVERSION;
tsdev.c:
static struct input_device_id tsdev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT
| INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) },
},/* A mouse like device, at least one button, two relative axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT
| INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
},/* A tablet like device, at least touch detection, two absolute axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) },
},/* A tablet like device with several gradations of pressure */
{},/* Terminating entry */
};
可以看到,tsdev.c接口定义了三项特性,对应id为0、1、2,
input_match_device函数依次取出其中的选项与s3c2410-ts.c里面定义的input_dev的选项进行对比。
这里对比的标准是tsdev.c里面定义的选项s3c2410-ts.c里面必须满足,否则continue,继续判断下一个id号的选项。
详细请看MATCH_BIT这个宏的定义:
#define MATCH_BIT(bit, max) \
for (i = 0; i < NBITS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
break; \
if (i != NBITS(max)) \
continue;
例如:
在这里,tsdev.c定义的id为0的选项里面定义的BIT(EV_REL)这一项
在s3c2410-ts.c里面定义的input_dev设备上是不具备的,
所以,执行到MATCH_BIT(evbit, EV_MAX);后直接continue,
继续判断tsdev.c里面 id为1
,直到找到合适的,然后返回真,否则返回NULL。
###########################################################################################
在list_for_each_entry(handler, &input_handler_list, node)这个大循环里与我们所注册的input_dev所匹配的不限于一个接口,例如,以下是我的调试记录:s3c2410 TouchScreen successfully loadedkbdinput_match_devicemousedevinput_match_devicemousedev_connectjoydevinput_match_deviceevdevinput_match_deviceevdev_connecttsdevinput_match_devicetsdev_connectevbuginput_match_deviceevbug_connect可以看到,对于s3c2410-ts.c里面定义的input_dev设备,同时与其匹配的就有mousedev、evdev、tsdev、evbug等众多接口(不知道我的理解是否正确,如果理解错了,还望指正^_^)###########################################################################################
找到匹配的选项以后,就可以开始着手把input_dev、input_handle、input_handler这三者联系齐来了,具体调用handle = handler->connect(handler, dev, id)函数,主要的目的是填充input_handle结构,然后接着调用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);}看到吧,就是上面那位大侠提到的,把input_handle分别链入input_dev和input_handler中h_list为Hash头的链中。好了,到此,input_dev、input_handle、input_handler这三者总算是联系起来了^_^