Chapter 4. E/S Asíncrona

Esto deja la característica perdida - E/S Asíncrona. Normalmente los programas UNIX usan la llamada poll (o su forma variante select) para esperar a que ocurra un evento en uno de los múltiples dispositivos de entrada o salida. Este modelo trabaja bien para la mayoría de las tareas porque las esperas poll y select para un evento no son convenientes para tareas que están continuamente haciendo trabajo computacional. Tales programas realmente quieren que el núcleo les golpee cuando pasa algo en vez de mirar por los eventos.

Poll es semejante a tener una fila de luces delante de tí. Puedes ver en un instante cuales de ellas están encendidas. No puedes, de cualquier forma, hacer nada útil mientras las estás mirando. La E/S asíncrona usa señales que trabajan más bien como un timbre. Es vez de mirar, dice que algo se ha manifestado.

La E/S asíncrona envía la señal SIGIO al proceso de usuario cuando ocurre el evento de E/S. En este caso esto significa cuando la gente mueve el ratón. La señal SIGIO causa que el proceso de usuario salga a su manejador de señales y ejecute el código en ese manejador antes de regresar a lo que estuviera haciendo previamente. Esta es la aplicación equivalente a un manejador de interrupciones.

La mayor parte del código necesitado para esta operación es común a todos los usuarios. El núcleo suministra un conjunto simple de funciones para administrar la E/S asíncrona.

Nuestro primer trabajo es permitir a los usuarioes establecer E/S asíncrona en el manejadores de archivos. Para hacer esto necesitamos añadir una nueva funciónn a la tabla de operaciones de archivo para nuestro ratón:


struct file_operations our_mouse_fops = {
        owner: THIS_MODULE
        read:  read_mouse,      /* Puedes leer un ratón */
        write: write_mouse,     /* Esto no hará mucho */
        poll:  poll_mouse,      /* Encuesta */
        open:  open_mouse,      /* Llamado en open */
        release: close_mouse,   /* Llamado en close */
        fasync: fasync_mouse,   /* E/S asíncrona */
};
  

Una vez que hemos instalado esta entrada, el núcleo conoce que soportamos E/S asíncrona y permitirá todas las operaciones relevantes en el dispositivo. Siempre que un usuario añade o quita la notificación de E/S asíncrona de un manejador de archivos, llama a nuestra rutina fasync_mouse que acabamos de añadir. Esta rutina usa las funciones de ayuda para mantener actualizada la cola de manejadores:


static struct fasync_struct *mouse_fasync = NULL;

static int fasync_mouse(int fd, struct file *filp, int on)
{
         int retval = fasync_helper(fd, filp, on, &mouse_fasync);

         if (retval < 0)
                 return retval;
        return 0;
}
  

La fasync helper añade y borra entradas administrando la lista suministrada. También necesitamos quitar entradas de esta lista cuando es cerradi el archivo. Esto requiere añadir una línea a nuestra función close:


static int close_mouse(struct inode *inode, struct file *file)
{
        fasync_mouse(-1, file, 0)
        if(--mouse_users)
                return 0;
        free_irq(OURMOUSE_IRQ, NULL);
        MOD_DEC_USE_COUNT;
        return 0;
}
  

Cuando cerramos el archivo podemos llamar a nuestro propio manejador fasync como si el usuario pidiera que este archivo cesara de ser usado para E/S asíncrona. Esto aproximadamente limpia cualesquiera finales perdidos. Seguramente no esperamos por la llegada de una señal para un archivo que no existirá más.

En este punto, el controlador del ratón soporta todas las operaciones de E/S asíncrona, y las aplicaciones usándolas no fallarán. Estas de todas formas no trabajarán todavía. Necesitamos realmente enviar las señales. Otra vez el núcleo suministra una función para manejar esto.

Actualizamos un poco nuestro manejador de interrupciones:


static void ourmouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
        char delta_x;
        char delta_y;
        unsigned char new_buttons;

        delta_x = inb(OURMOUSE_BASE);
        delta_y = inb(OURMOUSE_BASE+1);
        new_buttons = inb(OURMOUSE_BASE+2);

        if(delta_x || delta_y || new_buttons != mouse_buttons)
        {
                /* Algo ha pasado */

                spin_lock(&mouse_lock);
                mouse_event = 1;
                mouse_dx += delta_x;
                mouse_dy += delta_y;

                if(mouse_dx < -4096)
                        mouse_dx = -4096;
                if(mouse_dx > 4096)
                        mouse_dx = 4096;

                if(mouse_dy < -4096)
                        mouse_dy = -4096;
                if(mouse_dy > 4096)
                        mouse_dy = 4096;

                mouse_buttons = new_buttons;
                spin_unlock(&mouse_lock);

                /* Ahora hacemos E/S asíncrona */
                kill_fasync(&mouse_fasync, SIGIO); 
                
                wake_up_interruptible(&mouse_wait);
        }
}
  

El nuevo código simplemente llama a la rutina kill_fasync suminstrada por el núcleo si la cola no está vacía. Esto envía la señal requerida (SIGIO en este caso) al proceso que cada manejador de archivo dijo que quería ser informado sobre el excitante nuevo movimiento del ratón que acaba de ocurrir.

Con esto en su sitio y arreglados los fallos en la versión original, tienes ahora un controlador de ratón totalmente funcional usando el protocolo del bus del ratón. El trabajará con X window system, trabajará con GPM y debería de trabajar con todas las otras aplicaciones que necesites. Doom es, por supuesto, la forma ideal para probar que tu nuevo controlador de ratón está funcionando de forma adecuada. Asegúrate de probarlo de todas las formas posibles.