Linux Input 子系統總結

前言

基於韋東山視頻的簡單總結
內核基於 Linux2.6.22

框架

左邊是基於各種接口的輸入設備,比如 USB 鼠標,藍牙鼠標,2.4G 鼠標等
右邊呢則是封裝了統一設備層,這些亂七八槽的接口鍵盤,統一都由鼠標類處理。

左邊註冊流程:
	1. input_allocate_device(): 分配 input_dev
	2. 設置 input_dev
	3. input_register_device(): 註冊 input_dev

右邊註冊流程:
	1. 創建一個 input_handler
	2. 註冊 input_handler

兩邊連接: connect() 創建如下結構體管理
	左右就通過如下結構管理的:
		handle{
			input device: 左邊
			input handler: 右邊
		}

左邊:通過一個全局鏈表 input_dev_list 管理
右邊:通過一個全局鏈表 input_handler_list 管理

在這裏插入圖片描述

在這裏插入圖片描述

流程彙總

右邊通用驅動註冊流程

參考代碼: Evdev.c (linux-2.6.22.6\drivers\input)

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);

左邊具體驅動註冊流程

	1. input_allocate_device(): 分配 input_dev
	2. 設置 input_dev
	3. input_register_device(): 註冊 input_dev

參考代碼: Gpio_keys.c (drivers\input\keyboard)

///////////////////////////////////////////////////////////////////////////////
// 以一個普通的按鍵驅動爲例
module_init(gpio_keys_init);
static int __init gpio_keys_init//(void)
{
	return platform_driver_register(&gpio_keys_device_driver);
                                        struct platform_driver gpio_keys_device_driver = {
                                        	.probe		= gpio_keys_probe,
                                        	.remove		= __devexit_p(gpio_keys_remove),
                                        	.driver		= {
                                        		.name	= "gpio-keys",
                                        	}
                                        };
}

