[Beijing Xunwei] i.MX6ULL Terminator Linux INPUT subsistema experimento Subsistema de entrada

1 Introducción al subsistema de entrada

El subsistema de entrada es el subsistema que gestiona la entrada y, al igual que los subsistemas pinctrl y gpio, es un marco creado por el kernel de Linux para un determinado tipo de dispositivo. El subsistema de entrada procesa las transacciones de entrada.Cualquier controlador de dispositivo de entrada puede registrarse en el kernel a través de la interfaz proporcionada por el subsistema de entrada de entrada e interactuar con el espacio de usuario utilizando las funciones proporcionadas por el subsistema. Los dispositivos de entrada generalmente incluyen teclados, ratones, pantallas táctiles, etc., que aparecen como dispositivos de entrada en el kernel.
El subsistema de entrada tiene una estructura jerárquica, dividida en tres capas en total: capa de controlador de hardware, capa de núcleo del subsistema y capa de procesamiento de eventos.
(1) La capa del controlador de hardware es responsable de operar dispositivos de hardware específicos. El código de esta capa es para controladores específicos y debe ser escrito por el autor del controlador.
(2) La capa central del subsistema es el enlace y puente entre las otras dos capas, proporcionando la interfaz de la capa impulsora hacia abajo y la interfaz de la capa de procesamiento de eventos hacia arriba.
(3) La capa de procesamiento de eventos es responsable de tratar con el programa de usuario y de informar eventos desde la capa del controlador de hardware al programa de usuario.
Inserte la descripción de la imagen aquí

Figura 1.1

La unidad básica de comunicación entre las distintas capas es el evento. Las acciones de cualquier dispositivo de entrada se pueden abstraer en un evento, como presionar el teclado, presionar la pantalla táctil y mover el mouse. Los eventos tienen tres atributos: tipo, código y valor. Todos los eventos admitidos por el subsistema de entrada se definen en input.h, incluidos todos los tipos y códigos admitidos por el tipo. La dirección de transmisión de eventos es la capa de controlador de hardware -> núcleo del subsistema -> capa de procesamiento de eventos -> espacio de usuario.

2 Proceso de escritura del controlador de entrada

Primero, echemos un vistazo a las funciones que se implementan en la capa del núcleo de entrada. El archivo de la capa del núcleo de entrada es input.c, la ruta: drivers / input / input.c, y algunos de los contenidos son los siguientes:

1767 struct class input_class = {
    
     
1768       .name = "input",
1769       .devnode = input_devnode, 
1770 }; 
...... 
2414 static int __init input_init(void) 
2415 {
    
     
2416       int err;
2417 
2418       err = class_register(&input_class); 
2419       if (err) {
    
     
2420               pr_err("unable to register input_dev class\n"); 
2421               return err; 
2422       } 
2423 
2424       err = input_proc_init(); 
2425       if (err) 
2426           goto fail1; 
2427 
2428       err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0), 
2429                   INPUT_MAX_CHAR_DEVICES, "input"); 
2430       if (err) {
    
     
2431           pr_err("unable to register char major %d", INPUT_MAJOR); 
2432           goto fail2; 
2433       } 
2434 
2435   return 0; 
2436 
2437   fail2: input_proc_exit(); 
2438   fail1: class_unregister(&input_class); 
2439       return err; 
2440 } 

En la línea 2418, se registra una clase de entrada. Después de que se inicia el sistema, se generará un subdirectorio de la clase de entrada en el directorio / sys / class, como se muestra en la Figura 2.1:
Inserte la descripción de la imagen aquí

Figura 2.1

En las líneas 2428 y 2489, se registra un dispositivo de caracteres, por lo que el subsistema de entrada es esencialmente un controlador de dispositivo de caracteres. El número de dispositivo principal es INPUT_MAJOR, e INPUT_MAJOR se define en el archivo include / uapi / linux / major.h, que está definido de la siguiente manera:
#define INPUT_MAJOR 13
por lo que el sub de entrada El número de dispositivo principal de todos los dispositivos en el sistema es 13, y no es necesario registrar dispositivos de caracteres cuando se usa el subsistema de entrada para procesar dispositivos de entrada. Solo necesitamos registrar un dispositivo de entrada con el sistema.
1. Estructura Input_dev La estructura
input_dev es la estructura de dispositivo básica de los dispositivos de entrada. Cada controlador de entrada debe ser asignado e inicializado tal estructura. La estructura se define en el archivo include / linux / input.h y se define de la siguiente manera:

