Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / term / usb_keyboard.c
blobae00936b8039df05194447200453b0d69df8f8a3
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/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>
30 GRUB_MOD_LICENSE ("GPLv3+");
34 enum
36 KEY_NO_KEY = 0x00,
37 KEY_ERR_BUFFER = 0x01,
38 KEY_ERR_POST = 0x02,
39 KEY_ERR_UNDEF = 0x03,
40 KEY_CAPS_LOCK = 0x39,
41 KEY_NUM_LOCK = 0x53,
44 enum
46 LED_NUM_LOCK = 0x01,
47 LED_CAPS_LOCK = 0x02
50 /* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
51 #define USB_HID_GET_REPORT 0x01
52 #define USB_HID_GET_IDLE 0x02
53 #define USB_HID_GET_PROTOCOL 0x03
54 #define USB_HID_SET_REPORT 0x09
55 #define USB_HID_SET_IDLE 0x0A
56 #define USB_HID_SET_PROTOCOL 0x0B
58 #define USB_HID_BOOT_SUBCLASS 0x01
59 #define USB_HID_KBD_PROTOCOL 0x01
61 #define GRUB_USB_KEYBOARD_LEFT_CTRL 0x01
62 #define GRUB_USB_KEYBOARD_LEFT_SHIFT 0x02
63 #define GRUB_USB_KEYBOARD_LEFT_ALT 0x04
64 #define GRUB_USB_KEYBOARD_RIGHT_CTRL 0x10
65 #define GRUB_USB_KEYBOARD_RIGHT_SHIFT 0x20
66 #define GRUB_USB_KEYBOARD_RIGHT_ALT 0x40
68 struct grub_usb_keyboard_data
70 grub_usb_device_t usbdev;
71 grub_uint8_t status;
72 grub_uint16_t mods;
73 int interfno;
74 struct grub_usb_desc_endp *endp;
75 grub_usb_transfer_t transfer;
76 grub_uint8_t report[8];
77 int dead;
78 int last_key;
79 grub_uint64_t repeat_time;
80 grub_uint8_t current_report[8];
81 grub_uint8_t last_report[8];
82 int index;
83 int max_index;
86 static int grub_usb_keyboard_getkey (struct grub_term_input *term);
87 static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
89 static struct grub_term_input grub_usb_keyboard_term =
91 .getkey = grub_usb_keyboard_getkey,
92 .getkeystatus = grub_usb_keyboard_getkeystatus,
93 .next = 0
96 static struct grub_term_input grub_usb_keyboards[16];
98 static int
99 interpret_status (grub_uint8_t data0)
101 int mods = 0;
103 /* Check Shift, Control, and Alt status. */
104 if (data0 & GRUB_USB_KEYBOARD_LEFT_SHIFT)
105 mods |= GRUB_TERM_STATUS_LSHIFT;
106 if (data0 & GRUB_USB_KEYBOARD_RIGHT_SHIFT)
107 mods |= GRUB_TERM_STATUS_RSHIFT;
108 if (data0 & GRUB_USB_KEYBOARD_LEFT_CTRL)
109 mods |= GRUB_TERM_STATUS_LCTRL;
110 if (data0 & GRUB_USB_KEYBOARD_RIGHT_CTRL)
111 mods |= GRUB_TERM_STATUS_RCTRL;
112 if (data0 & GRUB_USB_KEYBOARD_LEFT_ALT)
113 mods |= GRUB_TERM_STATUS_LALT;
114 if (data0 & GRUB_USB_KEYBOARD_RIGHT_ALT)
115 mods |= GRUB_TERM_STATUS_RALT;
117 return mods;
120 static void
121 grub_usb_keyboard_detach (grub_usb_device_t usbdev,
122 int config __attribute__ ((unused)),
123 int interface __attribute__ ((unused)))
125 unsigned i;
126 for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
128 struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
130 if (!data)
131 continue;
133 if (data->usbdev != usbdev)
134 continue;
136 if (data->transfer)
137 grub_usb_cancel_transfer (data->transfer);
139 grub_term_unregister_input (&grub_usb_keyboards[i]);
140 grub_free ((char *) grub_usb_keyboards[i].name);
141 grub_usb_keyboards[i].name = NULL;
142 grub_free (grub_usb_keyboards[i].data);
143 grub_usb_keyboards[i].data = 0;
147 static int
148 grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
150 unsigned curnum;
151 struct grub_usb_keyboard_data *data;
152 struct grub_usb_desc_endp *endp = NULL;
153 int j;
155 grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
156 usbdev->descdev.class, usbdev->descdev.subclass,
157 usbdev->descdev.protocol, configno, interfno);
159 for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++)
160 if (!grub_usb_keyboards[curnum].data)
161 break;
163 if (curnum == ARRAY_SIZE (grub_usb_keyboards))
164 return 0;
166 if (usbdev->descdev.class != 0
167 || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0)
168 return 0;
170 if (usbdev->config[configno].interf[interfno].descif->subclass
171 != USB_HID_BOOT_SUBCLASS
172 || usbdev->config[configno].interf[interfno].descif->protocol
173 != USB_HID_KBD_PROTOCOL)
174 return 0;
176 for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
177 j++)
179 endp = &usbdev->config[configno].interf[interfno].descendp[j];
181 if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
182 == GRUB_USB_EP_INTERRUPT)
183 break;
185 if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt)
186 return 0;
188 grub_dprintf ("usb_keyboard", "HID found!\n");
190 data = grub_malloc (sizeof (*data));
191 if (!data)
193 grub_print_error ();
194 return 0;
197 data->usbdev = usbdev;
198 data->interfno = interfno;
199 data->endp = endp;
201 /* Configure device */
202 grub_usb_set_configuration (usbdev, configno + 1);
204 /* Place the device in boot mode. */
205 grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
206 USB_HID_SET_PROTOCOL, 0, interfno, 0, 0);
208 /* Reports every time an event occurs and not more often than that. */
209 grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
210 USB_HID_SET_IDLE, 0<<8, interfno, 0, 0);
212 grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term,
213 sizeof (grub_usb_keyboards[curnum]));
214 grub_usb_keyboards[curnum].data = data;
215 usbdev->config[configno].interf[interfno].detach_hook
216 = grub_usb_keyboard_detach;
217 grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum);
218 if (!grub_usb_keyboards[curnum].name)
220 grub_print_error ();
221 return 0;
224 /* Test showed that getting report may make the keyboard go nuts.
225 Moreover since we're reattaching keyboard it usually sends
226 an initial message on interrupt pipe and so we retrieve
227 the same keystatus.
229 #if 0
231 grub_uint8_t report[8];
232 grub_usb_err_t err;
233 grub_memset (report, 0, sizeof (report));
234 err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN,
235 USB_HID_GET_REPORT, 0x0100, interfno,
236 sizeof (report), (char *) report);
237 if (err)
238 data->status = 0;
239 else
240 data->status = report[0];
242 #else
243 data->status = 0;
244 #endif
246 data->transfer = grub_usb_bulk_read_background (usbdev,
247 data->endp->endp_addr,
248 sizeof (data->report),
249 (char *) data->report);
250 if (!data->transfer)
252 grub_print_error ();
253 return 0;
256 data->last_key = -1;
257 data->mods = 0;
258 data->dead = 0;
260 grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
262 return 1;
267 static void
268 send_leds (struct grub_usb_keyboard_data *termdata)
270 char report[1];
271 report[0] = 0;
272 if (termdata->mods & GRUB_TERM_STATUS_CAPS)
273 report[0] |= LED_CAPS_LOCK;
274 if (termdata->mods & GRUB_TERM_STATUS_NUM)
275 report[0] |= LED_NUM_LOCK;
276 grub_usb_control_msg (termdata->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
277 USB_HID_SET_REPORT, 0x0200, termdata->interfno,
278 sizeof (report), (char *) report);
279 grub_errno = GRUB_ERR_NONE;
282 static int
283 parse_keycode (struct grub_usb_keyboard_data *termdata)
285 int index = termdata->index;
286 int i, keycode;
288 /* Sanity check */
289 if (index < 2)
290 index = 2;
292 for ( ; index < termdata->max_index; index++)
294 keycode = termdata->current_report[index];
296 if (keycode == KEY_NO_KEY
297 || keycode == KEY_ERR_BUFFER
298 || keycode == KEY_ERR_POST
299 || keycode == KEY_ERR_UNDEF)
301 /* Don't parse (rest of) this report */
302 termdata->index = 0;
303 if (keycode != KEY_NO_KEY)
304 /* Don't replace last report with current faulty report
305 * in future ! */
306 grub_memcpy (termdata->current_report,
307 termdata->last_report,
308 sizeof (termdata->report));
309 return GRUB_TERM_NO_KEY;
312 /* Try to find current keycode in last report. */
313 for (i = 2; i < 8; i++)
314 if (keycode == termdata->last_report[i])
315 break;
316 if (i < 8)
317 /* Keycode is in last report, it means it was not released,
318 * ignore it. */
319 continue;
321 if (keycode == KEY_CAPS_LOCK)
323 termdata->mods ^= GRUB_TERM_STATUS_CAPS;
324 send_leds (termdata);
325 continue;
328 if (keycode == KEY_NUM_LOCK)
330 termdata->mods ^= GRUB_TERM_STATUS_NUM;
331 send_leds (termdata);
332 continue;
335 termdata->last_key = grub_term_map_key (keycode,
336 interpret_status (termdata->current_report[0])
337 | termdata->mods);
338 termdata->repeat_time = grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL;
340 grub_errno = GRUB_ERR_NONE;
342 index++;
343 if (index >= termdata->max_index)
344 termdata->index = 0;
345 else
346 termdata->index = index;
348 return termdata->last_key;
351 /* All keycodes parsed */
352 termdata->index = 0;
353 return GRUB_TERM_NO_KEY;
356 static int
357 grub_usb_keyboard_getkey (struct grub_term_input *term)
359 grub_usb_err_t err;
360 struct grub_usb_keyboard_data *termdata = term->data;
361 grub_size_t actual;
362 int keycode = GRUB_TERM_NO_KEY;
364 if (termdata->dead)
365 return GRUB_TERM_NO_KEY;
367 if (termdata->index)
368 keycode = parse_keycode (termdata);
369 if (keycode != GRUB_TERM_NO_KEY)
370 return keycode;
372 /* Poll interrupt pipe. */
373 err = grub_usb_check_transfer (termdata->transfer, &actual);
375 if (err == GRUB_USB_ERR_WAIT)
377 if (termdata->last_key != -1
378 && grub_get_time_ms () > termdata->repeat_time)
380 termdata->repeat_time = grub_get_time_ms ()
381 + GRUB_TERM_REPEAT_INTERVAL;
382 return termdata->last_key;
384 return GRUB_TERM_NO_KEY;
387 if (!err && (actual >= 3))
388 grub_memcpy (termdata->last_report,
389 termdata->current_report,
390 sizeof (termdata->report));
392 grub_memcpy (termdata->current_report,
393 termdata->report,
394 sizeof (termdata->report));
396 termdata->transfer = grub_usb_bulk_read_background (termdata->usbdev,
397 termdata->endp->endp_addr,
398 sizeof (termdata->report),
399 (char *) termdata->report);
400 if (!termdata->transfer)
402 grub_printf ("%s failed. Stopped\n", term->name);
403 termdata->dead = 1;
406 termdata->last_key = -1;
408 grub_dprintf ("usb_keyboard",
409 "err = %d, actual = %" PRIuGRUB_SIZE
410 " report: 0x%02x 0x%02x 0x%02x 0x%02x"
411 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
412 err, actual,
413 termdata->current_report[0], termdata->current_report[1],
414 termdata->current_report[2], termdata->current_report[3],
415 termdata->current_report[4], termdata->current_report[5],
416 termdata->current_report[6], termdata->current_report[7]);
418 if (err || actual < 1)
419 return GRUB_TERM_NO_KEY;
421 termdata->status = termdata->current_report[0];
423 if (actual < 3)
424 return GRUB_TERM_NO_KEY;
426 termdata->index = 2; /* New data received. */
427 termdata->max_index = actual;
429 return parse_keycode (termdata);
432 static int
433 grub_usb_keyboard_getkeystatus (struct grub_term_input *term)
435 struct grub_usb_keyboard_data *termdata = term->data;
437 return interpret_status (termdata->status) | termdata->mods;
440 static struct grub_usb_attach_desc attach_hook =
442 .class = GRUB_USB_CLASS_HID,
443 .hook = grub_usb_keyboard_attach
446 GRUB_MOD_INIT(usb_keyboard)
448 grub_usb_register_attach_hook_class (&attach_hook);
451 GRUB_MOD_FINI(usb_keyboard)
453 unsigned i;
454 for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
455 if (grub_usb_keyboards[i].data)
457 struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
459 if (!data)
460 continue;
462 if (data->transfer)
463 grub_usb_cancel_transfer (data->transfer);
465 grub_term_unregister_input (&grub_usb_keyboards[i]);
466 grub_free ((char *) grub_usb_keyboards[i].name);
467 grub_usb_keyboards[i].name = NULL;
468 grub_free (grub_usb_keyboards[i].data);
469 grub_usb_keyboards[i].data = 0;
471 grub_usb_unregister_attach_hook_class (&attach_hook);