// 平臺設備匹配後調用 probe 函數 
static int __devinit gpio_keys_probe//(struct platform_device *pdev)
{
	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
	struct input_dev *input;
	int i, error;
    ////////////////////////////////////////////////////////
    // 1. 分配 input_dev
    // 爲新的輸入設備分配內存
	input = input_allocate_device();
                    struct input_dev *input_allocate_device//(void)
                    {
                    	struct input_dev *dev;

                    	dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
                    	if (dev) {
                    		dev->cdev.class = &input_class;
                    		dev->cdev.groups = input_dev_attr_groups;
                    		class_device_initialize(&dev->cdev);
                    		mutex_init(&dev->mutex);
                    		INIT_LIST_HEAD(&dev->h_list);
                    		INIT_LIST_HEAD(&dev->node);

                    		__module_get(THIS_MODULE);
                    	}

                    	return dev;
                    }
	if (!input)
		return -ENOMEM;

	platform_set_drvdata(pdev, input);

    ////////////////////////////////////////////////////////
    // 2. 設置 input_dev
    // 能產生哪類事件
	input->evbit[0] = BIT(EV_KEY);

	input->name = pdev->name;
	input->phys = "gpio-keys/input0";
	input->dev.parent = &pdev->dev;

    // 匹配用的 id 
	input->id.bustype = BUS_HOST;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;

	for (i = 0; i < pdata->nbuttons; i++) {
		struct gpio_keys_button *button = &pdata->buttons[i];
		int irq = gpio_to_irq(button->gpio);
		unsigned int type = button->type ?: EV_KEY;

		set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);

        // 註冊按鍵中斷,當有按鍵發生時調用
		error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM, button->desc ? button->desc : "gpio_keys", pdev);
		if (error) {
			printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n",
				irq, error);
			goto fail;
		}

		input_set_capability(input, type, button->code);
	}
    //////////////////////////////////////////////////////// 
    // 3. 註冊 input_dev
	error = input_register_device(input); 
                int input_register_device//(struct input_dev *dev)
                {
                	static atomic_t input_no = ATOMIC_INIT(0);// static 這樣就會自增了,在 bss 段應該
                	struct input_handler *handler;
                	const char *path;
                	int error;

                	set_bit(EV_SYN, dev->evbit);

                	/*
                	 * 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;
                	}

                	if (!dev->getkeycode)
                		dev->getkeycode = input_default_getkeycode;

                	if (!dev->setkeycode)
                		dev->setkeycode = input_default_setkeycode;

                    // 將 input_dev 放入全局鏈表 input_dev_list 中管理
                	list_add_tail(&dev->node, &input_dev_list);

                	snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
                		 "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

                	if (!dev->cdev.dev)
                		dev->cdev.dev = dev->dev.parent;

                	error = class_device_add(&dev->cdev);
                	if (error)
                		return error;

                	path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
                	printk(KERN_INFO "input: %s as %s\n",
                		dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
                	kfree(path);

                    // 遍歷系統中的 handler,查找是否有對應的 handle,即設備驅動可以處理這個設備
                	list_for_each_entry(handler, &input_handler_list, node)
                		input_attach_handler(dev, 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;

									/* 註冊 input_dev 或 input_handler 時,會兩兩比較左邊的 input_dev 和右邊的 input_handler,
				                    根據 input_handler 的【id_table】判斷這個 input_handler 能否支持這個 input_dev,
				                    如果能支持,則調用 input_handler 的 connect 函數建立"連接"*/


									// 比較 handler->id_table 與 dev,
									id = input_match_device(handler->id_table, dev);								
												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(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);

														return id;
													}

													return NULL;
												}


				                    
									if (!id)
										return -ENODEV;

				                    // 調用 handler 的 connect() 函數,將其掛入 handle.input_dev 鏈表中 
									error = handler->connect(handler, dev, id);
											/*
											connect 怎麼建立連接?
											    1. 分配一個 input_handle 結構體
											    2. 
											        input_handle.dev = input_dev;            // 指向左邊的 input_dev
											        input_handle.handler = input_handler;    // 指向右邊的 input_handler
											    3. 註冊:
											       input_handler->h_list = &input_handle;
											       inpu_dev->h_list      = &input_handle; 
											*/
											// 以 event 驅動爲例:							
											static int evdev_connect//(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
											{
												struct evdev *evdev;
												struct class_device *cdev;
												dev_t devt;
												int minor;
												int error;

												for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
												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);
												init_waitqueue_head(&evdev->wait);

				                                // 設置,在驅動層保存驅動程序與設備的對應關係 handle: handler / input dev
												evdev->exist = 1;
												evdev->minor = minor;
												evdev->handle.dev = dev;
												evdev->handle.name = evdev->name;
												evdev->handle.handler = handler;
												evdev->handle.private = evdev;
												sprintf(evdev->name, "event%d", minor);

												evdev_table[minor] = evdev;

												devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),

												cdev = class_device_create(&input_class, &dev->cdev, devt,
															   dev->cdev.dev, evdev->name);
												if (IS_ERR(cdev)) {
													error = PTR_ERR(cdev);
													goto err_free_evdev;
												}

												/* temporary symlink to keep userspace happy */
												error = sysfs_create_link(&input_class.subsys.kobj,
															  &cdev->kobj, evdev->name);
												if (error)
													goto err_cdev_destroy;

				                                // 只是將其添加到 handle 雙鏈表中,如果有 handler->star ,則會調用。
												error = input_register_handle(&evdev->handle);
				                                            int input_register_handle//(struct input_handle *handle)
				                                            {
				                                            	struct input_handler *handler = handle->handler;

				                                            	list_add_tail(&handle->d_node, &handle->dev->h_list);
				                                            	list_add_tail(&handle->h_node, &handler->h_list);

				                                            	if (handler->start)
				                                            		handler->start(handle);

				                                            	return 0;
				                                            }
												if (error)
													goto err_remove_link;

												return 0;

											 err_remove_link:
												sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
											 err_cdev_destroy:
												class_device_destroy(&input_class, devt);
											 err_free_evdev:
												kfree(evdev);
												evdev_table[minor] = NULL;
												return error;
											}

                	input_wakeup_procfs_readers();

                	return 0;
                }

	if (error) {
		printk(KERN_ERR "Unable to register gpio-keys input device\n");
		goto fail;
	}

	return 0;

 fail:
	for (i = i - 1; i >= 0; i--)
		free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);

	input_free_device(input);

	return error;
}

