Linux设备驱动剖析之Input(二)
分别是总线类型、厂商号、产品号和版本号。
1156行,evbit,设备支持的事件类型的位图,每一位代表一种事件,比如EV_KEY、EV_REL事件等等。BITS_TO_LONGS(nr)是一个宏,假设long型变量为32位,1位可以表示一种事件,那么这个宏的意思就是nr种事件需要用多少个long型变量来表示。EV_CNT的值为0x1f+1,因此BITS_TO_LONGS(0x1f+1)的值就为1。
1157行,keybit,设备拥有的按键的位图,每一位代表一个按键。
1158行,relbit,设备拥有的相对轴的位图,每一位代表一个相对轴。
1159行,absbit,设备拥有的绝对轴的位图,每一位代表一个绝对轴。
1160行,mscbit,设备支持的杂项事件位图。
1161行,ledbit,设备拥有的LED位图。
1162行,sndbit,设备支持的音效位图。
1163行,ffbit,1164行,swbit,不说了,对于本文只需要关注evbit和keybit即可,其他那些只需要有个印象,等用到的时候自然就明白了。
1166行,hint_events_per_packet,输入事件到达事件驱动程序的时候会存放在一个缓冲区里,这里表示这个缓冲区的大小。
1168行,keycodemax,键码表的大小。
1169行,keycodesize,键码表元素的大小。
1170行,扫描码到键码的映射。
1171、1173行,设置和获取键码的函数指针。
1176至1187行,与本文没什么关系,略过吧。
1189至1192行,表示当前状态的位图。
1194至1197行,一些函数指针,用到再说。
1199行,grab,这有点意思,表示设备拥有的input handle,当输入事件准备传递给事件驱动程序时会input core会先判断设备是否拥有了input handle,如果有,则把事件投递给该handle来处理,否则,遍历所有已经注册了的handle,每遍历到一个handle,就把事件投递给它。
1204行,users,计数,每打开设备一次,计数加1,关闭设备,计数减1。
1207行,sync,当为true时表示自从上一次同步以来再也没有新的输入事件产生。
1209行,dev,嵌入到设备模型中用的。
1211行,设备拥有的handle,这些handle以链表的形式连接在一起。从这可以看出,一个设备是可以拥有多个handle的。
1212行,node,作为input_dev_list这个全局链表的节点使用,从而把系统中的所有Input设备连成一个链表。
回到input_allocate_device函数,1559行,为Input设备分配内存。
1561行,指定设备的类型为input_dev_type。
1562行,还记得Input子系统初始化时注册的那个input_class没?就是这么用的。
1563行,用设备模型的函数初始化设备。
1564至1567行,锁和链表的初始化。
1569行,啥也没做,是一个空函数。
回到gpio_keys_probe函数,456至460行,如果申请内存失败或者分配设备失败,那么就释放之前申请的内存,返回出错。
462至480行,一些赋值,不细说了。
483行,如果支持按键自动重复,则设置evbit位图中的相应位为1。
486行,循环的次数等于按键的个数。
489行,事件类型默认为按键类型(EV_KEY)。
494行,gpio_keys_setup_key函数的定义:
static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
struct gpio_button_data *bdata,
struct gpio_keys_button *button)
{
char *desc = button->desc ? button->desc : "gpio_keys";
struct device *dev = &pdev->dev;
unsigned long irqflags;
int irq, error; setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
INIT_WORK(&bdata->work, gpio_keys_work_func); error = gpio_request(button->gpio, desc);
if (error < ) {
dev_err(dev, "failed to request GPIO %d, error %d\n",
button->gpio, error);
goto fail2;
} error = gpio_direction_input(button->gpio);
if (error < ) {
dev_err(dev, "failed to configure"
" direction for GPIO %d, error %d\n",
button->gpio, error);
goto fail3;
} if (button->debounce_interval) {
error = gpio_set_debounce(button->gpio,
button->debounce_interval * );
/* use timer if gpiolib doesn't provide debounce */
if (error < )
bdata->timer_debounce = button->debounce_interval;
} irq = gpio_to_irq(button->gpio);
if (irq < ) {
error = irq;
dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
button->gpio, error);
goto fail3;
} irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
/*
00000407 * If platform has specified that the button can be disabled,
00000408 * we don't want it to share the interrupt line.
00000409 */
if (!button->can_disable)
irqflags |= IRQF_SHARED; error = request_irq(irq, gpio_keys_isr, irqflags, desc, bdata);
if (error) {
dev_err(dev, "Unable to claim irq %d; error %d\n",
irq, error);
goto fail3;
} return ; fail3:
gpio_free(button->gpio);
fail2:
return error;
}
371行,初始化按键定时器,定时器超时处理函数为gpio_keys_timer,函数的参数为bdata。之前说了,该定时器是用到延时消抖的。所谓延时消抖就是说当按键按下时,由于按键的机械特性会存在抖动,造成多次按下和抬起,因此当延时适当的时间后,如果按键仍然处于按下状态,那么就认为按键真的按下了,这是一种软件消抖的方法,当然,还有硬件消抖的方法。
372行,初始化按键的工作节点,使用系统默认的工作队列。
374至379行,申请IO口,如果IO口被占用,那么该IO口就不能用作按键的输入。
381至387行,设置IO口为输入功能。
389行,如果按键有设置延时的话那么就执行390行通过gpiolib的gpio_set_debounce函数来设置延时,如果有些平台不支持gpiolib,那么执行394行,通过定时器来实现延时。
397至403行,获取IO口对应的中断号。
405行,设置上升沿和下降沿都将触发中断的标志。
410、411行,如果IO口(按键)可以被失能的话,那么就不要设置共享中断标志,即此IO口独占一条中断线。
413至418行,申请中断,中断处理函数为gpio_keys_isr,函数的其中一个参数为bdata。
至此,gpio_keys_setup_key函数说完了,回到gpio_keys_probe函数,498、491行,如果按键可以作为唤醒源,那么设置wakeup变量为1,马上就会用到。
501行,调用drivers/input/input.c中的input_set_capability函数,定义如下:
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
switch (type) {
case EV_KEY:
__set_bit(code, dev->keybit);
break; case EV_REL:
__set_bit(code, dev->relbit);
break; case EV_ABS:
__set_bit(code, dev->absbit);
break; case EV_MSC:
__set_bit(code, dev->mscbit);
break; case EV_SW:
__set_bit(code, dev->swbit);
break; case EV_LED:
__set_bit(code, dev->ledbit);
break; case EV_SND:
__set_bit(code, dev->sndbit);
break; case EV_FF:
__set_bit(code, dev->ffbit);
break; case EV_PWR:
/* do nothing */
break; default:
printk(KERN_ERR
"input_set_capability: unknown type %u (code %u)\n",
type, code);
dump_stack();
return;
} __set_bit(type, dev->evbit);
}
很明显的排比句,根据按键支持的事件设置按键表示的键值,最后再设置按键支持的事件。通俗地说,按键产生的是按键事件(EV_KEY),一个按键只能代表一个键值,比如键盘上的F1、F2等等。
回到gpio_keys_probe函数,504至509行,sysfs文件系统相关的,简单地说,sysfs_create_group函数就是在/sys相应的目录下创建一些文件,这样就可以在用户空间通过echo或cat来设置或者读取驱动中的一些参数。
511至516行,调用input_register_device函数注册Input设备,定义如下:
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT();
struct input_handler *handler;
const char *path;
int error; /* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit); /* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit); /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev); /*
00001751 * If delay and period are pre-set by the driver, then autorepeating
00001752 * is handled by the driver itself and we don't do it in input.c.
00001753 */
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] = ;
dev->rep[REP_PERIOD] = ;
} if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - ); 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); list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); return ;
}
1736行,注意,这是一个static型变量,就是说当函数返回时它所占用的内存不会释放,它的值会维持不变,并且只会被初始化一次,第一次进该函数时input_no=0,第二次进时input_no=1,以此类推。
1737行,遇到了一个新的结构体struct input_handler,它定义了输入设备的接口,主要用在事件驱动程序中,它的定义在include/linux/input.h中:
struct input_handler { void *private; void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
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; struct list_head h_list;
struct list_head node;
};
1306行,private,驱动的私有数据指针。
1308至1313行,一些函数指针,其中,event是Input core向事件驱动程序传递消息时调用的函数。
1315行,fops,事件驱动实现的文件操作集。
1316行,minor,次设备号的起始值。
1317行,name,handler的名字,会出现在/proc/bus/input/handlers。
1319行,id_table,事件驱动支持的id table。
1321行,h_list,将所有handle连接起来。
1322行,node,作为全局链表input_handler_list的节点,input_handler_list将所有handler节点连成一个双向循环链表。
容易被这里的链表弄晕,索性一次把它讲清楚,下面看struct input_handle的定义,注意,是handle,不是handler。
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;
};
1339行,private,handler的私有数据指针。
1341行,open,handle的打开计数。
1342行,name,handle的名字。
1344行,dev,此handle依附的Input设备。
1345行,handler,通过此handle与设备进行交互的handler。
1347,d_node,1348,h_node,细心的话可以发现在说struct input_dev时也有两个关于链表的成员,而struct input_handler和struct input_handle也有两个关于链表的成员,它们三者形成的关系如下图所示:
应该很清楚了吧,这里只以其中的一个input_dev和handler为例画出来的图,其他的以此类推。其中input_dev_list和input_handler_list是两条全局的链表,每当调用input_register_device函数时会将Input设备加入到input_dev_list链表的尾部;每当调用input_register_handler函数时会将handler加入到input_handler_list链表的尾部;每当调用input_register_handle函数时会将handle加入到其对应的Input设备的h_list链表的尾部,并且还会该handle加入到其对应的handler的h_list链表的尾部。
回到input_register_device函数,1742行,设置事件位图的同步事件位为1,表示设备支持同步事件。
1745行,清0保留的键值,该键值不会被发送到用户空间。
1748行,清0事件位图中没有设置的位。
1754行,初始化设备的定时器。
1755至1760行,如果dev->rep[REP_DELAY]和dev->rep[REP_PERIOD]的值都为0,那么就给他们和定时器设置一些默认值,具体的函数和变量等用到的时候再说。
1762至1766行,如果dev->getkeycode和dev->setkeycode这两个函数指针没有设置,就为它们赋默认值。
1771行,将设备加入到设备模型中,关于设备模型的就略过了,下文也如此。
1786行,看到了吧,将Input设备加入到input_dev_list链表的尾部。
1788、1789行,遍历input_handler_list链表,每找到一个节点就调用input_attach_handler函数,它的定义如下:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error; id = input_match_device(handler, dev);
if (!id)
return -ENODEV; error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
printk(KERN_ERR
"input: failed to attach handler %s to device %s, "
"error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error); return error;
}
842行,struct input_device_id,关于这个结构体这里就不说了,不过可以看下它的定义,在include/input/mod_devicetable.h中:
struct input_device_id { kernel_ulong_t flags; __u16 bustype;
__u16 vendor;
__u16 product;
__u16 version; kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + ];
kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + ];
kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + ];
kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + ];
kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + ];
kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + ];
kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + ];
kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + ];
kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + ]; kernel_ulong_t driver_info;
};
845行,调用input_match_device函数,定义如下:
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
int i; for (id = handler->id_table; id->flags || id->driver_info; id++) { if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
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);
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); if (!handler->match || handler->match(handler, dev))
return id;
} return NULL;
}
805行,循环handler的id_table数组的每一项。
807至821行,都是检查handler的flags标志的,意思也很明显,不多说了。
823至831行,只要弄懂第一个就行了,其他的都是一样的,下面看MATCH_BIT这个宏的定义:
#define MATCH_BIT(bit, max) \
for (i = ; i < BITS_TO_LONGS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
break; \
if (i != BITS_TO_LONGS(max)) \
continue;
以MATCH_BIT(evbit, EV_MAX);为例,把宏展开后是这样的:
for (i = ; i < BITS_TO_LONGS(EV_MAX); i++)
if ((id->evbit [i] & dev->evbit [i]) != id->evbit [i])
break;
if (i != BITS_TO_LONGS(EV_MAX))
continue;
以id->evbit[i]的值为准,如果id-evbit[i]不等于dev-evbit[i],那么跳出793行的for循环,接着判断796行,如果条件不成立,那么后面的就不用比较了,直接进行id_table数组的下一个元素的比较。
824至831行,和上面相同。
833行,至少要满足其中一个条件,否则Input设备就不能与handler匹配。
回到input_attach_handler函数,849行,调用handler的connect函数,等讲事件驱动程序evdev.c的时候再讲。
回到input_register_device函数,1791行,proc文件系统相关的,略过。至此,input_register_device函数讲完了,回到gpio_keys_probe函数,这里把剩下的代码贴出来,省得再回去看。
Linux设备驱动剖析之Input(二)的更多相关文章
- Linux设备驱动剖析之Input(三)
/* get current state of buttons */ ; i < pdata->nbuttons; i++) gpio_keys_report_event(&dda ...
- Linux设备驱动剖析之Input(四)
static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) ...
- Linux设备驱动剖析之Input(一)
前言 以前在移植Qt到开发板上时只知道在配置文件中需要指定触摸屏的设备文件/dev/input/event0,仅此而已.直到一年半前突然想到用红外遥控器控制Tiny6410开发板上的Android系统 ...
- linux设备驱动归纳总结(二):模块的相关基础概念【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59415.html linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10 ...
- 【Linux开发】linux设备驱动归纳总结(二):模块的相关基础概念
linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10.04 开发平台:S3C2440开发板 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- Linux设备驱动剖析之SPI(二)
957至962行,一个SPI控制器用一个master来描述.这里使用SPI核心的spi_alloc_master函数请求分配master.它在drivers/spi/spi.c文件中定义: struc ...
- Linux设备驱动剖析之IIC(一)
写在前面 由于IIC总线只需要两根线就可以完成读写操作,而且通信协议简单,一条总线上可以挂载多个设备,因此被广泛使用.但是IIC总线有一个缺点,就是传输速率比较低.本文基于Linux-2.6.36版本 ...
- Linux设备驱动剖析之SPI(三)
572至574行,分配内存,注意对象的类型是struct spidev_data,看下它在drivers/spi/spidev.c中的定义: struct spidev_data { dev_t de ...
- Linux设备驱动剖析之SPI(一)
写在前面 初次接触SPI是因为几年前玩单片机的时候,由于普通的51单片机没有SPI控制器,所以只好用IO口去模拟.最近一次接触SPI是大三时参加的校内选拔赛,当时需要用2440去控制nrf24L01, ...
随机推荐
- Tracking Boost Regulator TYPICAL 5V REGULATION WITH BOOST CONVERTER AND LDO
Cs5171: Tracking Boost Regulator Adding a current mirror circuit to a typical boost circuit allows t ...
- JSON序列——主从表查询
JSON序列——主从表查询 客户端代码: procedure TForm1.Button4Click(Sender: TObject); // 主从表 查询 begin var url: TynUrl ...
- 针对UDP丢包问题,进行系统层面和程序层面调优
转自:https://blog.csdn.net/xingzheouc/article/details/49946191 1. UDP概念 用户数据报协议(英语:User Datagram Proto ...
- 七周七语言之Ruby
1.安装 Ubuntu 14.04 sudo apt-get install ruby version 1.9.1 2.命令行运行: irb 3.文挡查看:man RDoc 4.猜数字 2.2.7程序 ...
- Qt编译错误“GL/gl.h:No such file or directory”的解决方法
备注:1)操作系统:Ubuntu-14.04或12.042)Linux用户:root3)Qt版本:qt-linux-opensource-5.2.0-x86 为了迎接Qt的新纪元(从诺基亚移居到芬兰公 ...
- 启用phpstorm代码提示功能
参考:http://www.dawnfly.cn/article-1-331.html mac下实际上将省电禁用即可
- MySQL数据切分的相关概念和原理详解
对于数据切分,我们可能还不是很熟悉,但是它对于MySQL数据库来说也是相当重要的一门技术,本文我们就详细介绍一下MySQL数据库的数据切分的相关知识,接下来就让我们一起来了解一下这部分内容. 什么是数 ...
- <script> 的defer和async
<script src="../file.js" async="async"></script> file.js---- 仅仅只有ale ...
- TLS/HTTPS 证书生成与验证
最近在研究基于ssl的传输加密,涉及到了key和证书相关的话题,走了不少弯路,现在总结一下做个备忘 科普:TLS.SSL.HTTPS以及证书 不少人可能听过其中的超过3个名词,但它们究竟有什么关联呢? ...
- ASP.NET Web API(MVC API)
ASP.NET Web API是一个框架,可以很容易构建达成了广泛的HTTP服务客户端,包括浏览器和移动设备.是构建RESTful应用程序的理想平台的.NET框架. 上面是微软对Web API给出 ...