Browse Source

[usb] Add basic support for USB keyboards

When USB network card drivers are used, the BIOS' legacy USB
capability is necessarily disabled since there is no way to share the
host controller between the BIOS and iPXE.  This currently results in
USB keyboards becoming non-functional in USB-enabled builds of iPXE.

Fix by adding basic support for USB keyboards, enabled by default in
iPXE builds which include USB support.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
372672275e

+ 7
- 0
src/config/config_usb.c View File

@@ -43,3 +43,10 @@ REQUIRE_OBJECT ( ehci );
43 43
 #ifdef USB_HCD_UHCI
44 44
 REQUIRE_OBJECT ( uhci );
45 45
 #endif
46
+
47
+/*
48
+ * Drag in USB peripherals
49
+ */
50
+#ifdef USB_KEYBOARD
51
+REQUIRE_OBJECT ( usbkbd );
52
+#endif

+ 1
- 0
src/config/defaults/pcbios.h View File

@@ -39,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
39 39
 #define	USB_HCD_XHCI		/* xHCI USB host controller */
40 40
 #define	USB_HCD_EHCI		/* EHCI USB host controller */
41 41
 #define	USB_HCD_UHCI		/* UHCI USB host controller */
42
+#define	USB_KEYBOARD		/* USB keyboards */
42 43
 
43 44
 #define	REBOOT_CMD		/* Reboot command */
44 45
 #define	CPUID_CMD		/* x86 CPU feature detection command */

+ 6
- 0
src/config/usb.h View File

@@ -19,6 +19,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
19 19
 //#undef	USB_HCD_EHCI		/* EHCI USB host controller */
20 20
 //#undef	USB_HCD_UHCI		/* UHCI USB host controller */
21 21
 
22
+/*
23
+ * USB peripherals
24
+ *
25
+ */
26
+//#undef	USB_KEYBOARD		/* USB keyboards */
27
+
22 28
 #include <config/named.h>
23 29
 #include NAMED_CONFIG(usb.h)
24 30
 #include <config/local/usb.h>

+ 509
- 0
src/drivers/usb/usbkbd.c View File

