Saltar al contenido

Char devices: registrar un dispositivo

El primer driver de verdad: crear un dispositivo de carácter en /dev con el que los programas hablan. Números major/minor, struct cdev y file_operations.

Un char device es la forma clásica de que un driver exponga funcionalidad: aparece como un archivo en /dev, y los programas lo abren, leen y escriben como cualquier archivo. Crear uno es tu primer driver de verdad, y la puerta a entender el device model del kernel.

  • Qué es un char device y su lugar en /dev.
  • Números major y minor.
  • Registrar un dispositivo con cdev.
  • La struct file_operations.

El char device y /dev

Un dispositivo de carácter transfiere datos como un flujo de bytes (un teclado, un puerto serie, /dev/null). Aparece en /dev con un número major (qué driver lo maneja) y minor (qué instancia concreta):

Shell
ls -l /dev/null# crw-rw-rw- 1 root root 1, 3 ...   → 'c' = char, major 1, minor 3

Cuando un programa abre /dev/tudispositivo, el kernel usa el major para encontrar tu driver y le pasa las operaciones.

Registrar el dispositivo

El flujo moderno: pedir un número de dispositivo, inicializar un cdev con tus operaciones, y añadirlo:

C
#include <linux/cdev.h>#include <linux/fs.h>static dev_t numero;              /* major+minor empaquetados */static struct cdev mi_cdev;static int __init driver_init(void){	int ret = alloc_chrdev_region(&numero, 0, 1, "midispositivo");	if (ret)		return ret;	cdev_init(&mi_cdev, &mis_fops);      /* conecta las operaciones */	ret = cdev_add(&mi_cdev, numero, 1);	if (ret)		goto liberar_region;             /* patrón goto, nivel 11 */	return 0;liberar_region:	unregister_chrdev_region(numero, 1);	return ret;}

En exit deshaces esto en orden inverso: cdev_del y unregister_chrdev_region (la simetría del nivel 7.1).

file_operations: la tabla de funciones

El corazón de un char device es una struct file_operations: una tabla de punteros a función (el patrón del nivel 10 de C23) que dice qué hacer ante cada operación de archivo:

C
static struct file_operations mis_fops = {	.owner   = THIS_MODULE,	.open    = mi_open,	.release = mi_release,     /* close */	.read    = mi_read,	.write   = mi_write,	.unlocked_ioctl = mi_ioctl,};

file_operations: polimorfismo del kernel en acción

Detente en esto, porque es el corazón del diseño de Unix. Cuando un programa hace read() sobre tu /dev/midispositivo, el kernel no sabe ni le importa qué es tu dispositivo: mira su file_operations, encuentra tu puntero .read, y lo llama. Tu driver, un teclado, un socket, un archivo normal — todos exponen la misma interfaz (file_operations), y el VFS (nivel 14) los trata idénticamente. Esto es exactamente el polimorfismo de la orientación a objetos, implementado en C con una tabla de punteros a función (nivel 10 de C23): la struct es la “vtable”, tu driver es la “clase”, y read()/write() son “métodos virtuales”. Por eso “todo es un archivo” funciona: cualquier cosa que rellene una file_operations se comporta como un archivo. Cuando rellenas esa struct, estás enchufando tu código en la maquinaria universal de E/S del kernel. Es una de las abstracciones más poderosas jamás diseñadas, y ahora la usas tú.

Crea tu dispositivo

  1. Escribe un módulo que registre un char device con alloc_chrdev_region y cdev_add.
  2. Define una file_operations con .owner = THIS_MODULE (las funciones, en el nivel 13.2).
  3. Añade la limpieza simétrica en exit.
  4. Tras cargarlo, crea el nodo con mknod (o usa una clase de device para /dev automático) y compruébalo.