首页 文章 精选 留言 我的

精选列表

搜索[单体架构],共10000篇文章
优秀的个人博客,低调大师

input 子系统架构总结【转】

Linux输入子系统(Input Subsystem) 转自:http://blog.csdn.net/lbmygf/article/details/7360084 Linux 的输入子系统不仅支持鼠标、键盘等常规输入设备,而且还支持蜂鸣器、触摸屏等设备。本章将对 Linux 输 入子系统进行详细的分析。 一 前言 输入子系统又叫 input 子系统。其构建非常灵活,只需要调用一些简单的函数,就可以将一个输入设备的功能呈现 给应用程序。 二 设备驱动层 本节将讲述一个简单的输入设备驱动实例。 这个输入设备只有一个按键,按键被连接到一条中断线上,当按键被按下时,将产生一个中断,内核将检测到这个中 断,并对其进行处理。该实例的代码如下: #include <asm/irq.h> #include <asm/io.h> static struct input_dev *button_dev;/*输入设备结构体*/ static irqreturn_tbutton_interrupt(int irq, void *dummy)/*中断处理函数*/ { input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);/*向输入子系统报告产生按键事件*/ input_sync(button_dev);/*通知接收者,一个报告发送完毕*/ return IRQ_HANDLED; } static int __init button_init(void)/*加载函数*/ { int error; if (request_irq(BUTTON_IRQ,button_interrupt, 0, "button", NULL))/*申请中断,绑定中断处理函数*/ { printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq); return -EBUSY; } button_dev =input_allocate_device();/*分配一个设备结构体*/ //input_allocate_device()函数在内存中为输入设备结构体分配一个空间,并对其主要的成员进行了初始化. if (!button_dev) { printk(KERN_ERR "button.c: Not enough memory\n"); error = -ENOMEM; goto err_free_irq; } button_dev->evbit[0] = BIT_MASK(EV_KEY);/*设置按键信息*/ button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); //分别用来设置设备所产生的事件以及上报的按键值。Struct iput_dev中有两个成员,一个是evbit.一个是keybit.分别用 //表示设备所支持的动作和键值。 error =input_register_device(button_dev); /*注册一个输入设备*/ if (error) { printk(KERN_ERR "button.c: Failed to register device\n"); goto err_free_dev; } return 0; err_free_dev: input_free_device(button_dev); err_free_irq: free_irq(BUTTON_IRQ, button_interrupt); return error; } static void __exit button_exit(void)/*卸载函数*/ { input_unregister_device(button_dev);/*注销按键设备*/ free_irq(BUTTON_IRQ, button_interrupt);/*释放按键占用的中断线*/ } module_init(button_init); module_exit(button_exit); 这个实例程序代码比较简单,在初始化函数 button_init()中注册了一个中断处理函数,然后调用 input_allocate_device()函数分配了一个 input_dev 结构体,并调用 input_register_device()函数对其进行了注册。在中 断处理函数 button_interrupt()中,实例将接收到的按键信息上报给 input 子系统。从而通过 input 子系统,向用户态程序 提供按键输入信息。本实例采用了中断方式,除了中断相关的代码外,实例中包含了一些 input 子系统提供的函数,现对 其中一些重要的函数进行分析。 三 核心层 input_allocate_device()函数,驱动开发人员为了更深入的了解 input 子系统,应该对其代码有一点的认识,该函数的代码 如下: struct input_dev *input_allocate_device(void) { struct input_dev *dev; dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);/*分配一个 input_dev 结构体,并初始化为 0*/ 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); __module_get(THIS_MODULE); } return dev; } 该函数返回一个指向 input_dev 类型的指针,该结构体是一个输入设备结构体,包含了输入设备的一些相关信息,如 设备支持的按键码、设备的名称、设备支持的事件等。 =================================================== Input设备注册的接口为:input_register_device()。代码如下: 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; __set_bit(EV_SYN, dev->evbit); --------------------------------------------------- 调用__set_bit()函数设置 input_dev 所支持的事件类型。事件类型由 input_dev 的evbit 成员来表示,在这里将其 EV_SYN 置位,表示设 备支持所有的事件。注意,一个设备可以支持一种或者多种事件类型。常用的事件类型如下: 1. #define EV_SYN 0x00 /*表示设备支持所有的事件*/ 2. #define EV_KEY 0x01 /*键盘或者按键,表示一个键码*/ 3. #define EV_REL 0x02 /*鼠标设备,表示一个相对的光标位置结果*/ 4. #define EV_ABS 0x03 /*手写板产生的值,其是一个绝对整数值*/ 5. #define EV_MSC 0x04 /*其他类型*/ 6. #define EV_LED 0x11 /*LED 灯设备*/ 7. #define EV_SND 0x12 /*蜂鸣器,输入声音*/ 8. #define EV_REP 0x14 /*允许重复按键类型*/ 9. #define EV_PWR 0x16 /*电源管理事件*/ --------------------------------------------------- /* * 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);//初始化一个 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; } //如果dev->rep[REP_DELAY]和dev->rep[REP_PERIOD]没有设值,则将其赋默认值。这主要是处理重复按键的. if (!dev->getkeycode) dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode) dev->setkeycode = input_default_setkeycode; //检查 getkeycode()函数和 setkeycode()函数是否被定义,如果没定义,则使用默认的处理函数,这两个函数为 //input_default_getkeycode()和 input_default_setkeycode()。input_default_getkeycode()函数用来得到指定位置的键 //值。input_default_setkeycode()函数用来设置键值。具体啥用处,我也没搞清楚? snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); //设置 input_dev 中的 device 的名字,名字以 input0、input1、input2、input3、input4等的形式出现在 sysfs //文件系统中. error = device_add(&dev->dev); if (error) return error; //使用 device_add()函数将 input_dev 包含的 device 结构注册到 Linux 设备模型中,并可以在 sysfs //文件系统中表现出来。 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; } list_add_tail(&dev->node, &input_dev_list); //调用 list_add_tail()函数将 input_dev 加入 input_dev_list 链表中,input_dev_list 链 //表中包含了系统中所有的 input_dev 设备。 list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler); //将input device挂到input_dev_list链表上.然后,对每一个挂在input_handler_list的handler调用 //input_attach_handler().在这里的情况有好比设备模型中的device和driver的匹配。所有的input device都挂在 //input_dev_list链上。所有的handler都挂在input_handler_list上。 input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); return 0; } ==================================================================================== 匹配是在input_attach_handler()中完成的。代码如下: static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { const struct input_device_id *id; int error; if (handler->blacklist && input_match_device(handler->blacklist, dev)) return -ENODEV; //首先判断 handler的 blacklist 是否被赋值,如果被赋值,则匹配 blacklist 中的数据跟 dev->id 的数据是否匹配。blacklist //是一个 input_device_id*的类型,其指向 input_device_ids的一个表,这个表中存放了驱动程序应该忽略的设备。即使在 //id_table 中找到支持的项,也应该忽略这种设备。 id =input_match_device(handler->id_table, dev); //调用 input_match_device()函数匹配 handler->>id_table 和 dev->id 中的数据。如果不成功则返回。 handle->id_table也是一个 input_device_id 类型的指针,其表示驱动支持的设备列表。 if (!id) return -ENODEV; error =handler->connect(handler, dev, id); //如果匹配成功,则调用 handler->connect()函数将 handler 与 input_dev 连接起来。 //在connect()中会调用input_register_handle,而这些都需要handler的注册。 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的blacklist被赋值。要先匹配blacklist中的数据跟dev->id的数据是否匹配。匹配成功过后再来匹配 //handle->id和dev->id中的数据。如果匹配成功,则调用handler->connect(). ==================================================================================== input_match_device()代码如下: static const struct input_device_id *input_match_device(const struct input_device_id *id, struct input_dev *dev) { int i; for (; id->flags || id->driver_info; id++) { //匹配设备厂商的信息,设备号的信息。 if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) if (id->bustype != dev->id.bustype) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) if (id->vendor != dev->id.vendor) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) if (id->product != dev->id.product) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) if (id->version != dev->id.version) continue; MATCH_BIT(evbit, EV_MAX); MATCH_BIT(,, 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); -------------------------------------------------------------------------------------------------------------------------------------- MATCH_BIT宏的定义如下: #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; -------------------------------------------------------------------------------------------------------------------------------------- return id; } return NULL; } //从MATCH_BIT宏的定义可以看出。只有当iput device和input handler的id成员在evbit, keybit,… swbit项相同才会匹//配成功。而且匹配的顺序是从evbit, keybit到swbit.只要有一项不同,就会循环到id中的下一项进行比较. //简而言之,注册input device的过程就是为input device设置默认值,并将其挂以input_dev_list.与挂载在//input_handler_list中的handler相匹配。如果匹配成功,就会调用handler的connect函数. ==================================================================================== 这一条线先讲到这里因为接下去就要讲handler ,那就是事件层的东西了, 我们先把核心层的东西讲完, 在前面的设备驱动层中的中断响应函数里面,有input_report_key函数 ,下面我们来看看他。 input_report_key()函数向输入子系统报告发生的事件,这里就是一个按键事件。在 button_interrupt()中断函数中, 不需要考虑重复按键的重复点击情况,input_report_key()函数会自动检查这个问题,并报告一次事件给输入子系统。该 函数的代码如下: static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_KEY, code, !!value); } 该函数的第 1 个参数是产生事件的输入设备, 2 个参数是产生的事件, 3 个参数是事件的值。需要注意的是, 第2 个 参数可以取类似 BTN_0、 BTN_1、BTN_LEFT、BTN_RIGHT 等值,这些键值被定义在 include/linux/input.h 文件中。 当第 2 个参数为按键时,第 3 个参数表示按键的状态,value 值为 0 表示按键释放,非 0 表示按键按下。 =================================================== 在 input_report_key()函数中正在起作用的函数是 input_event()函数,该函数用来向输入子系统报告输入设备产生 的事件,这个函数非常重要,它的代码如下: voidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { unsigned long flags; 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); //调用 input_handle_event()函数来继续输入子系统的相关模块发送数据。 spin_unlock_irqrestore(&dev->event_lock, flags); } ==================================================================================== input_handle_event()函数向输入子系统传送事件信息。第 1 个参数是输入设备 input_dev,第 2 个参数是事件的类 型,第 3 个参数是键码,第 4 个参数是键值。 浏览一下该函数的大部分代码,主要由一个 switch 结构组成。该结构用来对不同的事件类型,分别处理。其中 case 语句包含了 EV_SYN、 EV_KEY、EV_SW、EV_SW、EV_SND 等事件类型。在这么多事件中,本例只要关注 EV_KEY 事件,因为本节的实例发送的是键盘事件。其实,只要对一个事件的处理过程了解后,对其他事件的处理过程也 就清楚了。该函数的代码如下: static void input_handle_event(struct input_dev *dev, { unsigned int type, unsigned int code, int value) int disposition = INPUT_IGNORE_EVENT; //定义了一个 disposition 变量,该变量表示使用什么样的方式处理事件 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; } break;case EV_KEY: 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); //处理重复按键的情况 } disposition = INPUT_PASS_TO_HANDLERS; //将 disposition变量设置为 INPUT_PASS_TO_HANDLERS,表示事件需要 handler 来处理。 --------------------------------------------------------------------------------------------------------------------- disposition 的取值有如下几种: 1. #define INPUT_IGNORE_EVENT 0 2. #define INPUT_PASS_TO_HANDLERS 1 3. #define INPUT_PASS_TO_DEVICE 2 4. #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) INPUT_IGNORE_EVENT 表示忽略事件,不对其进行处理。 INPUT_PASS_ TO_HANDLERS 表示将事件交给 handler 处理。 INPUT_PASS_TO_DEVICE 表示将事件交给 input_dev 处理。 INPUT_PASS_TO_ALL 表示将事件交给 handler 和 input_dev 共同处理。 -------------------------------------------------------------------------------------------------------------------- } 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)) { 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); //首先判断 disposition 等于 INPUT_PASS_TO_DEVICE,然后判断 dev->event 是否对其指定了一个处理函数,如果这些 //条件都满足,则调用自定义的 dev->event()函数处理事件。 //有些事件是发送给设备,而不是发送给 handler 处理的。event()函数用来向输入子系统报告一个将要发送给设备的事 //件,例如让 LED 灯点亮事件、蜂鸣器鸣叫事件等。当事件报告给输入子系统后,就要求设备处理这个事件。 if (disposition &INPUT_PASS_TO_HANDLERS) input_pass_event(dev, type, code, value); } ==================================================================================== input_pass_event()函数将事件传递到合适的函数,然后对其进行处理,该函数的代码如下: static voidinput_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); //得到 dev->grab 的指针。grab 是强制为 input device 的 handler,这时要调用 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); //如果该 handle 被打开,表示该设备已经被一个用户进程使用。就会调用与输入设备对应的 handler 的 event()函数。 //注意,只有在 handle 被打开的情况下才会接收到事件,这就是说,只有设备被用户程序使用时,才有必要向用户空间导出 //信息 //此处亦是用到了handle,核心层就到此为止,前面也讲过在device和handler connect()时会调用 //input_register_handle,而这些都需要handler的注册,所以接下来我们看看事件层 rcu_read_unlock(); } 四 事件层 input_handler 是输入子系统的主要数据结构,一般将其称为 handler 处理器,表示对输入事件的具体处理。 input_handler 为输入设备的功能实现了一个接口,输入事件最终传递到handler 处理器,handler 处理器根据一定的规则, 然后对事件进行处理,具体的规则将在下面详细介绍。 输入子系统由驱动层、输入子系统核心层(Input Core)和事件处理层(Event Handler)3 部分组成。一个输入事件, 如鼠标移动,键盘按键按下等通过驱动层->系统核心层->事件处理层->用户空间的顺序到达用户空间并传给应用程序使 用。其中 Input Core 即输入子系统核心层由 driver/input/input.c 及相关头文件实现。其对下提供了设备驱动的接口,对 上提供了事件处理层的编程接口。输入子系统主要设计 input_dev、input_handler、input_handle 等数据结构. struct input_dev物理输入设备的基本数据结构,包含设备相关的一些信息 struct input_handler 事件处理结构体,定义怎么处理事件的逻辑 struct input_handle用来创建 input_dev 和 input_handler 之间关系的结构体 在evdev.c 中: static struct input_handler evdev_handler = { .event = evdev_event, // 前面讲的传递信息是调用,在input_pass_event中 .connect = evdev_connect, //device 和 handler 匹配时调用 .disconnect = evdev_disconnect, .fops = &evdev_fops, //event 、connect、fops会在后面详细讲 .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, }; -------------------------------------------------------------------------------------------------------------------------------- struct input_handler { void *private; void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); 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;//定义了一个 name,表示 handler 的名字,显示在/proc/bus/input/handlers 目录 //中。 const struct input_device_id *blacklist;//指向一个 input_device_id 表,这个表包含 handler 应该忽略的设备 struct list_head h_list; struct list_head node; }; -------------------------------------------------------------------------------------------------------------------------------- //事件层注册 static int __init evdev_init(void) { returninput_register_handler(&evdev_handler); } ==================================================================================== intinput_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->minor 表示对应 input 设备结点的次设备号。 handler->minor以右移 5 位作为索引值插入到 //input_table[ ]中 if (handler->fops != NULL) { if (input_table[handler->minor >> 5]) { retval = -EBUSY; goto out; } input_table[handler->minor >> 5] = handler; } list_add_tail(&handler->node, &input_handler_list); //调用 list_add_tail()函数,将 handler 加入全局的 input_handler_list 链表中,该链表包含了系统中所有的 input_handler list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler); //主 要 调 用 了 input_attach_handler() 函 数 。 该 函 数 在 input_register_device()函数的第 35 行曾详细的介绍过。//input_attach_handler()函数的作用是匹配 input_dev_list 链表中的 input_dev 与 handler。如果成功会将 input_dev //与 handler 联系起来。也就是说在注册handler和dev时都会去调用该函数。 input_wakeup_procfs_readers(); out: mutex_unlock(&input_mutex); return retval; } ==================================================================================== ok下面我们来看下handle的注册,在前面evdev_handler结构体中,有一个.connect = evdev_connect, 在 connect里面会注册handle,在前面注册dev,匹配成功后调用。 static intevdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; int minor; int error; for (minor = 0; minor < EVDEV_MINORS; minor++) if (!evdev_table[minor]) break; 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); snprintf(evdev->name, sizeof(evdev->name), "event%d", minor); evdev->exist = 1; evdev->minor = minor; evdev->handle.dev = input_get_device(dev); evdev->handle.name = evdev->name; evdev->handle.handler = handler; evdev->handle.private = evdev; //分配了一个 evdev结构 ,并对这个结构进行初始化 .在这里我们可以看到 ,这个结构封装了一个 handle结构 ,这结构与 //我们之前所讨论的 handler是不相同的 .注意有一个字母的差别哦 .我们可以把 handle看成是 handler和 input device //的信息集合体 .在这个结构里集合了匹配成功的 handler和 input device strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id)); 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); //在这段代码里主要完成 evdev封装的 device的初始化 .注意在这里 ,使它所属的类指向 input_class.这样在 /sysfs中创 //建的设备目录就会在 /sys/class/input/下面显示 . error =input_register_handle(&evdev->handle); if (error) goto err_free_evdev; 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; } ==================================================================================== intinput_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; list_add_tail_rcu(&handle->d_node, &dev->h_list); mutex_unlock(&dev->mutex); synchronize_rcu(); /* * 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. */ list_add_tail(&handle->h_node, &handler->h_list); if (handler->start) handler->start(handle); return 0; } 将handle挂到所对应input device的h_list链表上.还将handle挂到对应的handler的hlist链表上.如果handler定 义了start函数,将调用之.到这里,我们已经看到了input device, handler和handle是怎么关联起来的了 ==================================================================================== 接下来我们看看上报信息是调用的 .event = evdev_event 。 每当input device上报一个事件时,会将其交给和它匹配的handler的event函数处理.在evdev中.这个event函数 对应的代码为: static voidevdev_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.type = type; event.code = code; event.value = value; rcu_read_lock(); 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); } =================================================================================== static voidevdev_pass_event(struct evdev_client *client, struct input_event *event) { /* * Interrupts are disabled, just acquire the lock */ spin_lock(&client->buffer_lock); client->buffer[client->head++] = *event; client->head &= EVDEV_BUFFER_SIZE - 1; spin_unlock(&client->buffer_lock); kill_fasync(&client->fasync, SIGIO, POLL_IN); } 这里的操作很简单.就是将event(上传数据)保存到client->buffer中.而client->head就是当前的数据位置.注意这里 是一个环形缓存区.写数据是从client->head写.而读数据则是从client->tail中读. ==================================================================================== 最后我们看下handler的相关操作函数 .fops = &evdev_fops, 我们知道.对主设备号为INPUT_MAJOR的设备节点进行操作,会将操作集转换成handler的操作集.在evdev中,这个 操作集就是evdev_fops.对应的open函数如下示: static intevdev_open(struct inode *inode, struct file *file) { struct evdev *evdev; struct evdev_client *client; 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 = 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; evdev_attach_client(evdev, client); error =evdev_open_device(evdev); if (error) goto err_free_client; 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_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据。evdev_open_device()函 数先获得互斥锁,然后检查设备是否存在,并判断设备是否已经被打开。如果没有打开,则调用 input_open_device() 函数打开设备. static intevdev_open_device(struct evdev *evdev) { 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; } ==================================================================================== 对于evdev设备节点的read操作都会由evdev_read()完成.它的代码如下: static ssize_tevdev_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; int retval; if (count < evdev_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 + evdev_event_size() <= count && evdev_fetch_next_event(client, &event)) { if (evdev_event_to_user(buffer + retval, &event)) return -EFAULT; retval += evdev_event_size(); } return retval; } 首先,它判断缓存区大小是否足够.在读取数据的情况下,可能当前缓存区内没有数据可读.在这里先睡眠等待缓存 区中有数据.如果在睡眠的时候,.条件满足.是不会进行睡眠状态而直接返回的.然后根据read()提够的缓存区大小.将 client中的数据写入到用户空间的缓存区中. 五 用户空间 到这就没啥讲的了, ok到此为止吧!!! 参考网页:http://hi.baidu.com/%B7%E8%D7%D3%D6%AE%CD%BD/blog/item/f4ed8135a4258c2d0b55a940.html http://blog.csdn.net/changjiang654/article/details/6154622 本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/4514363.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