121 struct input_dev {
    
     
122        const char *name; 
123        const char *phys; 
124        const char *uniq; 
125        struct input_id id; 
126 
127        unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; 
128 
129        unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 事件类型的位图 */ 
130        unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; /* 按键值的位图 */ 
131        unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; /* 相对坐标的位图 */ 
132        unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; /* 绝对坐标的位图 */ 
133        unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; /* 杂项事件的位图 */ 
134        unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; /*LED 相关的位图 */ 
135        unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];/* sound 有关的位图 */ 
136        unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 压力反馈的位图 */ 
137        unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /*开关状态的位图 */ 
...... 
189        bool devres_managed; 
190 };

En la línea 129, evbit representa el tipo de evento de entrada. Los tipos de eventos opcionales se definen en el archivo include / uapi / linux / input.h. Los tipos de eventos son los siguientes:

#define 	EV_SYN 			0x00 	/* 同步事件 */ 
#define 	EV_KEY 			0x01 	/* 按键事件 */ 
#define 	EV_REL 			0x02		 /* 相对坐标事件 */ 
#define 	EV_ABS 			0x03 	/* 绝对坐标事件 */ 
#define 	EV_MSC 			0x04 	/* 杂项(其他)事件 */ 
#define 	EV_SW	 		0x05 	/* 开关事件 */ 
#define 	EV_LED 			0x11 	/* LED */ 
#define 	EV_SND 			0x12 	/* sound(声音) */ 
#define 	EV_REP 			0x14 	/* 重复事件 */ 
#define 	EV_FF 			0x15 	/* 压力事件 */ 
#define 	EV_PWR 			0x16 	/* 电源事件 */ 
#define 	EV_FF_STATUS 	0x17 	/* 压力状态事件 */

Elija diferentes tipos de eventos de acuerdo con los diferentes dispositivos utilizados En los experimentos de este capítulo, utilizaremos dispositivos clave, por lo que debemos seleccionar el tipo de evento EV_KEY.

Mire evbit, keybit y otras variables miembro en las líneas 129 a 137 en la estructura input_dev, que son los valores de los diferentes tipos de eventos correspondientes. Por ejemplo, el miembro de bit de clave correspondiente al evento de clave. El bit de clave es el mapa de bits utilizado por el evento de clave. El kernel de Linux define muchos valores de clave. Estos valores de clave se definen en el archivo include / uapi / linux / input.h . Los valores clave son los siguientes:

215 #define 	KEY_RESERVED 	0 
216 #define 	KEY_ESC 			1 
217 #define 	KEY_1	 		2 
218 #define 	KEY_2 			3 
219 #define 	KEY_3 			4 
220 #define 	KEY_4 			5 
221 #define 	KEY_5 			6 
222 #define 	KEY_6 			7 
223 #define 	KEY_7 			8 
224 #define 	KEY_8 			9 
225 #define 	KEY_9 			10 
226 #define 	KEY_0 			11 
...... 
794 #define 	BTN_TRIGGER_HAPPY39 	0x2e6 
795 #define 	BTN_TRIGGER_HAPPY40 	0x2e7

Cuando escribimos el controlador del dispositivo de entrada, primero necesitamos crear una variable de estructura input_dev, pero no es necesario crearla manualmente.El subsistema de entrada proporciona las siguientes dos funciones para crear y anular el registro de la variable de estructura input_dev.

struct input_dev *input_allocate_device(void)	//申请input_dev结构体
void input_free_device(struct input_dev *dev) 	//注销input_dev结构体
input_allocate_device函数不需要参数,直接返回申请到的input_dev结构体。
input_free_device函数用来释放掉前面申请到的input_dev结构体。

