![]() | Publicación Anterior |
---|---|
Algunas partes de este documento aparecieron primero en Linux Magazine bajo una exclusividad de noventa dias. |
Table 1-1. Codificación de Datos del Ratón
Byte 0 | 0x80 + los botones actualmente pulsados. |
Byte 1 | Un valor con signo para el desplazamiento en la posición X |
Byte 2 | Un valor con signo para el desplazamiento en la posición Y |
Los valores de la posición son truncados si es que exceden del rango de los 8 bits (que es -127 <= delta <= 127). Como el valor -128 no encaja en un byte no es permitido.
Los
son numerados de izquierda a derecha como 0, 1, 2, 3.. y cada botón establece el bit relevante. Por lo tanto un usuario presionando los botonoes de la izquierda y de la derecha en un ratón de tres botones establecerán los bits 0 y 2.Todos los ratones están requeridos a soportar la operación poll. Sería algo verdaderamente muy bonito si todos los usuarios de un controlador de un dispositivo usaran poll para esperar a que tuvieran lugar los eventos.
Finalmente el soporte asíncrono de E/S de los ratonoes. Este es un tópico que todavía no hemos cubierto pero que explicaré más tarde, después de mirar en un controlador simple de ratón.
#define OURMOUSE_BASE 0x300 static struct miscdevice our_mouse = { OURMOUSE_MINOR, "ourmouse", &our_mouse_fops }; __init ourmouse_init(void) { if(check_region(OURMOUSE_BASE, 3)) return -ENODEV; request_region(OURMOUSE_BASE, 3, "ourmouse"); misc_register(&our_mouse); return 0; }
#ifdef MODULE int init_module(void) { if(ourmouse_init()<0) return -ENODEV: return 0; } void cleanup_module(void) { misc_deregister(&our_mouse); free_region(OURMOUSE_BASE, 3); } #endif
struct file_operations our_mouse_fops = { owner: THIS_MODULE, /* Automática administración de uso */ read: read_mouse, /* Puedes leer un ratón */ write: write_mouse, /* Esto debería de hacer un montón */ poll: poll_mouse, /* Encuesta */ open: open_mouse, /* Llamado en open */ release: close_mouse, /* Llamado en close */ };
static int mouse_users = 0; /* Cuenta de Usuarios */ static int mouse_dx = 0; /* Cambios de Posición */ static int mouse_dy = 0; static int mouse_event = 0; /* El ratón se movió */ static int open_mouse(struct inode *inode, struct file *file) { if(mouse_users++) return 0; if(request_irq(mouse_intr, OURMOUSE_IRQ, 0, "ourmouse", NULL)) { mouse_users--; return -EBUSY; } mouse_dx = 0; mouse_dy = 0; mouse_event = 0; mouse_buttons = 0; return 0; }
La función release (liberar) necesita desenrollar todas estas:
static int close_mouse(struct inode *inode, struct file *file) { if(--mouse_users) return 0; free_irq(OURMOUSE_IRQ, NULL); return 0; }
static ssize_t write_mouse(struct file *file, const char *buffer, size_t count, loff_t *ppos) { return -EINVAL; }
Esto es bastante auto-explicativo. Siempre que escribes dirán que era una función inválida.
static struct wait_queue *mouse_wait; static spinlock_t mouse_lock = SPIN_LOCK_UNLOCKED; 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; mouse_buttons = new_buttons; spin_unlock(&mouse_lock); wake_up_interruptible(&mouse_wait); } }
static unsigned int mouse_poll(struct file *file, poll_table *wait) { poll_wait(file, &mouse_wait, wait); if(mouse_event) return POLLIN | POLLRDNORM; return 0; }
static ssize_t mouse_read(struct file *file, char *buffer, size_t count, loff_t *pos) { int dx, dy; unsigned char button; unsigned long flags; int n; if(count<3) return -EINVAL; /* * Espera por un evento */ while(!mouse_event) { if(file->f_flags&O_NDELAY) return -EAGAIN; interruptible_sleep_on(&mouse_wait); if(signal_pending(current)) return -ERESTARTSYS; }
/* Coge el evento */ spinlock_irqsave(&mouse_lock, flags); dx = mouse_dx; dy = mouse_dy; button = mouse_buttons; if(dx<=-127) dx=-127; if(dx>=127) dx=127; if(dy<=-127) dy=-127; if(dy>=127) dy=127; mouse_dx -= dx; mouse_dy -= dy; if(mouse_dx == 0 && mouse_dy == 0) mouse_event = 0; spin_unlock_irqrestore(&mouse_lock, flags);
if(put_user(button|0x80, buffer)) return -EFAULT; if(put_user((char)dx, buffer+1)) return -EFAULT; if(put_user((char)dy, buffer+2)) return -EFAULT; for(n=3; n < count; n++) if(put_user(0x00, buffer+n)) return -EFAULT; return count; }
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); wake_up_interruptible(&mouse_wait); } }
Añadiendo estos chequeos limitamos el rango de movimiento acumulado a algo sensible.
while(!mouse_event) {
interruptible_sleep_on(&mouse_wait);
save_flags(flags); cli(); while(!mouse_event) { if(file->f_flags&O_NDELAY) { restore_flags(flags); return -EAGAIN; } interruptible_sleep_on(&mouse_wait); if(signal_pending(current)) { restore_flags(flags); return -ERESTARTSYS; } } restore_flags(flags);
struct wait_queue wait = { current, NULL }; add_wait_queue(&mouse_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); while(!mouse_event) { if(file->f_flags&O_NDELAY) { remove_wait_queue(&mouse_wait, &wait); set_current_state(TASK_RUNNING); return -EWOULDBLOCK; } if(signal_pending(current)) { remove_wait_queue(&mouse_wait, &wait); current->state = TASK_RUNNING; return -ERESTARTSYS; } schedule(); set_current_state(TASK_INTERRUPTIBLE); } remove_wait_wait(&mouse_wait, &wait); set_current_state(TASK_RUNNING);
Detrás de todos los envoltorios en el código original lo que está sucediendo es esto:
![]() | Nota |
---|---|
Este no es un tópico fácil. No tengas miedo de releer la descripción unas pocas veces y también de mirar en otros controladores de dispositivos para ver si funciona. Finalmente si todavía no puedes cogerlo, puedes usar el código como modelo para escribir otros controladores de dispositivos y confiar en mí. |
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 */ };
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; }
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; }
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); } }
Este documento ha sido traducido por Rubén Melcón <melkon@terra.es>; y es publicado por el Proyecto Lucas
Versión de la tradución 0.04 ( Julio de 2002 ).
Si tienes comentarios sobre la traducción, ponte en contacto con Rubén Melcón <melkon@terra.es>