在linux下,按键、触摸屏、鼠标等都可以利用input接口函数来实现设备驱动。
从上图可知:
输入子系统由三部分构成:
1 驱动
2 输入子系统
3 处理函数
其中2,3都是内核已经完成,我们要完成的就是1驱动
设备用input_dev结构体描述,使用input子系统实现输入设备驱动的时候,驱动的核心工作是向系统报告按键、触摸屏、键盘、鼠标等输入事件(event,通过input_event结构体描述),不再需要关心文件操作接口,因为input子系统已经完成了文件操作接口。驱动报告的事件经过InputCore和Eventhandler最终到达用户空间。
Input_dev 结构中比较重要的域有:
Name:名字
Evbit:设备所支持的事件类型
Keybit:按键类型
注册输入设备的函数为:
int input_register_device(struct input_dev *dev)
注销输入设备的函数为:
void input_unregister_device(struct input_dev *dev)
设备所支持的事件类型有:
EV_RST Reset EV_KEY 按键
EV_REL 相对坐标EV_ABS绝对坐标
EV_MSC 其它EV_LED LED
EV_SND 声音EV_REP Repeat
EV_FF 力反馈
并使用set_bit(EV_KEY, button_dev.evbit)告知结构体支持哪种设备类型
用于报告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)
code:
事件的代码。如果事件的类型是EV_KEY,该代码code为设备键盘代码。代码值0~127为键盘上的按键代码,0x110~0x116为鼠标上按键代码,其中0x110(BTN_LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_ MIDDLE)为鼠标中键。其它代码含义请参看include/linux/input.h文件
value
事件的值。如果事件的类型是EV_KEY,当按键按下时值为1,松开时值为0。
input_sync()用于事件同步,它告知事件的接收者:驱动已经发出了一个完整的报告。
在中断中,使用input_report_key等函数想用户空间报告,在应用程序中直接读取状态。
输入子系统的设备文件名默认为event0 – event31主设备号默认为13,类似于misc混杂设备。
下面基于mini2440的input输入按键驱动代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/input.h>
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");
应用程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>
int main(void)
{
int buttons_fd;
int key_value,i=0,count;
struct input_event ev_key;
buttons_fd = open("/dev/event0", O_RDWR);
if (buttons_fd < 0) {
perror("open device buttons");
exit(1);
}
for (;;) {
count = read(buttons_fd,&ev_key,sizeof(struct input_event));
// printf("count=%d\n",count);
for(i=0; i<(int)count/sizeof(struct input_event); i++)
if(EV_KEY==ev_key.type)
printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code-1,ev_key.value);
if(EV_SYN==ev_key.type)
printf("syn event\n\n");
}
close(buttons_fd);
return 0;
}
加载内核模块:
多了event0设备文件
运行应用程序:
、
下面我们分析input子系统源码:
在分析源代码之前,我们要先了解几个主要的结构:
Input_dev是代表一个输入设备。
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id; /*ID域*/
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)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
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;
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)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
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 absres[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; /*用于连接input_handler和input_dev的结构*/
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list; /*链表连接input_handle*/
struct list_head node;/*链表连接下一个input_dev*/
};
struct input_handler {
void *private;
/*事件处理函数*/
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
/*用来连接handler和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;
/*用来识别驱动不能处理的表(黑名单)*/
const struct input_device_id *blacklist;
/*用来连接handle*/
struct list_head h_list;
/*用来连接下一个hanlder*/
struct list_head node;
};
input_handle是用来连接input_handler和input_dev的结构
/*用于连接input_dev和input_hander*/
struct input_handle {
void *private; /*私有指针*/
/*打开计数*/
int open;
const char *name;
struct input_dev *dev; /*依附的input_dev结构*/
struct input_handler *handler; /*依附的handler*/
struct list_head d_node; /*连接dev*/
struct list_head h_node;/*连接handler*/
};
在内核中,因为Input是作为子系统存在,跟其子系统一样,首先会执行input.c的input_init,(如rtc子系统在加载具体设备驱动程序之前会加载rtc_init,iic子系统会加载iic_init)。
其中input_init的标志是subsys_initcall(input_init);“优先级”比module_init更高。
static int __init input_init(void)
{
int err;
/*填充input_abs_bypass数组*/
input_init_abs_bypass();
/*注册input类,在/sys/class/input*/
err = class_register(&input_class);
if (err) {
printk(KERN_ERR "input: unable to register input_dev class\n");
return err;
}
/*在Proc建立相关文件*/
err = input_proc_init();
if (err)
goto fail1;
/*注册cdev结构 主设备号是13*/
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
if (err) {
printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
有上可知 input_init主要功能(1)填充input_abs_bypass数组并注册input类(2)在proc目录下建立交互文件(3)注册cdev,主设备号为13
我们在查看input_fops结构:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
只有open函数,为什么呢,我们查看input_open_file的源码:
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
lock_kernel();
/* No load-on-demand here? */
/*通过次设备号左移5位找到handler结构,也就是次设备号0-31对应的handler结构都一样
32-63一样,依次类推*/
handler = input_table[iminor(inode) >> 5];
/*获取handler中的fops*/
if (!handler || !(new_fops = fops_get(handler->fops))) {
err = -ENODEV;
goto out;
}
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
/*如果open函数指针为空*/
if (!new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op;
file->f_op = new_fops;
/*执行handler的open函数*/
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
unlock_kernel();
return err;
}
由上可知:open函数最终会调用对应handler的open函数。
子系统初始化之后,下面就是事件的驱动了,在这里我们支持的是按键,对应的驱动是evdev.c
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
INIT_LIST_HEAD(&handler->h_list);
/*将handler存于input_handler数组*/
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
/*将hander添加到input_handler_list*/
list_add_tail(&handler->node, &input_handler_list);
/*遍历整个input_dev_list*/
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
return retval;
}
事件层注册完之后
下面就是具体按键驱动的注册了:
首先:
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
/*分配内存空间*/
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
/*填充结构*/
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
/*设备初始化,在/sys/device目录下建立目录*/
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
return dev;
}
input_allocate_device分配一个input_dev,并在/sys/device下建立目录。
下面注册dev结构
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;
/* 对每个输入设备都设置EV_SYN位 */
__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;
}
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) - 1);
/*添加设备到device*/
error = device_add(&dev->dev);
if (error)
return error;
/*获取kobj路径*/
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;
}
/*将dev结构加入到input_dev_list链表头*/
list_add_tail(&dev->node, &input_dev_list);
/*遍历整个input_handler_list链表,通过判断handler的id域和dev的id域是否匹配,如果匹配将handler和dev联系起来*/
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_register_device的主要作用是
(1) 对形参Input_dev结构的一些默认参数做填充,包括支持的事件类型,timer函数,getkeycode函数指针,setkeycode函数指针,并且对dev域的name填充。
(2) device_add 添加设备到device核心
(3) 将该Input_dev结构加入到input_dev_list链表
(4) 遍历整个input_handler_list链表,找到一个handler的id_table和input_dev的id一直的handler结构,并将此hanlder和此input_dev结构联系起来
对(4)要重点分析:
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
list_for_each_entry之前已经讲过,就是对input_handler_list整个链表进行遍历。
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
/*如果该dev的id在handler的blacklist已经登记,则跳过*/
if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV;
/*找到handler的id_table和dev的id一致的id_table结构*/
id = input_match_device(handler->id_table, dev);
if (!id)
return -ENODEV;
/*通过ID将input和handler联系起来了*/
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;
}
当handler的id_table和dev的id一致就执行handler的connect函数将dev和handler联系起来。执行handler的connect函数。那么在这里对应的就是evdev_handler的evdev_connect函数。
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
/*找到evdev_table中第一个为NULL的元素*/
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
/*如果evdev_table中每个元素存在则退出*/
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
/*分配内存*/
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
/*将hanlde handler input_dev三者联系起来*/
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
/*将evdev填充到evdev_table*/
error = evdev_install_chrdev(evdev);
if (error)
goto err_unregister_handle;
/*添加设备*/
error = device_add(&evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
return error;
}
由上:evdev_connect的函数主要功能:(1)分配一个evdev,并将它填充到evdev_table中(方便以后调用),(2)调用input_register_handle将hander handle input_dev连接起来,其中handle分别和input_dev和handler相连。(3)调用device_add添加设备。
调用完evdev_connect之后handle handler input_dev之间就通过链表关联起来了。
对于(2)要分析一下:
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
/*将handle连接到input的头部*/
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
/*将handle连接到handler的尾部*/
list_add_tail(&handle->h_node, &handler->h_list);
/*如果存在start域,则执行start*/
if (handler->start)
handler->start(handle);
return 0;
}
通过input_register_handle就将handler handle input_dev3个结构通过链表联系起来了。
当上面这些步骤完成之前,驱动就在内核注册好了,驱动程序加载完后,在/dev/input目录下就生成了even0设备,它的主设备号是13,次设备号为64.
当应用程序使用open函数打开/dev/input/even0设备时,在内核中Input子系统,open对应的驱动时input.c的input_open_file函数:
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
lock_kernel();
/* No load-on-demand here? */
/*通过次设备号左移5位找到handler结构,也就是次设备号0-31对应的handler结构都一样
32-63一样,依次类推*/
handler = input_table[iminor(inode) >> 5];
/*获取handler中的fops*/
if (!handler || !(new_fops = fops_get(handler->fops))) {
err = -ENODEV;
goto out;
}
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
/*如果open函数指针为空*/
if (!new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op;
file->f_op = new_fops;
/*执行handler的open函数*/
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
unlock_kernel();
return err;
}
input_open_file主要做了以下事情:(1)通过此设备号右移5位(因为Input_register_handler中次设备号左移5位存储于Input_table中)获取handler结构,在此处就是evdev_handler。(2)如果evdev_handler的open函数存在就执行evdev_handler的open函数。
上面已经提到:evdev_handler的fops:下面就执行evdev_handler的fops的open了。
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
/*evdev_client结构对应一个具体的设备*/
struct evdev_client *client;
/*次设备号减去64=0*/
int i = iminor(inode) - EVDEV_MINOR_BASE;
int error;
if (i >= EVDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
/*获取储存在evdev_table的evdev结构*/
evdev = evdev_table[i];
if (evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);
if (!evdev)
return -ENODEV;
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
spin_lock_init(&client->buffer_lock);
client->evdev = evdev;
/*通过链表将client置于evdev的链表头*/
evdev_attach_client(evdev, client);
error = evdev_open_device(evdev);
if (error)
goto err_free_client;
/*将client置于file的私有指针域*/
file->private_data = client;
return 0;
err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}
evdev_open函数主要做了以下事情:(1)通过次设备号获取evdev结构(2)分配一个endev_client结构并将client->evdev指向获取的evdev结构(3)调用evdev_attach_client函数将client置于evdev的链表头(4)调用evdev_open_device函数
evdev_open_device函数:
{
int retval;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
if (!evdev->exist)
retval = -ENODEV;
else if (!evdev->open++) {
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}
mutex_unlock(&evdev->mutex);
return retval;
}
函数调用input_open_device函数
int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int retval;
retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval;
if (dev->going_away) {
retval = -ENODEV;
goto out;
}
/*open计数加一*/
handle->open++;
/*当用户第一次调用时会调用dev的open函数*/
if (!dev->users++ && dev->open)
retval = dev->open(dev);
if (retval) {
dev->users--;
if (!--handle->open) {
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_rcu();
}
}
out:
mutex_unlock(&dev->mutex);
return retval;
}
因为在本按键驱动中,没有设置button_dev的open域,所以这里input_open_device无意义。
下面就是read函数监听键值了:
Read函数对应的驱动程序是 evdev.c的evdev_read。
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
/*获得client结构*/
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
int retval;
/*如果读取的字符数小于input_event结构的大小*/
if (count < input_event_size())
return -EINVAL;
/*如果头结点等于尾节点而且打开标志是不阻塞的,那么直接返回*/
if (client->head == client->tail && evdev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
/*条件为假阻塞,直到为真为止*/
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);
if (retval)
return retval;
if (!evdev->exist)
return -ENODEV;
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + retval, &event))
return -EFAULT;
retval += input_event_size();
}
return retval;
}
这里有必要提一下evdev_read函数中的client结构:
这里有必要提一下evdev_read函数中的client结构:
struct evdev_client {
/*存放事件的buffer*/
struct input_event buffer[EVDEV_BUFFER_SIZE];
/*事件头结点*/
int head;
/*事件尾节点*/
int tail;
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync;
/*evdev结构指针*/
struct evdev *evdev;
/*指向handler的链表*/
struct list_head node;
};
这个结构中的head tail相当于一个循环队列,只有当head不等于tail的时候才表示buffer里面不为空。那么当调用evdev_read时运行到retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);时evdev_read会阻塞。这样就会等待按键。
当按下键值,进入中断函数,调用input_report_key报告event。
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
/*判断evbit是否是事件所支持*/
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);
/*事件处理函数*/
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
函数最终调用input_handle_event
input_handle_event函数再根据type类型做相应处理,该驱动去type为EV_KEY,
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (!dev->sync) {
dev->sync = 1;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = 0;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
/*再次判断keybit是否是小于最大键值*/
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) {
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
if (test_bit(code, input_abs_bypass)) {
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
value = input_defuzz_abs_event(value,
dev->abs[code], dev->absfuzz[code]);
if (dev->abs[code] != value) {
dev->abs[code] = value;
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = 0;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
input_handle_event最终调用:input_pass_event
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
/*最终调用handler的event函数*/
if (handle)
handle->handler->event(handle, type, code, value);
else
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,
type, code, value);
rcu_read_unlock();
}
input_pass_event再调用evdev_event
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
do_gettimeofday(&event.time);
/*将按键信息存于event结构*/
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
/*获得client结构*/
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
rcu_read_unlock();
wake_up_interruptible(&evdev->wait);
}
evdev_event会调用evdev_pass_event函数:
static void evdev_pass_event(struct evdev_client *client,
struct input_event *event)
{
/*
* Interrupts are disabled, just acquire the lock
*/
spin_lock(&client->buffer_lock);
/*将事件结构存在buffer中并且head加一*/
client->buffer[client->head++] = *event;
/*溢出处理*/
client->head &= EVDEV_BUFFER_SIZE - 1;
spin_unlock(&client->buffer_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
从evdev_pass_event函数我们可以得到:将事件结构存在buffe,client的head指针加一,那么head加一之后,也就是报告键值完毕了,回到了evdev_read函数中:此时的head是不等于tail的,所以唤醒等待队列:
下面就会执行evdev_read的evdev_fetch_next_event函数:
static int evdev_fetch_next_event(struct evdev_client *client,
struct input_event *event)
{
int have_event;
spin_lock_irq(&client->buffer_lock);
have_event = client->head != client->tail;
if (have_event) {
/*获取事件指针并且tail加一*/
*event = client->buffer[client->tail++];
/*溢出处理*/
client->tail &= EVDEV_BUFFER_SIZE - 1;
}
spin_unlock_irq(&client->buffer_lock);
return have_event;
}
evdev_fetch_next_event将client的尾节点加一(这时候head和tail又相等了)。
随后evdev_read调用input_event_to_user
int input_event_to_user(char __user *buffer,
const struct input_event *event)
{
if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT;
return 0;
}
这样就将按键事件结构event传递到了用户空间。
总结:由上我们看出,input子系统的确是很复杂的。下面画一个简单的流程图。