Directorio de artículos
-
- análisis de código del kernel de linux
análisis de código del kernel de linux
Método de seguimiento del código
- Leer con preguntas
- Toma notas y haz dibujos
- 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
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:
- Asignar edev, inicializar, registrar la relación entre el dispositivo de entrada y el controlador
- Crear nodo de dispositivo / dev / event0
- Registre cdev e implemente fops **
- 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 - 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:
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
Como se describe abajo:
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);
- 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
- evdev_client registros a evdev
- evdev_client se registra en el archivo
Cómo obtener datos en la aplicación de lectura
leer
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:
- Si no hay datos, dormirá y esperará
- 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:
- 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; - 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