新加坡架构师面试总结

本来是想把之前面试的一些经历和体会以一个系列的形式写出来,但一直都有这样或者那样的事情“从中作梗”,所以直到现在也未能如愿。 我当时在新加坡一共面试了七家公司(有欧美企业,香港企业,新加坡政府、企业以及银行),其中有电话面试,视频面试,笔试,机试以及“面对面”面试;面试的次数和每次的时长都有所不同,一般每次面试在一个小时左右,次数最短的为三次,最长的则多达九次。最终结果一共收到4个offer,其中以新加坡管理大学和微软两个Offer最难取舍。 鉴于很多朋友发邮件提到这个问题,我本意是很想把这些文章补上,但是这个月忙着赶项目,下个月又要和老婆去澳大利亚旅游大半个月,所以先发一张之前概述的总结图,希望对需要这些资料的朋友一些帮助或者启示。 另外,我也附上原始的Visio图,欢迎有兴趣的朋友补上缺漏的部分:files.cnblogs.com/KnightsWarrior/ArchitectInterview.zip 之前一直被嫌文章过长,最近没有时间写博客,所以今天的文章就短了一些。不过也好,如果你仔细读图,也许你会找到一些你需要的东西! 由于图片尺寸比较大,建议大家直接点击查看图片:大图 Visio原图:ArchitectInterview.zip 本文转自KnightsWarrior51CTO博客,原文链接:http://blog.51cto.com/knightswarrior/1362697,如需转载请自行联系原作者

