]> git.proxmox.com Git - grub2.git/blob - grub-core/term/usb_keyboard.c
Support xz compression on yeeloong.
[grub2.git] / grub-core / term / usb_keyboard.c
1 /* Support for the HID Boot Protocol. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008, 2009 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/term.h>
21 #include <grub/time.h>
22 #include <grub/cpu/io.h>
23 #include <grub/misc.h>
24 #include <grub/term.h>
25 #include <grub/usb.h>
26 #include <grub/dl.h>
27 #include <grub/time.h>
28 #include <grub/keyboard_layouts.h>
29
30 \f
31
32 enum
33 {
34 KEY_NO_KEY = 0x00,
35 KEY_ERR_BUFFER = 0x01,
36 KEY_ERR_POST = 0x02,
37 KEY_ERR_UNDEF = 0x03,
38 KEY_CAPS_LOCK = 0x39,
39 KEY_NUM_LOCK = 0x53,
40 };
41
42 enum
43 {
44 LED_NUM_LOCK = 0x01,
45 LED_CAPS_LOCK = 0x02
46 };
47
48 /* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
49 #define USB_HID_GET_REPORT 0x01
50 #define USB_HID_GET_IDLE 0x02
51 #define USB_HID_GET_PROTOCOL 0x03
52 #define USB_HID_SET_REPORT 0x09
53 #define USB_HID_SET_IDLE 0x0A
54 #define USB_HID_SET_PROTOCOL 0x0B
55
56 #define USB_HID_BOOT_SUBCLASS 0x01
57 #define USB_HID_KBD_PROTOCOL 0x01
58
59 #define GRUB_USB_KEYBOARD_LEFT_CTRL 0x01
60 #define GRUB_USB_KEYBOARD_LEFT_SHIFT 0x02
61 #define GRUB_USB_KEYBOARD_LEFT_ALT 0x04
62 #define GRUB_USB_KEYBOARD_RIGHT_CTRL 0x10
63 #define GRUB_USB_KEYBOARD_RIGHT_SHIFT 0x20
64 #define GRUB_USB_KEYBOARD_RIGHT_ALT 0x40
65
66 struct grub_usb_keyboard_data
67 {
68 grub_usb_device_t usbdev;
69 grub_uint8_t status;
70 grub_uint16_t mods;
71 int interfno;
72 struct grub_usb_desc_endp *endp;
73 grub_usb_transfer_t transfer;
74 grub_uint8_t report[8];
75 int dead;
76 int last_key;
77 grub_uint64_t repeat_time;
78 grub_uint8_t current_report[8];
79 grub_uint8_t last_report[8];
80 int index;
81 int max_index;
82 };
83
84 static int grub_usb_keyboard_getkey (struct grub_term_input *term);
85 static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
86
87 static struct grub_term_input grub_usb_keyboard_term =
88 {
89 .getkey = grub_usb_keyboard_getkey,
90 .getkeystatus = grub_usb_keyboard_getkeystatus,
91 .next = 0
92 };
93
94 static struct grub_term_input grub_usb_keyboards[16];
95
96 static int
97 interpret_status (grub_uint8_t data0)
98 {
99 int mods = 0;
100
101 /* Check Shift, Control, and Alt status. */
102 if (data0 & GRUB_USB_KEYBOARD_LEFT_SHIFT)
103 mods |= GRUB_TERM_STATUS_LSHIFT;
104 if (data0 & GRUB_USB_KEYBOARD_RIGHT_SHIFT)
105 mods |= GRUB_TERM_STATUS_RSHIFT;
106 if (data0 & GRUB_USB_KEYBOARD_LEFT_CTRL)
107 mods |= GRUB_TERM_STATUS_LCTRL;
108 if (data0 & GRUB_USB_KEYBOARD_RIGHT_CTRL)
109 mods |= GRUB_TERM_STATUS_RCTRL;
110 if (data0 & GRUB_USB_KEYBOARD_LEFT_ALT)
111 mods |= GRUB_TERM_STATUS_LALT;
112 if (data0 & GRUB_USB_KEYBOARD_RIGHT_ALT)
113 mods |= GRUB_TERM_STATUS_RALT;
114
115 return mods;
116 }
117
118 static void
119 grub_usb_keyboard_detach (grub_usb_device_t usbdev,
120 int config __attribute__ ((unused)),
121 int interface __attribute__ ((unused)))
122 {
123 unsigned i;
124 for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
125 {
126 struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
127
128 if (!data)
129 continue;
130
131 if (data->usbdev != usbdev)
132 continue;
133
134 if (data->transfer)
135 grub_usb_cancel_transfer (data->transfer);
136
137 grub_term_unregister_input (&grub_usb_keyboards[i]);
138 grub_free ((char *) grub_usb_keyboards[i].name);
139 grub_usb_keyboards[i].name = NULL;
140 grub_free (grub_usb_keyboards[i].data);
141 grub_usb_keyboards[i].data = 0;
142 }
143 }
144
145 static int
146 grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
147 {
148 unsigned curnum;
149 struct grub_usb_keyboard_data *data;
150 struct grub_usb_desc_endp *endp = NULL;
151 int j;
152
153 grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
154 usbdev->descdev.class, usbdev->descdev.subclass,
155 usbdev->descdev.protocol, configno, interfno);
156
157 for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++)
158 if (!grub_usb_keyboards[curnum].data)
159 break;
160
161 if (curnum == ARRAY_SIZE (grub_usb_keyboards))
162 return 0;
163
164 if (usbdev->descdev.class != 0
165 || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0)
166 return 0;
167
168 if (usbdev->config[configno].interf[interfno].descif->subclass
169 != USB_HID_BOOT_SUBCLASS
170 || usbdev->config[configno].interf[interfno].descif->protocol
171 != USB_HID_KBD_PROTOCOL)
172 return 0;
173
174 for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
175 j++)
176 {
177 endp = &usbdev->config[configno].interf[interfno].descendp[j];
178
179 if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
180 == GRUB_USB_EP_INTERRUPT)
181 break;
182 }
183 if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt)
184 return 0;
185
186 grub_dprintf ("usb_keyboard", "HID found!\n");
187
188 data = grub_malloc (sizeof (*data));
189 if (!data)
190 {
191 grub_print_error ();
192 return 0;
193 }
194
195 data->usbdev = usbdev;
196 data->interfno = interfno;
197 data->endp = endp;
198
199 /* Configure device */
200 grub_usb_set_configuration (usbdev, configno + 1);
201
202 /* Place the device in boot mode. */
203 grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
204 USB_HID_SET_PROTOCOL, 0, interfno, 0, 0);
205
206 /* Reports every time an event occurs and not more often than that. */
207 grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
208 USB_HID_SET_IDLE, 0<<8, interfno, 0, 0);
209
210 grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term,
211 sizeof (grub_usb_keyboards[curnum]));
212 grub_usb_keyboards[curnum].data = data;
213 usbdev->config[configno].interf[interfno].detach_hook
214 = grub_usb_keyboard_detach;
215 grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum);
216 if (!grub_usb_keyboards[curnum].name)
217 {
218 grub_print_error ();
219 return 0;
220 }
221
222 /* Test showed that getting report may make the keyboard go nuts.
223 Moreover since we're reattaching keyboard it usually sends
224 an initial message on interrupt pipe and so we retrieve
225 the same keystatus.
226 */
227 #if 0
228 {
229 grub_uint8_t report[8];
230 grub_usb_err_t err;
231 grub_memset (report, 0, sizeof (report));
232 err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN,
233 USB_HID_GET_REPORT, 0x0100, interfno,
234 sizeof (report), (char *) report);
235 if (err)
236 data->status = 0;
237 else
238 data->status = report[0];
239 }
240 #else
241 data->status = 0;
242 #endif
243
244 data->transfer = grub_usb_bulk_read_background (usbdev,
245 data->endp->endp_addr,
246 sizeof (data->report),
247 (char *) data->report);
248 if (!data->transfer)
249 {
250 grub_print_error ();
251 return 0;
252 }
253
254 data->last_key = -1;
255 data->mods = 0;
256 data->dead = 0;
257
258 grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
259
260 return 1;
261 }
262
263 \f
264
265 static void
266 send_leds (struct grub_usb_keyboard_data *termdata)
267 {
268 char report[1];
269 report[0] = 0;
270 if (termdata->mods & GRUB_TERM_STATUS_CAPS)
271 report[0] |= LED_CAPS_LOCK;
272 if (termdata->mods & GRUB_TERM_STATUS_NUM)
273 report[0] |= LED_NUM_LOCK;
274 grub_usb_control_msg (termdata->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
275 USB_HID_SET_REPORT, 0x0200, termdata->interfno,
276 sizeof (report), (char *) report);
277 grub_errno = GRUB_ERR_NONE;
278 }
279
280 static int
281 parse_keycode (struct grub_usb_keyboard_data *termdata)
282 {
283 int index = termdata->index;
284 int i, keycode;
285
286 /* Sanity check */
287 if (index < 2)
288 index = 2;
289
290 for ( ; index < termdata->max_index; index++)
291 {
292 keycode = termdata->current_report[index];
293
294 if (keycode == KEY_NO_KEY
295 || keycode == KEY_ERR_BUFFER
296 || keycode == KEY_ERR_POST
297 || keycode == KEY_ERR_UNDEF)
298 {
299 /* Don't parse (rest of) this report */
300 termdata->index = 0;
301 if (keycode != KEY_NO_KEY)
302 /* Don't replace last report with current faulty report
303 * in future ! */
304 grub_memcpy (termdata->current_report,
305 termdata->last_report,
306 sizeof (termdata->report));
307 return GRUB_TERM_NO_KEY;
308 }
309
310 /* Try to find current keycode in last report. */
311 for (i = 2; i < 8; i++)
312 if (keycode == termdata->last_report[i])
313 break;
314 if (i < 8)
315 /* Keycode is in last report, it means it was not released,
316 * ignore it. */
317 continue;
318
319 if (keycode == KEY_CAPS_LOCK)
320 {
321 termdata->mods ^= GRUB_TERM_STATUS_CAPS;
322 send_leds (termdata);
323 continue;
324 }
325
326 if (keycode == KEY_NUM_LOCK)
327 {
328 termdata->mods ^= GRUB_TERM_STATUS_NUM;
329 send_leds (termdata);
330 continue;
331 }
332
333 termdata->last_key = grub_term_map_key (keycode,
334 interpret_status (termdata->current_report[0])
335 | termdata->mods);
336 termdata->repeat_time = grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL;
337
338 grub_errno = GRUB_ERR_NONE;
339
340 index++;
341 if (index >= termdata->max_index)
342 termdata->index = 0;
343 else
344 termdata->index = index;
345
346 return termdata->last_key;
347 }
348
349 /* All keycodes parsed */
350 termdata->index = 0;
351 return GRUB_TERM_NO_KEY;
352 }
353
354 static int
355 grub_usb_keyboard_getkey (struct grub_term_input *term)
356 {
357 grub_usb_err_t err;
358 struct grub_usb_keyboard_data *termdata = term->data;
359 grub_size_t actual;
360 int keycode = GRUB_TERM_NO_KEY;
361
362 if (termdata->dead)
363 return GRUB_TERM_NO_KEY;
364
365 if (termdata->index)
366 keycode = parse_keycode (termdata);
367 if (keycode != GRUB_TERM_NO_KEY)
368 return keycode;
369
370 /* Poll interrupt pipe. */
371 err = grub_usb_check_transfer (termdata->transfer, &actual);
372
373 if (err == GRUB_USB_ERR_WAIT)
374 {
375 if (termdata->last_key != -1
376 && grub_get_time_ms () > termdata->repeat_time)
377 {
378 termdata->repeat_time = grub_get_time_ms ()
379 + GRUB_TERM_REPEAT_INTERVAL;
380 return termdata->last_key;
381 }
382 return GRUB_TERM_NO_KEY;
383 }
384
385 if (!err && (actual >= 3))
386 grub_memcpy (termdata->last_report,
387 termdata->current_report,
388 sizeof (termdata->report));
389
390 grub_memcpy (termdata->current_report,
391 termdata->report,
392 sizeof (termdata->report));
393
394 termdata->transfer = grub_usb_bulk_read_background (termdata->usbdev,
395 termdata->endp->endp_addr,
396 sizeof (termdata->report),
397 (char *) termdata->report);
398 if (!termdata->transfer)
399 {
400 grub_printf ("%s failed. Stopped\n", term->name);
401 termdata->dead = 1;
402 }
403
404 termdata->last_key = -1;
405
406 grub_dprintf ("usb_keyboard",
407 "err = %d, actual = %" PRIuGRUB_SIZE
408 " report: 0x%02x 0x%02x 0x%02x 0x%02x"
409 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
410 err, actual,
411 termdata->current_report[0], termdata->current_report[1],
412 termdata->current_report[2], termdata->current_report[3],
413 termdata->current_report[4], termdata->current_report[5],
414 termdata->current_report[6], termdata->current_report[7]);
415
416 if (err || actual < 1)
417 return GRUB_TERM_NO_KEY;
418
419 termdata->status = termdata->current_report[0];
420
421 if (actual < 3)
422 return GRUB_TERM_NO_KEY;
423
424 termdata->index = 2; /* New data received. */
425 termdata->max_index = actual;
426
427 return parse_keycode (termdata);
428 }
429
430 static int
431 grub_usb_keyboard_getkeystatus (struct grub_term_input *term)
432 {
433 struct grub_usb_keyboard_data *termdata = term->data;
434
435 return interpret_status (termdata->status) | termdata->mods;
436 }
437
438 struct grub_usb_attach_desc attach_hook =
439 {
440 .class = GRUB_USB_CLASS_HID,
441 .hook = grub_usb_keyboard_attach
442 };
443
444 GRUB_MOD_INIT(usb_keyboard)
445 {
446 grub_usb_register_attach_hook_class (&attach_hook);
447 }
448
449 GRUB_MOD_FINI(usb_keyboard)
450 {
451 unsigned i;
452 for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
453 if (grub_usb_keyboards[i].data)
454 {
455 struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
456
457 if (!data)
458 continue;
459
460 if (data->transfer)
461 grub_usb_cancel_transfer (data->transfer);
462
463 grub_term_unregister_input (&grub_usb_keyboards[i]);
464 grub_free ((char *) grub_usb_keyboards[i].name);
465 grub_usb_keyboards[i].name = NULL;
466 grub_free (grub_usb_keyboards[i].data);
467 grub_usb_keyboards[i].data = 0;
468 }
469 grub_usb_unregister_attach_hook_class (&attach_hook);
470 }