gioser

tawasuyu/brahman

Rust 496TOML 195Markdown 55JSON 197001 Definiciones

⎇ 8fc1d99ddff618eaa0c2c29f4a51eaaed73443dd renaser/kernel/src/drivers/raton.rs Rust · 261 líneas · 10.1 KB Historia Quién tocó qué crudo

feat(renaser): Fase 13 — ratón, puntero y arrastre de flotantes 8fc1d99dd · sergio ·

1// =============================================================================
2//  renaser :: kernel/src/drivers/raton.rs — Fase 13 :: el raton PS/2
3// -----------------------------------------------------------------------------
4//  El raton cuelga del controlador 8042 —el mismo que sirve el teclado—, por su
5//  puerto AUXILIAR, y anuncia cada movimiento por la IRQ12. Su lenguaje es un
6//  paquete de tres bytes: banderas (botones + signos), delta X y delta Y.
7//
8//  Como el teclado en la Fase 8c, el raton respeta el GUARDARRAIL de
9//  interrupciones: el manejador de IRQ12 solo toca atomicos —la posicion del
10//  puntero— y una cola lock-free de eventos. Jamas un cerrojo cooperativo. El
11//  compositor drena esa cola desde el reactor, a su ritmo.
12// =============================================================================
13
14use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};
15
16use crossbeam_queue::ArrayQueue;
17use spin::Once;
18use x86_64::instructions::port::Port;
19
20use crate::pic;
21
22/// Puerto de datos del controlador 8042 (compartido con el teclado).
23const DATOS: u16 = 0x60;
24/// Puerto de estado / comando del 8042.
25const ESTADO: u16 = 0x64;
26
27/// Capacidad de la cola de eventos del raton — holgada: nadie agita tanto.
28const CAPACIDAD: usize = 128;
29
30/// Un evento del raton, tal como lo entrega un paquete completo: la posicion
31/// ya absoluta del puntero y el estado crudo de los botones (bit 0 izquierdo,
32/// bit 1 derecho, bit 2 central). Lo produce la IRQ12; lo consume el compositor.
33#[derive(Clone, Copy)]
34pub struct EventoRaton {
35    pub x: u16,
36    pub y: u16,
37    pub botones: u8,
38}
39
40/// Posicion del puntero, en pixeles. La escribe la IRQ12 a cada paquete; la lee
41/// la consola para estampar el puntero, y el compositor para situar los clics.
42static RATON_X: AtomicUsize = AtomicUsize::new(0);
43static RATON_Y: AtomicUsize = AtomicUsize::new(0);
44
45/// Limites de la pantalla — el puntero jamas se sale de ellos.
46static ANCHO: AtomicUsize = AtomicUsize::new(0);
47static ALTO: AtomicUsize = AtomicUsize::new(0);
48
49/// ¿Esta el raton inicializado y vivo? Hasta entonces no hay puntero que pintar.
50static ACTIVO: AtomicBool = AtomicBool::new(false);
51
52/// Estado del ensamblado del paquete de 3 bytes. Lo toca SOLO la IRQ12, que es
53/// no-reentrante en un solo nucleo: una secuencia de atomicos basta, sin cerrojo.
54static FASE: AtomicUsize = AtomicUsize::new(0);
55static BYTE0: AtomicU8 = AtomicU8::new(0);
56static BYTE1: AtomicU8 = AtomicU8::new(0);
57/// Estado de los botones en el paquete anterior — para detectar transiciones.
58static BOTONES_ANTES: AtomicU8 = AtomicU8::new(0);
59
60/// La cola de eventos: la IRQ12 deposita (lock-free, segura en interrupcion),
61/// el compositor drena desde el reactor cooperativo.
62static EVENTOS: Once<ArrayQueue<EventoRaton>> = Once::new();
63
64// =============================================================================
65//  Dialogo con el 8042 — esperas acotadas, sin colgar jamas el arranque
66// =============================================================================
67
68/// Espera, con un tope de intentos, a que el 8042 admita una escritura (su
69/// bufer de entrada vacio). Devuelve `false` si se agota la paciencia.
70fn esperar_envio() -> bool {
71    for _ in 0..100_000 {
72        // SEGURIDAD: 0x64 es el puerto de estado del 8042, fijo en el PC.
73        let estado = unsafe { Port::<u8>::new(ESTADO).read() };
74        if estado & 0b10 == 0 {
75            return true;
76        }
77    }
78    false
79}
80
81/// Espera, con un tope de intentos, a que el 8042 tenga un byte que entregar.
82fn esperar_recepcion() -> bool {
83    for _ in 0..100_000 {
84        // SEGURIDAD: ver `esperar_envio`.
85        let estado = unsafe { Port::<u8>::new(ESTADO).read() };
86        if estado & 0b01 != 0 {
87            return true;
88        }
89    }
90    false
91}
92
93/// Escribe un byte en el puerto de comando del 8042.
94fn comando_8042(byte: u8) {
95    esperar_envio();
96    // SEGURIDAD: 0x64 es el puerto de comando del 8042 en la arquitectura PC.
97    unsafe { Port::<u8>::new(ESTADO).write(byte) };
98}
99
100/// Escribe un byte en el puerto de datos del 8042.
101fn escribir_datos(byte: u8) {
102    esperar_envio();
103    // SEGURIDAD: 0x60 es el puerto de datos del 8042 en la arquitectura PC.
104    unsafe { Port::<u8>::new(DATOS).write(byte) };
105}
106
107/// Lee un byte del puerto de datos del 8042.
108fn leer_datos() -> u8 {
109    esperar_recepcion();
110    // SEGURIDAD: ver `escribir_datos`.
111    unsafe { Port::<u8>::new(DATOS).read() }
112}
113
114/// Envia un comando AL raton (no al 8042): el prefijo 0xD4 le dice al 8042 que
115/// el proximo byte de datos va al dispositivo auxiliar. Consume el ACK (0xFA).
116fn comando_raton(byte: u8) {
117    comando_8042(0xD4);
118    escribir_datos(byte);
119    let _ack = leer_datos();
120}
121
122/// Vacia el bufer de salida del 8042 — descarta bytes rezagados que pudieran
123/// desincronizar el ensamblado del primer paquete.
124fn vaciar() {
125    for _ in 0..16 {
126        // SEGURIDAD: ver `esperar_envio`.
127        let estado = unsafe { Port::<u8>::new(ESTADO).read() };
128        if estado & 0b01 == 0 {
129            return;
130        }
131        let _ = unsafe { Port::<u8>::new(DATOS).read() };
132    }
133}
134
135// =============================================================================
136//  Arranque
137// =============================================================================
138
139/// Funda el raton: despierta el dispositivo auxiliar del 8042, programa su IRQ,
140/// le ordena reportar movimiento y deja el puntero en el centro de la pantalla.
141/// Requiere el heap activo; debe invocarse una vez, antes de habilitar las
142/// interrupciones.
143pub fn init(ancho: usize, alto: usize) {
144    ANCHO.store(ancho, Ordering::Relaxed);
145    ALTO.store(alto, Ordering::Relaxed);
146    RATON_X.store(ancho / 2, Ordering::Relaxed);
147    RATON_Y.store(alto / 2, Ordering::Relaxed);
148    EVENTOS.call_once(|| ArrayQueue::new(CAPACIDAD));
149
150    vaciar();
151
152    // Despertar el dispositivo auxiliar (el raton) en el 8042.
153    comando_8042(0xA8);
154
155    // Leer el byte de configuracion, encender la IRQ del auxiliar (bit 1) y
156    // asegurar que su reloj corre (bit 5 a cero), y reescribirlo.
157    comando_8042(0x20);
158    let mut config = leer_datos();
159    config |= 0b0000_0010;
160    config &= !0b0010_0000;
161    comando_8042(0x60);
162    escribir_datos(config);
163
164    // Al raton: valores por defecto y, despues, reportar movimiento.
165    comando_raton(0xF6);
166    comando_raton(0xF4);
167
168    vaciar();
169    ACTIVO.store(true, Ordering::Relaxed);
170    // Levantar la mascara de la IRQ12 — el raton vive en el PIC esclavo.
171    pic::desenmascarar(12);
172}
173
174// =============================================================================
175//  El paquete de 3 bytes — punto de entrada desde la IRQ12
176// =============================================================================
177
178/// Punto de entrada DESDE el manejador de IRQ12. Ensambla el paquete de tres
179/// bytes y, al completarlo, actualiza la posicion del puntero y encola un
180/// evento. Deliberadamente breve y libre de panicos: corre en contexto de IRQ.
181pub fn recibir_byte(byte: u8) {
182    match FASE.load(Ordering::Relaxed) {
183        0 => {
184            // El primer byte SIEMPRE trae el bit 3 a 1. Si no, el flujo esta
185            // desincronizado: se descarta el byte y se sigue esperando uno bueno.
186            if byte & 0b0000_1000 == 0 {
187                return;
188            }
189            BYTE0.store(byte, Ordering::Relaxed);
190            FASE.store(1, Ordering::Relaxed);
191        }
192        1 => {
193            BYTE1.store(byte, Ordering::Relaxed);
194            FASE.store(2, Ordering::Relaxed);
195        }
196        _ => {
197            FASE.store(0, Ordering::Relaxed);
198            procesar(BYTE0.load(Ordering::Relaxed), BYTE1.load(Ordering::Relaxed), byte);
199        }
200    }
201}
202
203/// Procesa un paquete completo: traduce los deltas a una posicion absoluta del
204/// puntero, acotada a la pantalla, y encola el evento si hay algo que el
205/// compositor deba atender —un boton pulsado o un arrastre en curso—.
206fn procesar(banderas: u8, dx_crudo: u8, dy_crudo: u8) {
207    // Un paquete con desbordamiento de delta trae un salto disparatado: se
208    // ignora su movimiento por completo.
209    if banderas & 0b1100_0000 != 0 {
210        return;
211    }
212    let dx = dx_crudo as i8 as i32;
213    // El raton PS/2 da Y positivo hacia ARRIBA; la pantalla, hacia ABAJO.
214    let dy = dy_crudo as i8 as i32;
215
216    let ancho = ANCHO.load(Ordering::Relaxed) as i32;
217    let alto = ALTO.load(Ordering::Relaxed) as i32;
218    let x = (RATON_X.load(Ordering::Relaxed) as i32 + dx).clamp(0, (ancho - 1).max(0)) as usize;
219    let y = (RATON_Y.load(Ordering::Relaxed) as i32 - dy).clamp(0, (alto - 1).max(0)) as usize;
220    RATON_X.store(x, Ordering::Relaxed);
221    RATON_Y.store(y, Ordering::Relaxed);
222
223    // Encolar un evento SOLO si importa: si los botones cambiaron, o si alguno
224    // sigue pulsado —un arrastre—. El movimiento ocioso no satura la cola; el
225    // puntero, aun asi, ya se movio (los atomicos de arriba).
226    let botones = banderas & 0b0000_0111;
227    let antes = BOTONES_ANTES.swap(botones, Ordering::Relaxed);
228    if botones != antes || botones != 0 {
229        if let Some(cola) = EVENTOS.get() {
230            // Si la cola se desborda, el evento se pierde en silencio: mas vale
231            // perder un gesto que arriesgar un panico dentro de una IRQ.
232            let _ = cola.push(EventoRaton {
233                x: x as u16,
234                y: y as u16,
235                botones,
236            });
237        }
238    }
239}
240
241// =============================================================================
242//  Consulta — para la consola (puntero) y el compositor (eventos)
243// =============================================================================
244
245/// La posicion actual del puntero, o `None` si el raton aun no esta vivo. La
246/// consulta la consola para estampar el puntero en cada volcado de pantalla.
247pub fn posicion() -> Option<(usize, usize)> {
248    if !ACTIVO.load(Ordering::Relaxed) {
249        return None;
250    }
251    Some((
252        RATON_X.load(Ordering::Relaxed),
253        RATON_Y.load(Ordering::Relaxed),
254    ))
255}
256
257/// Extrae el siguiente evento del raton, o `None` si no hay ninguno pendiente.
258/// La drena el compositor, evento a evento, desde el reactor cooperativo.
259pub fn siguiente_evento() -> Option<EventoRaton> {
260    EVENTOS.get().and_then(ArrayQueue::pop)
261}