linux kernel source code input subsystem analysis (linux source code analysis)

linux kernel code analysis

Code follow-up method

  1. Read with questions
  2. Take notes and draw pictures
  3. Drive the contact application, the application is called, the drive is realized

Hierarchical analysis

  • Input handler层:/driver/input/evdev.c
module_init(evdev_init);
module_exit(evdev_exit);

   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);
}
//将当前的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方法
	//实际就是event handler
	error = handler->connect(handler, dev, id);

	//将当前的handler加入到/proc/bus/input/handlers文件中
	input_wakeup_procfs_readers();

Summary:
(1) Registered evdev_handler
(2) Traverse the input dev list, parallel matching, constant matching is successful, automatically call the connect method in the handler------evdev_connect

-Input core layer: /driver/input/input.c

	subsys_initcall(input_init);    //类似于module_init();不过等级较高
	module_exit(input_exit);

	//注册类,类似于class_create();
	err = class_register(&input_class);
	//在/proc目录下创建bus/input/devices handlers
	err = input_proc_init();
	//申请设备号
	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

Summary:
(1) Registered the main device number
(2) Registered the input class

  • Simple_input.c
  	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方法
	//实际就是event handler
	error = handler->connect(handler, dev, id);

Layered graph
Insert picture description here

Analysis: evdev_connect() in evdev.c-belongs to the input handler layer

evdev_connect();
|
//找到一个没有被使用的次设备号,从64开始,65,66
minior = input_get_new_minior(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, EVDEV_MINOR_BASE + minor);//12 64
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&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;

//将儿子关联到父亲(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
cdev_init(&evdev->cdev,&evdev_fops);
evdev->cdev.kobj.parent = &evdev->dev.kobj;
error = cdev_add(&evdev->cdev,evdev->dev.devt,1);



device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt,...)
	|
	dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
		|
		dev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
		device_initialize(dev);
		
		dev->devt = devt;
		dev->class = class;
		dev->parent = parent;
		dev->release = device_create_release;
		dev_set_drvdata(dev,drvdata);
		kobject_set_name_vargs(&dev->kobj,fmt,args);  //设置名字
		device_add(dev);  //注册到系统

**to sum up:

  1. Allocate edev, initialize, record the relationship between input device and handler
  2. Create device node /dev/event0
  3. Register cdev and implement fops**
  4. Relationship:
    Multiple input devices can correspond to one event handler,
    one input device corresponds to one evdev, and one device node /dev/event1,1,2
  5. When all device nodes call open, read, and write file IO, they actually call each interface in fops in cdev, and they are finally called
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,
	.llseek		= no_llseek,
};

The relationship between input_dev and input_handler in the input kernel layer:
Insert picture description here

The code that calls the input subsystem in the application, how the data is passed to the user layer

Application layer and driver layer call graph
Insert picture description here

As described below:
Insert picture description here

Summary:
One device node corresponds to one cdev, one cdev corresponds to one evdev, the relationship between input_dev and input_handler is recorded in evdev, and there is also a buffer queue. The role of evdev is to record everything used.

evdev_open method

What the evdev_open method does


	//通过儿子,找到老母 input device
	bufsize = evdev_compute_buffer_size(evdev->handle.dev);

	//分配一个client对象,描述一个缓冲队列,存放发就是 input_event
	/*sizeof(struct evdev_client) 
		+bufsize * sizeof(struct input_event)
		包含了很多input_event大小
	*/
	client = kzalloc(sizeof(struct evdev_client) +
				bufsize * sizeof(struct input_event),
			 GFP_KERNEL);
	if (!client) {
    
    
		error = -ENOMEM;
		goto err_put_evdev;
	}

	//client 中有一个缓冲区
	client->bufsize = bufsize;
	spin_lock_init(&client->buffer_lock);
	//在client中记录evdev
	client->evdev = evdev;
	//将client加入到evdev中的一个小链表
	evdev_attach_client(evdev, client);
		|
		list_add_tail_rcu(&client->node,&evdev->client_list);

	error = evdev_open_device(evdev);
	if (error)
		goto err_free_client;

	//将client记录到file,方便其他的接口使用
	file->private_data = client;
	nonseekable_open(inode, file);
  1. Allocate a buffer evdev_client for the input device, and the user stores the data reported by the input device layer
  2. evdev_client records to evdev
  3. evdev_client is recorded in file

How to get data in read application

read
Insert picture description here

