分别是总线类型、厂商号、产品号和版本号。

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(二)的更多相关文章

  1. Linux设备驱动剖析之Input(三)

    /* get current state of buttons */ ; i < pdata->nbuttons; i++) gpio_keys_report_event(&dda ...

  2. Linux设备驱动剖析之Input(四)

    static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) ...

  3. Linux设备驱动剖析之Input(一)

    前言 以前在移植Qt到开发板上时只知道在配置文件中需要指定触摸屏的设备文件/dev/input/event0,仅此而已.直到一年半前突然想到用红外遥控器控制Tiny6410开发板上的Android系统 ...

  4. linux设备驱动归纳总结(二):模块的相关基础概念【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59415.html linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10 ...

  5. 【Linux开发】linux设备驱动归纳总结(二):模块的相关基础概念

    linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10.04 开发平台:S3C2440开发板 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  6. Linux设备驱动剖析之SPI(二)

    957至962行,一个SPI控制器用一个master来描述.这里使用SPI核心的spi_alloc_master函数请求分配master.它在drivers/spi/spi.c文件中定义: struc ...

  7. Linux设备驱动剖析之IIC(一)

    写在前面 由于IIC总线只需要两根线就可以完成读写操作,而且通信协议简单,一条总线上可以挂载多个设备,因此被广泛使用.但是IIC总线有一个缺点,就是传输速率比较低.本文基于Linux-2.6.36版本 ...

  8. Linux设备驱动剖析之SPI(三)

    572至574行,分配内存,注意对象的类型是struct spidev_data,看下它在drivers/spi/spidev.c中的定义: struct spidev_data { dev_t de ...

  9. Linux设备驱动剖析之SPI(一)

    写在前面 初次接触SPI是因为几年前玩单片机的时候,由于普通的51单片机没有SPI控制器,所以只好用IO口去模拟.最近一次接触SPI是大三时参加的校内选拔赛,当时需要用2440去控制nrf24L01, ...

随机推荐

  1. android:ViewHolder模式

    ViewHolder holder = null; if(convertView == null){ convertView = mInflater.inflate(R.layout.xxx null ...

  2. Go语言之高级篇beego框架之model设计构造查询

    一.model设计构造查询 QueryBuilder 提供了一个简便,流畅的 SQL 查询构造器.在不影响代码可读性的前提下用来快速的建立 SQL 语句. QueryBuilder 在功能上与 ORM ...

  3. OS面试题(转载)

    转载自:http://placement.freshersworld.com/power-preparation/technical-interview-preparation/os-intervie ...

  4. Apktool编译找不到“keyboardNavigationCluster”

    喜欢用使用apktool来反编译.编译安卓程序,然后用其他工具来分析.签名.优化等,它比其他工具的优点是不易出错. 命令 反编译命令:apktool d -f XX.apk -o 反编译输出的目录(如 ...

  5. ORA-16447 Redo apply was not active at the target standby database

    Cause ALTER SYSTEM FLUSH REDO TO STANDBY failed because redo apply is not active at the target datab ...

  6. 网站菜单CSS

    #site-nav .down-menu a{height:88px;line-height:88px;border-bottom:0px solid #9e5ae2;transition-durat ...

  7. Easyui datagrid 特殊处理,记录笔记

    1. 特殊的单元格样式 columns中 { field: , styler: function (value, row, index) { ') { return 'background-color ...

  8. 在SharePoint 2013 场中移除服务器,提示 cacheHostInfo is null 错误

    Problem 在SharePoint 2013 场中移除服务器,提示 cacheHostInfo is null 错误 Resolution 这是由于SharePoint 2013中分布式缓存实例( ...

  9. 解决zabbix的中文乱码

    CentOS7.1 x64上下载了zabbix官方的rpm包,导入后使用yum安装了zabbix 3.2.6,但是启动zabbix server的时候报了个段错误的错,谷歌了一会儿,发现段错误不止一次 ...

  10. app 调用接口

    app 调用接口 /// <summary> /// 是否跳转到活动注册成功页面 /// </summary> /// <returns></returns& ...