用戶空間系統調用路由

仔細看 input.c 核心,裏面註冊了一個字符設備,然後只聲明瞭一個 open() 函數

static int __init input_init(void)
	/////////////////////////////////////////////////////////////////////////////////////////
	// static const struct file_operations input_fops = {
	//	.owner = THIS_MODULE,
	//	.open = input_open_file,
	// };
	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

在 open() 函數裏面,將路徑到各通用程序中註冊的 file_operations 結構體

// 打來按鍵,調用的是這裏的打開函數
static int input_open_file(struct inode *inode, struct file *file)
{
    // 通過子設備號獲得相應的設備操作函數
	struct input_handler *handler = input_table[iminor(inode) >> 5];
                            				/**
                            				 * 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;
                            				};

	const struct file_operations *old_fops, *new_fops = NULL;
	int err;

	/* No load-on-demand here? */
	if (!handler || !(new_fops = fops_get(handler->fops)))
		return -ENODEV;

	/*
	 * That's _really_ odd. Usually NULL ->open means "nothing special",
	 * not "no device". Oh, well...
	 */
	if (!new_fops->open) {
		fops_put(new_fops);
		return -ENODEV;
	}
    // 替換 file 結構體的 f_op,並保存,這樣在以後操作就是直接操作驅動的 file_operations 結構了
	old_fops = file->f_op;
	file->f_op = new_fops;

    // 調用驅動程序的 open() 函數
	err = new_fops->open(inode, file);

	if (err) {
		fops_put(file->f_op);
		file->f_op = fops_get(old_fops);
	}
	fops_put(old_fops);
	return err;
}

對應按鍵驅動,相關操作是在 input_register_handler() 中註冊的

