[內容概要]
介紹了input-subsystem的三個組成部分,並對主要結構體和函數進行了分析以及它們如何關聯。
[概述]
內核的輸入子系統是對分散的、多種不同類別的輸入設備(如鍵盤、鼠標、跟蹤球、操作桿、觸摸屏、加速計和手寫板)進行統一處理的驅動程序。輸入子系統帶來的好處:
統一了物理形態各異的相似的輸入設備的處理功能。例如,各種鼠標,不論是PS/2、USB,還是藍牙,都做同樣的處理;
提供了用於分發輸入報告給用戶應用程序的簡單的事件接口;
抽取出了輸入驅動程序的通用部分,簡化了驅動程序,並引入了一致性。
[input-subsystem架構圖]
[input-core]
核心層對下提供了設備驅動層的編程接口,對上有提供了事件處理層的編程接口。
[相關文件]
Driver/input/input.c
Include/linux/input.h
[關鍵的數據結構]
Struct input_dev 物理輸入設備的基本數據結構,包含了設備相關的一些信息;
- struct input_dev {
- const char *name;
- constchar *phys;
- constchar *uniq;
- struct input_id id;
-
- unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
-
- unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
- unsignedlong relbit[BITS_TO_LONGS(REL_CNT)];
- unsignedlong absbit[BITS_TO_LONGS(ABS_CNT)];
- unsignedlong mscbit[BITS_TO_LONGS(MSC_CNT)];
- unsignedlong ledbit[BITS_TO_LONGS(LED_CNT)];
- unsignedlong sndbit[BITS_TO_LONGS(SND_CNT)];
- unsignedlong ffbit[BITS_TO_LONGS(FF_CNT)];
- unsignedlong swbit[BITS_TO_LONGS(SW_CNT)];
- ……..
- struct device dev;
-
- struct list_head h_list;
- struct list_head node;
- };
Name 輸入設備名字;
Evbit 事件支持的位圖;
Kerbit 按鍵支持的位圖;
Dev 內嵌的設備結構體;
h_list 鏈表 表示與這個input_dev相聯系的前後一個handler;
node鏈表 將此Input_dev連接到全局的input_dev_list鏈表中,www.linuxidc.com內核中所有的Input_dev都連接在其上;
Struct input_handler 事件處理結構體,定義處理事件的方法;
- struct input_handler {
- void*private;
-
- void (*event)(struct input_handle *handle, unsigned inttype, unsigned int code, int value);
- int (*connect)(struct input_handler *handler, structinput_dev *dev, const struct input_device_id *id);
- void(*disconnect)(struct input_handle *handle);
- void(*start)(struct input_handle *handle);
-
- conststruct file_operations *fops;
- intminor;
- constchar *name;
-
- conststruct input_device_id *id_table;
- conststruct input_device_id *blacklist;
-
- struct list_head h_list;
- struct list_head node;
- };
Event()函數被輸入子系統用於處理發送給設備的事件;
Connet()用來連接handler和Input_dev;
h_list鏈表 表示與這個Input_handler相聯系的前後一個handler;
node鏈表 將此Input_handler連接到全局的input_handler_list鏈表中,內核中所有的Input_handler都連接在其上;
Struct input_handle 用來創建input_dev和Input_handler之間關系的結構體;
- struct input_handle {
- void*private;
- intopen;
- constchar *name;
-
- structinput_dev *dev;
- structinput_handler *handler;
-
- struct list_head d_node;
- struct list_head h_node;
- };
d_node 將handle放到設備相關的鏈表中,也就是放到input_dev->h_list表示的鏈表中;
h_node 將handle放到input_handler相關的鏈表中,也就是放到input_handler->h_list表示的鏈表中;
[子系統初始化函數]
- static int __init input_init(void)
- {
- interr;
-
- input_init_abs_bypass();
-
- err= class_register(&input_class);
- if(err) {
- printk(KERN_ERR"input: unable to register input_dev class\n");
- returnerr;
- }
-
- err= input_proc_init();
- if(err)
- gotofail1;
-
- err= register_chrdev(INPUT_MAJOR, "input", &input_fops);
- if(err) {
- printk(KERN_ERR"input: unable to register char major %d", INPUT_MAJOR);
- gotofail2;
- }
-
- return0;
-
- fail2: input_proc_exit();
- fail1: class_unregister(&input_class);
- return err;
- }
初始化函數將完成三個工作:
1、 class_register(&input_class) 注冊一個Input類;
2、 input_proc_init() 在proc下建立相關的交互文件;
3、 register_chrdev(INPUT_MAJOR, "input", &input_fops) 注冊為字符設備。
[file_operations]
- static const struct file_operationsinput_fops = {
- .owner= THIS_MODULE,
- .open= input_open_file,
- };
你會感到會奇怪為什麼file_operations中只實現了open方法,OK,我們來看下input_open_file的源代碼
- static int input_open_file(struct inode*inode, struct file *file)
- {
- structinput_handler *handler;
- conststruct file_operations *old_fops, *new_fops = NULL;
- interr;
-
- lock_kernel();
- /*No load-on-demand here? */
- handler= input_table[iminor(inode) >> 5];
- if(!handler || !(new_fops = fops_get(handler->fops))) {
- err= -ENODEV;
- gotoout;
- }
-
- /*
- * That's _really_ odd. Usually NULL ->openmeans "nothing special",
- * not "no device". Oh, well...
- */
- if(!new_fops->open) {
- fops_put(new_fops);
- err= -ENODEV;
- gotoout;
- }
- old_fops= file->f_op;
- file->f_op= new_fops;
-
- 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();
- returnerr;
- }
這個open方法完成了兩個工作:
1、 將file->fops重定向到Input_handler->fops,因為input_handler->fops定義了對具體的事件的操作方法;通過file->fops我們就能對不同的設備進行操作;
2、 用input_handler->fops的open方法開打設備。
[inputdevice drivers]
驅動層提供對硬件各寄存器的讀寫訪問和講底層硬件對用戶輸入訪問的響應轉換為標准的輸入事件,再通過核心層提交給事件處理層。
[相關文件]
Driver/input/mouse
Drvier/input/touchscreen
Driver/input/joystick
……
[input_dev]
Struct input_dev 代表一個輸入設備,驅動層主要的結構體,www.linuxidc.com我們所做的工作就是填充此結構體。
[分配一個輸入設備]
struct input_dev*input_allocate_device(void)
[注冊和注銷]
int input_register_device(struct input_dev*dev)
void input_unregister_device(structinput_dev *dev)
[input_register_device分析]
- int input_register_device(struct input_dev*dev)
- {
- staticatomic_t input_no = ATOMIC_INIT(0);
- structinput_handler *handler;
- constchar *path;
- interror;
-
- __set_bit(EV_SYN, dev->evbit); /* support all event */
-
- /*
- * If delay and period are pre-set by thedriver, then autorepeating
- * is handled by the driver itself and we don'tdo 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);
-
- error = device_add(&dev->dev);
- if(error)
- returnerror;
-
- 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);
- returnerror;
- }
-
- list_add_tail(&dev->node,&input_dev_list);
-
- list_for_each_entry(handler,&input_handler_list, node)
- input_attach_handler(dev,handler);
-
- input_wakeup_procfs_readers();
-
- mutex_unlock(&input_mutex);
-
- return0;
- }
Input_register_device函數做了兩個工作:
1、 把input_dev包含的device結構注冊到linux設備模型中;
device_add(&dev->dev);
2、 調用list_add_tail函數將input_dev加入到Input_dev_list鏈表中,input_dev_list鏈表包含了系統中所有的input_dev設備。
List_for_each_entry函數用來遍歷input_handler_list;
Input_attch_handler函數用來匹配input_dev和handler,只有匹配成功了,才會調用connect函數,使input_dev和handler處理器關聯起來;
[inputevent driver]
Input event driver 也就是eventhandler,事件處理層;
為用戶空間的應用程序提供了統一訪問設備的接口和驅動層提交來的事件處理。
[相關文件]
Drivers/input/evdev.c
Drivers/input/joydev.c
Drivers/input/mousedev.c
……
[注冊input_handler]
- int input_register_handler(structinput_handler *handler)
- {
- structinput_dev *dev;
- intretval;
-
- retval= mutex_lock_interruptible(&input_mutex);
- if(retval)
- returnretval;
-
- INIT_LIST_HEAD(&handler->h_list);
-
- if(handler->fops != NULL) {
- if(input_table[handler->minor >> 5]) {
- retval= -EBUSY;
- gotoout;
- }
- input_table[handler->minor>> 5] = handler;
- }
-
- list_add_tail(&handler->node,&input_handler_list);
-
- list_for_each_entry(dev,&input_dev_list, node)
- input_attach_handler(dev,handler);
-
- input_wakeup_procfs_readers();
-
- out:
- mutex_unlock(&input_mutex);
- returnretval;
- }
注冊一個新的input_handler處理器
list_add_tail(&handler->node, &input_handler_list)把handler加入到全局鏈表input_handler_list中;
List_for_each_entry函數用來遍歷input_dev_list;
Input_attch_handler函數用來匹配input_dev和handler,只有匹配成功了,才會調用connect函數,使input_dev和handler處理器關聯起來;
[input_register_handle]
- int input_register_handle(struct input_handle*handle)
- {
- structinput_handler *handler = handle->handler;
- structinput_dev *dev = handle->dev;
- interror;
-
- /*
- * We take dev->mutex here to prevent racewith
- * input_release_device().
- */
- error= mutex_lock_interruptible(&dev->mutex);
- if(error)
- returnerror;
- list_add_tail_rcu(&handle->d_node,&dev->h_list);
- mutex_unlock(&dev->mutex);
-
- list_add_tail(&handle->h_node,&handler->h_list);
-
- if(handler->start)
- handler->start(handle);
-
- return0;
- }
Input_register_handle函數主要工作是將input_dev和Input_handler關聯起來,和設備、驅動、總線的驅動模型非常相似。
[input_dev、handler和handle三者的關系]
Input_register_device、input_register_handler和input_register_handle的關系在下圖有很好的體現:
if you have any questions, please contact me<[email protected]> or leave a comment, we will exchange views, it's good for us, so great!