读书人

深层次分析linux2.6input分系统

发布时间: 2012-09-06 10:37:01 作者: rapoo

深层次分析linux2.6input子系统

在linux下,按键、触摸屏、鼠标等都可以利用input接口函数来实现设备驱动。

深层次分析linux2.6input分系统

从上图可知:

输入子系统由三部分构成:

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;

}

加载内核模块:

深层次分析linux2.6input分系统

多了event0设备文件

运行应用程序:

深层次分析linux2.6input分系统

下面我们分析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*/
};

input_handler表示一个Input设备对应的handler

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子系统的确是很复杂的。下面画一个简单的流程图。


深层次分析linux2.6input分系统



读书人网 >UNIXLINUX

热点推荐