// 而這個 open() 是在哪裏設置的呢,根據匹配 id, 知道按鍵設備最終匹配的是 evdev.c 裏的驅動
static int __init evdev_init(void)
{
	return input_register_handler(&evdev_handler);
                            //    static struct input_handler evdev_handler = {
                            //    	.event =	evdev_event,
                            //    	.connect =	evdev_connect,
                            //    	.disconnect =	evdev_disconnect,
                            //    	.fops =		&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,
                            //    	                #ifdef CONFIG_COMPAT
                            //    	                	.compat_ioctl =	evdev_ioctl_compat,
                            //    	                #endif
                            //    	                	.fasync =	evdev_fasync,
                            //    	                	.flush =	evdev_flush
                            //    	                };
                            //    	.minor =	EVDEV_MINOR_BASE,
                            //    	.name =		"evdev",
                            //    	.id_table =	evdev_ids,
							//						static const struct input_device_id evdev_ids[] = {
							//							{ .driver_info = 1 },	/* Matches all devices */
							//							{ },			/* Terminating zero entry */
							//						};
                            //    };
                    int input_register_handler//(struct input_handler *handler)
                    {
                    	struct input_dev *dev;

                        // 初始化鏈表
                    	INIT_LIST_HEAD(&handler->h_list);

                        // 根據次設備號,將操作函數結構體放入數組
                    	if (handler->fops != NULL) {
                    		if (input_table[handler->minor >> 5])
                    			return -EBUSY;

                    		input_table[handler->minor >> 5] = handler;
                    	}

                        // 添加到全局數組中方便管理
                    	list_add_tail(&handler->node, &input_handler_list);

                        // 遍歷 input_dev 輸入設備鏈表,並匹配
                    	list_for_each_entry(dev, &input_dev_list, node)
                    		input_attach_handler(dev, 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;

                    					/* 註冊 input_dev 或 input_handler 時,會兩兩比較左邊的 input_dev 和右邊的 input_handler,
                                        根據 input_handler 的【id_table】判斷這個 input_handler 能否支持這個 input_dev,
                                        如果能支持,則調用 input_handler 的 connect 函數建立"連接"*/


                    					// 比較 handler->id_table 與 dev,
                    					id = input_match_device(handler->id_table, dev);								
                    								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(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);

                    										return id;
                    									}

                    									return NULL;
                    								}


                                        
                    					if (!id)
                    						return -ENODEV;

                                        // 調用 handler 的 connect() 函數, 將 handler 與對應的 input_dev 連接起來,這裏是鏈接在 event_handler 上面
                    					error = handler->connect(handler, dev, id);
                    							/*
                    							connect 怎麼建立連接?
                    							    1. 分配一個 input_handle 結構體
                    							    2. 
                    							        input_handle.dev = input_dev;            // 指向左邊的input_dev
                    							        input_handle.handler = input_handler;    // 指向右邊的input_handler
                    							    3. 註冊:
                    							       input_handler->h_list = &input_handle;
                    							       inpu_dev->h_list      = &input_handle; 
                    							*/
                                                // 對應按鍵驅動,應該鏈接在 event_handler 下面的鏈表中 						
                    							static int evdev_connect//(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
                    							{
                    								struct evdev *evdev;
                    								struct class_device *cdev;
                    								dev_t devt;
                    								int minor;
                    								int error;

                    								for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
                    								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);
                    								init_waitqueue_head(&evdev->wait);

                                                    // 設置,在驅動層保存驅動程序與設備的對應關係 handle: handler / input dev
                    								evdev->exist = 1;
                    								evdev->minor = minor;
                    								evdev->handle.dev = dev;
                    								evdev->handle.name = evdev->name;
                    								evdev->handle.handler = handler;
                    								evdev->handle.private = evdev;
                    								sprintf(evdev->name, "event%d", minor);

                    								evdev_table[minor] = evdev;

                    								devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),

                    								cdev = class_device_create(&input_class, &dev->cdev, devt,
                    											   dev->cdev.dev, evdev->name);
                    								if (IS_ERR(cdev)) {
                    									error = PTR_ERR(cdev);
                    									goto err_free_evdev;
                    								}

                    								/* temporary symlink to keep userspace happy */
                    								error = sysfs_create_link(&input_class.subsys.kobj,
                    											  &cdev->kobj, evdev->name);
                    								if (error)
                    									goto err_cdev_destroy;

                                                    // 只是將其添加到 handle 雙鏈表中,如果有 handler->star ,則會調用。
                    								error = input_register_handle(&evdev->handle);
                                                                int input_register_handle//(struct input_handle *handle)
                                                                {
                                                                	struct input_handler *handler = handle->handler;

                                                                	list_add_tail(&handle->d_node, &handle->dev->h_list);
                                                                	list_add_tail(&handle->h_node, &handler->h_list);

                                                                	if (handler->start)
                                                                		handler->start(handle);

                                                                	return 0;
                                                                }
                    								if (error)
                    									goto err_remove_link;

                    								return 0;

                    							 err_remove_link:
                    								sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
                    							 err_cdev_destroy:
                    								class_device_destroy(&input_class, devt);
                    							 err_free_evdev:
                    								kfree(evdev);
                    								evdev_table[minor] = NULL;
                    								return error;
                    							}

                                        
                    					if (error && error != -ENODEV)
                    						printk(KERN_ERR
                    							"input: failed to attach handler %s to device %s, "
                    							"error: %d\n",
                    							handler->name, kobject_name(&dev->cdev.kobj), error);

                    					return error;
                    				}

                    	input_wakeup_procfs_readers();
                    	return 0;
                    }
}

所以對於按鍵,最終調用的 open() 函數爲:

// 最終調用的 open() 函數爲 
static int evdev_open//(struct inode *inode, struct file *file)
{
	struct evdev_client *client;
	struct evdev *evdev;
	int i = iminor(inode) - EVDEV_MINOR_BASE;
	int error;

	if (i >= EVDEV_MINORS)
		return -ENODEV;

	evdev = evdev_table[i];

	if (!evdev || !evdev->exist)
		return -ENODEV;

	client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
	if (!client)
		return -ENOMEM;

	client->evdev = evdev;
    // evdev.c 用來管理下轄設備驅動的鏈表
	list_add_tail(&client->node, &evdev->client_list);

	if (!evdev->open++ && evdev->exist) {
		error = input_open_device(&evdev->handle);
		if (error) {
			list_del(&client->node);
			kfree(client);
			return error;
		}
	}

	file->private_data = client;
	return 0;
}