Después de solicitar la estructura input_dev, debe inicializar y especificar el tipo de evento y el valor del evento de acuerdo con su propio dispositivo. Por ejemplo, el tipo de evento de un dispositivo clave es evbit y el valor del evento es keybit.
Después de inicializar la estructura input_dev, use la función input_register_device para registrar el dispositivo input_dev con el kernel de Linux. El prototipo de la función es el siguiente:
int input_register_device(struct input_dev *dev)
dev: input_dev a registrar.
Valor de retorno: 0, registro input_dev exitoso; valor negativo, registro input_dev fallido.
De manera similar, al anular el registro del controlador de entrada, debe utilizar la función input_unregister_device para anular el registro del input_dev registrado anteriormente. El prototipo de la función input_unregister_device es el siguiente:
void input_unregister_device(struct input_dev *dev)
Para resumir el contenido anterior, el proceso de registro input_dev se divide en los siguientes pasos:
① Primer uso la función input_allocate_device para solicitar un input_dev.
② Inicialice el tipo de evento y el valor de evento de input_dev.
③ La función input_register_device () es una función proporcionada por el núcleo de entrada. Esta función registra la estructura input_dev en el núcleo del subsistema de entrada.
④ Si la función input_register_device () no se registra, debe llamar a la función input_free_device () para liberar el espacio asignado. Si la función se registra con éxito, la función input_unregister_device () debe llamarse en la función de desinstalación para anular el registro de la estructura del dispositivo de entrada.
El código de ejemplo del proceso de registro input_dev es el siguiente:

1 struct input_dev *inputdev; /* input 结构体变量 */ 
2 
3 /* 驱动入口函数 */ 
4 static int __init xxx_init(void) 
5 {
    
     
6  ...... 
7    inputdev = input_allocate_device(); /* 申请 input_dev */ 
8  inputdev->name = "test_inputdev"; /* 设置 input_dev 名字 */ 
9 
10     /*********第一种设置事件和事件值的方法***********/ 
11     __set_bit(EV_KEY, inputdev->evbit); /* 设置产生按键事件 */ 
12     __set_bit(EV_REP, inputdev->repbit); /* 重复事件 */ 
13     __set_bit(KEY_0, inputdev->keybit); /*设置产生哪些按键值 */ 
14     /************************************************/ 
15 
16     /*********第二种设置事件和事件值的方法***********/ 
17     keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); 
18     keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0); 
19     /************************************************/ 
20 
21     /*********第三种设置事件和事件值的方法***********/ 
22     keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); 
23     input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0); 
24     /************************************************/ 
25 
26     /* 注册 input_dev */ 
27     input_register_device(inputdev); 
28     ...... 
29     return 0; 
30 } 
31 
32 /* 驱动出口函数 */ 
33 static void __exit xxx_exit(void)
34 {
    
     
35     input_unregister_device(inputdev); /* 注销 input_dev */ 
36     input_free_device(inputdev); /* 删除 input_dev */ 
37 }

Las líneas 10 ~ 23 son todas de inicialización de eventos de dispositivos de entrada y valores clave Se utilizan tres métodos para configurar eventos y valores clave.
2. Se informa el evento de entrada
. Después de que la estructura input_dev se aplica y se registra en el controlador del dispositivo de entrada, el subsistema de entrada no se puede usar normalmente porque el dispositivo de entrada ingresa alguna información, pero el kernel de Linux aún no sabe qué significa la información de entrada y lo que significa Función, por lo que necesitamos conducir para obtener valores de entrada específicos, o eventos de entrada, y luego reportar los eventos de entrada al kernel de Linux. Por ejemplo, para un dispositivo de botón, necesitamos informar el valor del botón al kernel de Linux después de que se genera el botón, y el kernel de Linux ejecutará la función correspondiente después de obtener el valor del botón específico. Los diferentes eventos reportan diferentes funciones. Echemos un vistazo a las funciones de API más utilizadas.
Función input_event: se utiliza para informar el evento especificado y el valor correspondiente. El prototipo de función es el siguiente:

void input_event(struct input_dev *dev, 
unsigned int type, 
unsigned int code, 
int value) 

Los significados de los parámetros de función y los valores de retorno son los siguientes:
dev: input_dev para ser informado.
type: el tipo de evento informado, como EV_KEY.
código: código de evento, que es el valor clave que registramos, como KEY_0, KEY_1, etc.
valor: valor de evento, por ejemplo, 1 significa que se presiona el botón, 0 significa que se suelta el botón.
Valor de retorno: Ninguno.
La función input_event se puede utilizar para informar sobre todos los tipos de eventos y valores de eventos.

