Análisis del subsistema de entrada del código fuente del kernel de Linux (análisis del código fuente de Linux)

análisis de código del kernel de linux

Método de seguimiento del código

  1. Leer con preguntas
  2. Toma notas y haz dibujos
  3. Conducir la aplicación de contacto, se llama a la aplicación, se realiza la conducción

Análisis jerárquico

  • Controlador de entrada 层 : /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();

Resumen:
(1) evdev_handler registrado
(2) Recorre la lista de desarrollo de entrada, coincidencia en paralelo, coincidencia constante es exitosa, llama automáticamente al método de conexión en el controlador ------ evdev_connect

-Capa de núcleo de entrada: /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);

Resumen:
(1) registró el número de dispositivo principal
(2) registró la clase de entrada

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

Gráfico en capas
Inserte la descripción de la imagen aquí

Análisis: evdev_connect () en evdev.c-pertenece a la capa del controlador de entrada

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);  //注册到系统

**para resumir:

  1. Asignar edev, inicializar, registrar la relación entre el dispositivo de entrada y el controlador
  2. Crear nodo de dispositivo / dev / event0
  3. Registre cdev e implemente fops **
  4. Relación:
    varios dispositivos de entrada pueden corresponder a un controlador de eventos,
    un dispositivo de entrada corresponde a un evdev y un nodo de dispositivo / dev / event1,1,2
  5. Cuando todos los nodos de dispositivos llaman a abrir, leer y escribir el archivo IO, en realidad llaman a cada interfaz en fops en cdev, y finalmente se llaman
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,
};

La relación entre input_dev y input_handler en la capa del kernel de entrada:
Inserte la descripción de la imagen aquí

El código que llama al subsistema de entrada en la aplicación, cómo se pasan los datos a la capa de usuario

Gráfico de llamada de capa de aplicación y capa de controlador
Inserte la descripción de la imagen aquí

Como se describe abajo:
Inserte la descripción de la imagen aquí

Resumen:
un nodo de dispositivo corresponde a un cdev, un cdev corresponde a un evdev, la relación entre input_dev y input_handler se registra en evdev y también hay una cola de búfer. El papel de evdev es registrar todo lo utilizado.

método evdev_open

Que hace el método evdev_open


	//通过儿子,找到老母 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. Asigne un búfer evdev_client para el dispositivo de entrada, y el usuario almacena los datos informados por la capa del dispositivo de entrada
  2. evdev_client registros a evdev
  3. evdev_client se registra en el archivo

Cómo obtener datos en la aplicación de lectura

leer
Inserte la descripción de la imagen aquí

método evdev_read

	//获取到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);
		}
	}

para resumir:

  1. Si no hay datos, dormirá y esperará
  2. Si hay datos, tome los datos del búfer cliente-> búfer [cliente-> cola ++] e infórmelo al usuario a través de copy_to_user

Pregunta:
¿Cómo se almacenan los datos en el búfer
? ¿Quién despierta la cola de espera?
Input_report_key (inputdev, pdesc-> key_code, 0);
input_sync (inputdev); // Fin del informe de datos

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

para resumir:

  • Si se informan los datos, el evento () o el evento () en el controlador se
    llama en realidad 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);
			 

Por lo tanto, la respuesta a la pregunta anterior se puede analizar a partir de lo anterior:
¿Cómo se almacenan los datos en el búfer?
Respuesta: Los datos son input_report_key (inputdev, pdesc-> key_code, 0); ¿
quién despierta la cola de espera?
Respuesta: input_sync (inputdev) despierta la cola de espera;

para resumir:

  1. input_report_key (inputdev, pdesc-> key_code, 0)
    * Da los datos generados por el dispositivo de entrada al manejador de entrada, y el manejador de entrada llama a event (); almacena los datos en el búfer cliente-> búfer [cliente-> cabeza ++] = evento;
  2. input_sync (inputdev);
    Fin del informe de datos, despierta la cola de espera, lo que indica que el dispositivo de entrada ha completado el informe de datos

El diagrama lógico general es el siguiente
Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_41782149/article/details/100663756
Recomendado
Clasificación