經過上面的折騰,讀按鍵時則會調用, 真正的讀按鍵了,open() 之後就調用 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;
	int retval;

	if (count < evdev_event_size())
		return -EINVAL;

	if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
		return -EAGAIN;

    // 等待 event 中斷,如果沒有數據就掛起等待
	retval = wait_event_interruptible(evdev->wait, client->head != client->tail || !evdev->exist);
	if (retval)
		return retval;

	if (!evdev->exist)
		return -ENODEV;

    // 這裏將獲得的 event 複製到用戶空間去
	while (client->head != client->tail && retval + evdev_event_size() <= count) {

		struct input_event *event = (struct input_event *) client->buffer + client->tail;

		if (evdev_event_to_user(buffer + retval, event))
			return -EFAULT;

		client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
		retval += evdev_event_size();
	}

	return retval;
}

異步喚醒上報鍵值

用戶例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>
#include<poll.h>
#include<signal.h>




int Oflags;
int buttons_fd;
int key_value,i=0,count;
struct input_event ev_key;

void my_signal_fun(int signum)
{

	printf("my_signal_fun: start!\n");
	for (;;) {
		printf("for()\n");
		count = read(buttons_fd,&ev_key,sizeof(struct input_event));
		for(i=0; i<(int)count/sizeof(struct input_event); i++)
			if(EV_KEY==ev_key.type)
				printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code,ev_key.value);
		if(EV_SYN==ev_key.type)
		{
			printf("syn event\n\n");
			break;
		}
	}


}

int main(void)
{

	signal(SIGIO,my_signal_fun);
	
	buttons_fd = open("/dev/input/event0", O_RDWR);
	if (buttons_fd < 0) {
		perror("open device buttons");
		exit(1);
	}
	
	fcntl(buttons_fd, F_SETOWN, getpid());  

	Oflags = fcntl(buttons_fd, F_GETFL);  
	
	fcntl(buttons_fd, F_SETFL, Oflags | FASYNC);
	
	while(1)
	{
		sleep(1000);
	}
	
	close(buttons_fd);
	return 0;
}

驅動上報流程

/* 驅動程序上報按鍵事件
// 在設備的中斷服務程序裏,確定事件是什麼,然後調用相應的 input_handler 的 event 處理函數
gpio_keys_isr//(int irq, void * dev_id)
    // 上報事件
    input_event(input, type, button->code, !!state);
    input_sync(input);
*/
// 上報按鍵事件的函數    