优秀的个人博客,低调大师

window php支持 memcache nginx 架构

首先下载资源包 要跟你的php 版本一样 否则有的不能用 1. 下载memcached的windows稳定版,解压放某个盘下面,比如在d:\memcached2. 在终端(也即cmd命令界面)下输入 'd:\memcached\memcached.exe -d install' 安装3. 再输入: 'd:\memcached\memcached.exe -d start' 启动。NOTE: 以后memcached将作为windows的一个服务每次开机时自动启动。这样服务器端已经安装完毕了。4.下载php_memcache.dll,请自己查找对应的php版本的文件5. 在D:\php\php.ini 加入一行 'extension=php_memcache.dll' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 D:\soft\PHPnow-1.5.6\php-5.2.14-Win32>php -m [PHP Modules] bcmath calendar com_dotnet ctype curl date dom filter ftp gd hash iconv json libxml mbstring mcrypt mysql mysqli odbc pcre Reflection session SimpleXML SPL standard tokenizer wddx xml xmlreader xmlwriter zlib [Zend Modules] 没有memcache 模块 去 php.ini 里添加 1 2 3 4 5 6 7 ; Note that ODBC support is built in , so no dll is needed for it. ; Note that many DLL files are located in the extensions/ (PHP 4) ext/ (PHP 5) ; extension folders as well as the separate PECL DLL download (PHP 5). ; Be sure to appropriately set the extension_dir directive. extension=php_memcache.dll ;extension=php_bz2.dll extension=php_curl.dll 然后重新查看 php -m 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 [PHP Modules] bcmath calendar com_dotnet ctype curl date dom filter ftp gd hash iconv json libxml mbstring mcrypt memcache mysql mysqli odbc pcre Reflection session SimpleXML SPL standard tokenizer wddx xml xmlreader xmlwriter zlib 重新nginx 查看php info 也会看到 ok 搞定了 telnet 127.0.0.1 11211 C:\Documents and Settings\Administrator>telnet 127.0.0.1 11211 端口起来就行了 本文转自 cs312779641 51CTO博客,原文链接:http://blog.51cto.com/chenhao6/1351735

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册