全部博文(1493)
分类:
2012-10-15 18:15:56
原文地址:Android4.0 input输入子系统详解 作者:shangbaogen
下面的文章是基于mini2440的gpio按键来讲解input子系统。
以mini2440为例,用该板的bsp文件,进行input子系统的讲解.所用的版本为android4.0.
先来看下板级支持文件都注册了那些资源。
下面是五个按键的资源:
#define KEY_POWER 116 /* SC System Power Down */
#define KEY_F1 59
#define KEY_F2 60
#define KEY_F3 61
#define KEY_F5 63
struct gpio_keys_button {
/* Configuration parameters */
unsigned int code; /* input event code (KEY_*, SW_*) *///上报事件的code
int gpio;//所用的gpio引脚
int active_low;//是否低电平有效
const char *desc; //该按键的描述符
unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */
int wakeup; /* configure the button as a wake-up source */
int debounce_interval; /* debounce ticks interval in msecs */
bool can_disable;
int value; /* axis value for EV_ABS */
};
static struct gpio_keys_button mini2440_buttons[] = {
{
.gpio = S3C2410_GPG(0), /* K1 */
.code = KEY_F1,
.desc = "Button 1",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(3), /* K2 */
.code = KEY_F2,
.desc = "Button 2",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(5), /* K3 */
.code = KEY_F3,
.desc = "Button 3",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(6), /* K4 */
.code = KEY_POWER,
.desc = "Power",
.active_low = 1,
},
{
.gpio = S3C2410_GPG(7), /* K5 */
.code = KEY_F5,
.desc = "Button 5",
.active_low = 1,
},
};
/*下面是平台数据的声明*/
struct gpio_keys_platform_data {
struct gpio_keys_button *buttons;
int nbuttons;
unsigned int poll_interval; /* polling interval in msecs -
for polling driver only */
unsigned int rep:1; /* enable input subsystem auto repeat */
int (*enable)(struct device *dev);
void (*disable)(struct device *dev);
const char *name; /* input device name */
};
static struct gpio_keys_platform_data mini2440_button_data = {
.buttons = mini2440_buttons,
.nbuttons = ARRAY_SIZE(mini2440_buttons),
};
/*下面是平台设备的声明*/
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
static struct platform_device mini2440_button_device = {
.name = "gpio-keys",
.id = -1,
.dev = {
.platform_data = &mini2440_button_data,
}
};
static struct platform_device *mini2440_devices[] __initdata = {
...................
&mini2440_button_device,
………
};
static void __init mini2440_init(void)
{
.............................
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
……………
}
MACHINE_START(MINI2440, "MINI2440")
/* Maintainer: Michel Pollet
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = mini2440_map_io,
.init_machine = mini2440_init,
.init_irq = s3c24xx_init_irq,
.timer = &s3c24xx_timer,
MACHINE_END
上面是把该设备注册到平台总线上。
下面看下平台驱动的注册:
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove),
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
}
};
static int __init gpio_keys_init(void)
{
return platform_driver_register(&gpio_keys_device_driver);
}
module_init(gpio_keys_init);
在注册平台驱动时,如果成功匹配平台设备后,会调用平台驱动的probe函数。
下面看下该驱动的probe函数。
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
/*取出再bsp文件注册的平台数据*/
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
/*这里出现了一个新的结构体,该结构体定义如下*/
/*struct gpio_keys_drvdata {
struct input_dev *input;
struct mutex disable_lock;
unsigned int n_buttons;
int (*enable)(struct device *dev);
void (*disable)(struct device *dev);
struct gpio_button_data data[0];
};*/
struct gpio_keys_drvdata *ddata;
struct device *dev = &pdev->dev;
struct input_dev *input;
/*分配gpio_keys_drvdata结构体内存*/
ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons * sizeof(struct gpio_button_data),
GFP_KERNEL);
/*分配一个input结构体,并初始化部分成员*/
input = input_allocate_device();
/*为ddata的各个成员变量赋值*/
ddata->input = input;
ddata->n_buttons = pdata->nbuttons;
mutex_init(&ddata->disable_lock);
/*把ddata设备pdev平台设备的driver data*/
platform_set_drvdata(pdev, ddata);
/*把ddata设备input设备的driver data*/
input_set_drvdata(input, ddata);
/*设置input设备的各个成员变量*/
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;
input->open = gpio_keys_open;
input->close = gpio_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
/* 根据pdata的rep成员值,设备input子系统的功能*/
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
/*取出pdata中得资源进行赋值*/
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
struct gpio_button_data *bdata = &ddata->data[i];
/*为三目运算符,相当于button->type ?: button->type:EV_KEY;*/
unsigned int type = button->type ?: EV_KEY;
bdata->input = input;//
bdata->button = button;
error = gpio_keys_setup_key(pdev, bdata, button);
if (button->wakeup)//该键能否作为唤醒源?
wakeup = 1;
input_set_capability(input, type, button->code);
}
}
error = input_register_device(input);
/* get current state of buttons */
for (i = 0; i < pdata->nbuttons; i++)
gpio_keys_report_event(&ddata->data[i]);
input_sync(input);
device_init_wakeup(&pdev->dev, wakeup);
return 0;
}
下面逐步分解上面标成粉色的函数。
第一个分配一个input dev并进行初始化
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;
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);
}
return dev;
}
分析第二个:
static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
struct gpio_button_data *bdata,
struct gpio_keys_button *button)
{
/*取出按键的描述符*/
const char *desc = button->desc ? button->desc : "gpio_keys";
struct device *dev = &pdev->dev;
/*设置该bdata的定时器函数*/
setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
/*设置该bdata的work函数*/
INIT_WORK(&bdata->work, gpio_keys_work_func);
/*申请button的gpio*/
error = gpio_request(button->gpio, desc);
/*设置gpio的方向*/
error = gpio_direction_input(button->gpio);
if (button->debounce_interval) { //设置gpio的去抖间隔
error = gpio_set_debounce(button->gpio,
button->debounce_interval * 1000);
/* use timer if gpiolib doesn't provide debounce */
if (error < 0)
bdata->timer_debounce = button->debounce_interval;
}
irq = gpio_to_irq(button->gpio); //该gpio引脚对应分配的中断
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
if (!button->can_disable)
irqflags |= IRQF_SHARED;
/*注册该irq的中断处理函数,并设置标记*/
error = request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata);
其中中断处理函数如下:
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
struct gpio_keys_button *button = bdata->button;
BUG_ON(irq != gpio_to_irq(button->gpio));
if (bdata->timer_debounce)//如果有去抖间隔则修改定时器
mod_timer(&bdata->timer,
jiffies + msecs_to_jiffies(bdata->timer_debounce));
else
schedule_work(&bdata->work);//如果没有,直接执行work
return IRQ_HANDLED;
}
}
如果定时器到期,则执行定时器处理函数:
static void gpio_keys_timer(unsigned long _data)
{
struct gpio_button_data *data = (struct gpio_button_data *)_data;
schedule_work(&data->work);//执行相应的work
}
中断处理的结果是执行相应的work。看下work函数
static void gpio_keys_work_func(struct work_struct *work)
{
struct gpio_button_data *bdata =
container_of(work, struct gpio_button_data, work);
gpio_keys_report_event(bdata);//用input子系统,向上层报事件
}
第三个函数,设置该input dev的能力记录本设备对那些事件感兴趣
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
switch (type) {
case EV_KEY:
__set_bit(code, dev->keybit);// 比如按键,应该对哪些键值的按键进行处理(对于其它按键不予理睬)
break;
__set_bit(type, dev->evbit);
}
第四个函数:向input核心注册input设备
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;
/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit); //设置支持的能力
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);//清除该支持的能力
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);//确保在dev->evbit中没有支持的能力被清除掉
if (!dev->hint_events_per_packet)
dev->hint_events_per_packet = input_estimate_events_per_packet(dev);
/*
* 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;
}
/*设置input dev成员变量的处理函数*/
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
/*设置该dev name*/
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1);
error = device_add(&dev->dev);//把该设备增加到设备驱动模型中
/*把该dev加入到input_dev_list 链表*/
list_add_tail(&dev->node, &input_dev_list);
/*遍历input_hander_list链表中得hander,以便匹配input dev*/
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
return 0;
}
下面看下匹配函数:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
id = input_match_device(handler, dev);//返回匹配成功的id
error = handler->connect(handler, dev, id);//如果匹配成功,则调用hander的connect函数
return error;
}
下面主要看下match的过程:
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)//如果是匹配bus,则比较id.bus
if (id->bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) //如果是匹配vender则比较id.vender
if (id->vendor != dev->id.vendor)
continue;
//如果是匹配product则比较id.product
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
continue;
//如果是匹配versiont则比较id.version
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
continue;
/*如果hander支持该能力,则dev也要支持,否则不匹配*/
MATCH_BIT(evbit, EV_MAX);
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
MATCH_BIT(swbit, SW_MAX);
下面看下这个宏:
#define MATCH_BIT(bit, max) \
for (i = 0; i < BITS_TO_LONGS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
break; \
if (i != BITS_TO_LONGS(max)) \
continue;
/*如果hander的match空,则返回该id,或者调用match继续匹配,匹配成员的话也返回id*/
if (!handler->match || handler->match(handler, dev))
return id;
}
return NULL;
}
上面input dev已经注册完了,下面看看hander的注册.
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices 来则不拒,公交车*/
{ }, /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
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,//匹配的列表
};
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
module_init(evdev_init);
下面看下hander的注册:
static struct input_handler *input_table[8];
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {//判断input_table的相应项是否被占用
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler; // 如果没有占用,则把hander填入
}
/*把要注册的hander加入input_handler_list链表中*/
list_add_tail(&handler->node, &input_handler_list);
/*遍历input_dev_list链表上得每一个dev,去匹配该hander*/
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);//开始进行匹配
}
匹配成功后,返回匹配成功的id,然后调用该handler的connect函数。
static struct evdev *evdev_table[EVDEV_MINORS]; //evdev的容器
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
/*在容器中找个空闲的地方*/
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
/*分配一个evdev变量*/
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
/*初始化该evdev的成员变量*/
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 = true;
evdev->minor = minor;
/*初始化该evdev的成员变量handle,handle相当于是红娘连接input dev和相应的hander*/
evdev->handle.dev = input_get_device(dev);//增加该dev的引用计数
evdev->handle.name = dev_name(&evdev->dev);//设置该evdev的name
evdev->handle.handler = handler;
evdev->handle.private = evdev;//设置hander的私有数据,这个在下面会用到
/*初始化该evdev的成员变量dev*/
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);
/*注册上面初始化好的handle*/
error = input_register_handle(&evdev->handle);
/*安装evdev,其实就是放到全局的evdev_table 数组中*/
error = evdev_install_chrdev(evdev);
该函数如下:
static int evdev_install_chrdev(struct evdev *evdev)
{
evdev_table[evdev->minor] = evdev;
return 0;
}
/*把该evdev设备增加到设备驱动模型中*/
error = device_add(&evdev->dev);
return 0;
}
下面主要看input_register_handle干了啥活?
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
list_add_tail_rcu(&handle->d_node, &dev->h_list);//加入到dev hist链表的末尾
list_add_tail_rcu(&handle->h_node, &handler->h_list);//加入到hander的hist尾部
return 0;
注册的过程也就是把该handle加入dev和hander的链表中
}
上面input dev和handler用网上的一个图可以表示:
该图形象的描述了三者的关系.
该搭的关系已经搞好啦,下面就是要用啦,用的时候看三者是怎么配合的。
下面看现在中断处理中,是如何用的?
上面有说过,在中断发生后,会调用work,在work中去处理上报键值:上报函数如下:
static void gpio_keys_report_event(struct gpio_button_data *bdata)
{
struct gpio_keys_button *button = bdata->button;//取出每一个键的结构体
struct input_dev *input = bdata->input; //把该键的input设备也取出来
unsigned int type = button->type ?: EV_KEY; //类型为key
int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
input_event(input, type, button->code, !!state);
input_sync(input);
}
继续分析:
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
if (is_event_supported(type, dev->evbit, EV_MAX)) {//判断该事件是否被支持
……….
input_handle_event(dev, type, code, value);
..................
}
}
下面继续跟踪:
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_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) && /判断该code是否被支持
!!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;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = false;
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
继续跟踪该函数:
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handler *handler;
struct input_handle *handle;
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)//如果该handle没有被打开,则找下一个handle
continue;
handler = handle->handler;
if (!handler->filter) {
handler->event(handle, type, code, value);//调用handler的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;//要上报的事件结构体变量
struct timespec ts;
/*填充event,要上报的事件结构体*/
/*该事件发生的时间*/
ktime_get_ts(&ts);
event.time.tv_sec = ts.tv_sec;
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
/*赋值传进来的参数*/
event.type = type;
event.code = code;
event.value = value;
/*遍历evdev的client链表*/
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
}
下面看传递函数:
static void evdev_pass_event(struct evdev_client *client,struct input_event *event)
{
client->buffer[client->head++] = *event;//把传递的事件赋值给client的buffer中
client->head &= client->bufsize - 1;//管理循环缓冲区
}
*******************************************************************************
下面看下提供给上层的接口
下面看下hander的evdev_fops函数操作结构体:
看下具体的实现:
static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
.fasync = evdev_fasync,
.flush = evdev_flush,
.llseek = no_llseek,
};
下面具体看下open函数:
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE;
unsigned int bufsize;
evdev = evdev_table[i];//根据inode的值取出evdev的值
/*为client分配缓存*/
bufsize = evdev_compute_buffer_size(evdev->handle.dev);
/*为每一个打开实例,分配一个client结构体*/
client = kzalloc(sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event),
GFP_KERNEL);
/*初始化client的各个成员变量*/
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
snprintf(client->name, sizeof(client->name), "%s-%d",
dev_name(&evdev->dev), task_tgid_vnr(current));
client->evdev = evdev;//和该client关联的evdev
evdev_attach_client(evdev, client); //把该client加入该evdev的client链表中
下面是该函数的实现:
static void evdev_attach_client(struct evdev *evdev,struct evdev_client *client)
{
//把该client加入该evdev的client链表中
list_add_tail_rcu(&client->node, &evdev->client_list);
}
error = evdev_open_device(evdev);
file->private_data = client;//把该client挂载到file的私有结构体下
nonseekable_open(inode, file);
return 0;
}
下面接着看evdev_open_device(evdev);函数
static int evdev_open_device(struct evdev *evdev)
{
int retval;
if (!evdev->exist)//在connenct的时候就被设置为true
retval = -ENODEV;
else if (!evdev->open++) {
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}
return retval;
}
看下最后一个函数:
int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int retval;
handle->open++;//打开的计数器加1
if (!dev->users++ && dev->open)
retval = dev->open(dev);//该函数为空
return retval;
}
下一个目标是分析read函数:
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
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;
}
看下里面的两个细节
static int evdev_fetch_next_event(struct evdev_client *client,struct input_event *event)
{
int have_event;
have_event = client->packet_head != client->tail;
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= client->bufsize - 1;
}
return have_event;
}
/**/
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;
}
***************************************************************************
下面再看看input class是咋回事?
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
struct class input_class = {
.name = "input",
.devnode = input_devnode,
};
static int __init input_init(void)
{
/*注册input class*/
err = class_register(&input_class);
/*注册input dev字符设备,其中#define INPUT_MAJOR 13*/
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
return 0;
}
subsys_initcall(input_init);
***************************************************************************
看input_fops 中的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;
handler = input_table[iminor(inode) >> 5];//根据上层传入的inode结构体,找到对应的handler
if (handler)//如果handler不为空,则取出该handler的fops指针赋值给新的new_fops
new_fops = fops_get(handler->fops);
old_fops = file->f_op;//备份老的
file->f_op = new_fops;//赋值新的,以后的对该设备的操作都会映射到该操作结构体
err = new_fops->open(inode, file);//调用新的open函数
if (err) {如果出错,则回退
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
}