gioser

tawasuyu/brahman

Rust 326TOML 123JSON 14Markdown 114554 Definitions

⎇ 28c2e6af189bd8467f59a79727354901bbeb1a33 renaser/kernel/src/drivers/altavoz.rs Rust · 155 lines · 6.8 KB History Who touched what raw

feat(renaser): Fase 15 — la voz del sistema (acorde + eventos) 28c2e6af1 · sergio ·

1// =============================================================================
2//  renaser :: kernel/src/drivers/altavoz.rs — Fase 12 :: la bocina del PC
3// -----------------------------------------------------------------------------
4//  La bocina del PC es el instrumento mas humilde del hardware: un solo bit que,
5//  conmutado a la frecuencia justa, hace vibrar una membrana. No hay PCM, ni
6//  DMA, ni mezclador — solo el canal 2 del PIT generando una onda cuadrada y
7//  una compuerta en el puerto 0x61 que la deja pasar al altavoz, o no.
8//
9//  El canal 0 del PIT es el latido del kernel (ver `pic`); el canal 2 es de la
10//  bocina y de nadie mas — programarlo aqui no perturba el temporizador—. Esta
11//  es la unica via del kernel hacia el sonido; la capacidad `sys_tono` la
12//  ofrece al userspace, gobernada por el foco del compositor.
13// =============================================================================
14
15use core::sync::atomic::{AtomicU64, Ordering};
16
17use alloc::collections::VecDeque;
18use spin::Mutex;
19use x86_64::instructions::port::Port;
20
21/// Frecuencia del cristal del PIT, en Hz — el divisor se calcula contra ella.
22const PIT_BASE_HZ: u32 = 1_193_182;
23
24/// Puerto de comando del PIT.
25const PIT_COMANDO: u16 = 0x43;
26/// Puerto de datos del canal 2 del PIT — el de la bocina.
27const PIT_CANAL2: u16 = 0x42;
28/// Puerto de control de la bocina (bits 0 y 1: compuerta del PIT y dato).
29const CONTROL_BOCINA: u16 = 0x61;
30
31/// Pone la bocina a sonar a `frecuencia_hz`. Un `0` —o una frecuencia que un
32/// divisor de 16 bits no pueda representar (por debajo de ~19 Hz)— la SILENCIA.
33/// Es la unica via del kernel hacia el sonido.
34pub fn tono(frecuencia_hz: u32) {
35    if frecuencia_hz == 0 || PIT_BASE_HZ / frecuencia_hz > 0xFFFF {
36        silenciar();
37        return;
38    }
39    // El divisor cabe en 16 bits; un `.max(1)` lo protege de una frecuencia
40    // disparatadamente alta que lo dejara en cero.
41    let divisor = (PIT_BASE_HZ / frecuencia_hz).max(1) as u16;
42
43    // SEGURIDAD: 0x43 y 0x42 son los puertos del PIT en la arquitectura PC;
44    // 0xB6 selecciona el canal 2, acceso lobyte+hibyte, modo 3 (onda cuadrada).
45    // El canal 2 es exclusivo de la bocina: no perturba el latido del kernel.
46    unsafe {
47        let mut comando = Port::<u8>::new(PIT_COMANDO);
48        let mut canal2 = Port::<u8>::new(PIT_CANAL2);
49        comando.write(0xB6u8);
50        canal2.write((divisor & 0xFF) as u8);
51        canal2.write((divisor >> 8) as u8);
52    }
53    abrir_compuerta();
54}
55
56/// Abre la compuerta del puerto 0x61: deja pasar la onda del canal 2 al altavoz.
57fn abrir_compuerta() {
58    // SEGURIDAD: 0x61 es el puerto de control de la bocina; sus bits 0 y 1
59    // —compuerta del PIT y dato del altavoz— se tocan con leer-modificar-
60    // escribir para no perturbar los demas bits del chipset.
61    unsafe {
62        let mut control = Port::<u8>::new(CONTROL_BOCINA);
63        let estado = control.read();
64        control.write(estado | 0b11);
65    }
66}
67
68/// Silencia la bocina: cierra la compuerta del puerto 0x61. La onda del canal 2
69/// sigue generandose, pero ya no alcanza la membrana.
70fn silenciar() {
71    // SEGURIDAD: ver `abrir_compuerta`. Limpiar los bits 0 y 1 corta el sonido.
72    unsafe {
73        let mut control = Port::<u8>::new(CONTROL_BOCINA);
74        let estado = control.read();
75        control.write(estado & !0b11);
76    }
77}
78
79// =============================================================================
80//  SECUENCIAS DEL KERNEL — la voz propia del sistema (Fase 15)
81// -----------------------------------------------------------------------------
82//  La bocina es de la ventana enfocada (Fase 12), pero el kernel tambien
83//  necesita hablar: un acorde al arrancar, un repique al lanzar una app, un
84//  bajo al desalojarla. Una cola de notas pendientes —`(frecuencia, ms)`— y
85//  un reloj de fin —`FIN_NOTA`— que la tarea del compositor consulta cada
86//  fotograma: si la nota actual ya termino, pasa a la siguiente. Mientras el
87//  kernel suena, las llamadas de los apps a `sys_tono` se ignoran — el
88//  kernel manda en su propia voz.
89// =============================================================================
90
91/// La cola de notas pendientes — `(frecuencia_hz, duracion_ms)`. Solo la
92/// tocan tareas cooperativas: agendar (desde los hitos del kernel) y atender
93/// (desde la tarea del compositor). Ninguna IRQ se la disputa.
94static SECUENCIA: Mutex<VecDeque<(u32, u32)>> = Mutex::new(VecDeque::new());
95
96/// Milisegundo (lectura del reloj monotono) en que la nota actual acaba. Lo
97/// consulta `kernel_sonando` para gatear a las apps.
98static FIN_NOTA: AtomicU64 = AtomicU64::new(0);
99
100/// Agenda una secuencia de notas: cada `(frecuencia_hz, duracion_ms)` se hara
101/// sonar en orden. Un `frecuencia_hz=0` es una pausa silenciosa. Si ya habia
102/// una secuencia sonando, las nuevas notas se encolan al final.
103pub fn agendar(secuencia: &[(u32, u32)]) {
104    let mut cola = SECUENCIA.lock();
105    for &(frec, dur) in secuencia {
106        cola.push_back((frec, dur));
107    }
108}
109
110/// ¿Esta el kernel sonando una nota suya? Mientras dure, las llamadas de los
111/// apps a `sys_tono` quedan silenciadas — el kernel no se interrumpe a si
112/// mismo.
113pub fn kernel_sonando() -> bool {
114    crate::async_system::reloj::milisegundos() < FIN_NOTA.load(Ordering::Relaxed)
115}
116
117/// Atiende el reloj de la secuencia: si la nota actual ya termino, saca la
118/// siguiente de la cola y la hace sonar; si la cola esta vacia, calla la
119/// bocina. La invoca la tarea del compositor cada fotograma.
120pub fn atender() {
121    let ahora = crate::async_system::reloj::milisegundos();
122    if ahora < FIN_NOTA.load(Ordering::Relaxed) {
123        return; // la nota actual sigue sonando
124    }
125    let siguiente = SECUENCIA.lock().pop_front();
126    match siguiente {
127        Some((frec, dur)) => {
128            tono(frec);
129            FIN_NOTA.store(ahora + dur as u64, Ordering::Relaxed);
130        }
131        None => {
132            // Sin notas que sonar: silenciar. Las apps recuperaran la bocina
133            // en cuanto su proxima llamada a `sys_tono` vea `kernel_sonando`
134            // ya en `false`.
135            tono(0);
136        }
137    }
138}
139
140// =============================================================================
141//  CATALOGO DE VOCES — los hitos del sistema y su sonido
142// =============================================================================
143
144/// Acorde de bienvenida: Do — Mi — Sol del Do mayor. Suena una vez, al
145/// completarse el arranque del kernel.
146pub const VOZ_BIENVENIDA: [(u32, u32); 3] = [(523, 130), (659, 130), (784, 240)];
147
148/// Llamada al lanzar una app NUEVA en vivo: dos notas ascendentes.
149pub const VOZ_LANZAR: [(u32, u32); 2] = [(700, 70), (1050, 90)];
150
151/// Llamada al cerrar una app LIMPIAMENTE (`Alt+Q`): dos notas descendentes.
152pub const VOZ_CERRAR: [(u32, u32); 2] = [(900, 70), (520, 100)];
153
154/// Llamada al DESALOJAR una app por falla: un bajo de aviso.
155pub const VOZ_DESALOJO: [(u32, u32); 1] = [(180, 260)];