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?.