Procederemos ahora a ver todo el código del módulo “puertopar”. En general habrá que cambiar en todo el código, respecto al módulo memoria.c, las palabras “memoria” por “puertopar”. El driver final queda como sigue:
<<puertopar.c>>= <<puertopar inicio>> <<puertopar init module>> <<puertopar cleanup module>> <<puertopar open>> <<puertopar release>> <<puertopar read>> <<puertopar write>> |
En la parte inicial del driver utilizaremos un número mayor diferente, el 61, cambiaremos la variable global memoria_buffer por port e incluiremos con #include los ficheros ioport.h y io.h.
<<puertopar inicio>>= /* Definiciones e includes necesarios para los drivers */ #define MODULE #define __KERNEL__ #include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> /* printk() */ #include <linux/malloc.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/ioport.h> #include <asm/system.h> /* cli(), *_flags */ #include <asm/uaccess.h> /* copy_from/to_user */ #include <asm/io.h> /* inb, outb */ /* Declaracion de funciones de puertopar.c */ int puertopar_open(struct inode *inode, struct file *filp); int puertopar_release(struct inode *inode, struct file *filp); ssize_t puertopar_read(struct file *filp, char *buf, size_t count, loff_t *f_pos); ssize_t puertopar_write(struct file *filp, char *buf, size_t count, loff_t *f_pos); void cleanup_module(void); /* Estructura que declara las funciones tipicas */ /* de acceso a ficheros */ struct file_operations puertopar_fops = { read: puertopar_read, write: puertopar_write, open: puertopar_open, release: puertopar_release }; /* Variables globales del driver */ /* Numero mayor */ int puertopar_major = 61; /* Variable de control para la reserva */ /* de memoria del puerto paralelo*/ int port; |
En la rutina de inicio del módulo incluiremos la reserva de la dirección de memoria del puerto paralelo como describimos antes.
<<puertopar init module>>= int init_module(void) { int result; /* Registrando dispositivo */ result = register_chrdev(puertopar_major, "puertopar", &puertopar_fops); if (result < 0) { printk( "<1>puertopar: no puedo obtener numero mayor %d\n", puertopar_major); return result; } <<puertopar modificacion init module>> printk("<1>Insertando modulo\n"); return 0; fallo: cleanup_module(); return result; } |
La rutina de eliminación del módulo incluirá las modificaciones antes reseñadas.
<<puertopar cleanup module>>= void cleanup_module(void) { /* Liberamos numero mayor */ unregister_chrdev(puertopar_major, "memoria"); <<puertopar modificacion cleanup module>> printk("<1>Quitando modulo\n"); } |
Esta rutina es idéntica a la del driver “memoria”.
<<puertopar open>>= int puertopar_open(struct inode *inode, struct file *filp) { /* Aumentamos la cuenta de uso */ MOD_INC_USE_COUNT; /* Exito */ return 0; } |
De nuevo la similitud es exacta.
<<puertopar release>>= int puertopar_release(struct inode *inode, struct file *filp) { /* Decrementamos la cuenta de uso */ MOD_DEC_USE_COUNT; /* Exito */ return 0; } |
La función de lectura es análoga a la del “memoria” con las consiguientes modificaciones de lectura del puerto del dispositivo.
<<puertopar read>>= ssize_t puertopar_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { /* Buffer para leer el dispositivo */ char puertopar_buffer; <<puertopar inport>> /* Transferimos datos al espacio de usuario */ copy_to_user(buf,&puertopar_buffer,1); /* Cambiamos posición de lectura segun convenga */ if (*f_pos == 0) { *f_pos+=1; return 1; } else { return 0; } } |
Es igual que el del “memoria” pero añadiendo la escritura al puerto del dispositivo.
<<puertopar write>>= ssize_t puertopar_write( struct file *filp, char *buf, size_t count, loff_t *f_pos) { char *tmp; /* Buffer para leer el dispositivo */ char puertopar_buffer; tmp=buf+count-1; copy_from_user(&puertopar_buffer,tmp,1); <<puertopar outport>> return 1; } |