función input_report_key: reporta eventos clave. El contenido de la función específica es el siguiente:

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) 
{
    
     
input_event(dev, EV_KEY, code, !!value); 
} 

Se puede ver que la esencia de la función input_report_key es la función input_event. Por supuesto, no hay problema con qué función se usa. Es más apropiado usar la función correspondiente para diferentes dispositivos.
También hay algunas funciones de informes correspondientes a otros eventos:

void input_report_rel(struct input_dev *dev, unsigned int code, int value) 
void input_report_abs(struct input_dev *dev, unsigned int code, int value)
void input_report_ff_status(struct input_dev *dev, unsigned int code, int value) 
void input_report_switch(struct input_dev *dev, unsigned int code, int value) 
void input_mt_sync(struct input_dev *dev)

Función input_sync: se utiliza para decirle al subsistema de entrada del kernel de Linux que informe el final. La función input_sync esencialmente informa un evento de sincronización. El prototipo de la función es el siguiente:
void input_sync(struct input_dev *dev)
Se enumeran varias funciones. Tome el dispositivo clave como ejemplo, veamos cómo usarlo:

1 /* 用于按键消抖的定时器服务函数 */ 
2 void timer_function(unsigned long arg) 
3 {
    
     
4      unsigned char value;
5 
6      value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */ 
7      if(value == 0){
    
     /* 按下按键 */ 
8           /* 上报按键值 */ 
9          input_report_key(inputdev, KEY_0, 1); /* 最后一个参数 1,按下 */ 
10             input_sync(inputdev); /* 同步事件 */ 
11         } else {
    
     /* 按键松开 */ 
12             input_report_key(inputdev, KEY_0, 0); /* 最后一个参数 0,松开 */ 
13             input_sync(inputdev); /* 同步事件 */ 
14         } 
15 }

Obtenga el valor de la clave y luego determine si la tecla está presionada e informe el valor de la clave a través de la función input_report_key, y la función input_sync indica el final del informe.

3 estructura input_event

El kernel de Linux usa la estructura input_event para representar todos los eventos de entrada. La estructura input_envent se define en el archivo include / uapi / linux / input.h. El contenido de la estructura es el siguiente:

24 struct input_event {
    
     
25         struct timeval time; 
26         __u16 type; 
27       __u16 code; 
28         __s32 value; 
29 };

Echemos un vistazo a cada variable miembro en la estructura input_event a su vez:
time: time, que es el momento en que ocurre este evento, es el tipo de estructura timeval, y la estructura timeval se define de la siguiente manera:

1 typedef long __kernel_long_t; 
2 typedef __kernel_long_t __kernel_time_t; 
3 typedef __kernel_long_t __kernel_suseconds_t; 
4
5 struct timeval {
    
     
6  __kernel_time_t tv_sec; /* 秒 */ 
7  __kernel_suseconds_t tv_usec; /* 微秒 */ 
8 };

Las dos variables miembro tv_sec y tv_usec son de tipo largo, que es de 32 bits, esto debe recordarse y lo usaremos cuando analicemos los datos del informe de eventos más adelante.
type: tipo de evento, como EV_KEY, que indica que este evento es un evento clave y esta variable miembro es de 16 bits.
código: código de evento. Por ejemplo, en el evento EV_KEY, el código representa el código de clave específico, como KEY_0, KEY_1, etc. Esta variable miembro es de 16 bits.
valor: valor. Por ejemplo, en el evento EV_KEY, valor es el valor de la tecla, que indica si la tecla ha sido presionada, si es 1, significa que la tecla está presionada, si es 0, significa que la tecla no está presionada o se suelta la tecla.
La estructura input_envent es muy importante, porque todos los dispositivos de entrada finalmente se presentan al usuario de acuerdo con la estructura input_event, y las aplicaciones de usuario pueden obtener eventos de entrada específicos o valores relacionados, como valores clave, a través de input_event.

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/BeiJingXunWei/article/details/112171281
Recomendado
Clasificación