tpm2_key_protector: Enable build for powerpc_ieee1275
[grub.git] / grub-core / term / usb_keyboard.c
blob7322d8dff51116c948e54d01ed0628eafc2284d6
1 /* Support for the HID Boot Protocol. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008, 2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/term.h>
21 #include <grub/time.h>
22 #include <grub/misc.h>
23 #include <grub/term.h>
24 #include <grub/usb.h>
25 #include <grub/dl.h>
26 #include <grub/time.h>
27 #include <grub/keyboard_layouts.h>
29 GRUB_MOD_LICENSE ("GPLv3+");
33 enum
35 KEY_NO_KEY = 0x00,
36 KEY_ERR_BUFFER = 0x01,
37 KEY_ERR_POST = 0x02,
38 KEY_ERR_UNDEF = 0x03,
39 KEY_CAPS_LOCK = 0x39,
40 KEY_NUM_LOCK = 0x53,
43 enum
45 LED_NUM_LOCK = 0x01,
46 LED_CAPS_LOCK = 0x02
49 /* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
50 #define USB_HID_GET_REPORT 0x01
51 #define USB_HID_GET_IDLE 0x02
52 #define USB_HID_GET_PROTOCOL 0x03
53 #define USB_HID_SET_REPORT 0x09
54 #define USB_HID_SET_IDLE 0x0A
55 #define USB_HID_SET_PROTOCOL 0x0B
57 #define USB_HID_BOOT_SUBCLASS 0x01
58 #define USB_HID_KBD_PROTOCOL 0x01
60 #define GRUB_USB_KEYBOARD_LEFT_CTRL 0x01
61 #define GRUB_USB_KEYBOARD_LEFT_SHIFT 0x02
62 #define GRUB_USB_KEYBOARD_LEFT_ALT 0x04
63 #define GRUB_USB_KEYBOARD_RIGHT_CTRL 0x10
64 #define GRUB_USB_KEYBOARD_RIGHT_SHIFT 0x20
65 #define GRUB_USB_KEYBOARD_RIGHT_ALT 0x40
67 struct grub_usb_keyboard_data
69 grub_usb_device_t usbdev;
70 grub_uint8_t status;
71 grub_uint16_t mods;
72 int interfno;
73 struct grub_usb_desc_endp *endp;
74 grub_usb_transfer_t transfer;
75 grub_uint8_t report[8];
76 int dead;
77 int last_key;
78 grub_uint64_t repeat_time;
79 grub_uint8_t current_report[8];
80 grub_uint8_t last_report[8];
81 int index;
82 int max_index;
85 static int grub_usb_keyboard_getkey (struct grub_term_input *term);
86 static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
88 static struct grub_term_input grub_usb_keyboard_term =
90 .getkey = grub_usb_keyboard_getkey,
91 .getkeystatus = grub_usb_keyboard_getkeystatus,
92 .next = 0
95 static struct grub_term_input grub_usb_keyboards[16];
97 static int
98 interpret_status (grub_uint8_t data0)
100 int mods = 0;
102 /* Check Shift, Control, and Alt status. */
103 if (data0 & GRUB_USB_KEYBOARD_LEFT_SHIFT)
104 mods |= GRUB_TERM_STATUS_LSHIFT;
105 if (data0 & GRUB_USB_KEYBOARD_RIGHT_SHIFT)
106 mods |= GRUB_TERM_STATUS_RSHIFT;
107 if (data0 & GRUB_USB_KEYBOARD_LEFT_CTRL)
108 mods |= GRUB_TERM_STATUS_LCTRL;
109 if (data0 & GRUB_USB_KEYBOARD_RIGHT_CTRL)
110 mods |= GRUB_TERM_STATUS_RCTRL;
111 if (data0 & GRUB_USB_KEYBOARD_LEFT_ALT)
112 mods |= GRUB_TERM_STATUS_LALT;
113 if (data0 & GRUB_USB_KEYBOARD_RIGHT_ALT)
114 mods |= GRUB_TERM_STATUS_RALT;
116 return mods;
119 static void
120 grub_usb_keyboard_detach (grub_usb_device_t usbdev,
121 int config __attribute__ ((unused)),
122 int interface __attribute__ ((unused)))
124 unsigned i;
125 for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
127 struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
129 if (!data)
130 continue;
132 if (data->usbdev != usbdev)
133 continue;
135 if (data->transfer)
136 grub_usb_cancel_transfer (data->transfer);
138 grub_term_unregister_input (&grub_usb_keyboards[i]);
139 grub_free ((char *) grub_usb_keyboards[i].name);
140 grub_usb_keyboards[i].name = NULL;
141 grub_free (grub_usb_keyboards[i].data);
142 grub_usb_keyboards[i].data = 0;
146 static int
147 grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
149 unsigned curnum;
150 struct grub_usb_keyboard_data *data;
151 struct grub_usb_desc_endp *endp = NULL;
152 int j;
154 grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
155 usbdev->descdev.class, usbdev->descdev.subclass,
156 usbdev->descdev.protocol, configno, interfno);
158 for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++)
159 if (!grub_usb_keyboards[curnum].data)
160 break;
162 if (curnum == ARRAY_SIZE (grub_usb_keyboards))
163 return 0;
165 if (usbdev->descdev.class != 0
166 || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0)
167 return 0;
169 if (usbdev->config[configno].interf[interfno].descif->subclass
170 != USB_HID_BOOT_SUBCLASS
171 || usbdev->config[configno].interf[interfno].descif->protocol
172 != USB_HID_KBD_PROTOCOL)
173 return 0;
175 for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
176 j++)
178 endp = &usbdev->config[configno].interf[interfno].descendp[j];
180 if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
181 == GRUB_USB_EP_INTERRUPT)
182 break;
184 if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt)
185 return 0;
187 grub_dprintf ("usb_keyboard", "HID found!\n");
189 data = grub_malloc (sizeof (*data));
190 if (!data)
192 grub_print_error ();
193 return 0;
196 data->usbdev = usbdev;
197 data->interfno = interfno;
198 data->endp = endp;
200 /* Configure device */
201 grub_usb_set_configuration (usbdev, configno + 1);
203 /* Place the device in boot mode. */
204 grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
205 USB_HID_SET_PROTOCOL, 0, interfno, 0, 0);
207 /* Reports every time an event occurs and not more often than that. */
208 grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
209 USB_HID_SET_IDLE, 0<<8, interfno, 0, 0);
211 grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term,
212 sizeof (grub_usb_keyboards[curnum]));
213 grub_usb_keyboards[curnum].data = data;
214 usbdev->config[configno].interf[interfno].detach_hook
215 = grub_usb_keyboard_detach;
216 grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum);
217 if (!grub_usb_keyboards[curnum].name)
219 grub_print_error ();
220 return 0;
223 /* Test showed that getting report may make the keyboard go nuts.
224 Moreover since we're reattaching keyboard it usually sends
225 an initial message on interrupt pipe and so we retrieve
226 the same keystatus.
228 #if 0
230 grub_uint8_t report[8];
231 grub_usb_err_t err;
232 grub_memset (report, 0, sizeof (report));
233 err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN,
234 USB_HID_GET_REPORT, 0x0100, interfno,
235 sizeof (report), (char *) report);
236 if (err)
237 data->status = 0;
238 else
239 data->status = report[0];
241 #else
242 data->status = 0;
243 #endif
245 data->transfer = grub_usb_bulk_read_background (usbdev,
246 data->endp,
247 sizeof (data->report),
248 (char *) data->report);
249 if (!data->transfer)
251 grub_print_error ();
252 return 0;
255 data->last_key = -1;
256 data->mods = 0;
257 data->dead = 0;
259 grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
261 return 1;
266 static void
267 send_leds (struct grub_usb_keyboard_data *termdata)
269 char report[1];
270 report[0] = 0;
271 if (termdata->mods & GRUB_TERM_STATUS_CAPS)
272 report[0] |= LED_CAPS_LOCK;
273 if (termdata->mods & GRUB_TERM_STATUS_NUM)
274 report[0] |= LED_NUM_LOCK;
275 grub_usb_control_msg (termdata->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
276 USB_HID_SET_REPORT, 0x0200, termdata->interfno,
277 sizeof (report), (char *) report);
278 grub_errno = GRUB_ERR_NONE;
281 static int
282 parse_keycode (struct grub_usb_keyboard_data *termdata)
284 int index = termdata->index;
285 int i, keycode;
287 /* Sanity check */
288 if (index < 2)
289 index = 2;
291 for ( ; index < termdata->max_index; index++)
293 keycode = termdata->current_report[index];
295 if (keycode == KEY_NO_KEY
296 || keycode == KEY_ERR_BUFFER
297 || keycode == KEY_ERR_POST
298 || keycode == KEY_ERR_UNDEF)
300 /* Don't parse (rest of) this report */
301 termdata->index = 0;
302 if (keycode != KEY_NO_KEY)
303 /* Don't replace last report with current faulty report
304 * in future ! */
305 grub_memcpy (termdata->current_report,
306 termdata->last_report,
307 sizeof (termdata->report));
308 return GRUB_TERM_NO_KEY;
311 /* Try to find current keycode in last report. */
312 for (i = 2; i < 8; i++)
313 if (keycode == termdata->last_report[i])
314 break;
315 if (i < 8)
316 /* Keycode is in last report, it means it was not released,
317 * ignore it. */
318 continue;
320 if (keycode == KEY_CAPS_LOCK)
322 termdata->mods ^= GRUB_TERM_STATUS_CAPS;
323 send_leds (termdata);
324 continue;
327 if (keycode == KEY_NUM_LOCK)
329 termdata->mods ^= GRUB_TERM_STATUS_NUM;
330 send_leds (termdata);
331 continue;
334 termdata->last_key = grub_term_map_key (keycode,
335 interpret_status (termdata->current_report[0])
336 | termdata->mods);
337 termdata->repeat_time = grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL;
339 grub_errno = GRUB_ERR_NONE;
341 index++;
342 if (index >= termdata->max_index)
343 termdata->index = 0;
344 else
345 termdata->index = index;
347 return termdata->last_key;
350 /* All keycodes parsed */
351 termdata->index = 0;
352 return GRUB_TERM_NO_KEY;
355 static int
356 grub_usb_keyboard_getkey (struct grub_term_input *term)
358 grub_usb_err_t err;
359 struct grub_usb_keyboard_data *termdata = term->data;
360 grub_size_t actual;
361 int keycode = GRUB_TERM_NO_KEY;
363 if (termdata->dead)
364 return GRUB_TERM_NO_KEY;
366 if (termdata->index)
367 keycode = parse_keycode (termdata);
368 if (keycode != GRUB_TERM_NO_KEY)
369 return keycode;
371 /* Poll interrupt pipe. */
372 err = grub_usb_check_transfer (termdata->transfer, &actual);
374 if (err == GRUB_USB_ERR_WAIT)
376 if (termdata->last_key != -1
377 && grub_get_time_ms () > termdata->repeat_time)
379 termdata->repeat_time = grub_get_time_ms ()
380 + GRUB_TERM_REPEAT_INTERVAL;
381 return termdata->last_key;
383 return GRUB_TERM_NO_KEY;
386 if (!err && (actual >= 3))
387 grub_memcpy (termdata->last_report,
388 termdata->current_report,
389 sizeof (termdata->report));
391 grub_memcpy (termdata->current_report,
392 termdata->report,
393 sizeof (termdata->report));
395 termdata->transfer = grub_usb_bulk_read_background (termdata->usbdev,
396 termdata->endp,
397 sizeof (termdata->report),
398 (char *) termdata->report);
399 if (!termdata->transfer)
401 grub_printf ("%s failed. Stopped\n", term->name);
402 termdata->dead = 1;
405 termdata->last_key = -1;
407 grub_dprintf ("usb_keyboard",
408 "err = %d, actual = %" PRIuGRUB_SIZE
409 " report: 0x%02x 0x%02x 0x%02x 0x%02x"
410 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
411 err, actual,
412 termdata->current_report[0], termdata->current_report[1],
413 termdata->current_report[2], termdata->current_report[3],
414 termdata->current_report[4], termdata->current_report[5],
415 termdata->current_report[6], termdata->current_report[7]);
417 if (err || actual < 1)
418 return GRUB_TERM_NO_KEY;
420 termdata->status = termdata->current_report[0];
422 if (actual < 3)
423 return GRUB_TERM_NO_KEY;
425 termdata->index = 2; /* New data received. */
426 termdata->max_index = actual;
428 return parse_keycode (termdata);
431 static int
432 grub_usb_keyboard_getkeystatus (struct grub_term_input *term)
434 struct grub_usb_keyboard_data *termdata = term->data;
436 return interpret_status (termdata->status) | termdata->mods;
439 static struct grub_usb_attach_desc attach_hook =
441 .class = GRUB_USB_CLASS_HID,
442 .hook = grub_usb_keyboard_attach
445 GRUB_MOD_INIT(usb_keyboard)
447 grub_usb_register_attach_hook_class (&attach_hook);
450 GRUB_MOD_FINI(usb_keyboard)
452 unsigned i;
453 for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
454 if (grub_usb_keyboards[i].data)
456 struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
458 if (!data)
459 continue;
461 if (data->transfer)
462 grub_usb_cancel_transfer (data->transfer);
464 grub_term_unregister_input (&grub_usb_keyboards[i]);
465 grub_free ((char *) grub_usb_keyboards[i].name);
466 grub_usb_keyboards[i].name = NULL;
467 grub_free (grub_usb_keyboards[i].data);
468 grub_usb_keyboards[i].data = 0;
470 grub_usb_unregister_attach_hook_class (&attach_hook);