Browse Source

[usb] Add support for numeric keypad on USB keyboards

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
2f861d736f
3 changed files with 134 additions and 7 deletions
  1. 82
    6
      src/drivers/usb/usbkbd.c
  2. 18
    1
      src/drivers/usb/usbkbd.h
  3. 34
    0
      src/include/ipxe/usbhid.h

+ 82
- 6
src/drivers/usb/usbkbd.c View File

@@ -53,13 +53,14 @@ static LIST_HEAD ( usb_keyboards );
53 53
  *
54 54
  * @v keycode		Keycode
55 55
  * @v modifiers		Modifiers
56
+ * @v leds		LED state
56 57
  * @ret key		iPXE key
57 58
  *
58 59
  * Key codes are defined in the USB HID Usage Tables Keyboard/Keypad
59 60
  * page.
60 61
  */
61
-static unsigned int usbkbd_map ( unsigned int keycode,
62
-				 unsigned int modifiers ) {
62
+static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers,
63
+				 unsigned int leds ) {
63 64
 	unsigned int key;
64 65
 
65 66
 	if ( keycode < USBKBD_KEY_A ) {
@@ -70,7 +71,8 @@ static unsigned int usbkbd_map ( unsigned int keycode,
70 71
 		key = ( keycode - USBKBD_KEY_A + 'a' );
71 72
 		if ( modifiers & USBKBD_CTRL ) {
72 73
 			key -= ( 'a' - CTRL_A );
73
-		} else if ( modifiers & USBKBD_SHIFT ) {
74
+		} else if ( ( modifiers & USBKBD_SHIFT ) ||
75
+			    ( leds & USBKBD_LED_CAPS_LOCK ) ) {
74 76
 			key -= ( 'a' - 'A' );
75 77
 		}
76 78
 	} else if ( keycode <= USBKBD_KEY_0 ) {
@@ -100,7 +102,22 @@ static unsigned int usbkbd_map ( unsigned int keycode,
100 102
 			KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
101 103
 			KEY_LEFT, KEY_DOWN, KEY_UP
102 104
 		};
103
-		key = special[ keycode - USBKBD_KEY_CAPSLOCK ];
105
+		key = special[ keycode - USBKBD_KEY_CAPS_LOCK ];
106
+	} else if ( keycode <= USBKBD_KEY_PAD_ENTER ) {
107
+		/* Keypad (unaffected by Num Lock) */
108
+		key = "\0/*-+\n" [ keycode - USBKBD_KEY_NUM_LOCK ];
109
+	} else if ( keycode <= USBKBD_KEY_PAD_DOT ) {
110
+		/* Keypad (affected by Num Lock) */
111
+		if ( leds & USBKBD_LED_NUM_LOCK ) {
112
+			key = "1234567890." [ keycode - USBKBD_KEY_PAD_1 ];
113
+		} else {
114
+			static const uint16_t keypad[] = {
115
+				KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, 0,
116
+				KEY_RIGHT, KEY_HOME, KEY_UP, KEY_PPAGE,
117
+				KEY_IC, KEY_DC
118
+			};
119
+			key = keypad[ keycode - USBKBD_KEY_PAD_1 ];
120
+		};
104 121
 	} else {
105 122
 		key = 0;
106 123
 	}
@@ -124,10 +141,25 @@ static unsigned int usbkbd_map ( unsigned int keycode,
124 141
  */
125 142
 static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode,
126 143
 			     unsigned int modifiers ) {
144
+	unsigned int leds = 0;
127 145
 	unsigned int key;
128 146
 
147
+	/* Check for LED-modifying keys */
148
+	if ( keycode == USBKBD_KEY_CAPS_LOCK ) {
149
+		leds = USBKBD_LED_CAPS_LOCK;
150
+	} else if ( keycode == USBKBD_KEY_NUM_LOCK ) {
151
+		leds = USBKBD_LED_NUM_LOCK;
152
+	}
153
+
154
+	/* Handle LED-modifying keys */
155
+	if ( leds ) {
156
+		kbd->leds ^= leds;
157
+		kbd->leds_changed = 1;
158
+		return;
159
+	}
160
+
129 161
 	/* Map to iPXE key */
130
-	key = usbkbd_map ( keycode, modifiers );
162
+	key = usbkbd_map ( keycode, modifiers, kbd->leds );
131 163
 
132 164
 	/* Do nothing if this keycode has no corresponding iPXE key */
133 165
 	if ( ! key ) {
@@ -333,6 +365,37 @@ static struct usb_endpoint_driver_operations usbkbd_operations = {
333 365
 	.complete = usbkbd_complete,
334 366
 };
335 367
 
368
+/******************************************************************************
369
+ *
370
+ * Keyboard LEDs
371
+ *
372
+ ******************************************************************************
373
+ */
374
+
375
+/**
376
+ * Set keyboard LEDs
377
+ *
378
+ * @v kbd		USB keyboard
379
+ * @ret rc		Return status code
380
+ */
381
+static int usbkbd_set_leds ( struct usb_keyboard *kbd ) {
382
+	struct usb_function *func = kbd->hid.func;
383
+	int rc;
384
+
385
+	DBGC2 ( kbd, "KBD %s setting LEDs to %#02x\n", kbd->name, kbd->leds );
386
+
387
+	/* Set keyboard LEDs */
388
+	if ( ( rc = usbhid_set_report ( func->usb, func->interface[0],
389
+					USBHID_REPORT_OUTPUT, 0, &kbd->leds,
390
+					sizeof ( kbd->leds ) ) ) != 0 ) {
391
+		DBGC ( kbd, "KBD %s could not set LEDs to %#02x: %s\n",
392
+		       kbd->name, kbd->leds, strerror ( rc ) );
393
+		return rc;
394
+	}
395
+
396
+	return 0;
397
+}
398
+
336 399
 /******************************************************************************
337 400
  *
338 401
  * USB interface
@@ -400,6 +463,9 @@ static int usbkbd_probe ( struct usb_function *func,
400 463
 	/* Add to list of USB keyboards */
401 464
 	list_add_tail ( &kbd->list, &usb_keyboards );
402 465
 
466
+	/* Set initial LED state */
467
+	usbkbd_set_leds ( kbd );
468
+
403 469
 	usb_func_set_drvdata ( func, kbd );
404 470
 	return 0;
405 471
 
@@ -484,10 +550,20 @@ static int usbkbd_iskey ( void ) {
484 550
 	struct usb_keyboard *kbd;
485 551
 	unsigned int fill;
486 552
 
487
-	/* Poll all USB keyboards and refill endpoints */
553
+	/* Poll USB keyboards, refill endpoints, and set LEDs if applicable */
488 554
 	list_for_each_entry ( kbd, &usb_keyboards, list ) {
555
+
556
+		/* Poll keyboard */
489 557
 		usb_poll ( kbd->bus );
558
+
559
+		/* Refill endpoints */
490 560
 		usb_refill ( &kbd->hid.in );
561
+
562
+		/* Update keyboard LEDs, if applicable */
563
+		if ( kbd->leds_changed ) {
564
+			usbkbd_set_leds ( kbd );
565
+			kbd->leds_changed = 0;
566
+		}
491 567
 	}
492 568
 
493 569
 	/* Check for a non-empty keyboard buffer */

+ 18
- 1
src/drivers/usb/usbkbd.h View File

@@ -68,8 +68,20 @@ enum usb_keycode {
68 68
 	USBKBD_KEY_SPACE = 0x2c,
69 69
 	USBKBD_KEY_MINUS = 0x2d,
70 70
 	USBKBD_KEY_SLASH = 0x38,
71
-	USBKBD_KEY_CAPSLOCK = 0x39,
71
+	USBKBD_KEY_CAPS_LOCK = 0x39,
72
+	USBKBD_KEY_F1 = 0x3a,
72 73
 	USBKBD_KEY_UP = 0x52,
74
+	USBKBD_KEY_NUM_LOCK = 0x53,
75
+	USBKBD_KEY_PAD_ENTER = 0x58,
76
+	USBKBD_KEY_PAD_1 = 0x59,
77
+	USBKBD_KEY_PAD_DOT = 0x63,
78
+};
79
+
80
+/** USB keyboard LEDs */
81
+enum usb_keyboard_led {
82
+	USBKBD_LED_NUM_LOCK = 0x01,
83
+	USBKBD_LED_CAPS_LOCK = 0x02,
84
+	USBKBD_LED_SCROLL_LOCK = 0x04,
73 85
 };
74 86
 
75 87
 /** Keyboard idle duration (in 4ms units)
@@ -120,6 +132,11 @@ struct usb_keyboard {
120 132
 	/** Autorepeat hold-off time (in number of completions reported) */
121 133
 	unsigned int holdoff;
122 134
 
135
+	/** Keyboard LED state */
136
+	uint8_t leds;
137
+	/** Keyboard LEDs changed */
138
+	uint8_t leds_changed;
139
+
123 140
 	/** Keyboard buffer
124 141
 	 *
125 142
 	 * This stores iPXE key values.

+ 34
- 0
src/include/ipxe/usbhid.h View File

@@ -33,6 +33,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
33 33
 	( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
34 34
 	  USB_REQUEST_TYPE ( 0x0a ) )
35 35
 
36
+/** Set report */
37
+#define USBHID_SET_REPORT						\
38
+	( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
39
+	  USB_REQUEST_TYPE ( 0x09 ) )
40
+
41
+/** Input report type */
42
+#define USBHID_REPORT_INPUT 0x01
43
+
44
+/** Output report type */
45
+#define USBHID_REPORT_OUTPUT 0x02
46
+
47
+/** Feature report type */
48
+#define USBHID_REPORT_FEATURE 0x03
49
+
36 50
 /** A USB human interface device */
37 51
 struct usb_hid {
38 52
 	/** USB function */
@@ -97,6 +111,26 @@ usbhid_set_idle ( struct usb_device *usb, unsigned int interface,
97 111
 			     interface, NULL, 0 );
98 112
 }
99 113
 
114
+/**
115
+ * Set report
116
+ *
117
+ * @v usb		USB device
118
+ * @v interface		Interface number
119
+ * @v type		Report type
120
+ * @v report		Report ID
121
+ * @v data		Report data
122
+ * @v len		Length of report data
123
+ * @ret rc		Return status code
124
+ */
125
+static inline __attribute__ (( always_inline )) int
126
+usbhid_set_report ( struct usb_device *usb, unsigned int interface,
127
+		    unsigned int type, unsigned int report, void *data,
128
+		    size_t len ) {
129
+
130
+	return usb_control ( usb, USBHID_SET_REPORT, ( ( type << 8 ) | report ),
131
+			     interface, data, len );
132
+}
133
+
100 134
 extern int usbhid_open ( struct usb_hid *hid );
101 135
 extern void usbhid_close ( struct usb_hid *hid );
102 136
 extern int usbhid_refill ( struct usb_hid *hid );

Loading…
Cancel
Save