next up previous contents index
Next: Módulos para dispositivos de Up: La función ioctl(). Previous: La función ioctl().   Índice General   Índice de Materias

Un ejemplo de función ioctl().

Lo primero que hay que hacer es decidir el número mágico, por ejemplo la 'c' (Si, la letra 'c' es un número!).

#define MPCINTA_IOC_NMAGICO 'c'

A continuación construimos una lista con todas las funciones que vamos a querer que ioctl() implemente, y le asignamos a cada una su comando:

#define MPCINTA_IOC_ABRIR _IO(MPCINTA_IOC_NMAGICO, 1) 
#define MPCINTA_IOC_CERRAR _IO(MPCINTA_IOC_NMAGICO, 2) 
#define MPCINTA_IOC_PLAY _IO(MPCINTA_IOC_NMAGICO, 3) 
#define MPCINTA_IOC_STOP _IO(MPCINTA_IOC_NMAGICO, 4) 
#define MPCINTA_IOC_PAUSE _IOW(MPCINTA_IOC_NMAGICO, 5, 1) /* pausa durante 
                                                             tantos segundos */

Para poder comprobar que no nos hemos pasado podemos definir un límite para el number de los comandos:

#define MPCINTA_IOC_NUM_MAX 5

Según estas definiciones podemos construir la siguiente implementación de ioctl para nuestro módulo:

/*  mpcinta.c */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/malloc.h>
#include <asm/uaccess.h>
#include <asm/ioctl.h>
#include "mpcinta.h"

unsigned int mpcinta_major=MPCINTA_MAYOR;
struct file_operations mpcinta_fops = {
        mpcinta_lseek,       /* lseek */
        mpcinta_read,       /* read */
        mpcinta_write,       /* write */
        NULL,       /* readdir */
        NULL,       /* poll */
        mpcinta_ioctl,       /* ioctl */
        NULL,       /* mmap */
        mpcinta_open,       /* open */
        NULL,       /* flush */
        mpcinta_release,       /* release */
        NULL,       /* fsync */
        NULL,       /* fasync */
        NULL,       /* check_media_change */
        NULL,       /* revalidate */
        NULL,       /* lock */
};              

char *cintas; /* puntero a nuestras primera cinta */

int init_module(void) {
        int result;
        EXPORT_NO_SYMBOLS;

        /* imprmimos por pantalla los códigos de las ioctls para poder
           probarlas una por una */
        printk(KERN_INFO "mpcinta> (INFO) IOCTL_ABRIR: %x\n",MPCINTA_IOC_ABRIR);
        printk(KERN_INFO "mpcinta> (INFO) IOCTL_CERRAR: %x\n",MPCINTA_IOC_CERRAR);
        printk(KERN_INFO "mpcinta> (INFO) IOCTL_PLAY: %x\n",MPCINTA_IOC_PLAY);
        printk(KERN_INFO "mpcinta> (INFO) IOCTL_STOP: %x\n",MPCINTA_IOC_STOP);
        printk(KERN_INFO "mpcinta> (INFO) IOCTL_PAUSE: %x\n",MPCINTA_IOC_PAUSE);

                .
                .
                .

int mpcinta_ioctl(struct inode *pinodo, struct file *pfile, unsigned int comando,
                 unsigned long argumentos) {
        

        if (_IOC_TYPE(comando) != MPCINTA_IOC_NMAGICO) 
                return -EINVAL;
        if (_IOC_NR(comando) > MPCINTA_IOC_NUM_MAX) 
                return -EINVAL;
        
        switch(comando) {
        case MPCINTA_IOC_ABRIR: /* Query: return it (it's positive) */
                printk(KERN_INFO "mpcinta> (ioctl %d) Se ha abierto la 
                                       portezuela\n", _IOC_NR(comando));
                /* aquí iría el código que abre la portezuela */
                break;
        case MPCINTA_IOC_CERRAR: /* eXchange: use arg as pointer */
                printk(KERN_INFO "mpcinta> (ioctl %d) Se ha cerrado la 
                                         portezuela\n", _IOC_NR(comando));
                /* bla, bla, bla */
                break;
                
        case MPCINTA_IOC_PLAY: /* sHift: like Tell + Query */
                printk(KERN_INFO "mpcinta> (ioctl %d) play\n", _IOC_NR(comando));
                /* bla, bla, bla */
                break;
                
        case MPCINTA_IOC_STOP:
                printk(KERN_INFO "mpcinta> (ioctl %d) stop\n", _IOC_NR(comando));
                /* bla, bla, bla */
                break;
                
        case MPCINTA_IOC_PAUSE:
                printk(KERN_INFO "mpcinta> (ioctl %d) pause durante %d segundos\n",
                       _IOC_NR(comando),*((int *)argumentos)); /* !!atencion 
                                                                 al casting¡¡ */
                /* bla, bla, bla */
                break;
                
        default:
                return -EINVAL;
        }
        return 0;
}

Para probar las ioctls recién implementadas podemos usar el siguiente programa, observando los mensajes que nos devuelve el kernel para cada ioctl:

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


/* IOCTL_ABRIR: 6301
   IOCTL_CERRAR: 6302
   IOCTL_PLAY: 6303
   IOCTL_STOP: 6304
   IOCTL_PAUSE: 40046305 */

int main() {
        int file, result;
        int segundos=10;
        file=open("/dev/mi_cinta1",O_RDWR);
        if (file==-1) return(-1);

        result=ioctl(file, 0x6301);
        if (result==0) printf("OK\n");
        result=ioctl(file, 0x6302);
        if (result==0) printf("OK\n");
        result=ioctl(file, 0x6303);
        if (result==0) printf("OK\n");
        result=ioctl(file, 0x6304);
        if (result==0) printf("OK\n");
        result=ioctl(file, 0x40046305,&segundos); /* atención al tipo del
                                                     argumento */
        if (result==0) printf("OK\n");

        /* ahora una que falle */
        result=ioctl(file, 0x6305);
        if (result) printf("ioctl(file,0x6305) falló, como debe ser\n");

       
        result=close(file);
        if (file==-1) return(-1);
        return(result);
}

Si quisiéramos implementar una ioctl que cambiase el tamaño de las cintas, por ejemplo entre 45, 60 y 90 minutos, ¿Que cambios tendríamos que hacer en nuestro módulo?.


next up previous contents index
Next: Módulos para dispositivos de Up: La función ioctl(). Previous: La función ioctl().   Índice General   Índice de Materias
Alberto Cortés 2001-03-26