A la hora de insertar un módulo podemos hacer que este se asocie a un número mayor que no esté en uso. El problema de esto es que no sabemos que número mayor a escogido hasta que el módulo no ha sido insertado, por lo que si queremos crear dispositivos asociados a este numero mayor, tendremos que esperar a insertar primero el módulo, averiguar que número mayor se le ha otorgado y luego crear los dispositivos ligados a este número mayor.
Si no queremos hacer esto, lo único que podemos hacer es buscar un número mayor que no esté reservado para ningún dispositivo y utilizar este para nuestro módulo. En Documentation/devices.txt4.10 hay una lista completa de los números mayores que están asociados a los servicios más comunes.
De ahora en adelante usaremos la asignación dinámica de números mayores, para ello cuando nuestro módulo se da de alta (init_module()) tenemos que decirle que pruebe diferentes números mayores hasta que encuentre uno que esté sin usar.
La función register_chrdev() da de alta un determinado driver en la lista de drivers de caracter, con un determinado número mayor4.11. Si este número mayor ya esta reservado nos devuelve un valor negativo, con esto en mente, nuestro módulo quedaría como sigue:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #define MPCINTA_MAYOR 0 /* por defecto usamos asignación dinámica */ unsigned int mpcinta_mayor=MPCINTA_MAYOR; struct file_operations mpcinta_fops = { NULL, /* lseek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* poll */ NULL, /* ioctl */ NULL, /* mmap */ NULL, /* open */ NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL, /* revalidate */ NULL, /* lock */ }; int init_module(void) { int result; EXPORT_NO_SYMBOLS; /* reserva dinamica del número mayor del módulo */ result=register_chrdev(mpcinta_major, "mpcinta", &mpcinta_fops); if (result < 0) { printk(KERN_WARNING "mpcinta> (init_module) no se pudo obtener el major %d\n",mpcinta_major); return result; } if (mpcinta_major == 0) mpcinta_major = result; /* mensaje y salimos */ printk( KERN_INFO "mpcinta> (int_module) cargado satisfactoriamente\n"); return 0; } void cleanup_module(void) { int result; result = unregister_chrdev(mpcinta_major, "mpcinta"); printk( KERN_INFO "mpcinta> (cleanup_module) descargado sin problemas\n"); }
Además la función register_chrdev() da de alta los punteros a función de las
operaciones (
file_operations
) que sobre los ficheros controlados por este driver se
pueden realizar (leer un caracter, escribir un caracter, abrir el fichero,
cerrarlo, etc.). Por eso es necesario declararla, aunque de momento, como
nuestro módulo no implementa ninguna de estas funciones, estos punteros apuntan
a null
.
Acompañando register_chrdev(), tenemos
unregister_chrdev()
que da de
baja nuestro módulo en la lista de drivers de caracter. Una vez ejecutada esta
función, cualquier acceso a /dev/mi_cinta
nos dará un error, pues ningún
módulo resuelve las file_operations de ese fichero.
Si por alguna razón unregister_chrdev() no es capaz de dar de baja el
módulo (por que le pasemos un mayor diferente por ejemplo), retornará un
-EINVAL
. Si a pesar de eso terminamos la ejecución de cleanup_module(),
el kernel dará un Oops
la próxima vez que accedamos a
/proc/devices
, pues nuestro mdcinta
todavía apunta a la posición
de memoria donde estaba, que ahora ya no nos pertenece.
Esta situación no tiene arreglo, reinicia la máquina y soluciona el problema en tu código del módulo antes de seguir adelante.
Nótese que para poder utilizar estas dos funciones es necesario la inclusión del fichero de cabeceras linux/fs.h donde están declaradas.
Una vez insertado el módulo podemos ver que número mayor se le a asignado,
usando una vez más el sistema de ficheros /proc/
:
$ cat /proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 ttyS 5 cua 7 vcs 10 misc 14 sound 128 ptm 136 pts 254 mpcinta Block devices: 3 ide0 22 ide1
Ahora ya sabemos que número mayor se nos ha asignado, por lo que podemos crear el dispositivo con toda confianza:
$ mknod /dev/mi_cinta c 254 0
Como no podemos asegurar que cada vez que insertemos el módulo nos den el mismo número mayor, vamos a automatizar el proceso de carga del módulo y creación del dispositivo a través del siguiente script:
#!/bin/bash #carga_modulo modulo=mpcinta dispositivo=/dev/mi_cinta permisos=666 # de la bestia #insertamos el módulo y averiguamos su número mayor /sbin/insmod ${modulo}.o mayor=`cat /proc/devices | grep ${modulo} | cut -d' ' -f1` #Borramos los dispositivos mi_cinta? de anteriores pruebas y # creamos los nuevos con el número mayor correcto # y los permisos que queramos rm -f ${dispositivo} mknod ${dispositivo}0 c ${mayor} 0 mknod ${dispositivo}1 c ${mayor} 1 chmod ${permisos} ${dispositivo}?
Nótese que estamos creando varios dispositivos, cada uno con un número menor diferente.