(1) 输入子系统事件处理层
(2) 输入子系统核心层(input.c就是核心层)
(3) 输入子系统设备驱动层
输入子系统主要为了简化开发过程,驱动的编写者只需要进行硬件底层的代码编写,上层的事件驱动由系统提供,不需要我们再去编写从上到下分别是事件驱动层,核心层,硬件驱动层,硬件驱动层代码由自己进行编写。当发生硬件上的改变的时候由硬件驱动层进行上报,上报给事件驱动层(事件处理层),通过input.c(核心层)进行事件的上报,最终事件处理层得到信息之后再进行进一步的处理
static struct *input_dev buttons_dev; static struct timer_list buttons_timer; //下面分配了input_dev的空间,timer的初始化在2.5里面的init_timer中,申请空间 buttons_dev = input_allocate_device();
set_bit(EV_KEY, buttons_dev->evbit); set_bit(KEY_L, buttons_dev->keybit); set_bit(KEY_S, buttons_dev->keybit); set_bit(KEY_ENTER, buttons_dev->keybit); set_bit(KEY_LEFTSHIFT, buttons_dev->keybit); set_bit(EV_REP, buttons_dev->evbit);
int error = input_register_device(buttons_dev);
for(error = 0; error < 4; error ++) { if(request_irq(key_struct[error].irq, buttons_interrupt, IRQT_BOTHEDGE, key_struct[error].name, &key_struct[error])) { printk("Can't allocate irq\n"); return -EBUSY; } }
init_timer(&buttons_timer); buttons_timer.function = buttons_timer_function; add_timer(&buttons_timer);
static irqreturn_t buttons_interrupt(int irq, void *dev_desc) { key_stmod = (struct key_st *)dev_desc; mod_timer(&buttons_timer, jiffies + 10); return IRQ_RETVAL(IRQ_HANDLED); }
static void buttons_timer_function(unsigned long dat) { unsigned long pinval; if(!key_stmod) return; pinval = s3c2410_gpio_getpin(key_stmod->pin); if(pinval) { input_report_key(buttons_dev, key_stmod->key_val, 0); input_sync(buttons_dev); } else { input_report_key(buttons_dev, key_stmod->key_val, 1); input_sync(buttons_dev); } }
buttons_dev = input_allocate_device(); 这句是向系统申请input_dev结构体内存 set_bit(EV_KEY, buttons_dev->evbit); 等效于buttons_dev->keybit[0] = BIT(KEY_L); set_bit(KEY_L, buttons_dev->keybit); 上面两句是置位相应的选项,对于本驱动只有按键事件会被响应 error = input_register_device(buttons_dev); 注册设备判断返回值 input_register_device(buttons_dev); ->set_bit(EV_SYN, dev->evbit); //所有设备都支持同步事件 ->list_add_tail(&dev->node, &input_dev_list); //将buttons_dev加入链表 ->list_for_each_entry(handler, &input_handler_list, node) //列出所有的handler,目的是与buttons_dev进行匹配 -->input_attach_handler(dev, handler); --->id = input_match_device(handler->id_table, dev); //前面排除黑名单之后进行id号的匹配 --->error = handler->connect(handler, dev, id); //调用handler的connect函数进行双方的连接 对于本例来说connect函数就是evdev.c里面的connect函数 connect函数里面 devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name); //为设备创建设备节点 error = input_register_handle(&evdev->handle); //此处进行dev与handler的连接 ---->list_add_tail(&handle->d_node, &handle->dev->h_list); ---->list_add_tail(&handle->h_node, &handler->h_list); //两边互联,通过h_list链表 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, }; //evdev.c里面的handler结构体,与本例的buttons_dev相结合,在evdev_handler结构体里面由新的evdev_fops,读写都会调用到这里面的读写函数 struct input_device_id { kernel_ulong_t flags; /*标志信息*/ __u16 bustype; /*总线类型*/ __u16 vendor; /*制造商ID*/ __u16 product; /*产品ID*/ __u16 version; /*版本号*/ ... kernel_ulong_t driver_info; /*驱动额外的信息*/ }; //id_table的内容
消息的上报,另外还有input_report_abs等等消息上报函数,每个函数都对应相应的消息
但是通用的接口事件函数都是input_event,此函数可用于处理任意一种linux已定义的消息上报,内部使用switch-case的结构进行消息选择上报
在input_report_key之后还需要使用input_sync进行事件的同步,即通知接收到消息的事件处理模块有消息到达,在多变量的传送过程中有很重要
的作用
input_report_key(buttons_dev, key_stmod->key_val, 0);
input_sync(buttons_dev);
按键触发之后上传消息,到input_sync的时候会有
dev->event(dev, type, code, value);调用到设备对应事件处理函数进行处理,对于按键驱动使用到的evdev事件来说 ->static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) //上面的dev->event --> kill_fasync(&client->fasync, SIGIO, POLL_IN); //同步机制 wake_up_interruptible(&evdev->wait); //唤醒中断,之后会有相应的处理函数将消息发给特定的pid号所在的程序接下来的动作可以参考另一篇文章: 目录里面有记录fasync机制
struct input_dev { void *private; //私有指针,以供用户进行一些私有功能扩展 const char *name; const char *phys; const char *uniq; struct input_id id; //input_handle主要就是通过id来进行识别连接的 /* 各种事件标志,按键,相对位移,绝对位移等等,相应的位置位代表这个设备支持相应的事件类型 */ unsigned long evbit[NBITS(EV_MAX)]; unsigned long keybit[NBITS(KEY_MAX)]; unsigned long relbit[NBITS(REL_MAX)]; unsigned long absbit[NBITS(ABS_MAX)]; unsigned long mscbit[NBITS(MSC_MAX)]; unsigned long ledbit[NBITS(LED_MAX)]; unsigned long sndbit[NBITS(SND_MAX)]; unsigned long ffbit[NBITS(FF_MAX)]; unsigned long swbit[NBITS(SW_MAX)]; /* .................. */ 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; /* .................. */ struct list_head h_list; //所有的东西初始化完毕之后会指向input_handle结构体,input_handle结构体负责input_dev与input_handler进行联系 struct list_head node; };
/** * struct input_handler - implements one of interfaces for input devices * @private: driver-specific data * @event: event handler * @connect: called when attaching a handler to an input device * @disconnect: disconnects a handler from input device * @start: starts handler for given handle. This function is called by * input core right after connect() method and also when a process * that "grabbed" a device releases it * @fops: file operations this driver implements * @minor: beginning of range of 32 minors for devices this driver * can provide * @name: name of the handler, to be shown in /proc/bus/input/handlers * @id_table: pointer to a table of input_device_ids this driver can * handle * @blacklist: prointer to a table of input_device_ids this driver should * ignore even if they match @id_table * @h_list: list of input handles associated with the handler * @node: for placing the driver onto input_handler_list */ 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; const struct input_device_id *blacklist; struct list_head h_list; struct list_head node; };
struct input_handle { void *private; int open; const char *name; struct input_dev *dev; //指向事件的硬件设备结构体 struct input_handler *handler; //指向事件的处理结构体 struct list_head d_node; struct list_head h_node; };
struct input_event { struct timeval time; //时间结构体,包含当前系统的秒数与微秒 __u16 type; //事件类型。比如按键类事件或者绝对位移类事件 __u16 code; //码。比如按键类的shift键 __s32 value; //值,按键会有按下或者松开,0或者1,绝对位移会有x与y方向上的大小 };
在linux系统中使用hexdump /dev/event0可以查看到事件返回的值
//事件类型如下 #define EV_SYN 0x00 //同步 #define EV_KEY 0x01 //按键 #define EV_REL 0x02 //相对坐标(例如鼠标的位置移动,报告相对坐标) #define EV_ABS 0x03 //绝对坐标(例如触摸屏的位置点击,报告绝对坐标) #define EV_MSC 0x04 //其他 #define EV_SW 0x05 //开关设备 #define EV_LED 0x11 //LED灯,(例如键盘LED) #define EV_SND 0x12 //声音 #define EV_REP 0x14 //警报 #define EV_FF 0x15 //力反馈 #define EV_PWR 0x16 //电源 #define EV_FF_STATUS 0x17 //力反馈状态 #define EV_MAX 0x1f //时间类型最大个数以及提供掩码支持*******************以上是linux内核提供的事件类型*********************