LED parpadeante desde el módulo del kernel de Linux

Hola a todos En este artículo quiero compartir la experiencia de crear un simple módulo de kernel de Linux. Este artículo será útil para aquellos que deseen entender cómo escribir módulos de kernel, pero no saben por dónde empezar.

Hace tiempo que quería entender este tema, pero hasta hace poco no sabía cómo abordarlo. Quería que el módulo fuera lo suficientemente simple, pero más complicado que el mensaje "¡Hola, mundo!" salida en un archivo de registro. Al final, decidí intentar flashear un LED. Un objetivo adicional era deducir el parámetro responsable de la frecuencia de parpadeo en sysfs.

Para el experimento, utilicé la placa Orange Pi One con Ubuntu Linux a bordo (versión del kernel 3.4.113). Para construir el módulo del núcleo, necesita el compilador gcc, la utilidad make y los archivos de encabezado del núcleo. Para instalar los archivos de encabezado, ejecute el siguiente comando:

sudo apt-get install linux-headers-$(uname -r) 

A continuación, analizaré en mi opinión las partes más interesantes del módulo. Para ahorrar espacio, no daré todo el código aquí, está disponible en github junto con el archivo make.

En el módulo, utilicé los archivos de encabezado:

 #include <linux/kernel.h> #include <linux/module.h> #include <linux/gpio.h> #include <linux/hrtimer.h> #include <linux/moduleparam.h> 

kernel.h y module.h siempre deben estar habilitados al escribir el módulo del kernel, gpio.h es realmente responsable de trabajar con GPIO, hrtimer.h (temporizador de alta resolución) es el archivo de encabezado del temporizador, se necesita moduleparam.h para mostrar los parámetros en sysfs.

Para no hacer brillar sus variables y funciones en el núcleo del sistema, todas ellas deben describirse como estáticas. Por si acaso, noto que el núcleo está escrito en C y estático, a diferencia de C ++, significa que el objeto está disponible solo dentro del archivo ejecutable.

El punto de entrada es:

 static int blink_module_init(void) 

Aquí inicializo las variables que usaré en el futuro, incluyendo:

 gpio_timer_interval = ktime_set(gpio_blink_interval_s, 0); 

ktime_set inicializa el tipo de datos ktime_t dándole el número deseado de segundos (gpio_blink_interval_s) y nanosegundos (0). En el futuro, esta variable será utilizada por el temporizador.

La siguiente es una solicitud para usar GPIO:

 err = gpio_request(BLINK_PIN_NR, "blink_led"); 

Esta función devuelve 0 si tiene éxito, por lo que en el futuro verifico que haya regresado. A continuación, el pin seleccionado debe establecerse en la salida de señal y especificar el valor predeterminado.

 err = gpio_direction_output(BLINK_PIN_NR, GPIOF_INIT_LOW); 

Si no hubo errores, inicialice e inicie el temporizador

 hrtimer_init(&gpio_blink_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); gpio_blink_timer.function = &gpio_blink_timer_callback; hrtimer_start(&gpio_blink_timer, gpio_timer_interval, HRTIMER_MODE_REL); 

La función de devolución de llamada del temporizador será gpio_blink_timer_callback. En esta función, cambio el valor del pin al opuesto

 gpio_value ^= 0x01; gpio_set_value(BLINK_PIN_NR, gpio_value); 

Pregunto cuándo debería funcionar el temporizador la próxima vez

 hrtimer_forward_now(&gpio_blink_timer, gpio_timer_interval); 

y devolver HRTIMER_RESTART.

Ahora, veamos cómo mostrar algún tipo de variable en sysfs. Para esto uso una macro

 module_param_cb(gpio_blink_interval_s, &kp_ops, &gpio_blink_interval_s, 0660); 

El primer parámetro de esta macro es el nombre del archivo en sysfs. El segundo es una estructura de datos que contiene funciones de devolución de llamada. El tercer parámetro es un puntero a la variable real y los permisos de cuarto archivo en sysfs.

Las funciones de kp_ops se invocan cuando el usuario cambia los valores del archivo sysfs o lee su valor. Así es como los inicialicé:

 static const struct kernel_param_ops kp_ops = { .set = &set_blink_interval, .get = &get_blink_interval }; 

En este caso, set es de interés, ya que establece un nuevo valor, gpio_timer_interval.

 gpio_timer_interval = ktime_set(gpio_blink_interval_s, 0); 

En el punto de salida, borro todos los recursos utilizados

 static void blink_module_exit(void) { hrtimer_cancel(&gpio_blink_timer); gpio_set_value(BLINK_PIN_NR, 0); gpio_free(BLINK_PIN_NR); printk(KERN_ALERT "Blink module unloaded\n"); } 

Los puntos de entrada y salida deben indicarse en las macros correspondientes.

 module_init(blink_module_init); module_exit(blink_module_exit); 

Parece describir todos los puntos importantes. Si los lectores tienen alguna pregunta, estaré encantado de responderla en los comentarios.

Source: https://habr.com/ru/post/es411721/


All Articles