@@ -0,0 +1,509 @@
1
+/*
2
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+#include <stdlib.h>
27
+#include <string.h>
28
+#include <errno.h>
29
+#include <assert.h>
30
+#include <ipxe/console.h>
31
+#include <ipxe/keys.h>
32
+#include <ipxe/usb.h>
33
+#include "usbkbd.h"
34
+
35
+/** @file
36
+ *
37
+ * USB keyboard driver
38
+ *
39
+ */
40
+
41
+/** List of USB keyboards */
42
+static LIST_HEAD ( usb_keyboards );
43
+
44
+/******************************************************************************
45
+ *
46
+ * Keyboard map
47
+ *
48
+ ******************************************************************************
49
+ */
50
+
51
+/**
52
+ * Map USB keycode to iPXE key
53
+ *
54
+ * @v keycode		Keycode
55
+ * @v modifiers		Modifiers
56
+ * @ret key		iPXE key
57
+ *
58
+ * Key codes are defined in the USB HID Usage Tables Keyboard/Keypad
59
+ * page.
60
+ */
61
+static unsigned int usbkbd_map ( unsigned int keycode,
62
+				 unsigned int modifiers ) {
63
+	unsigned int key;
64
+
65
+	if ( keycode < USBKBD_KEY_A ) {
66
+		/* Not keys */
67
+		key = 0;
68
+	} else if ( keycode <= USBKBD_KEY_Z ) {
69
+		/* Alphabetic keys */
70
+		key = ( keycode - USBKBD_KEY_A + 'a' );
71
+		if ( modifiers & USBKBD_CTRL ) {
72
+			key -= ( 'a' - CTRL_A );
73
+		} else if ( modifiers & USBKBD_SHIFT ) {
74
+			key -= ( 'a' - 'A' );
75
+		}
76
+	} else if ( keycode <= USBKBD_KEY_0 ) {
77
+		/* Numeric key row */
78
+		if ( modifiers & USBKBD_SHIFT ) {
79
+			key = "!@#$%^&*()" [ keycode - USBKBD_KEY_1 ];
80
+		} else {
81
+			key = ( ( ( keycode - USBKBD_KEY_1 + 1 ) % 10 ) + '0' );
82
+		}
83
+	} else if ( keycode <= USBKBD_KEY_SPACE ) {
84
+		/* Unmodifiable keys */
85
+		static const uint8_t unmodifable[] =
86
+			{ LF, ESC, BACKSPACE, TAB, ' ' };
87
+		key = unmodifable[ keycode - USBKBD_KEY_ENTER ];
88
+	} else if ( keycode <= USBKBD_KEY_SLASH ) {
89
+		/* Punctuation keys */
90
+		if ( modifiers & USBKBD_SHIFT ) {
91
+			key = "_+{}|~:\"~<>?" [ keycode - USBKBD_KEY_MINUS ];
92
+		} else {
93
+			key = "-=[]\\#;'`,./" [ keycode - USBKBD_KEY_MINUS ];
94
+		}
95
+	} else if ( keycode <= USBKBD_KEY_UP ) {
96
+		/* Special keys */
97
+		static const uint16_t special[] = {
98
+			0, 0, 0, 0, 0, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9,
99
+			KEY_F10, KEY_F11, KEY_F12, 0, 0, 0, KEY_IC, KEY_HOME,
100
+			KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
101
+			KEY_LEFT, KEY_DOWN, KEY_UP
102
+		};
103
+		key = special[ keycode - USBKBD_KEY_CAPSLOCK ];
104
+	} else {
105
+		key = 0;
106
+	}
107
+
108
+	return key;
109
+}
110
+
111
+/******************************************************************************
112
+ *
113
+ * Keyboard buffer
114
+ *
115
+ ******************************************************************************
116
+ */
117
+
118
+/**
119
+ * Insert keypress into keyboard buffer
120
+ *
121
+ * @v kbd		USB keyboard
122
+ * @v keycode		Keycode
123
+ * @v modifiers		Modifiers
124
+ */
125
+static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode,
126
+			     unsigned int modifiers ) {
127
+	unsigned int key;
128
+
129
+	/* Map to iPXE key */
130
+	key = usbkbd_map ( keycode, modifiers );
131
+
132
+	/* Do nothing if this keycode has no corresponding iPXE key */
133
+	if ( ! key ) {
134
+		DBGC ( kbd, "KBD %s has no key for keycode %#02x:%#02x\n",
135
+		       kbd->name, modifiers, keycode );
136
+		return;
137
+	}
138
+
139
+	/* Check for buffer overrun */
140
+	if ( usbkbd_fill ( kbd ) >= USBKBD_BUFSIZE ) {
141
+		DBGC ( kbd, "KBD %s buffer overrun (key %#02x)\n",
142
+		       kbd->name, key );
143
+		return;
144
+	}
145
+
146
+	/* Insert into buffer */
147
+	kbd->key[ ( kbd->prod++ ) % USBKBD_BUFSIZE ] = key;
148
+	DBGC2 ( kbd, "KBD %s key %#02x produced\n", kbd->name, key );
149
+}
150
+
151
+/**
152
+ * Consume character from keyboard buffer
153
+ *
154
+ * @v kbd		USB keyboard
155
+ * @ret character	Character
156
+ */
157
+static unsigned int usbkbd_consume ( struct usb_keyboard *kbd ) {
158
+	static char buf[] = "\x1b[xx~";
159
+	char *tmp = &buf[2];
160
+	unsigned int key;
161
+	unsigned int character;
162
+	unsigned int ansi_n;
163
+	unsigned int len;
164
+
165
+	/* Sanity check */
166
+	assert ( usbkbd_fill ( kbd ) > 0 );
167
+
168
+	/* Get current keypress */
169
+	key = kbd->key[ kbd->cons % USBKBD_BUFSIZE ];
170
+
171
+	/* If this is a straightforward key, just consume and return it */
172
+	if ( key < KEY_MIN ) {
173
+		kbd->cons++;
174
+		DBGC2 ( kbd, "KBD %s key %#02x consumed\n", kbd->name, key );
175
+		return key;
176
+	}
177
+
178
+	/* Construct ANSI sequence */
179
+	ansi_n = KEY_ANSI_N ( key );
180
+	if ( ansi_n )
181
+		tmp += sprintf ( tmp, "%d", ansi_n );
182
+	*(tmp++) = KEY_ANSI_TERMINATOR ( key );
183
+	*tmp = '\0';
184
+	len = ( tmp - buf );
185
+	assert ( len < sizeof ( buf ) );
186
+	if ( kbd->subcons == 0 ) {
187
+		DBGC2 ( kbd, "KBD %s key %#02x consumed as ^[%s\n",
188
+			kbd->name, key, &buf[1] );
189
+	}
190
+
191
+	/* Extract character from ANSI sequence */
192
+	assert ( kbd->subcons < len );
193
+	character = buf[ kbd->subcons++ ];
194
+
195
+	/* Consume key if applicable */
196
+	if ( kbd->subcons == len ) {
197
+		kbd->cons++;
198
+		kbd->subcons = 0;
199
+	}
200
+
201
+	return character;
202
+}
203
+
204
+/******************************************************************************
205
+ *
206
+ * Keyboard report
207
+ *
208
+ ******************************************************************************
209
+ */
210
+
211
+/**
212
+ * Check for presence of keycode in report
213
+ *
214
+ * @v report		Keyboard report
215
+ * @v keycode		Keycode (must be non-zero)
216
+ * @ret has_keycode	Keycode is present in report
217
+ */
218
+static int usbkbd_has_keycode ( struct usb_keyboard_report *report,
219
+				unsigned int keycode ) {
220
+	unsigned int i;
221
+
222
+	/* Check for keycode */
223
+	for ( i = 0 ; i < ( sizeof ( report->keycode ) /
224
+			    sizeof ( report->keycode[0] ) ) ; i++ ) {
225
+		if ( report->keycode[i] == keycode )
226
+			return keycode;
227
+	}
228
+
229
+	return 0;
230
+}
231
+
232
+/**
233
+ * Handle keyboard report
234
+ *
235
+ * @v kbd		USB keyboard
236
+ * @v new		New keyboard report
237
+ */
238
+static void usbkbd_report ( struct usb_keyboard *kbd,
239
+			    struct usb_keyboard_report *new ) {
240
+	struct usb_keyboard_report *old = &kbd->report;
241
+	unsigned int keycode;
242
+	unsigned int i;
243
+
244
+	/* Check if current key has been released */
245
+	if ( kbd->keycode && ! usbkbd_has_keycode ( new, kbd->keycode ) ) {
246
+		DBGC2 ( kbd, "KBD %s keycode %#02x released\n",
247
+			kbd->name, kbd->keycode );
248
+		kbd->keycode = 0;
249
+	}
250
+
251
+	/* Decrement auto-repeat hold-off timer, if applicable */
252
+	if ( kbd->holdoff )
253
+		kbd->holdoff--;
254
+
255
+	/* Check if a new key has been pressed */
256
+	for ( i = 0 ; i < ( sizeof ( new->keycode ) /
257
+			    sizeof ( new->keycode[0] ) ) ; i++ ) {
258
+
259
+		/* Ignore keys present in the previous report */
260
+		keycode = new->keycode[i];
261
+		if ( ( keycode == 0 ) || usbkbd_has_keycode ( old, keycode ) )
262
+			continue;
263
+		DBGC2 ( kbd, "KBD %s keycode %#02x pressed\n",
264
+			kbd->name, keycode );
265
+
266
+		/* Insert keypress into keyboard buffer */
267
+		usbkbd_produce ( kbd, keycode, new->modifiers );
268
+
269
+		/* Record as most recent keycode */
270
+		kbd->keycode = keycode;
271
+
272
+		/* Start auto-repeat hold-off timer */
273
+		kbd->holdoff = USBKBD_HOLDOFF;
274
+	}
275
+
276
+	/* Insert auto-repeated keypress into keyboard buffer, if applicable */
277
+	if ( kbd->keycode && ! kbd->holdoff )
278
+		usbkbd_produce ( kbd, kbd->keycode, new->modifiers );
279
+
280
+	/* Record report */
281
+	memcpy ( old, new, sizeof ( *old ) );
282
+}
283
+
284
+/******************************************************************************
285
+ *
286
+ * Interrupt endpoint
287
+ *
288
+ ******************************************************************************
289
+ */
290
+
291
+/**
292
+ * Complete interrupt transfer
293
+ *
294
+ * @v ep		USB endpoint
295
+ * @v iobuf		I/O buffer
296
+ * @v rc		Completion status code
297
+ */
298
+static void usbkbd_complete ( struct usb_endpoint *ep,
299
+			      struct io_buffer *iobuf, int rc ) {
300
+	struct usb_keyboard *kbd = container_of ( ep, struct usb_keyboard,
301
+						  hid.in );
302
+	struct usb_keyboard_report *report;
303
+
304
+	/* Ignore packets cancelled when the endpoint closes */
305
+	if ( ! ep->open )
306
+		goto drop;
307
+
308
+	/* Ignore packets with errors */
309
+	if ( rc != 0 ) {
310
+		DBGC ( kbd, "KBD %s interrupt IN failed: %s\n",
311
+		       kbd->name, strerror ( rc ) );
312
+		goto drop;
313
+	}
314
+
315
+	/* Ignore underlength packets */
316
+	if ( iob_len ( iobuf ) < sizeof ( *report ) ) {
317
+		DBGC ( kbd, "KBD %s underlength report:\n", kbd->name );
318
+		DBGC_HDA ( kbd, 0, iobuf->data, iob_len ( iobuf ) );
319
+		goto drop;
320
+	}
321
+	report = iobuf->data;
322
+
323
+	/* Handle keyboard report */
324
+	usbkbd_report ( kbd, report );
325
+
326
+ drop:
327
+	/* Recycle I/O buffer */
328
+	usb_recycle ( &kbd->hid.in, iobuf );
329
+}
330
+
331
+/** Interrupt endpoint operations */
332
+static struct usb_endpoint_driver_operations usbkbd_operations = {
333
+	.complete = usbkbd_complete,
334
+};
335
+
336
+/******************************************************************************
337
+ *
338
+ * USB interface
339
+ *
340
+ ******************************************************************************
341
+ */
342
+
343
+/**
344
+ * Probe device
345
+ *
346
+ * @v func		USB function
347
+ * @v config		Configuration descriptor
348
+ * @ret rc		Return status code
349
+ */
350
+static int usbkbd_probe ( struct usb_function *func,
351
+			  struct usb_configuration_descriptor *config ) {
352
+	struct usb_device *usb = func->usb;
353
+	struct usb_keyboard *kbd;
354
+	int rc;
355
+
356
+	/* Allocate and initialise structure */
357
+	kbd = zalloc ( sizeof ( *kbd ) );
358
+	if ( ! kbd ) {
359
+		rc = -ENOMEM;
360
+		goto err_alloc;
361
+	}
362
+	kbd->name = func->name;
363
+	kbd->bus = usb->port->hub->bus;
364
+	usbhid_init ( &kbd->hid, func, &usbkbd_operations, NULL );
365
+	usb_refill_init ( &kbd->hid.in, sizeof ( kbd->report ),
366
+			  USBKBD_INTR_MAX_FILL );
367
+
368
+	/* Describe USB human interface device */
369
+	if ( ( rc = usbhid_describe ( &kbd->hid, config ) ) != 0 ) {
370
+		DBGC ( kbd, "KBD %s could not describe: %s\n",
371
+		       kbd->name, strerror ( rc ) );
372
+		goto err_describe;
373
+	}
374
+	DBGC ( kbd, "KBD %s using %s (len %zd)\n",
375
+	       kbd->name, usb_endpoint_name ( &kbd->hid.in ), kbd->hid.in.mtu );
376
+
377
+	/* Set boot protocol */
378
+	if ( ( rc = usbhid_set_protocol ( usb, func->interface[0],
379
+					  USBHID_PROTOCOL_BOOT ) ) != 0 ) {
380
+		DBGC ( kbd, "KBD %s could not set boot protocol: %s\n",
381
+		       kbd->name, strerror ( rc ) );
382
+		goto err_set_protocol;
383
+	}
384
+
385
+	/* Set idle time */
386
+	if ( ( rc = usbhid_set_idle ( usb, func->interface[0], 0,
387
+				      USBKBD_IDLE_DURATION ) ) != 0 ) {
388
+		DBGC ( kbd, "KBD %s could not set idle time: %s\n",
389
+		       kbd->name, strerror ( rc ) );
390
+		goto err_set_idle;
391
+	}
392
+
393
+	/* Open USB human interface device */
394
+	if ( ( rc = usbhid_open ( &kbd->hid ) ) != 0 ) {
395
+		DBGC ( kbd, "KBD %s could not open: %s\n",
396
+		       kbd->name, strerror ( rc ) );
397
+		goto err_open;
398
+	}
399
+
400
+	/* Add to list of USB keyboards */
401
+	list_add_tail ( &kbd->list, &usb_keyboards );
402
+
403
+	usb_func_set_drvdata ( func, kbd );
404
+	return 0;
405
+
406
+	usbhid_close ( &kbd->hid );
407
+ err_open:
408
+ err_set_idle:
409
+ err_set_protocol:
410
+ err_describe:
411
+	free ( kbd );
412
+ err_alloc:
413
+	return rc;
414
+}
415
+
416
+/**
417
+ * Remove device
418
+ *
419
+ * @v func		USB function
420
+ */
421
+static void usbkbd_remove ( struct usb_function *func ) {
422
+	struct usb_keyboard *kbd = usb_func_get_drvdata ( func );
423
+
424
+	/* Remove from list of USB keyboards */
425
+	list_del ( &kbd->list );
426
+
427
+	/* Close USB human interface device */
428
+	usbhid_close ( &kbd->hid );
429
+
430
+	/* Free device */
431
+	free ( kbd );
432
+}
433
+
434
+/** USB keyboard device IDs */
435
+static struct usb_device_id usbkbd_ids[] = {
436
+	{
437
+		.name = "kbd",
438
+		.vendor = USB_ANY_ID,
439
+		.product = USB_ANY_ID,
440
+		.class = {
441
+			.class = USB_CLASS_HID,
442
+			.subclass = USB_SUBCLASS_HID_BOOT,
443
+			.protocol = USBKBD_PROTOCOL,
444
+		},
445
+	},
446
+};
447
+
448
+/** USB keyboard driver */
449
+struct usb_driver usbkbd_driver __usb_driver = {
450
+	.ids = usbkbd_ids,
451
+	.id_count = ( sizeof ( usbkbd_ids ) / sizeof ( usbkbd_ids[0] ) ),
452
+	.probe = usbkbd_probe,
453
+	.remove = usbkbd_remove,
454
+};
455
+
456
+/******************************************************************************
457
+ *
458
+ * Console interface
459
+ *
460
+ ******************************************************************************
461
+ */
462
+
463
+/**
464
+ * Read a character from the console
465
+ *
466
+ * @ret character	Character read
467
+ */
468
+static int usbkbd_getchar ( void ) {
469
+	struct usb_keyboard *kbd;
470
+
471
+	/* Consume first available key */
472
+	list_for_each_entry ( kbd, &usb_keyboards, list ) {
473
+		if ( usbkbd_fill ( kbd ) )
474
+			return usbkbd_consume ( kbd );
475
+	}
476
+
477
+	return 0;
478
+}
479
+
480
+/**
481
+ * Check for available input
482
+ *
483
+ * @ret is_available	Input is available
484
+ */
485
+static int usbkbd_iskey ( void ) {
486
+	struct usb_keyboard *kbd;
487
+	unsigned int fill;
488
+
489
+	/* Poll all USB keyboards and refill endpoints */
490
+	list_for_each_entry ( kbd, &usb_keyboards, list ) {
491
+		usb_poll ( kbd->bus );
492
+		usb_refill ( &kbd->hid.in );
493
+	}
494
+
495
+	/* Check for a non-empty keyboard buffer */
496
+	list_for_each_entry ( kbd, &usb_keyboards, list ) {
497
+		fill = usbkbd_fill ( kbd );
498
+		if ( fill )
499
+			return fill;
500
+	}
501
+
502
+	return 0;
503
+}
504
+
505
+/** USB keyboard console */
506
+struct console_driver usbkbd_console __console_driver = {
507
+	.getchar = usbkbd_getchar,
508
+	.iskey = usbkbd_iskey,
509
+};

