rustubs/machine/
keyctrl.rs

1//! Driver for the PS/2 keybard/mouse controller
2//!
3//! beyound OOStuBS:
4//!
5//! The 'gather' field should NEVER have imcomplete state: it should either be a
6//! valid key, or nothing.
7//! TODO what if IO ops fails or timeout?
8//! TODO figure out how to abstract the "interrupt control" layer.
9//! Keyboard controller should not be arch dependent
10//!
11
12use self::super::key::*;
13use crate::arch::x86_64::is_int_enabled;
14use crate::io::*;
15use crate::machine::device_io::*;
16use crate::proc::sync::semaphore::{Semaphore, SleepSemaphore};
17use crate::proc::sync::IRQHandlerEpilogue;
18use crate::proc::sync::L3Sync;
19use alloc::collections::VecDeque;
20use bitflags::bitflags;
21use core::cmp::{Eq, PartialEq};
22use core::sync::atomic::AtomicU32;
23use core::sync::atomic::Ordering;
24
25#[cfg(target_arch = "x86_64")]
26use crate::arch::x86_64::interrupt::{pic_8259, pic_8259::PicDeviceInt as PD};
27
28use super::key::Modifiers;
29
30/// this is the global keybard controller. It can only be used in prologue level,
31/// except for the `gather` field (consume_key is safe), which is atomic that can be safely used.
32/// (TODO maybe we can change this L3/L2 shared state abstraction)
33pub static KBCTL_GLOBAL: L3Sync<KeyboardController> =
34	L3Sync::new(KeyboardController::new());
35pub static KEY_BUFFER: SleepSemaphore<VecDeque<Key>> =
36	SleepSemaphore::new(VecDeque::new());
37
38pub struct KeyboardController {
39	keystate: KeyState,
40	/// gather require synchronization between L2 and L3, therefore we pack the
41	/// Key into a u32 and use an Atomic type here.
42	gather: AtomicU32,
43	cport: IOPort,
44	dport: IOPort,
45}
46
47/// we have a global controller, but we can't use it as the "driver" because
48/// drivers should be stateless.
49pub struct KeyboardDriver {}
50
51impl IRQHandlerEpilogue for KeyboardDriver {
52	unsafe fn do_prologue() {
53		debug_assert!(!is_int_enabled());
54		KBCTL_GLOBAL.l3_get_ref_mut().fetch_key();
55	}
56	unsafe fn do_epilogue() {
57		debug_assert!(is_int_enabled());
58		// it's safe to do atomic operactions without locking
59		let k = KBCTL_GLOBAL.l3_get_ref_mut_unchecked().consume_key();
60		if let Some(key) = k {
61			// this is not ideal. starvation can happen if a thread is
62			// intentionally holding the lock forever. This could also starve
63			// the other threads because context swap can only happen in-between
64			// epilogue execution.
65			KEY_BUFFER.v_unguarded(key);
66		}
67	}
68}
69
70struct KeyState {
71	modi: Modifiers, // active modifiers
72	prefix: Prefix,  // prefix state for some certain modifiers
73	scan: Option<u8>,
74}
75
76#[derive(Clone, Copy, Eq, PartialEq)]
77enum Prefix {
78	PREFIX1,
79	PREFIX2,
80	NONE, // don't confuse Option None with Prefix enum NONE
81}
82
83impl Prefix {
84	fn try_from_u8(val: u8) -> Option<Prefix> {
85		match val {
86			Defs::C_PREFIX1 => Some(Self::PREFIX1),
87			Defs::C_PREFIX2 => Some(Self::PREFIX2),
88			_ => None,
89		}
90	}
91}
92
93impl KeyState {
94	pub const fn new() -> Self {
95		Self {
96			modi: Modifiers::NONE,
97			prefix: Prefix::NONE,
98			scan: None,
99		}
100	}
101
102	fn get_leds(&self) -> u8 { return self.modi.bits() & 0b111; }
103}
104
105impl KeyboardController {
106	pub const fn new() -> Self {
107		Self {
108			keystate: KeyState::new(),
109			cport: IOPort::new(Defs::CTRL),
110			dport: IOPort::new(Defs::DATA),
111			gather: AtomicU32::new(Key::NONE_KEY),
112		}
113	}
114
115	// TODO: rollback lock state if setting led fails
116	fn toggle_lock(&mut self, lock: Modifiers) {
117		self.keystate.modi.toggle(lock);
118		self.update_led();
119	}
120	/// in some corner cases, e.g. keyboard control I/O may fail, while the
121	/// CAPSLOCK is set and the uppcase table is used for key decoding. So we
122	/// never set the leds explicitly, instead we update the leds from the
123	/// current keyboard state i.e. keystate.modi
124	fn update_led(&self) {
125		let leds = self.keystate.get_leds();
126		// TODO perhaps disable interrupts here
127		// TODO set a timeout. The ACK reply may never come
128		// 1. write command
129		self.dport.outb(Cmd::SetLed as u8);
130		unsafe { self.__block_until_data_available() }
131		// 2. wait for ack
132		let reply = self.dport.inb();
133		// 3. write leds
134		if reply == Msg::ACK as u8 {
135			self.dport.outb(leds);
136		}
137		// we should wait for another ACK but we don't really care.
138		// just make sure we wait...
139		unsafe {
140			self.__block_until_data_available();
141		}
142	}
143
144	fn update_state(&mut self, code: u8) {
145		// TODO investigate this code pattern: is there much runtime cost??
146		self.keystate.scan = Some(code);
147		if let Some(p) = Prefix::try_from_u8(code) {
148			self.keystate.prefix = p;
149			return;
150		}
151		if code & Defs::BREAK_BIT == 0 {
152			if self.press_event() {
153				self.decode_key();
154			}
155		} else {
156			self.release_event();
157		}
158		// the prefix should have been comsumed at this point so clear it
159		self.keystate.prefix = Prefix::NONE;
160	}
161	// TODO: need a rewrite for the toggle_lock and toggle_led
162	fn press_event(&mut self) -> bool {
163		let mut should_decode_ascii = false;
164		let code = self.keystate.scan.unwrap();
165		match code {
166			Defs::C_SHIFT_L | Defs::C_SHIFT_R => {
167				self.keystate.modi.insert(Modifiers::SHIFT)
168			}
169			Defs::C_ALT => match self.keystate.prefix {
170				Prefix::PREFIX1 => {
171					self.keystate.modi.insert(Modifiers::ALT_RIGHT)
172				}
173				_ => self.keystate.modi.insert(Modifiers::ALT_LEFT),
174			},
175			Defs::C_CTRL => match self.keystate.prefix {
176				Prefix::PREFIX1 => {
177					self.keystate.modi.insert(Modifiers::CTRL_RIGHT)
178				}
179				_ => self.keystate.modi.insert(Modifiers::CTRL_LEFT),
180			},
181			Defs::C_CAPSLOCK => self.toggle_lock(Modifiers::CAPSLOCK),
182			Defs::C_NUM_P => {
183				if !self.keystate.modi.contains(Modifiers::CTRL_LEFT) {
184					self.toggle_lock(Modifiers::NUMLOCK);
185				}
186			}
187			Defs::C_SCRLOCK => self.toggle_lock(Modifiers::SCROLL_LOCK),
188			Defs::C_DEL => {
189				if self
190					.keystate
191					.modi
192					.contains(Modifiers::CTRL_LEFT | Modifiers::ALT_LEFT)
193				{
194					unsafe {
195						self.reboot();
196					}
197				}
198			}
199			_ => {
200				should_decode_ascii = true;
201			}
202		}
203		should_decode_ascii
204	}
205
206	fn release_event(&mut self) {
207		// we only care about release events for shift/alt/ctrl
208		let code = self.keystate.scan.unwrap() & !Defs::BREAK_BIT;
209		match code {
210			Defs::C_SHIFT_L | Defs::C_SHIFT_R => {
211				self.keystate.modi.remove(Modifiers::SHIFT)
212			}
213			Defs::C_ALT => match self.keystate.prefix {
214				Prefix::PREFIX1 => {
215					self.keystate.modi.remove(Modifiers::ALT_RIGHT)
216				}
217				_ => self.keystate.modi.remove(Modifiers::ALT_LEFT),
218			},
219			Defs::C_CTRL => match self.keystate.prefix {
220				Prefix::PREFIX1 => {
221					self.keystate.modi.remove(Modifiers::CTRL_RIGHT)
222				}
223				_ => self.keystate.modi.remove(Modifiers::CTRL_LEFT),
224			},
225			_ => {}
226		}
227	}
228
229	#[inline(always)]
230	fn read_status(&self) -> Option<StatusReg> {
231		// TODO maybe there is a path which leads to invalid SR, e.g. timeout?
232		Some(StatusReg::from_bits_truncate(self.cport.inb()))
233	}
234
235	// this should be called by the interrupt handler prologue
236	fn fetch_key(&mut self) {
237		// mask keyboard interrupts when polling.
238		let was_masked = Self::is_int_masked();
239		if !was_masked {
240			Self::disable_keyboard_int();
241		}
242
243		// I'd like to see if this panics....
244		let sr = self.read_status().unwrap();
245		// ignore mouse events
246		if !sr.contains(StatusReg::OUTB) || sr.contains(StatusReg::AUXB) {
247			return;
248		}
249		self.update_state(self.dport.inb());
250		if !was_masked {
251			Self::enable_keyboard_int();
252		}
253	}
254
255	/// this is safe to be called from all sync levels
256	#[inline]
257	fn consume_key(&mut self) -> Option<Key> {
258		let res = self.gather.swap(Key::NONE_KEY, Ordering::Relaxed);
259		return Key::from_u32(res);
260	}
261
262	fn decode_key(&mut self) {
263		// the decode_key should not be called when there is no scancode.
264		// mask the breakbit
265		let s = self.keystate.scan.unwrap();
266		let c = s & !Defs::BREAK_BIT;
267		let m = self.keystate.modi;
268		let p = self.keystate.prefix;
269
270		if c == 53 && p == Prefix::PREFIX1 {
271			let k = Key { asc: b'/', modi: m, scan: s };
272			self.gather.store(k.to_u32(), Ordering::Relaxed);
273			return;
274		}
275
276		let asc = if m.contains(Modifiers::NUMLOCK)
277			&& p == Prefix::NONE
278			&& (71..=83).contains(&c)
279		{
280			ASC_NUM_TAB[c as usize - 71]
281		} else if m.contains(Modifiers::ALT_RIGHT) {
282			ALT_TAB[c as usize]
283		} else if m.contains(Modifiers::SHIFT) {
284			SHIFT_TAB[c as usize]
285		} else if m.contains(Modifiers::CAPSLOCK) {
286			if (16..=26).contains(&c)
287				|| (30..=40).contains(&c)
288				|| (44..=50).contains(&c)
289			{
290				SHIFT_TAB[c as usize]
291			} else {
292				NORMAL_TAB[c as usize]
293			}
294		} else {
295			NORMAL_TAB[c as usize]
296		};
297
298		let k = Key { asc, modi: m, scan: s };
299		self.gather.store(k.to_u32(), Ordering::Relaxed);
300	}
301
302	fn cycle_repeat_rate() {
303		todo!();
304	}
305
306	fn cycle_deley() {
307		todo!();
308	}
309
310	#[inline(always)]
311	unsafe fn __block_until_cmd_buffer_empty(&self) {
312		loop {
313			let s = self.read_status().unwrap();
314			if !s.contains(StatusReg::INB) {
315				break;
316			};
317		}
318	}
319
320	/// block until there is something in the data register to read
321	#[inline(always)]
322	unsafe fn __block_until_data_available(&self) {
323		loop {
324			let status = self.read_status().unwrap();
325			if status.contains(StatusReg::OUTB) {
326				break;
327			}
328		}
329	}
330}
331
332// pic_8259 is x86 only
333#[cfg(target_arch = "x86_64")]
334impl KeyboardController {
335	#[inline(always)]
336	fn disable_keyboard_int() { pic_8259::forbid(PD::KEYBOARD); }
337
338	#[inline(always)]
339	fn enable_keyboard_int() { pic_8259::allow(PD::KEYBOARD); }
340
341	#[inline(always)]
342	fn is_int_masked() -> bool { pic_8259::is_masked(PD::KEYBOARD) }
343}
344
345// for whatever reason the PS/2 keyboard controls the "shutdown"...
346impl KeyboardController {
347	pub unsafe fn reboot(&self) {
348		// what ever magic it is, tell BIOS this is an intentional reset and don't
349		// run memory test
350		println!("reboot...");
351		*(0x472 as *mut u16) = 0x1234;
352		self.__block_until_cmd_buffer_empty();
353		self.cport.outb(Cmd::CpuReset as u8);
354	}
355}
356
357enum Cmd {
358	// these commands are sent through DATA port
359	SetLed   = 0xed,
360	ScanCode = 0xf0, // Get or set current scancode set
361	SetSpeed = 0xf3,
362	CpuReset = 0xfe,
363}
364
365bitflags! {
366#[derive(Debug)]
367struct StatusReg:u8 {
368	const NONE			= 0;
369	const OUTB			= 1 << 0;	// output buffer full (can read)
370	const INB			= 1 << 1;	// input buffer full (don't write yet)
371	const SYS			= 1 << 2;	// System flag, self test success
372	const CMD_DATA		= 1 << 3;	// 0: last write to input buffer was data (0x60)
373									// 1: last write to input buffer was command (0x64)
374	const NOT_LOCKED	= 1 << 4;	// Keyboard Lock. 1: not locked 0: locked
375	const AUXB			= 1 << 5;	// AUXB: aux output buffer full.
376									// on AT,	1: TIMEOUT
377									// on PS/2, 0: keyboard		1: mouse
378	const TIMEOUT		= 1 << 6;	// 0: OK 1: Timeout.
379	const PARITY_ERR	= 1 << 7;
380}
381}
382
383enum Msg {
384	ACK = 0xfa,
385}
386
387struct Defs;
388impl Defs {
389	pub const CTRL: u16 = 0x64;
390	pub const DATA: u16 = 0x60;
391	pub const CPU_RESET: u8 = 0xfe;
392	pub const BREAK_BIT: u8 = 1 << 7;
393	// defs of special scan codes.
394	pub const C_PREFIX1: u8 = 0xe0;
395	pub const C_PREFIX2: u8 = 0xe1;
396	pub const C_SHIFT_L: u8 = 0x2a;
397	pub const C_SHIFT_R: u8 = 0x36;
398	pub const C_ALT: u8 = 0x38;
399	pub const C_CTRL: u8 = 0x1d;
400	pub const C_CAPSLOCK: u8 = 0x3a;
401	pub const C_SCRLOCK: u8 = 0x46;
402	pub const C_NUM_P: u8 = 0x45; // NumLock or Pause
403	pub const C_F1: u8 = 0x3b;
404	pub const C_DEL: u8 = 0x53;
405	pub const C_UP: u8 = 0x48;
406	pub const C_DOWN: u8 = 0x50;
407	pub const C_LEFT: u8 = 0x4b;
408	pub const C_RIGHT: u8 = 0x4d;
409	pub const C_DIV: u8 = 0x8;
410}