next up previous contents index
Next: Operaciones sobre el dispositivo, Up: Un módulo algo más Previous: Un módulo algo más   Índice General   Índice de Materias


Asignación dinámica de números mayores.

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.


next up previous contents index
Next: Operaciones sobre el dispositivo, Up: Un módulo algo más Previous: Un módulo algo más   Índice General   Índice de Materias
Alberto Cortés 2001-03-26