1
/* Support for the HID Boot Protocol. */
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>
27
#include <grub/time.h>
28
#include <grub/keyboard_layouts.h>
35
KEY_ERR_BUFFER = 0x01,
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
56
#define USB_HID_BOOT_SUBCLASS 0x01
57
#define USB_HID_KBD_PROTOCOL 0x01
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
66
struct grub_usb_keyboard_data
68
grub_usb_device_t usbdev;
72
struct grub_usb_desc_endp *endp;
73
grub_usb_transfer_t transfer;
74
grub_uint8_t report[8];
77
grub_uint64_t repeat_time;
78
grub_uint8_t current_report[8];
79
grub_uint8_t last_report[8];
84
static int grub_usb_keyboard_getkey (struct grub_term_input *term);
85
static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
87
static struct grub_term_input grub_usb_keyboard_term =
89
.getkey = grub_usb_keyboard_getkey,
90
.getkeystatus = grub_usb_keyboard_getkeystatus,
94
static struct grub_term_input grub_usb_keyboards[16];
97
interpret_status (grub_uint8_t data0)
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;
119
grub_usb_keyboard_detach (grub_usb_device_t usbdev,
120
int config __attribute__ ((unused)),
121
int interface __attribute__ ((unused)))
124
for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
126
struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
131
if (data->usbdev != usbdev)
135
grub_usb_cancel_transfer (data->transfer);
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;
146
grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
149
struct grub_usb_keyboard_data *data;
150
struct grub_usb_desc_endp *endp = NULL;
153
grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
154
usbdev->descdev.class, usbdev->descdev.subclass,
155
usbdev->descdev.protocol, configno, interfno);
157
for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++)
158
if (!grub_usb_keyboards[curnum].data)
161
if (curnum == ARRAY_SIZE (grub_usb_keyboards))
164
if (usbdev->descdev.class != 0
165
|| usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0)
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)
174
for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
177
endp = &usbdev->config[configno].interf[interfno].descendp[j];
179
if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
180
== GRUB_USB_EP_INTERRUPT)
183
if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt)
186
grub_dprintf ("usb_keyboard", "HID found!\n");
188
data = grub_malloc (sizeof (*data));
195
data->usbdev = usbdev;
196
data->interfno = interfno;
199
/* Configure device */
200
grub_usb_set_configuration (usbdev, configno + 1);
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);
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);
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)
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
229
grub_uint8_t report[8];
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);
238
data->status = report[0];
244
data->transfer = grub_usb_bulk_read_background (usbdev,
245
data->endp->endp_addr,
246
sizeof (data->report),
247
(char *) data->report);
258
grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
266
send_leds (struct grub_usb_keyboard_data *termdata)
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;
281
parse_keycode (struct grub_usb_keyboard_data *termdata)
283
int index = termdata->index;
290
for ( ; index < termdata->max_index; index++)
292
keycode = termdata->current_report[index];
294
if (keycode == KEY_NO_KEY
295
|| keycode == KEY_ERR_BUFFER
296
|| keycode == KEY_ERR_POST
297
|| keycode == KEY_ERR_UNDEF)
299
/* Don't parse (rest of) this report */
301
if (keycode != KEY_NO_KEY)
302
/* Don't replace last report with current faulty report
304
grub_memcpy (termdata->current_report,
305
termdata->last_report,
306
sizeof (termdata->report));
307
return GRUB_TERM_NO_KEY;
310
/* Try to find current keycode in last report. */
311
for (i = 2; i < 8; i++)
312
if (keycode == termdata->last_report[i])
315
/* Keycode is in last report, it means it was not released,
319
if (keycode == KEY_CAPS_LOCK)
321
termdata->mods ^= GRUB_TERM_STATUS_CAPS;
322
send_leds (termdata);
326
if (keycode == KEY_NUM_LOCK)
328
termdata->mods ^= GRUB_TERM_STATUS_NUM;
329
send_leds (termdata);
333
termdata->last_key = grub_term_map_key (keycode,
334
interpret_status (termdata->current_report[0])
336
termdata->repeat_time = grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL;
338
grub_errno = GRUB_ERR_NONE;
341
if (index >= termdata->max_index)
344
termdata->index = index;
346
return termdata->last_key;
349
/* All keycodes parsed */
351
return GRUB_TERM_NO_KEY;
355
grub_usb_keyboard_getkey (struct grub_term_input *term)
358
struct grub_usb_keyboard_data *termdata = term->data;
360
int keycode = GRUB_TERM_NO_KEY;
363
return GRUB_TERM_NO_KEY;
366
keycode = parse_keycode (termdata);
367
if (keycode != GRUB_TERM_NO_KEY)
370
/* Poll interrupt pipe. */
371
err = grub_usb_check_transfer (termdata->transfer, &actual);
373
if (err == GRUB_USB_ERR_WAIT)
375
if (termdata->last_key != -1
376
&& grub_get_time_ms () > termdata->repeat_time)
378
termdata->repeat_time = grub_get_time_ms ()
379
+ GRUB_TERM_REPEAT_INTERVAL;
380
return termdata->last_key;
382
return GRUB_TERM_NO_KEY;
385
if (!err && (actual >= 3))
386
grub_memcpy (termdata->last_report,
387
termdata->current_report,
388
sizeof (termdata->report));
390
grub_memcpy (termdata->current_report,
392
sizeof (termdata->report));
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)
400
grub_printf ("%s failed. Stopped\n", term->name);
404
termdata->last_key = -1;
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",
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]);
416
if (err || actual < 1)
417
return GRUB_TERM_NO_KEY;
419
termdata->status = termdata->current_report[0];
422
return GRUB_TERM_NO_KEY;
424
termdata->index = 2; /* New data received. */
425
termdata->max_index = actual;
427
return parse_keycode (termdata);
431
grub_usb_keyboard_getkeystatus (struct grub_term_input *term)
433
struct grub_usb_keyboard_data *termdata = term->data;
435
return interpret_status (termdata->status) | termdata->mods;
438
struct grub_usb_attach_desc attach_hook =
440
.class = GRUB_USB_CLASS_HID,
441
.hook = grub_usb_keyboard_attach
444
GRUB_MOD_INIT(usb_keyboard)
446
grub_usb_register_attach_hook_class (&attach_hook);
449
GRUB_MOD_FINI(usb_keyboard)
452
for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
453
if (grub_usb_keyboards[i].data)
455
struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
461
grub_usb_cancel_transfer (data->transfer);
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;
469
grub_usb_unregister_attach_hook_class (&attach_hook);