aboutsummaryrefslogtreecommitdiff
path: root/src/machine/keyctrl.rs
blob: 40385a2199cfeed0743dadc1da25f779066814cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
use self::super::key::*;
use crate::machine::device_io::*;
use bitflags::bitflags;
use core::cmp;
use core::cmp::{Eq, PartialEq};
use core::ffi::c_uchar;
use num_enum::{IntoPrimitive, TryFromPrimitive};

// Driver for the PS/2 keybard/mouse controller
// beyound OOStuBS:
// The 'gather' field should NEVER have imcomplete state: it should either be a
// valid key, or nothing.
// TODO what if IO ops fails or timeout?
// TODO figure out how to abstract the "interrupt control" layer.
// Keyboard controller should not be arch dependent
#[cfg(target_arch = "x86_64")]
use crate::arch::x86_64::interrupt::{pic_8259, pic_8259::PicDeviceInt as PD};

use super::key::Modifiers;

// TODO
// reboot()
// set_led(char led,bool on)
// set_repeat_rate(int speed,int delay)

pub struct KeyboardController {
	leds: Led,
	keystate: KeyState,
	gather: Option<Key>, // if not collected timely it will be overwritten
	cport: IOPort,
	dport: IOPort,
}

struct KeyState {
	modi: Modifiers, // active modifiers
	prefix: Prefix,  // prefix state for some certain modifiers
	scan: Option<u8>,
}

#[derive(Clone, Copy, Eq, PartialEq)]
enum Prefix {
	PREFIX1,
	PREFIX2,
	NONE, // don't confuse Option None with Prefix enum NONE
}

impl Prefix {
	pub fn try_from_u8(val: u8) -> Option<Prefix> {
		match val {
			Defs::C_PREFIX1 => Some(Self::PREFIX1),
			Defs::C_PREFIX2 => Some(Self::PREFIX2),
			_ => None,
		}
	}
}

impl KeyState {
	pub fn new() -> Self {
		return Self {
			modi: Modifiers::NONE,
			prefix: Prefix::NONE,
			scan: None,
		};
	}

	pub fn toggle_capslock(&mut self) {
		self.modi.toggle(Modifiers::CAPSLOCK);
	}

	pub fn toggle_numlock(&mut self) {
		self.modi.toggle(Modifiers::NUMLOCK);
	}

	pub fn toggle_scroll_lock(&mut self) {
		self.modi.toggle(Modifiers::SCROLL_LOCK);
	}
}

impl KeyboardController {
	pub fn new() -> Self {
		Self {
			leds: Led::NONE,
			keystate: KeyState::new(),
			cport: IOPort::new(Defs::CTRL),
			dport: IOPort::new(Defs::DATA),
			gather: None,
		}
	}

	// Led and lock state are toggled together, to prevent out-of-sync
	// TODO: rollback lock state if setting led fails
	fn toggle_lock(&mut self, lock: Modifiers) {
		let led = match lock {
			Modifiers::SCROLL_LOCK => Some(Led::SCROLL_LOCK),
			Modifiers::CAPSLOCK => Some(Led::CAPS_LOCK),
			Modifiers::NUMLOCK => Some(Led::NUM_LOCk),
			_ => None,
		};
		if let Some(led) = led {
			self.keystate.modi.toggle(lock);
			self.toggle_led(led);
		}
	}

	fn toggle_led(&mut self, led: Led) {
		self.leds.toggle(led);
		todo!("toggle keyboard led: ");
	}

	pub fn update_state(&mut self, code: u8) {
		// TODO investigate this code pattern: is there much runtime cost??
		self.keystate.scan = Some(code);
		if let Some(p) = Prefix::try_from_u8(code) {
			self.keystate.prefix = p;
			return;
		}
		if code & Defs::BREAK_BIT == 0 {
			if self.press_event() {
				self.decode_key();
			}
		} else {
			self.release_event();
		}
		// the prefix should have been comsumed at this point so clear it
		self.keystate.prefix = Prefix::NONE;
	}