evdev_read method

	//获取到open中分配的缓冲区对象
	struct evdev_client *client = file->private_data;
	//获取到 evdev
	struct evdev *evdev = client->evdev;
	//表示一个数据包,要给用户
	struct input_event event;

	for(;;)
	{
    
    
		
		//实现非阻塞
		if (client->head == client->tail  &&
		    (file->f_flags & O_NONBLOCK))
			return -EAGAIN;
			
		while (retval + input_event_size() <= count &&
		//1.从缓冲区获取数据,存放在input_event数据包
	       evdev_fetch_next_event(client, &event)) {
    
    
	       |
	       *event = client->buffer[client->tail++];
		// 2.将数据上报给用户
		if (input_event_to_user(buffer + retval, &event))
				|
				copy_to_user(buffer,event,sizeof)
			return -EFAULT;
		// 3.统计上报多少数据
		retval += input_event_size();
		}
		
		//如果当前是阻塞模式
		if(!(file->f_flags & O_NONBLOCK))
		{
    
    
			error = wait_event_interruptible(evdev->wait,
					client->packet_head != client->tail) ||
					!evdev->exist || client->revoked);
		}
	}

to sum up:

  1. If there is no data, it will sleep and wait
  2. If there is data, take the data from the buffer client->buffer[client->tail++] and report it to the user through copy_to_user

Question:
How is the data stored in the buffer
? Who wakes up the waiting queue?
input_report_key(inputdev,pdesc->key_code,0);
input_sync(inputdev); //End of reporting data

input_report_key(inputdev,pdesc->key_code,0);
input_sync(inputdev);   //上报数据结束
	|//input_event(dev,EV_KEY,code,!!value);
	input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);//上报数据
		|
		input_handle_event(dev, type, code, value);
			|
			//如果将数据较交给input handler去处理
			if(disposition & INPUT_PASS_TO_HANDLERS)
			{
    
    
				struct input_value *v;
				//将input device获取到数据暂存一下input value
				v = &dev->vals[dev->num_vals++];
				v->type = type;
				v->code = code;
				v->value = value;
				
				input_pass_values(dev,dev->vals,dev->num_vals)
					|
					//从input device中获取到input handle
					else
					{
    
    
						list_for_each_entry_rcu(handle, &dev->h_list, d_node) 
						if (!handle->open)
							count=input_to_handler(handle,vals,count);
							|
							//如果handler儿子找到handler父亲
							struct input_handler *handler = handler->handler;
							//如果有events函数指针,那就调用
							if(hadnler->events)
								handler->events(handle,vals,count);
								else if(handler->event)  //否则调用event();
								else if(handler->event)
									for(v = vals; v != end; v++)
										handler->event(handle,v->type,v->code,v->value);
							
			}	
			
			

to sum up:

  • If the data is reported, the event() or event() in the handler is
    actually called evdev.c
.event = evdev_event,
.events = evdev_events,
	|
	//拿到evdev ,肯定要拿到缓冲区
	struct evdev *evdev = handle->private;
	struct evdev_client *client;
	
	//获取到缓冲区evdev_client
	else
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_event(client,vals,count,time_mono,time_real);
			|
			//通过client获取到evdev
			struct evdev *evdev = client->evdev;
			
			const struct input_value *v;
			struct input_event event;
			//获取到当前的时间戳
			 event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? mono : real);
			 for(v = vals; v != vals + count; v++)
			 {
    
    
			 	//将input device 上报的数据获取到,并且封装成input event对象
			 	event.type = v->type;
			 	event.code = v->code;
			 	event.value = v->valuel;
			 	//将input event数据存放到缓冲区中
			 	__pass_event(client,&event);
			 	client->buffer[client->head++] = *event;
			 	client->head &= client->bufsize - 1;
			 	//如果调用 input_sync()
			 	if(v->type == EV_SYN && v->code == SYN_REPORT)
			 		wakeup = true;
			 }
			
			//唤醒等待队列
			if(wakeup)
				wake_up_interruptible(&evdev->wait);
			 

Therefore, the answer to the above question can be analyzed from the above:
How is the data stored in the buffer?
Answer: The data is input_report_key(inputdev,pdesc->key_code,0);
who wakes up the waiting queue?
Answer: The waiting queue is awakened by input_sync(inputdev);

to sum up:

  1. input_report_key(inputdev,pdesc->key_code,0)
    * The data generated by the input device is given to the input handler, and the input handler calls event(); stores the data in the buffer client->buffer[client->head++] = event;
  2. input_sync(inputdev);
    End of reporting data, wake up the waiting queue, indicating that the input device has completed reporting data

The overall logic diagram is as follows
Insert picture description here

Guess you like

Origin blog.csdn.net/qq_41782149/article/details/100663756