Linux驱动分析之Input子系统

input子系统框架代码分析:

 *****************************************************************************
 * ls /dev/event* 或者 /dev/input*   用户在应用程序中可以操作的文件
 * ls /sys/class/input/event*  用户可以查看对应的event具体是什么事件
 * ***************************************************************************
 * input handler层:              evdev.c
 * module_init(evdev_init);
 * module_exit(evdev_exit);
 * 
 * static struct input_handler evdev_handler = {
 * 						.event		= evdev_event,
 * 						.events		= evdev_events,
 * 						.connect	= evdev_connect,
 * 						.disconnect	= evdev_disconnect,
 * 						.legacy_minors	= true,
 * 						.minor		= EVDEV_MINOR_BASE,
 * 						.name		= "evdev",
 * 						.id_table	= evdev_ids,
 * 						};
 * 
 * evdev_init(void)------>
 * input_register_handler(&evdev_handler);------>
 * 
 * 		//初始化好——list
 * 		INIT_LIST_HEAD(&handler->h_list);
 * 
 * 		//将当前的handler加入到一个input_handler_list
 * 		list_add_tail(&handler->node,&input_handler_list);
 * 
 * 		//遍历链表input_dev_list
 * 		list_for_each_entry(dev, &input_dev_list, node)
 * 		input_attach_handler(dev, handler);	-------->
 * 				
 * 				//将当前的handler和input dev进行匹配,event handler能够匹配所有的input dev
 * 				id = input_match_device(handler, dev);
 * 
 * 				//匹配成功后调用了handler中的connect方法
 *              //实际就是调用了evdev_handler中的evdev_connect
 * 				error = handler->connect(handler, dev, id);
 * 
 * 		//将当前的handler加入到/proc/bus/input/handlers文件中
 * 		input_wakeup_procfs_readers();
 * 
 * 总结:
 * 		注册了evdev_handler,遍历input_dev_list并进行匹配,且会恒匹配成功
 * 		自动调用handler中的connect方法-----evdev_connect
 * 
 * ***************************************************************************
 * evdev_connect()函数分析:属于input handler层
 * static int evdev_connect(struct input_handler *handler, 
 *                          struct input_dev *dev,const struct input_device_id *id)
 * 
 *  	//找到一个没有被使用的次设备号,从64开始
 * 		minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
 * 
 * 		//实例化一个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);
 * 		evdev->exist = true;
 * 
 * 		dev_no = minor;	
 * 		dev_no -= EVDEV_MINOR_BASE;//减去64
 * 
 * 		//创建设备文件/dev/event0,1,2
 * 		dev_set_name(&evdev->dev, "event%d", dev_no);
 * 		evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
 * 		evdev->dev.class = &input_class;
 * 		evdev->dev.parent = &dev->dev;
 * 		evdev->dev.release = evdev_free;
 * 		device_initialize(&evdev->dev);
 * 		device_add(&evdev->dev);
 * 		//以上部分的代码和device_create完成的工作是一样的
 * 
 * 		//利用handle记录input device和input handler
 * 		evdev->handle.dev = input_get_device(dev);
 * 		evdev->handle.name = dev_name(&evdev->dev);
 * 		evdev->handle.handler = handler;
 * 		evdev->handle.private = evdev;
 * 
 * 		//将handle关联input handler和input dev
 * 		error = input_register_handle(&evdev->handle);
 * 				list_add_tail_rcu(&handle->d_node, &dev->h_list);
 * 				list_add_tail_rcu(&handle->h_node, &handler->h_list);
 * 		//初始化了cdev,完成了fops,为用户提供文件io接口
 * 		cdev_init(&evdev->cdev, &evdev_fops);
 * 		evdev->cdev.kobj.parent = &evdev->dev.kobj;
 * 		error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
 * 总结:
 * 		1.分配edev并初始化,记录input device和handler的关系
 * 		2.创建设备节点/dev/event0
 * 		3.注册cdev,并实现fops
 * 		4.关系:
 * 				多个input device可以对应一个event handler
 * 				一个input device对应一个evdev,对应于一个设备节点/dev/event0,1,2
 * 		5.所有设备节点都调用了open,read,write文件接口时
 * 		  实际是调用了cdev中的fops中各个接口,最终调用了结构体evdev_ops
 * ****************************************************************************
 * input core层:input.c
 * 
 * input_init()
 * subsys_initcall(input_init);
 * module_exit(input_exit);
 * 
 * 			    //注册类,类似于class_create();
 * 				err = class_register(&input_class);
 * 				//在/proc目录创建 /bus/input/devices 
 * 				err = input_proc_init();
 * 				//申请设备号
 * 				err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
 * 			     INPUT_MAX_CHAR_DEVICES, "input");	
 * 
 * 总结:
 * 		注册了主设备号,注册了input_class类
 * ***************************************************************************
 * input device层:自己写的代码simple_input.c
 * 
 * ret=input_register_device(inputdev);
 * 		//将当前的input dev加入到一个input_dev_list
 * 		list_add_tail(&dev->node,&input_dev_list);
 * 
 *  	//遍历链表input_handler_list,进行匹配
 * 		list_for_each_entry(handler, &input_handler_list, node)
 * 		input_attach_handler(dev, handler);
 * 
 * 		//匹配成功后调用了handler中的connect方法,实际就是调用了evdev_handler中的evdev_connect
 * 		error = handler->connect(handler, dev, id);
 * **************************************************************************
 * 应用程序调用open函数时候实际调用的是evedv_open
 * 
 * evedv_open函数分析:
 * static int evdev_open(struct inode *inode, struct file *file)
 * {
 * 	 //实际的evdev是谁,就是evdev_connect注册的那个	
 * 	 struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
 * 	 //通过儿子handle找到母亲input device
 * 	 unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
 *   //size包含了很多input_event
 *   unsigned int size = sizeof(struct evdev_client) +
 * 	 					bufsize * sizeof(struct input_event);
 * 	 
 * 	 struct evdev_client *client;
 * 	 	int error;
 * 	 //分派一个client对象,描述一个缓冲队列,存放的就是input_event
 * 	 client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
 * 	 if (!client)
 * 	 	client = vzalloc(size);
 * 	 if (!client)
 * 	 	return -ENOMEM;
 * 
 *   //client中有一个缓冲区
 * 	 client->bufsize = bufsize;
 * 	 spin_lock_init(&client->buffer_lock);
 *   //在client中记录evdev
 *   client->evdev = evdev;
 * 	 //将client加入到evdev中的一个小链表
 * 	 evdev_attach_client(evdev, client);
 * 
 *   error = evdev_open_device(evdev);
 *   if (error)
 *        goto err_free_client;
 * 
 *   //将client记录到file,方便其他接口使用
 * 	 file->private_data = client;
 *   nonseekable_open(inode, file);
 * 
 *   return 0;
 * 
 *  err_free_client:
 * 			evdev_detach_client(evdev, client);
 * 			kfree(client);
 * 			return error;
 * }
 * 总结:
 *    1.为输入设备分配一个输入缓冲区evdev_client,用户存放input device层上报的数据
 * 	  2.evdev_client记录到evdev中
 * 	  3.evdev_client记录到file中,方便其他接口使用
 *    一个设备节点对应一个cdev,一个cdev对应一个evdev,在evdev中记录了
 *    input_handler和input_dev的关系,并设置了一个缓冲队列,
 *    一个input_dev对应一个input_handler,而一个input_handler可以对应多个input_handler
 * *******************************************************************************
 *  应用程序调用read函数时候实际调用的是evedv_read
 * 
 * evdev_read函数分析:
 * static ssize_t evdev_read(struct file *file, char __user *buffer,
 * 			  size_t count, loff_t *ppos)
 * {
 *  //获取到open中分配的缓冲区对象
 * 	struct evdev_client *client = file->private_data;
 *  //获取到evdev	
 *  struct evdev *evdev = client->evdev;
 *  //表示一个数据包,要给用户
 * 	struct input_event event;
 * 
 * 	size_t read = 0;
 * 	int error;
 * 
 * 	if (count != 0 && count < input_event_size())
 * 		return -EINVAL;
 * 
 * 	for (;;) {
 * 		if (!evdev->exist || client->revoked)
 * 			return -ENODEV;
 * 		//实现非阻塞
 * 		if (client->packet_head == client->tail &&
 * 		    (file->f_flags & O_NONBLOCK))
 * 			return -EAGAIN;
 *      //如果用户需要的数据量为0,则结束
 * 		if (count == 0)
 * 			break;
 * 		//从open函数的缓冲区client->bufsize获取数据,存放在input_event数据包中
 * 		while (read + input_event_size() <= count &&
 * 		       evdev_fetch_next_event(client, &event)) {
 *          //将数据上报用户
 * 			if (input_event_to_user(buffer + read, &event))
 * 				return -EFAULT;
 * 			//统计上报多少数据
 * 			read += input_event_size();
 * 		}
 * 
 * 		if (read)
 * 			break;
 *      //如果当前是阻塞模式
 * 		if (!(file->f_flags & O_NONBLOCK)) {
 *          //等待(休眠),有数据时候唤醒
 * 			error = wait_event_interruptible(evdev->wait,
 * 					client->packet_head != client->tail ||
 *  					!evdev->exist || client->revoked);
 * 			if (error)
 * 				return error;
 * 		}
 * 	}
 * 
 * 	return read;
 * }
 * 总结:
 * 		1.如果没有数据,就会休眠等待
 *      2.如果有数据,就从缓冲区client->bufsize[client->tail++]获取数据
 *        通过input_event_to_user调用copy_to_user上报给用户
 * 
 *      3.input_event函数将数据存放到缓冲区,且将队列唤醒
 * 		4.如果将数据上报最终将调用的是handler中的events或者event
 * *************************************************************************************
 * input_event函数分析:
 * void input_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);
 * 		input_handle_event(dev, type, code, value);
 * 		spin_unlock_irqrestore(&dev->event_lock, flags);
 * 	 }
 * }
 * 总结:
 * 			input_report_key(inputdev,pdesc->key_code,0);
 * 			//将输入设备产生的数据交给input handler,调用events,将数据存放在
 *  		//缓冲区client->buffer[client->head++]= *event;
 * 			input_sync(inputdev);
 *  		//数据上报结束
 * 			//唤醒等待队列,表示输入设备上报数据完毕
 * **********************************************************************************/

input子系统框图分析:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值