	fn press_event(&mut self) -> bool {
		let mut should_decode_ascii = false;
		let code = self.keystate.scan.unwrap();
		match code {
			Defs::C_SHIFT_L | Defs::C_SHIFT_R => self.keystate.modi.insert(Modifiers::SHIFT),
			Defs::C_ALT => match self.keystate.prefix {
				Prefix::PREFIX1 => self.keystate.modi.insert(Modifiers::ALT_RIGHT),
				_ => self.keystate.modi.insert(Modifiers::ALT_LEFT),
			},
			Defs::C_CTRL => match self.keystate.prefix {
				Prefix::PREFIX1 => self.keystate.modi.insert(Modifiers::CTRL_RIGHT),
				_ => self.keystate.modi.insert(Modifiers::CTRL_LEFT),
			},
			Defs::C_CAPSLOCK => self.toggle_lock(Modifiers::CAPSLOCK),
			Defs::C_NUM_P => {
				if !self.keystate.modi.contains(Modifiers::CTRL_LEFT) {
					self.toggle_lock(Modifiers::NUMLOCK);
				}
			}
			Defs::C_SCRLOCK => self.toggle_lock(Modifiers::SCROLL_LOCK),
			_ => {
				should_decode_ascii = true;
			}
		}
		should_decode_ascii
	}

	fn release_event(&mut self) {
		// we only care about release events for shift/alt/ctrl
		let code = self.keystate.scan.unwrap() & !Defs::BREAK_BIT;
		match code {
			Defs::C_SHIFT_L | Defs::C_SHIFT_R => self.keystate.modi.remove(Modifiers::SHIFT),
			Defs::C_ALT => match self.keystate.prefix {
				Prefix::PREFIX1 => self.keystate.modi.remove(Modifiers::ALT_RIGHT),
				_ => self.keystate.modi.remove(Modifiers::ALT_LEFT),
			},
			Defs::C_CTRL => match self.keystate.prefix {
				Prefix::PREFIX1 => self.keystate.modi.remove(Modifiers::CTRL_RIGHT),
				_ => self.keystate.modi.remove(Modifiers::CTRL_LEFT),
			},
			_ => {}
		}
	}

	#[inline(always)]
	pub fn read_status(&self) -> Option<StatusReg> {
		// TODO maybe there is a path which leads to invalid SR, e.g. timeout?
		Some(StatusReg::from_bits_truncate(self.cport.inb()))
	}
	
	// this should be called by the interrupt handler prologue
	pub fn fetch_key(&mut self) {
		// I'd like to see if this panics....
		let sr = self.read_status().unwrap();
		// ignore mouse events
		if !sr.contains(StatusReg::OUTB) || sr.contains(StatusReg::AUXB) {
			return;
		}
		self.update_state(self.dport.inb());
	}
	
	// this should be called by the "epilogue"
	pub fn consume_key(&mut self) -> Option<Key>{
		let res = self.gather.clone();
		self.gather = None;
		return res
	}

	pub fn decode_key(&mut self) {
		// the decode_key should not be called when there is no scancode.
		// mask the breakbit
		let s = self.keystate.scan.unwrap();
		let c = s & !Defs::BREAK_BIT;
		let m = self.keystate.modi;
		let p = self.keystate.prefix;

		if c == 53 && p == Prefix::PREFIX1 {
			self.gather = Some(Key {
				asc: b'/',
				modi: m,
				scan: s,
			});
			return;
		}

		let asc = if m.contains(Modifiers::NUMLOCK) && p == Prefix::NONE && c >= 71 && c <= 83 {
			ASC_NUM_TAB[c as usize - 71]
		} else if m.contains(Modifiers::ALT_RIGHT) {
			ALT_TAB[c as usize]
		} else if m.contains(Modifiers::SHIFT) {
			SHIFT_TAB[c as usize]
		} else if m.contains(Modifiers::CAPSLOCK) {
			if (c >= 16 && c <= 26) || (c >= 30 && c <= 40) || (c >= 44 && c <= 50) {
				SHIFT_TAB[c as usize]
			} else {
				NORMAL_TAB[c as usize]
			}
		} else {
			NORMAL_TAB[c as usize]
		};

		self.gather = Some(Key {
			asc,
			modi: m,
			scan: s,
		});
	}

