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