共计 5633 个字符,预计需要花费 15 分钟才能阅读完成。
导读 | linux 输入子系统由驱动层、输入子系统核心、事件处理层三部分组成。一个输入事件,如鼠标移动、键盘按下等通过 Driver->Inputcore->Event handler->userspace 的顺序到达用户控件的应用程序。 |
将底层的硬件输入转化为统一事件形式,想输入核心(Input Core)汇报。
它承上启下为驱动层提供输入设备注册与操作接口,如:input_register_device; 通知事件处理层对事件进行处理;在 /Proc 下产生相应的设备信息。
主要是和用户空间交互(Linux 中在用户空间将所有的设备都当作文件来处理,由于在一般的驱动程序中都有提供 fops 接口,以及在 /dev 下生成相应的设备文件 nod,这些操作在输入子系统中由事件处理层完成)。
input_dev 结构是实现设备驱动核心工作:向系统报告按键、触摸屏等输入事件(event,通过 input_event 结构描述),不再需要关心文件操作接口。驱动报告事件经过 inputCore 和 Eventhandler 到达用户空间。
注册输入设备函数:
int input_register_device(struct input_dev *dev)
注销输入设备函数:
void input_unregister_device(struct input_dev *dev)
驱动实现——初始化 (事件支持)set_bit() 告诉 input 输入子系统支持哪些事件,哪些按键。例如:
set_bit(EV_KEY,button_dev.evbit) (其中 button_dev 是 struct input_dev 类型)
struct input_dev中有两个成员为:
1)evbit 事件类型(包括 EV_RST,EV_REL,EV_MSC,EV_KEY,EV_ABS,EV_REP 等)。
2)keybit 按键类型(当事件类型为 EV_KEY 时包括 BTN_LEFT,BTN_0,BTN_1,BTN_MIDDLE 等)。
驱动实现——报告事件用于报告 EV_KEY,EV_REL,EV_ABS 事件的函数分别为:
void input_report_key(struct input_dev *dev,unsigned int code,int value) | |
void input_report_rel(struct input_dev *dev,unsigned int code,int value) | |
void input_report_abs(struct input_dev *dev,unsigned int code,int value) |
驱动实现——报告结束 input_sync()同步用于告诉 input core 子系统报告结束,触摸屏设备驱动中,一次点击的整个报告过程如下:
input_reprot_abs(input_dev,ABS_X,x); // x 坐标 | |
input_reprot_abs(input_dev,ABS_Y,y); // y 坐标 | |
input_reprot_abs(input_dev,ABS_PRESSURE,1); | |
input_sync(input_dev);// 同步结束 |
实例分析(按键中断程序):
// 按键初始化 | |
static int __init button_init(void) | |
{// 申请中断 | |
if(request_irq(BUTTON_IRQ,button_interrupt,0,”button”,NUll)) | |
return –EBUSY; | |
set_bit(EV_KEY,button_dev.evbit); // 支持 EV_KEY 事件 | |
set_bit(BTN_0,button_dev.keybit); // 支持设备两个键 | |
set_bit(BTN_1,button_dev.keybit); // | |
input_register_device(&button_dev);// 注册 input 设备 | |
} |
/* 在按键中断中报告事件 */ | |
Static void button_interrupt(int irq,void *dummy,struct pt_regs *fp) | |
{input_report_key(&button_dev,BTN_0,inb(BUTTON_PORT0));// 读取寄存器 BUTTON_PORT0 的值 | |
input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT1)); | |
input_sync(&button_dev); | |
} |
总结:input子系统仍然是字符设备驱动程序,但是代码量减少很多,input子系统只需要完成两个工作:初始化和事件报告(这里在 linux 中是通过中断来实现的)。
struct input_dev *button_dev; | |
struct button_irq_desc { | |
int irq; | |
int pin; | |
int pin_setting; | |
int number; | |
char *name; | |
}; | |
/* 定义一个结构体数组 */ | |
static struct button_irq_desc button_irqs [] = {{IRQ_EINT8 , S3C2410_GPG0 , S3C2410_GPG0_EINT8 , 0, "KEY0"}, | |
{IRQ_EINT11, S3C2410_GPG3 , S3C2410_GPG3_EINT11 , 1, "KEY1"}, | |
{IRQ_EINT13, S3C2410_GPG5 , S3C2410_GPG5_EINT13 , 2, "KEY2"}, | |
{IRQ_EINT14, S3C2410_GPG6 , S3C2410_GPG6_EINT14 , 3, "KEY3"}, | |
{IRQ_EINT15, S3C2410_GPG7 , S3C2410_GPG7_EINT15 , 4, "KEY4"}, | |
{IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_EINT19, 5, "KEY5"}, | |
}; | |
static int key_values = 0; | |
static irqreturn_t buttons_interrupt(int irq, void *dev_id) | |
{struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id; | |
int down; | |
udelay(0); | |
/* 获取按键值 */ | |
down = !s3c2410_gpio_getpin(button_irqs->pin); //down: 1(按下),0(弹起)if (!down) { | |
/* 报告事件 */ | |
key_values = button_irqs->number; | |
//printk("====>rising key_values=%d\n",key_values); | |
if(key_values==0) | |
input_report_key(button_dev, KEY_1, 0); | |
if(key_values==1) | |
input_report_key(button_dev, KEY_2, 0); | |
if(key_values==2) | |
input_report_key(button_dev, KEY_3, 0); | |
if(key_values==3) | |
input_report_key(button_dev, KEY_4, 0); | |
if(key_values==4) | |
input_report_key(button_dev, KEY_5, 0); | |
if(key_values==5) | |
input_report_key(button_dev, KEY_6, 0); | |
/* 报告结束 */ | |
input_sync(button_dev); | |
} | |
else { | |
key_values = button_irqs->number; | |
//printk("====>falling key_values=%d\n",key_values); | |
if(key_values==0) | |
input_report_key(button_dev, KEY_1, 1); | |
if(key_values==1) | |
input_report_key(button_dev, KEY_2, 1); | |
if(key_values==2) | |
input_report_key(button_dev, KEY_3, 1); | |
if(key_values==3) | |
input_report_key(button_dev, KEY_4, 1); | |
if(key_values==4) | |
input_report_key(button_dev, KEY_5, 1); | |
if(key_values==5) | |
input_report_key(button_dev, KEY_6, 1); | |
input_sync(button_dev); | |
} | |
return IRQ_RETVAL(IRQ_HANDLED); | |
} | |
static int s3c24xx_request_irq(void) | |
{ | |
int i; | |
int err = 0; | |
for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {continue;} | |
/* IRQ_TYPE_EDGE_FALLING,IRQ_TYPE_EDGE_RISING,IRQ_TYPE_EDGE_BOTH */ | |
err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, | |
button_irqs[i].name, (void *)&button_irqs[i]); | |
if (err) | |
break; | |
} | |
/* 错误处理 */ | |
if (err) { | |
i--; | |
for (; i >= 0; i--) {if (button_irqs[i].irq < 0) {continue;} | |
disable_irq(button_irqs[i].irq); | |
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); | |
} | |
return -EBUSY; | |
} | |
return 0; | |
} | |
static int __init dev_init(void) | |
{ | |
/*request irq*/ | |
s3c24xx_request_irq(); | |
/* Initialise input stuff */ | |
button_dev = input_allocate_device(); | |
if (!button_dev) {printk(KERN_ERR "Unable to allocate the input device !!\n"); | |
return -ENOMEM; | |
} | |
button_dev->name = "s3c2440_button"; | |
button_dev->id.bustype = BUS_RS232; | |
button_dev->id.vendor = 0xDEAD; | |
button_dev->id.product = 0xBEEF; | |
button_dev->id.version = 0x0100; | |
button_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SYN); | |
//set_bit(EV_KEY, button_dev->evbit)// 支持 EV_KEY 事件 | |
/* 设置支持哪些按键 */ | |
set_bit(KEY_1, button_dev->keybit); | |
set_bit(KEY_2, button_dev->keybit); | |
set_bit(KEY_3, button_dev->keybit); | |
set_bit(KEY_4, button_dev->keybit); | |
set_bit(KEY_5, button_dev->keybit); | |
set_bit(KEY_6, button_dev->keybit); | |
//printk("KEY_RESERVED=%d ,KEY_1=%d",KEY_RESERVED,KEY_1); | |
input_register_device(button_dev); // 注册 input 设备 | |
printk ("initialized\n"); | |
return 0; | |
} | |
static void __exit dev_exit(void) | |
{ | |
int i; | |
for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {continue;} | |
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); | |
} | |
input_unregister_device(button_dev); | |
} | |
module_init(dev_init); | |
module_exit(dev_exit); | |
MODULE_LICENSE("GPL"); | |
MODULE_AUTHOR("David Xie"); |