	pub fn set_repeat_rate_delay() {
		todo!();
	}

	pub fn reboot(&mut self) {
		todo!();
	}
}

// x86 and arm has different interrupt controller
#[cfg(target_arch = "x86_64")]
impl KeyboardController {
	#[inline(always)]
	fn disable_keyboard_int() {
		pic_8259::forbid(PD::KEYBOARD);
	}

	#[inline(always)]
	fn enable_keyboard_int() {
		pic_8259::allow(PD::KEYBOARD);
	}

	#[inline(always)]
	fn toggle_keyboard_int(enable: bool) {
		if enable {
			Self::enable_keyboard_int()
		} else {
			Self::disable_keyboard_int()
		}
	}

	#[inline(always)]
	fn is_int_masked() -> bool {
		pic_8259::is_masked(PD::KEYBOARD)
	}
}

enum Cmd {
	// these commands are sent through DATA port
	SetLed = 0xed,
	ScanCode = 0xf0, // Get or set current scancode set
	SetSpeed = 0xf3,
}

bitflags! {
pub struct Led:u8 {
	const NONE			= 0;
	const SCROLL_LOCK	= 1<<0;
	const NUM_LOCk		= 1<<1;
	const CAPS_LOCK		= 1<<2;
}
}

bitflags! {
pub struct StatusReg:u8 {
	const NONE			= 0;
	const OUTB			= 1 << 0;	// output buffer full (can read)
	const INB			= 1 << 1;	// input buffer full (don't write yet)
	const SYS			= 1 << 2;	// System flag, self test success
	const CMD_DATA		= 1 << 3;	// 0: last write to input buffer was data (0x60)
									// 1: last write to input buffer was command (0x64)
	const NOT_LOCKED	= 1 << 4;	// Keyboard Lock. 1: not locked 0: locked
	const AUXB			= 1 << 5;	// AUXB: aux output buffer full.
									// on AT,	1: TIMEOUT
									// on PS/2, 0: keyboard		1: mouse
	const TIMEOUT		= 1 << 6;	// 0: OK 1: Timeout.
	const PARITY_ERR	= 1 << 7;
}
}

enum Msg {
	ACK = 0xfa,
}

pub struct Defs;
impl Defs {
	pub const CTRL: u16 = 0x64;
	pub const DATA: u16 = 0x60;
	pub const CPU_RESET: u8 = 0xfe;
	pub const BREAK_BIT: u8 = 1 << 7;
	// defs of special scan codes.
	pub const C_PREFIX1: u8 = 0xe0;
	pub const C_PREFIX2: u8 = 0xe1;
	pub const C_SHIFT_L: u8 = 0x2a;
	pub const C_SHIFT_R: u8 = 0x36;
	pub const C_ALT: u8 = 0x38;
	pub const C_CTRL: u8 = 0x1d;
	pub const C_CAPSLOCK: u8 = 0x3a;
	pub const C_SCRLOCK: u8 = 0x46;
	pub const C_NUM_P: u8 = 0x45; // NumLock or Pause
	pub const C_F1: u8 = 0x3b;
	pub const C_DEL: u8 = 0x53;
	pub const C_UP: u8 = 0x48;
	pub const C_DOWN: u8 = 0x50;
	pub const C_LEFT: u8 = 0x4b;
	pub const C_RIGHT: u8 = 0x4d;
	pub const C_DIV: u8 = 0x8;
}