`
yesjavame
  • 浏览: 712428 次
  • 性别: Icon_minigender_2
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

Linux设备模型之input子系统详解

阅读更多

一:前言
在键盘驱动代码分析的笔记中,接触到了input子系统.键盘驱动,键盘驱动将检测到的所有按键都上报给了input子系统。Input子系统是所有I/O设备驱动的中间层,为上层提供了一个统一的界面。例如,在终端系统中,我们不需要去管有多少个键盘,多少个鼠标。它只要从input子系统中去取对应的事件(按键,鼠标移位等)就可以了。今天就对input子系统做一个详尽的分析.
下面的代码是基于linux kernel 2.6.25.分析的代码主要位于kernel2.6.25/drivers/input下面.
二:使用input子系统的例子
在内核自带的文档Documentation/input/input-programming.txt中。有一个使用input子系统的例子,并附带相应的说明。以此为例分析如下:
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/io.h>

static void button_interrupt(int irq, void *dummy, struct pt_regs *fp)
{
input_report_key(&button_dev, BTN_1, inb(BUTTON_PORT) & 1);
input_sync(&button_dev);
}

static int __init button_init(void)
{
if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
return -EBUSY;
}

button_dev.evbit[0] = BIT(EV_KEY);
button_dev.keybit[LONG(BTN_0)] = BIT(BTN_0);

input_register_device(&button_dev);
}

static void __exit button_exit(void)
{
input_unregister_device(&button_dev);
free_irq(BUTTON_IRQ, button_interrupt);
}

module_init(button_init);
module_exit(button_exit);

这个示例module代码还是比较简单,在初始化函数里注册了一个中断处理例程。然后注册了一个input device.在中断处理程序里,将接收到的按键上报给input子系统。
文档的作者在之后的分析里又对这个module作了优化。主要是在注册中断处理的时序上。在修改过后的代码里,为input device定义了open函数,在open的时候再去注册中断处理例程。具体的信息请自行参考这篇文档。在资料缺乏的情况下,kernel自带的文档就是剖析kernel相关知识的最好资料.
文档的作者还分析了几个api函数。列举如下:

1):set_bit(EV_KEY, button_dev.evbit);
set_bit(BTN_0, button_dev.keybit);
分别用来设置设备所产生的事件以及上报的按键值。Struct iput_dev中有两个成员,一个是evbit.一个是keybit.分别用表示设备所支持的动作和按键类型。
2): input_register_device(&button_dev);
用来注册一个input device.
3): input_report_key()
用于给上层上报一个按键动作
4): input_sync()
用来告诉上层,本次的事件已经完成了.
5): NBITS(x) - returns the length of a bitfield array in longs for x bits
LONG(x) - returns the index in the array in longs for bit x
BIT(x) - returns the index in a long for bit x
这几个宏在input子系统中经常用到。上面的英文解释已经很清楚了。

三:input设备注册分析.
Input设备注册的接口为:input_register_device()。代码如下:
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;

__set_bit(EV_SYN, dev->evbit);

/*
* 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;
}
在前面的分析中曾分析过。Input_device的evbit表示该设备所支持的事件。在这里将其EV_SYN置位,即所有设备都支持这个事件.如果dev->rep[REP_DELAY]和dev->rep[REP_PERIOD]没有设值,则将其赋默认值。这主要是处理重复按键的.

if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;

if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;

snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

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;
}
如果input device没有定义getkeycode和setkeycode.则将其赋默认值。还记得在键盘驱动中的分析吗?这两个操作函数就可以用来取键的扫描码和设置键的扫描码。然后调用device_add()将input_dev中封装的device注册到sysfs

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 0;
}
这里就是重点了。将input device挂到input_dev_list链表上.然后,对每一个挂在input_handler_list的handler调用input_attach_handler().在这里的情况有好比设备模型中的device和driver的匹配。所有的input device都挂在input_dev_list链上。所有的handle都挂在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;

if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV;

id = input_match_device(handler->id_table, 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;
}
如果handle的blacklist被赋值。要先匹配blacklist中的数据跟dev->id的数据是否匹配。匹配成功过后再来匹配handle->id和dev->id中的数据。如果匹配成功,则调用handler->connect().
来看一下具体的数据匹配过程,这是在input_match_device()中完成的。代码如下:
static const struct input_device_id *input_match_device(const 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)
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(,, 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;
}
MATCH_BIT宏的定义如下:
#define MATCH_BIT(bit, max)
for (i = 0; i < BITS_TO_LONGS(max); i++)
if ((id->bit[i] & dev->bit[i]) != id->bit[i])
break;
if (i != BITS_TO_LONGS(max))
continue;

由此看到。在id->flags中定义了要匹配的项。定义INPUT_DEVICE_ID_MATCH_BUS。则是要比较input device和input handler的总线类型。INPUT_DEVICE_ID_MATCH_VENDOR,INPUT_DEVICE_ID_MATCH_PRODUCT,INPUT_DEVICE_ID_MATCH_VERSION分别要求设备厂商。设备号和设备版本.
如果id->flags定义的类型匹配成功。或者是id->flags没有定义,就会进入到MATCH_BIT的匹配项了.从MATCH_BIT宏的定义可以看出。只有当iput device和input handler的id成员在evbit, keybit,… swbit项相同才会匹配成功。而且匹配的顺序是从evbit, keybit到swbit.只要有一项不同,就会循环到id中的下一项进行比较.
简而言之,注册input device的过程就是为input device设置默认值,并将其挂以input_dev_list.与挂载在input_handler_list中的handler相匹配。如果匹配成功,就会调用handler的connect函数.

分享到:
评论

相关推荐

    Linux设备模型之input子系统详解.doc

    Linux 设备模型之 input 子系统详解 Linux 设备模型中的 input 子系统是一个核心组件,它是所有 I/O 设备驱动的中间层,为上层提供了一个统一的界面。input 子系统的主要作用是将设备驱动检测到的事件上报给上层...

    Linux设备模型之input子系统详解.docx

    Linux 设备模型之 input 子系统详解 Linux 设备模型中的 input 子系统是一种中间层,负责将 Input/Output(I/O)设备的事件传递给上层应用程序。该子系统提供了一个统一的界面,使得上层应用程序无需关心底层设备的...

    Linux设备模型之input子系统详解.rar

    Linux设备模型中的Input子系统是Linux内核中处理输入设备的核心部分,它负责管理各种输入设备,如键盘、鼠标、触摸屏、游戏控制器等。Input子系统的架构设计旨在提供一个统一的接口,使得驱动程序可以方便地与内核...

    Linux2.6设备模型之Input子系统详解

    Linux 2.6设备模型中的Input子系统是一个关键的组件,它充当了操作系统与各种输入设备(如键盘、鼠标、触摸屏等)之间的桥梁。Input子系统的主要目标是为上层应用程序提供一个统一的接口,使得系统能够方便地处理...

    Linux内核之input子系统详解

    ### Linux内核之input子系统详解 #### 一、Input 输入子系统框架 Linux内核中的input子系统是一个非常重要的组成部分,它负责管理和处理来自各种输入设备的数据,例如键盘、鼠标、触摸屏等。该子系统的高效性和...

    Linux 下的input 子系统 原理

    总之,Linux input子系统是连接硬件输入设备与用户空间应用程序的桥梁,它通过标准化的接口和事件模型简化了驱动开发,同时也提供了丰富的功能以满足各种输入设备的需求。了解和掌握input子系统的工作原理,对于理解...

    Android linux内核input子系统解析.doc

    Android Linux内核的input子系统是操作系统与各种输入设备交互的关键组件,它为键盘、鼠标、触摸屏等设备提供了统一的接口。这个子系统的设计使得开发者能够方便地处理来自不同输入设备的事件,无论是硬件设备还是...

    input_subsystem.zip

    "Linux设备模型之input子系统详解"和"linux input subsystem 架构分析 -- Bob's Blog.htm"这两篇文章深入解析了这一架构,包括设备注册、事件处理机制以及设备的添加和移除流程。 2. **输入驱动开发** 输入设备...

    linux input子系统 内核与应用 接口 分析详解

    Linux 输入子系统是操作系统核心中一个关键的部分,它负责处理来自各种输入设备的事件,如键盘、鼠标、触摸屏等。这个子系统为内核和用户空间应用提供了一个高效的交互接口,使得应用程序能够方便地获取并响应用户的...

    全网络对Linux input子系统最清晰、详尽的分析 - CSDN博客1

    input_handler是input子系统中处理特定类型事件的模块,它定义了如何解析和处理输入事件。例如,Mouse Handler用于处理鼠标事件,Event Handler则适用于触摸屏等事件设备。每个input_handler都包含了处理特定事件的...

    linux输入子系统

    Linux 输入子系统是Linux内核中的一个重要组成部分,它负责处理来自各种输入设备的事件,如键盘、鼠标、触摸屏等。这个子系统的设计目的是为了提供一个统一的接口,让操作系统可以方便地管理和处理来自不同类型的...

    基于Linux内核输入子系统的驱动研究.pdf

    在Linux内核中,输入子系统是一个关键部分,它为按键、键盘、触摸屏、鼠标等输入设备提供了统一的驱动模型。本文将深入探讨Linux内核输入子系统的概念、架构以及如何实现设备驱动。 1. Linux设备驱动基础知识 在...

    从需求的角度去理解嵌入式Linux:总线、设备和驱动

    Linux平台上的各个子系统,如设备驱动模型、input子系统、I2C总线、frame buffer驱动等等都属于软件框架,它是针对特定的硬件体系需求以面向对象的思维去设计的一种软件解决方案,而且已经经过长时间的多平台验证。...

Global site tag (gtag.js) - Google Analytics