MonkOS  v0.1
A simple 64-bit operating system (x86_64)
keyboard.c
Go to the documentation of this file.
1 //============================================================================
2 /// @file keyboard.c
3 /// @brief Keyboard input routines.
4 //
5 // Copyright 2016 Brett Vickers.
6 // Use of this source code is governed by a BSD-style license that can
7 // be found in the MonkOS LICENSE file.
8 //============================================================================
9 
10 #include <core.h>
11 #include <libc/string.h>
12 #include <kernel/x86/cpu.h>
13 #include <kernel/device/keyboard.h>
15 
16 // Keyboard I/O ports
17 #define KB_PORT_DATA 0x60 ///< Keyboard I/O data port.
18 
19 // Other constants
20 #define MAX_BUFSIZ 32 ///< Keyboard input buffer size.
21 
22 // Key code abbreviations, used to set up the default scan map table below.
23 #define BSP KEY_BACKSPACE
24 #define TAB KEY_TAB
25 #define ENT KEY_ENTER
26 #define ESC KEY_ESCAPE
27 #define CTL KEY_CTRL
28 #define SHF KEY_SHIFT
29 #define ALT KEY_ALT
30 #define PSC KEY_PRTSCR
31 #define CLK KEY_CAPSLOCK
32 #define NLK KEY_NUMLOCK
33 #define SLK KEY_SCRLOCK
34 #define KIN KEY_INSERT
35 #define KEN KEY_END
36 #define KDN KEY_DOWN
37 #define KPD KEY_PGDN
38 #define KLT KEY_LEFT
39 #define KCT KEY_CENTER
40 #define KRT KEY_RIGHT
41 #define KHM KEY_HOME
42 #define KUP KEY_UP
43 #define KPU KEY_PGUP
44 #define KDL KEY_DEL
45 #define KMI KEY_MINUS
46 #define KPL KEY_PLUS
47 #define F_1 KEY_F1
48 #define F_2 KEY_F2
49 #define F_3 KEY_F3
50 #define F_4 KEY_F4
51 #define F_5 KEY_F5
52 #define F_6 KEY_F6
53 #define F_7 KEY_F7
54 #define F_8 KEY_F8
55 #define F_9 KEY_F9
56 #define F10 KEY_F10
57 #define F11 KEY_F11
58 #define F12 KEY_F12
59 #define SES KEY_SCANESC
60 #define INV KEY_INVALID
61 #define APO '\''
62 #define BSL '\\'
63 
64 /// US English PSC/2 keyboard scan map (default setting)
65 static const keylayout_t ps2_layout =
66 {
67  .shifted =
68  {
69  INV, ESC, '!', '@', '#', '$', '%', '^',
70  '&', '*', '(', ')', '_', '+', BSP, TAB, // 0
71  'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
72  'O', 'P', '{', '}', ENT, CTL, 'A', 'S', // 1
73  'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
74  '"', '~', SHF, '|', 'Z', 'X', 'C', 'V', // 2
75  'B', 'N', 'M', '<', '>', '?', SHF, PSC,
76  ALT, ' ', CLK, F_1, F_2, F_3, F_4, F_5, // 3
77  F_6, F_7, F_8, F_9, F10, NLK, SLK, KHM,
78  KUP, KPU, KMI, KLT, KCT, KRT, KPL, KEN, // 4
79  KDN, KPD, KIN, KDL, INV, INV, INV, F11,
80  F12, INV, INV, INV, INV, INV, INV, INV, // 5
81  SES, SES, INV, INV, INV, INV, INV, INV,
82  INV, INV, INV, INV, INV, INV, INV, INV, // 6
83  INV, INV, INV, INV, INV, INV, INV, INV,
84  INV, INV, INV, INV, INV, INV, INV, INV, // 7
85  },
86  .unshifted =
87  {
88  INV, ESC, '1', '2', '3', '4', '5', '6',
89  '7', '8', '9', '0', '-', '=', BSP, TAB, // 0
90  'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
91  'o', 'p', '[', ']', ENT, CTL, 'a', 's', // 1
92  'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
93  APO, '`', SHF, BSL, 'z', 'x', 'c', 'v', // 2
94  'b', 'n', 'm', ',', '.', '/', SHF, PSC,
95  ALT, ' ', CLK, F_1, F_2, F_3, F_4, F_5, // 3
96  F_6, F_7, F_8, F_9, F10, NLK, SLK, KHM,
97  KUP, KPU, KMI, KLT, KCT, KRT, KPL, KEN, // 4
98  KDN, KPD, KIN, KDL, INV, INV, INV, F11,
99  F12, INV, INV, INV, INV, INV, INV, INV, // 5
100  SES, SES, INV, INV, INV, INV, INV, INV,
101  INV, INV, INV, INV, INV, INV, INV, INV, // 6
102  INV, INV, INV, INV, INV, INV, INV, INV,
103  INV, INV, INV, INV, INV, INV, INV, INV, // 7
104  },
105 };
106 
107 /// Keyboard state.
108 struct kbstate
109 {
110  keylayout_t layout; ///< The installed keyboard layout.
111  uint8_t meta; ///< Mask of meta keys currently pressed.
112  uint8_t buf_head; ///< Index of oldest key in buf.
113  uint8_t buf_tail; ///< Index of next empty slot in buf.
114  atomic_uchar buf_size; ///< Number of keys in the buf.
115  key_t buf[MAX_BUFSIZ]; ///< Buffer holding unconsumed keys.
116 };
117 
118 typedef struct kbstate kbstate_t;
119 
120 /// Current keyboard state.
121 static kbstate_t state;
122 
123 static inline void
124 toggle(uint8_t flag)
125 {
126  if (state.meta & flag)
127  state.meta &= ~flag;
128  else
129  state.meta |= flag;
130 }
131 
132 static void
133 addkey(uint8_t brk, uint8_t meta, uint8_t code, uint8_t ch)
134 {
135  // Reset the scan code escape state whenever a new key is added to the
136  // buffer.
137  state.meta &= ~META_ESCAPED;
138 
139  // Is the buffer full?
140  // There is no need for an atomic comparison here, because the ISR
141  // function calling addkey can never be interrupted by anything that
142  // touches the buffer.
143  if (state.buf_size == MAX_BUFSIZ)
144  return;
145 
146  key_t key =
147  {
148  .brk = brk,
149  .meta = meta,
150  .code = code,
151  .ch = ch
152  };
153 
154  // Add the character to the tail of the buffer.
155  state.buf[state.buf_tail++] = key;
156  if (state.buf_tail == MAX_BUFSIZ)
157  state.buf_tail = 0;
158 
159  // state.buf_size++;
160  atomic_fetch_add_explicit(&state.buf_size, 1, memory_order_relaxed);
161 }
162 
163 static void
164 isr_keyboard(const interrupt_context_t *context)
165 {
166  (void)context;
167 
168  // Get the scan code and the break state (key up or key down).
169  uint8_t scancode = io_inb(KB_PORT_DATA);
170  bool keyup = !!(scancode & 0x80);
171 
172  // Chop off the break bit.
173  scancode &= ~0x80;
174 
175  // Get the shifted state.
176  bool shifted = !!(state.meta & META_SHIFT);
177 
178  // Convert the scan code into an unshifted key code.
179  uint8_t ukeycode = state.layout.unshifted[scancode];
180 
181  // Is the key a keyboard scan escape code? If so, don't add it to the
182  // buffer, but track the escape as a meta-state.
183  if (ukeycode == KEY_SCANESC) {
184  state.meta |= META_ESCAPED;
185  goto done;
186  }
187 
188  // Alter shift state based on capslock state.
189  if (state.meta & META_CAPSLOCK) {
190  if ((ukeycode >= 'a') && (ukeycode <= 'z'))
191  shifted = !shifted;
192  }
193 
194  // Convert the scan code to a properly shifted key code.
195  uint8_t keycode = shifted ? state.layout.shifted[scancode] : ukeycode;
196 
197  // Key up?
198  if (keyup) {
199  switch (keycode)
200  {
201  case KEY_SHIFT:
202  state.meta &= ~META_SHIFT;
203  break;
204 
205  case KEY_CTRL:
206  state.meta &= ~META_CTRL;
207  break;
208 
209  case KEY_ALT:
210  state.meta &= ~META_ALT;
211  break;
212 
213  case KEY_CAPSLOCK:
215  break;
216 
217  case KEY_NUMLOCK:
219  break;
220 
221  case KEY_SCRLOCK:
223  break;
224  }
225  addkey(KEYBRK_UP, state.meta, ukeycode, 0);
226  }
227  // Key down?
228  else {
229  switch (keycode)
230  {
231  case KEY_SHIFT:
232  state.meta |= META_SHIFT;
233  break;
234 
235  case KEY_CTRL:
236  state.meta |= META_CTRL;
237  break;
238 
239  case KEY_ALT:
240  state.meta |= META_ALT;
241  break;
242  }
243 
244  // Convert the key to a character.
245  char ch = 0;
246  if (keycode < 0x80) {
247  switch (state.meta & (META_CTRL | META_ALT))
248  {
249  case 0:
250  ch = (char)keycode;
251  break;
252 
253  case META_CTRL:
254  if ((ukeycode >= 'a') && (ukeycode <= 'z'))
255  ch = (char)(ukeycode - 'a' + 1);
256  break;
257  }
258  }
259  addkey(KEYBRK_DOWN, state.meta, ukeycode, ch);
260  }
261 
262 done:
263  // Send the end-of-interrupt signal.
265 }
266 
267 void
269 {
270  // Default to the PSC/2 keyboard layout.
271  memcpy(&state.layout, &ps2_layout, sizeof(state.layout));
272 
273  // Initialize keyboard state.
274  state.meta = 0;
275  state.buf_head = 0;
276  state.buf_tail = 0;
277  state.buf_size = 0;
278  memzero(&state.buf, sizeof(state.buf));
279 
280  // Assign the interrupt service routine.
282 
283  // Enable the keyboard hardware interrupt (IRQ1).
285 }
286 
287 void
288 kb_setlayout(keylayout_t *layout)
289 {
290  memcpy(&state.layout, layout, sizeof(state.layout));
291 }
292 
293 char
295 {
296  for (;;) {
297  // Buffer empty? (state.buf_size == 0?) Check atomically because
298  // this function could be interrupted by the keyboard ISR.
299  uint8_t size = 0;
300  if (atomic_compare_exchange_strong(&state.buf_size, &size, 0))
301  return 0;
302 
303  // Pull the next character from the head of the buffer.
304  char ch = state.buf[state.buf_head++].ch;
305  if (state.buf_head == MAX_BUFSIZ)
306  state.buf_head = 0;
307 
308  // state.buf_size--
309  atomic_fetch_sub_explicit(&state.buf_size, 1, memory_order_relaxed);
310 
311  // Valid character?
312  if (ch != 0)
313  return ch;
314  }
315 }
316 
317 bool
318 kb_getkey(key_t *key)
319 {
320  // Buffer empty? (state.buf_size == 0?) Check atomically because this
321  // function could be interrupted by the keyboard ISR.
322  uint8_t size = 0;
323  if (atomic_compare_exchange_strong(&state.buf_size, &size, 0)) {
324  *(uint32_t *)key = 0;
325  return false;
326  }
327 
328  // Pull the next character from the head of the buffer.
329  *key = state.buf[state.buf_head++];
330  if (state.buf_head == MAX_BUFSIZ)
331  state.buf_head = 0;
332 
333  // state.buf_size--
334  atomic_fetch_sub_explicit(&state.buf_size, 1, memory_order_relaxed);
335 
336  return true;
337 }
338 
339 uint8_t
341 {
342  return state.meta;
343 }
#define ALT
Definition: keyboard.c:29
bool kb_getkey(key_t *key)
Return the available next key from the keyboard&#39;s input buffer.
Definition: keyboard.c:318
#define PIC_PORT_CMD_MASTER
Command port for master PIC.
Definition: interrupt.h:28
uint8_t kb_meta()
Return the current meta-key bit mask.
Definition: keyboard.c:340
#define KEYBRK_DOWN
Key-down break code.
Definition: keyboard.h:19
#define META_SHIFT
Set while the shift key is pressed.
Definition: keyboard.h:23
__forceinline uint8_t io_inb(uint16_t port)
Definition: cpu_inl.h:47
#define META_SCRLOCK
Set while scroll lock is on.
Definition: keyboard.h:29
__forceinline void io_outb(uint16_t port, uint8_t value)
Definition: cpu_inl.h:59
#define CTL
Definition: keyboard.c:27
#define PIC_CMD_EOI
End of interrupt.
Definition: interrupt.h:34
String and memory operations.
#define KCT
Definition: keyboard.c:39
key_t buf[MAX_BUFSIZ]
Buffer holding unconsumed keys.
Definition: keyboard.c:115
#define KUP
Definition: keyboard.c:42
#define TRAP_IRQ_KEYBOARD
Definition: interrupt.h:25
void isr_set(int interrupt, isr_handler handler)
Set an interrupt service routine for the given interrupt number.
Keyboard input routines.
#define F10
Definition: keyboard.c:56
#define NLK
Definition: keyboard.c:32
#define BSL
Definition: keyboard.c:62
Core include file.
Keyboard state.
Definition: keyboard.c:108
#define META_CAPSLOCK
Set while caps lock is on.
Definition: keyboard.h:27
keylayout_t layout
The installed keyboard layout.
Definition: keyboard.c:110
#define KLT
Definition: keyboard.c:38
#define F_7
Definition: keyboard.c:53
#define KMI
Definition: keyboard.c:45
#define IRQ_KEYBOARD
Definition: interrupt.h:21
#define KHM
Definition: keyboard.c:41
#define F_1
Definition: keyboard.c:47
static const keylayout_t ps2_layout
US English PSC/2 keyboard scan map (default setting)
Definition: keyboard.c:65
#define KEN
Definition: keyboard.c:35
#define KPU
Definition: keyboard.c:43
void kb_init()
Initialize the keyboard so that it can provide input to the kernel.
Definition: keyboard.c:268
#define KPD
Definition: keyboard.c:37
atomic_uchar buf_size
Number of keys in the buf.
Definition: keyboard.c:114
char kb_getchar()
Return the next available character from the keyboard&#39;s input buffer.
Definition: keyboard.c:294
void kb_setlayout(keylayout_t *layout)
Install a new keyboard layout.
Definition: keyboard.c:288
#define META_CTRL
Set while the ctrl key is pressed.
Definition: keyboard.h:24
#define F_4
Definition: keyboard.c:50
#define F12
Definition: keyboard.c:58
#define KRT
Definition: keyboard.c:40
#define KEYBRK_UP
Key-up break code.
Definition: keyboard.h:20
#define ENT
Definition: keyboard.c:25
uint8_t buf_head
Index of oldest key in buf.
Definition: keyboard.c:112
#define META_NUMLOCK
Set while num lock is on.
Definition: keyboard.h:28
#define KIN
Definition: keyboard.c:34
static void toggle(uint8_t flag)
Definition: keyboard.c:124
uint8_t meta
Mask of meta keys currently pressed.
Definition: keyboard.c:111
#define ESC
Definition: keyboard.c:26
#define BSP
Definition: keyboard.c:23
Interrupt handling operations.
uint8_t brk
KEYBRK_UP or KEYBRK_DOWN.
Definition: keyboard.h:89
x86 CPU-specific function implementations.
#define PSC
Definition: keyboard.c:30
#define INV
Definition: keyboard.c:60
void irq_enable(uint8_t irq)
Tell the PIC to enable a hardware interrupt.
#define CLK
Definition: keyboard.c:31
#define SHF
Definition: keyboard.c:28
#define F_9
Definition: keyboard.c:55
static void isr_keyboard(const interrupt_context_t *context)
Definition: keyboard.c:164
#define F11
Definition: keyboard.c:57
#define KDL
Definition: keyboard.c:44
static void addkey(uint8_t brk, uint8_t meta, uint8_t code, uint8_t ch)
Definition: keyboard.c:133
Escaped scan code.
Definition: keyboard.h:76
void * memzero(void *dst, size_t num)
Fill a region of memory with zeroes.
#define F_3
Definition: keyboard.c:49
#define TAB
Definition: keyboard.c:24
#define F_6
Definition: keyboard.c:52
#define MAX_BUFSIZ
Keyboard input buffer size.
Definition: keyboard.c:20
#define SLK
Definition: keyboard.c:33
#define META_ALT
Set while the alt key is pressed.
Definition: keyboard.h:25
The log context describes the state of the log buffers.
Definition: log.c:46
#define META_ESCAPED
Set if key&#39;s scan code is escaped.
Definition: keyboard.h:26
keycode
Key code values representing individual keys on the keyboard.
Definition: keyboard.h:38
static kbstate_t state
Current keyboard state.
Definition: keyboard.c:121
uint8_t buf_tail
Index of next empty slot in buf.
Definition: keyboard.c:113
#define F_8
Definition: keyboard.c:54
#define F_2
Definition: keyboard.c:48
#define KB_PORT_DATA
Keyboard I/O data port.
Definition: keyboard.c:17
#define KDN
Definition: keyboard.c:36
A record representing the state of the keyboard at the time a key was presssed or unpressed...
Definition: keyboard.h:87
#define KPL
Definition: keyboard.c:46
#define SES
Definition: keyboard.c:59
#define APO
Definition: keyboard.c:61
#define F_5
Definition: keyboard.c:51
void * memcpy(void *dst, const void *src, size_t num)
Copy bytes from one memory region to another.