对技术执着
分类: LINUX
2013-05-29 18:46:31
原文地址:linux输入子系统(1) 作者:chumojing
Linux输入子系统将输入驱动抽象为三层:事件处理层、核心层、设备驱动层。应用程序只需要跟事件处理层打交道,不需要察觉设备的变化。核心层是负责管理输入设备,并将消息在事件处理层和设备驱动层之间传递。
由于事件处理和设备驱动的分离,使得应用程序读取输入信息的接口固定不变就可以适应新的同类输入设备。
表示事件处理层的数据结构是struct input_handler,每个handler代表一种处理事件的方式,允许多个handler共存。代表设备驱动层的数据结构是struct input_dev。input_dev和handler可以建立连接,连接它们的就是struct input_handle。核心层一般被称为input core。
在输入子系统的设备驱动中,最重要的数据结构是struct input_dev,如程序清单 1.1所示。需要完成的大部分工作都是围绕着它来的,它是驱动的主体。每个struct input_dev代表一个输入设备。
1.1 struct input_dev成员介绍
/* include/linux/input.h */
struct input_dev {
const char *name; /* 设备名 */
const char *phys;
const char *uniq;
struct input_id id; /* 用于匹配事件处理层handler */
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /*用于记录支持的事件类型的位图*/
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)]; /* led */
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; /* beep */
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; /*最后一次同步后没有新的事件置1*/
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)]; /* 反映当前led状态的位图 */
unsigned long snd[BITS_TO_LONGS(SND_CNT)]; /* 反映当前beep状态的位图 */
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; /* handle链表 */ struct list_head node; /* input_dev链表 */
};
struct input_event是事件传送的载体,输入子系统的事件都是包装成struct input_event传给用户空间。各个成员如程序清单 1.2所示。
1.2 struct input_event成员介绍
/* include/linux/input.h */
struct input_event {
struct timeval time; /* 时间戳 */
__u16 type; /* 事件类型 */
__u16 code; /* 事件代码 */
__s32 value; /* 事件值,如坐标的偏移值 */
};
struct input_dev注册的时候需要跟匹配的hanlder建立连接,匹配的依据就是struct input_dev所包含的struct input_id。struct input_id的各个成员如程序清单 1.3所示。
1.3 struct input_id成员描述
/* include/linux/input.h */
struct input_id {
__u16 bustype; /* 总线类型 */
__u16 vendor; /* 生产商编号 */
__u16 product; /* 产品编号 */
__u16 version; /* 版本号 */
};
下面提供一个基于输入子系统的键盘驱动例程,这里只是介绍编写一个键盘驱动需要哪些步骤,并不解释本程序的全部细节。如程序清单 1.4所示。
1.4 输入子系统设备驱动例程-键盘驱动
#define NR_SCANCODES (4)
typedef unsigned char KEYCODE_T;
static KEYCODE_T keypad_keycode[NR_SCANCODES] = /* 按键值 */
{
KEY_BACKLIGHT, KEY_X, KEY_Y, KEY_Z
};
typedef struct { /* 自己定义的结构体 */
struct input_dev *input;
int irq;
KEYCODE_T keycode[ARRAY_SIZE(keypad_keycode)];
KEYCODE_T lastkey;
char phys[32];
}KEYPAD_T;
static int __init keypad_init(void)
{
int err;
int i;
zlgkpd = kzalloc(sizeof(KEYPAD_T), GFP_KERNEL); ①
if (unlikely(!zlgkpd))
{
return -ENOMEM;
}
strcpy(zlgkpd->phys, "keypad/input0");
zlgkpd->input = input_allocate_device(); ②
if (!zlgkpd->input)
{
kfree(zlgkpd);
return -ENOMEM;
}
zlgkpd->input->name = CHIP_NAME; ③
zlgkpd->input->phys = zlgkpd->phys;
zlgkpd->input->id.bustype = BUS_HOST; ④
zlgkpd->input->id.vendor = 0x0001;
zlgkpd->input->id.product = 0x0001;
zlgkpd->input->id.version = 0x0100;
zlgkpd->input->keycode = zlgkpd->keycode; ⑤
zlgkpd->input->keycodesize = sizeof(KEYCODE_T);
zlgkpd->input->keycodemax = ARRAY_SIZE(keypad_keycode);
zlgkpd->input->open = keypad_open; ⑥
zlgkpd->input->close = keypad_close;
zlgkpd->irq = KEYPAD_IRQ;
set_bit(EV_KEY, zlgkpd->input->evbit); ⑦
for (i = 0; i < ARRAY_SIZE(keypad_keycode); i++) { ⑧
set_bit(keypad_keycode[i], zlgkpd->input->keybit);
}
clear_bit(KEY_RESERVED, zlgkpd->input->keybit);
err = input_register_device(zlgkpd->input); ⑨
if (err)
goto err;
DPRINTK("init OK!/n");
return 0;
err:
DPRINTK("init error/n");
input_free_device(zlgkpd->input);
kfree(zlgkpd);
return err;
}
static void __exit keypad_exit(void)
{
input_unregister_device(zlgkpd->input); ⑩
input_free_device(zlgkpd->input);
kfree(zlgkpd);
}
module_init(keypad_init);
module_exit(keypad_exit);
下面来一步步解释:
①为自己定义的类型申请空间。这一步不是典型输入子系统驱动的必须步骤。
②申请 struct input_dev并做初始化。
③初始化设备的名字。
④初始化设备ID。在注册设备之前,名字和ID是必须初始化的。
⑤初始化设备键值数组首地址、键值字节大小和按键的个数。
⑥初始化设备的打开和关闭函数指针。
⑦使设备支持按键事件。只有注册过的事件类型才能响应。
⑧注册支持的按键键值。只有注册过的键值才能响应。
⑨注册设备结构体。
⑩释放占用的资源。
发送事件的代码如下
1.5 发送按键事件
static void keypad_do_workqueue(struct work_struct *data)
{
/* ··········
some codes
···········*/
if (pressed) {
············
input_report_key(zlgkpd->input, code, KEY_PRESSED); ①
input_sync(zlgkpd->input);
zlgkpd->lastkey = code;
DPRINTK("KEY is %d, pressed: %d, index:%d/n", code, pressed, index);
input_report_key(zlgkpd->input, code, KEY_RELEASED); ②
input_sync(zlgkpd->input);
}
enable_irq(KEYPAD_IRQ);
}
static irqreturn_t keypad_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
disable_irq(KEYPAD_IRQ);
schedule_work(&key_wq);
return IRQ_HANDLED;
}
在中断底半部中,读取硬件决定发送的按键值,首先发送按键按下的信息,然后发送按键抬起的信息。每次发送事件之后都同步一次。