Android 输入系统架构 笔记4
2014-11-27 09:43
477 查看
下面看下Linux 下 input 驱动的架构,以具体例子分析下:
下面的文章是基于mini2440的gpio按键来讲解input子系统。以mini2440为例用该板的bsp文件进行input子系统的讲解.所用的版本为android4.0.先来看下板级支持文件都注册了那些资源。
上面是把该设备注册到平台总线上。
在注册平台驱动时如果成功匹配平台设备后会调用平台驱动的probe函数。
下面看下该驱动的probe函数。
上面input dev和handler用网上的一个图可以表示
该图形象的描述了三者的关系.
该搭的关系已经搞好啦下面就是要用啦,用的时候看三者是怎么配合的。下面看现在中断处理中是如何用的上面有说过在中断发生后会调用work,在work中去处理上报键值,上报函数如下:
下面继续跟踪
跟踪继续
*******************************************************************************
下面看下提供给上层的接口,下面看下hander的evdev_fops函数操作结构体
看下具体的实现:
下面的文章是基于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 <buserror@gmail.com> */ .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的成员变量handlehandle相当于是红娘连接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); } }
相关文章推荐
- Android 输入系统架构 笔记1
- Android 输入系统架构 笔记1
- Android 输入系统架构 笔记2
- Android 输入系统架构 笔记3
- android_系统架构笔记之--android系统层次
- Android系统架构学习笔记
- Android源码笔记——Camera系统架构
- Android自学笔记(一)Android系统架构
- Android源码笔记——Camera系统架构
- Android GPS学习笔记—系统架构
- Android源码笔记——Camera系统架构
- Android源码笔记——Camera系统架构
- imx6平台Android源码笔记——Camera系统架构
- Android源码笔记——Camera系统架构
- Android源码笔记——Camera系统架构
- Android源码笔记——Camera系统架构
- Android输入系统笔记
- Android GPS学习笔记(1)—系统架构
- Android系统架构(Android第一行代码学习笔记1)
- Android源码笔记——Camera系统架构