Linux输入子系统将输入驱动抽象为三层:事件处理层、核心层、设备驱动层。应用程序只需要跟事件处理层打交道,不需要察觉设备的变化。核心层是负责管理输入设备,并将消息在事件处理层和设备驱动层之间传递。
由于事件处理和设备驱动的分离,使得应用程序读取输入信息的接口固定不变就可以适应新的同类输入设备。
表示事件处理层的数据结构是structinput_handler,每个handler代表一种处理事件的方式,允许多个handler共存。代表设备驱动层的数据结构是struct input_dev。input_dev和handler可以建立连接,连接它们的就是struct input_handle。核心层一般被称为input core。
1.1重要的数据结构
在输入子系统的设备驱动中,最重要的数据结构是struct input_dev,如程序清单 1.1所示。需要完成的大部分工作都是围绕着它来的,它是驱动的主体。每个struct input_dev代表一个输入设备。
程序清单 1.1 struct input_dev成员介绍
/* include/linux/input.h */
struct input_dev {
constchar *name; /*设备名 */
constchar *phys;
constchar *uniq;
structinput_id id; /*用于匹配事件处理层handler */
unsignedlong evbit[BITS_TO_LONGS(EV_CNT)]; /*用于记录支持的事件类型的位图*/
unsignedlong keybit[BITS_TO_LONGS(KEY_CNT)]; /*记录支持的按键值的位图 */
unsignedlong relbit[BITS_TO_LONGS(REL_CNT)]; /*记录支持的相对坐标的位图 */
unsignedlong absbit[BITS_TO_LONGS(ABS_CNT)]; /*记录支持的绝对坐标的位图 */
unsignedlong mscbit[BITS_TO_LONGS(MSC_CNT)];
unsignedlong ledbit[BITS_TO_LONGS(LED_CNT)]; /*led */
unsignedlong sndbit[BITS_TO_LONGS(SND_CNT)]; /*beep */
unsignedlong ffbit[BITS_TO_LONGS(FF_CNT)];
unsignedlong swbit[BITS_TO_LONGS(SW_CNT)];
unsignedint keycodemax; /*支持的按键值的个数 */
unsignedint keycodesize; /*每个键值的字节数 */
void *keycode; /*存储按键值的数组首地址 */
int(*setkeycode)(struct input_dev *dev, int scancode, int keycode); /* 修改键值的函数,可选 */
int(*getkeycode)(struct input_dev *dev, int scancode, int *keycode); /* 获取扫描码的键值,可选 */
structff_device *ff;
unsignedint repeat_key; /*最近一次按键值,用于连击 */
structtimer_list timer; /*自动连击计时器 */
intsync; /*最后一次同步后没有新的事件置1*/
intabs[ABS_MAX + 1]; /* 当前各个坐标的值 */
int rep[REP_MAX + 1]; /*自动连击的参数 */
unsigned longkey[BITS_TO_LONGS(KEY_CNT)]; /*反映当前按键状态的位图 */
unsignedlong led[BITS_TO_LONGS(LED_CNT)]; /*反映当前led状态的位图 */
unsignedlong snd[BITS_TO_LONGS(SND_CNT)]; /*反映当前beep状态的位图 */
unsignedlong sw[BITS_TO_LONGS(SW_CNT)];
intabsmax[ABS_MAX + 1]; /*记录各个坐标的最大值 */
intabsmin[ABS_MAX + 1]; /*记录各个坐标的最小值 */
intabsfuzz[ABS_MAX + 1]; /*记录各个坐标的分辨率 */
intabsflat[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, intvalue); /* 回调函数,可选 */
structinput_handle *grab;
spinlock_tevent_lock;
structmutex mutex;
unsignedint users;
intgoing_away;
structdevice dev;
structlist_head h_list; /*handle链表 */ structlist_head node; /*input_dev链表 */
};
struct input_event是事件传送的载体,输入子系统的事件都是包装成struct input_event传给用户空间。各个成员如程序清单 1.2所示。
程序清单 1.2 struct input_event成员介绍
/* include/linux/input.h */
struct input_event {
structtimeval time; /*时间戳 */
__u16type; /*事件类型 */
__u16code; /*事件代码 */
__s32value; /*事件值,如坐标的偏移值 */
};
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 {
__u16bustype; /*总线类型 */
__u16vendor; /*生产商编号 */
__u16product; /*产品编号 */
__u16version; /*版本号 */
};
1.2基于输入子系统的键盘驱动例程
下面提供一个基于输入子系统的键盘驱动例程,这里只是介绍编写一个键盘驱动需要哪些步骤,并不解释本程序的全部细节。如程序清单 1.4所示。
程序清单 1.4 输入子系统设备驱动例程-键盘驱动
#define NR_SCANCODES (4)
typedef unsigned charKEYCODE_T;
static KEYCODE_T keypad_keycode[NR_SCANCODES] = /* 按键值 */
{
KEY_BACKLIGHT, KEY_X, KEY_Y, KEY_Z
};
typedef struct { /*自己定义的结构体 */
structinput_dev *input;
int irq;
KEYCODE_Tkeycode[ARRAY_SIZE(keypad_keycode)];
KEYCODE_Tlastkey;
charphys[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)
gotoerr;
DPRINTK("initOK!/n");
return 0;
err:
DPRINTK("initerror/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("KEYis %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);
returnIRQ_HANDLED;
}
在中断底半部中,读取硬件决定发送的按键值,首先发送按键按下的信息,然后发送按键抬起的信息。每次发送事件之后都同步一次。
为你的快乐而快乐的是朋友,为你的难过而难过的才是你的知己。