+ 154
- 0
src/drivers/usb/usbkbd.h View File

@@ -0,0 +1,154 @@
1
+#ifndef _USBKBD_H
2
+#define _USBKBD_H
3
+
4
+/** @file
5
+ *
6
+ * USB keyboard driver
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include <assert.h>
13
+#include <ipxe/usb.h>
14
+#include <ipxe/usbhid.h>
15
+
16
+/** Keyboard protocol */
17
+#define USBKBD_PROTOCOL 1
18
+
19
+/** A USB keyboard report */
20
+struct usb_keyboard_report {
21
+	/** Modifier keys */
22
+	uint8_t modifiers;
23
+	/** Reserved */
24
+	uint8_t reserved;
25
+	/** Keycodes */
26
+	uint8_t keycode[6];
27
+} __attribute__ (( packed ));
28
+
29
+/** USB modifier keys */
30
+enum usb_keyboard_modifier {
31
+	/** Left Ctrl key */
32
+	USBKBD_CTRL_LEFT = 0x01,
33
+	/** Left Shift key */
34
+	USBKBD_SHIFT_LEFT = 0x02,
35
+	/** Left Alt key */
36
+	USBKBD_ALT_LEFT = 0x04,
37
+	/** Left GUI key */
38
+	USBKBD_GUI_LEFT = 0x08,
39
+	/** Right Ctrl key */
40
+	USBKBD_CTRL_RIGHT = 0x10,
41
+	/** Right Shift key */
42
+	USBKBD_SHIFT_RIGHT = 0x20,
43
+	/** Right Alt key */
44
+	USBKBD_ALT_RIGHT = 0x40,
45
+	/** Right GUI key */
46
+	USBKBD_GUI_RIGHT = 0x80,
47
+};
48
+
49
+/** Either Ctrl key */
50
+#define USBKBD_CTRL ( USBKBD_CTRL_LEFT | USBKBD_CTRL_RIGHT )
51
+
52
+/** Either Shift key */
53
+#define USBKBD_SHIFT ( USBKBD_SHIFT_LEFT | USBKBD_SHIFT_RIGHT )
54
+
55
+/** Either Alt key */
56
+#define USBKBD_ALT ( USBKBD_ALT_LEFT | USBKBD_ALT_RIGHT )
57
+
58
+/** Either GUI key */
59
+#define USBKBD_GUI ( USBKBD_GUI_LEFT | USBKBD_GUI_RIGHT )
60
+
61
+/** USB keycodes */
62
+enum usb_keycode {
63
+	USBKBD_KEY_A = 0x04,
64
+	USBKBD_KEY_Z = 0x1d,
65
+	USBKBD_KEY_1 = 0x1e,
66
+	USBKBD_KEY_0 = 0x27,
67
+	USBKBD_KEY_ENTER = 0x28,
68
+	USBKBD_KEY_SPACE = 0x2c,
69
+	USBKBD_KEY_MINUS = 0x2d,
70
+	USBKBD_KEY_SLASH = 0x38,
71
+	USBKBD_KEY_CAPSLOCK = 0x39,
72
+	USBKBD_KEY_UP = 0x52,
73
+};
74
+
75
+/** Keyboard idle duration (in 4ms units)
76
+ *
77
+ * This is a policy decision.  We choose to use an autorepeat rate of
78
+ * approximately 40ms.
79
+ */
80
+#define USBKBD_IDLE_DURATION 10 /* 10 x 4ms = 40ms */
81
+
82
+/** Keyboard auto-repeat hold-off (in units of USBKBD_IDLE_DURATION)
83
+ *
84
+ * This is a policy decision.  We choose to use an autorepeat delay of
85
+ * approximately 500ms.
86
+ */
87
+#define USBKBD_HOLDOFF 12 /* 12 x 40ms = 480ms */
88
+
89
+/** Interrupt endpoint maximum fill level
90
+ *
91
+ * When idling, we are likely to poll the USB endpoint at only the
92
+ * 18.2Hz system timer tick rate.  With a typical observed bInterval
93
+ * of 10ms (which will be rounded down to 8ms by the HCI drivers),
94
+ * this gives approximately 7 completions per poll.
95
+ */
96
+#define USBKBD_INTR_MAX_FILL 8
97
+
98
+/** Keyboard buffer size
99
+ *
100
+ * Must be a power of two.
101
+ */
102
+#define USBKBD_BUFSIZE 8
103
+
104
+/** A USB keyboard device */
105
+struct usb_keyboard {
106
+	/** Name */
107
+	const char *name;
108
+	/** List of all USB keyboards */
109
+	struct list_head list;
110
+
111
+	/** USB bus */
112
+	struct usb_bus *bus;
113
+	/** USB human interface device */
114
+	struct usb_hid hid;
115
+
116
+	/** Most recent keyboard report */
117
+	struct usb_keyboard_report report;
118
+	/** Most recently pressed non-modifier key (if any) */
119
+	unsigned int keycode;
120
+	/** Autorepeat hold-off time (in number of completions reported) */
121
+	unsigned int holdoff;
122
+
123
+	/** Keyboard buffer
124
+	 *
125
+	 * This stores iPXE key values.
126
+	 */
127
+	unsigned int key[USBKBD_BUFSIZE];
128
+	/** Keyboard buffer producer counter */
129
+	unsigned int prod;
130
+	/** Keyboard buffer consumer counter */
131
+	unsigned int cons;
132
+	/** Keyboard buffer sub-consumer counter
133
+	 *
134
+	 * This represents the index within the ANSI escape sequence
135
+	 * corresponding to an iPXE key value.
136
+	 */
137
+	unsigned int subcons;
138
+};
139
+
140
+/**
141
+ * Calculate keyboard buffer fill level
142
+ *
143
+ * @v kbd		USB keyboard
144
+ * @ret fill		Keyboard buffer fill level
145
+ */
146
+static inline __attribute__ (( always_inline )) unsigned int
147
+usbkbd_fill ( struct usb_keyboard *kbd ) {
148
+	unsigned int fill = ( kbd->prod - kbd->cons );
149
+
150
+	assert ( fill <= USBKBD_BUFSIZE );
151
+	return fill;
152
+}
153
+
154
+#endif /* _USBKBD_H */

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -82,6 +82,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
82 82
 #define ERRFILE_ehci		     ( ERRFILE_DRIVER | 0x000a0000 )
83 83
 #define ERRFILE_uhci		     ( ERRFILE_DRIVER | 0x000b0000 )
84 84
 #define ERRFILE_usbhid		     ( ERRFILE_DRIVER | 0x000c0000 )
85
+#define ERRFILE_usbkbd		     ( ERRFILE_DRIVER | 0x000d0000 )
85 86
 
86 87
 #define ERRFILE_nvs		     ( ERRFILE_DRIVER | 0x00100000 )
87 88
 #define ERRFILE_spi		     ( ERRFILE_DRIVER | 0x00110000 )

+ 2
- 0
src/include/ipxe/keys.h View File

@@ -58,6 +58,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
58 58
  */
59 59
 
60 60
 #define KEY_ANSI( n, terminator ) ( 0x100 * ( (n) + 1 ) + (terminator) )
61
+#define KEY_ANSI_N( key ) ( ( (key) / 0x100 ) - 1 )
62
+#define KEY_ANSI_TERMINATOR( key ) ( (key) & 0xff )
61 63
 
62 64
 #define KEY_MIN		0x101
63 65
 #define KEY_UP		KEY_ANSI ( 0, 'A' )	/**< Up arrow */

Loading…
Cancel
Save