// 爲了好看,打亂的順序
//////////////////////////////////////////////////////////
// 第一步    
input_event//(input, type, button->code, !!state); 
///////////////////////////////////////////////////////////////////////////////////////
// 第二步
static inline void input_sync//(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_REPORT, 0);
        	/**
        	 * input_event() - report new input event
        	 * @dev: device that generated the event
        	 * @type: type of the event
        	 * @code: event code
        	 * @value: value of the event
        	 *
        	 * This function should be used by drivers implementing various input devices
        	 * See also input_inject_event()
        	 */
        	void input_event//(struct input_dev *dev, unsigned int type, unsigned int code, int value)
        	{
        		struct input_handle *handle;

        		if (type > EV_MAX || !test_bit(type, dev->evbit))
        			return;

        		add_input_randomness(type, code, value);

        	    // 根據 event 事件類似,做相關處理
        		switch (type) {

        			case EV_SYN:
        				switch (code) {
        					case SYN_CONFIG:
        						if (dev->event)
        							dev->event(dev, type, code, value);
        						break;

        					case SYN_REPORT:
        						if (dev->sync)
        							return;
        						dev->sync = 1;
        						break;
        				}
        				break;

        			case EV_KEY:

        				if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
        					return;

        				if (value == 2)
        					break;

                        // 置位 input_dev.key[] 表示有按鍵發生?
        				change_bit(code, dev->key);

        				if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
        					dev->repeat_key = code;
                            // 這裏的時鐘是 input_register_device() 中設置,對應處理函數爲 input_repeat_key()
        					mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
                                                static void input_repeat_key//(unsigned long data)
                                                {
                                                	struct input_dev *dev = (void *) data;

                                                	if (!test_bit(dev->repeat_key, dev->key))
                                                		return;

                                                	input_event(dev, EV_KEY, dev->repeat_key, 2);
                                                	input_sync(dev);

                                                	if (dev->rep[REP_PERIOD])
                                                		mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
                                                }
        				}

        				break;

        			case EV_SW:

        				if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value)
        					return;

        				change_bit(code, dev->sw);

        				break;

        			case EV_ABS:

        				if (code > ABS_MAX || !test_bit(code, dev->absbit))
        					return;

        				if (dev->absfuzz[code]) {
        					if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
        					    (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
        						return;

        					if ((value > dev->abs[code] - dev->absfuzz[code]) &&
        					    (value < dev->abs[code] + dev->absfuzz[code]))
        						value = (dev->abs[code] * 3 + value) >> 2;

        					if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
        					    (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
        						value = (dev->abs[code] + value) >> 1;
        				}

        				if (dev->abs[code] == value)
        					return;

        				dev->abs[code] = value;
        				break;

        			case EV_REL:

        				if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
        					return;

        				break;

        			case EV_MSC:

        				if (code > MSC_MAX || !test_bit(code, dev->mscbit))
        					return;

        				if (dev->event)
        					dev->event(dev, type, code, value);

        				break;

        			case EV_LED:

        				if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
        					return;

        				change_bit(code, dev->led);

        				if (dev->event)
        					dev->event(dev, type, code, value);

        				break;

        			case EV_SND:

        				if (code > SND_MAX || !test_bit(code, dev->sndbit))
        					return;

        				if (!!test_bit(code, dev->snd) != !!value)
        					change_bit(code, dev->snd);

        				if (dev->event)
        					dev->event(dev, type, code, value);

        				break;

        			case EV_REP:

        				if (code > REP_MAX || value < 0 || dev->rep[code] == value)
        					return;

        				dev->rep[code] = value;
        				if (dev->event)
        					dev->event(dev, type, code, value);

        				break;

        			case EV_FF:

        				if (value < 0)
        					return;

        				if (dev->event)
        					dev->event(dev, type, code, value);
        				break;
        		}

        		if (type != EV_SYN)
        			dev->sync = 0;

        		if (dev->grab)
        			dev->grab->handler->event(dev->grab, type, code, value);
        		else
        	        // 遍歷所有的 handle,如果是打開的,則調用其 event 函數,這個 handle 是在 connect 中註冊的。
        	        // 所有的 event0,event1...eventn 他用使用的都是相同的 【evdev_handler】
        			list_for_each_entry(handle, &dev->h_list, d_node)
        				if (handle->open)
        					handle->handler->event(handle, type, code, value);
        								// 有數據時的喚醒位置, 這個函數在 input_event() 那層被調用,即 input.c 函數中
        								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;

        									if (evdev->grab) {
        										client = evdev->grab;

                                                //////////////////////////////////////////////////////////////////////
                                                // 在這裏構造要上報給應用層的數據
                                                /*  struct input_event {
	                                                	struct timeval time;
	                                                	__u16 type;
	                                                	__u16 code;
	                                                	__s32 value;
	                                                };
                                                */
        										do_gettimeofday(&client->buffer[client->head].time);
        										client->buffer[client->head].type = type;
        										client->buffer[client->head].code = code;
        										client->buffer[client->head].value = value;
        										client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);

        	                                    // 異步通知用戶程序,如果有等待的話,其實就是發送信號給那個進程處理,關於信號處理,看我其他博文了
        										kill_fasync(&client->fasync, SIGIO, POLL_IN);
        									} else
        										list_for_each_entry(client, &evdev->client_list, node) {

        											do_gettimeofday(&client->buffer[client->head].time);
        											client->buffer[client->head].type = type;
        											client->buffer[client->head].code = code;
        											client->buffer[client->head].value = value;
        											client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
        	                    
        											kill_fasync(&client->fasync, SIGIO, POLL_IN);
        										}

        								    // 這裏進行喚醒在讀/寫上睡眠的進程
        									wake_up_interruptible(&evdev->wait);
        								}

        	            
        	}
    
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章