next up previous contents index
Next: Lectura y escritura de Up: Abriendo dispositivos. Previous: Abriendo dispositivos.   Índice General   Índice de Materias

Averiguando el numero menor del dispositivo.

Como ya sabemos un determinado driver puede atender a varios dispositivos, todos los que tengan el mismo número mayor que el driver. Si pudiésemos conocer el número menor del dispositivo que provocó la llamada podremos hacer código condicional al dispositivo dentro de nuestro módulo.

El número menor que vemos con "ls -l /dev/mi_cinta" está almacenado en el inodo de dicho fichero. Cuando se invoca una operación sobre ese fichero, al modulo correspondiente se le pasa un puntero al inodo, siendo así posible la identificación del fichero que provocó la llamada.

El campo i_dev del inodo contiene la información sobre el número menor y el número mayor de dicho inodo. EL tipo de datos de i_dev es kdev_t . Para poder extraer de forma sencilla estos datos, en linux/kdev_t.h4.14 se definen un par de macros que nos pueden ser de utilidad:

Nótese como gracias a esto podemos crear diferentes ``dispositivos lógicos'' (ficheros en /dev/) que sean en realidad diferentes formas de acceder a un mismo ``dispositivo físico''.

A continuación se presenta una posible implementación para nuestro módulo, este código ya implementa las funciones open y release:

/*  mpcinta.c */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include "mpcinta.h"

unsigned int mpcinta_major=MPCINTA_MAYOR;
struct file_operations mpcinta_fops = {
        NULL,       /* lseek */
        NULL,       /* read */
        NULL,       /* write */
        NULL,       /* readdir */
        NULL,       /* poll */
        NULL,       /* ioctl */
        NULL,       /* mmap */
        mpcinta_open,       /* open */
        NULL,       /* flush */
        mpcinta_release,       /* 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> (init_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");
}


int mpcinta_open(struct inode *pinode, struct file *pfile) {
        int menor= MINOR(pinode->i_rdev);
        printk(KERN_INFO "mpcinta> (open) menor= %d\n",menor);
        if (menor>=NUM_MAX_CINTAS) {
                printk(KERN_INFO "mpcinta> (open) el device con minor number 
                                                    %i no existe\n",menor);
                return(-ENODEV);
        }
        MOD_INC_USE_COUNT;
        return 0;
}

int mpcinta_release(struct inode *pinode, struct file *pfile) {
        int menor= MINOR(pinode->i_rdev);
        printk(KERN_INFO "mpcinta> (release) menor= %d\n",menor);
        MOD_DEC_USE_COUNT;
        return 0;
}

MOD_INC_USE_COUNT es una macro definida en linux/module.h, que ayuda al programador de módulos a manejar de forma sencilla la cuenta de uso del módulo. Su uso en open es fundamental si se quiere evitar que nos desmonten el módulo mientras está siendo usado. Por supuesto, release tendrá que decrementar dicha cuenta ( MOD_DEC_USE_COUNT ) si queremos que una vez que el módulo no esté siendo utilizado se pueda eliminar de la memoria satisfactoriamente.

Al haber incluido las dos funciones que manejan la apertura y el cierre del dispositivo (mpcinta_open y mpcinta_release), tenemos que declarar sus cabeceras, por eso hemos creado también el fichero mpcinta.h en el que hemos incluido también algunas definiciones de utilidad para el módulo.

/*  mpcinta.h */
/*  el comportamiento por defecto es la asignación dinámica */
#define MPCINTA_MAYOR 0
/*  el número máximo de dispositivos de cinta que vamos a tener */
#define NUM_MAX_CINTAS 2 

int mpcinta_open(struct inode *pinode, struct file *pfile);
int mpcinta_release(struct inode *pinode, struct file *pfile);

Ya estamos en condiciones de probar nuestro módulo, para ello vamos a construir un sencillo programa en c que nos va a servir para invocar las llamadas al sistema que hacen que se ejecuten mpcinta_open() y mpcinta_release():

/*  prueba.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
        int file0, file1, result;
        /* probaremos los dos dispositivos */
        file0=open("/dev/mi_cinta0",O_RDONLY);
        if (file0==-1) return(-1);
        file1=open("/dev/mi_cinta1",O_RDONLY);
        if (file1==-1) return(-1);

        sleep(10); /* dormimos un rato para ver si el módulo...  */
        result=close(file0);             /* ...aparece como usado */
        if (file==-1) return(-1);
        result=close(file1);     
        if (file==-1) return(-1);
        return(result);
}

Con este sencillo programa podremos ver si nuestro módulo funciona bien, este es la manera de comprobarlo:

El resultado en /var/messages tendría que ser algo parecido a esto:

Feb 28 18:20:30 shire kernel: mpcinta> (init_module) cargado satisfactoriamente
Feb 28 18:20:33 shire kernel: mpcinta> (open) menor= 0
Feb 28 18:20:33 shire kernel: mpcinta> (open) menor= 1
Feb 28 18:20:33 shire kernel: mpcinta> (release) menor= 0
Feb 28 18:20:33 shire kernel: mpcinta> (release) menor= 1
Feb 28 18:20:36 shire kernel: mpcinta> (cleanup_module) descargado sin problemas

Todavía podemos ir más allá en nuestras pruebas, podemos intentar hacer:

dd if=/dev/mi_cinta0 of=/tmp/salida ibs=1 count=10

Veremos como dd falla al ejecutarse sobre nuestro dispositivo, sin embargo el dispositivo ha sido abierto y cerrado satisfactoriamente, como se puede ver en /var/log/messages, ¿Cual ha sido el problema?4.15.


next up previous contents index
Next: Lectura y escritura de Up: Abriendo dispositivos. Previous: Abriendo dispositivos.   Índice General   Índice de Materias
Alberto Cortés 2001-03-26