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