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>
30 static char keyboard_map
[128] =
32 '\0', '\0', '\0', '\0', 'a', 'b', 'c', 'd',
33 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
34 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
35 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
36 '3', '4', '5', '6', '7', '8', '9', '0',
37 '\n', GRUB_TERM_ESC
, GRUB_TERM_BACKSPACE
, GRUB_TERM_TAB
, ' ', '-', '=', '[',
38 ']', '\\', '#', ';', '\'', '`', ',', '.',
39 '/', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
40 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
41 '\0', '\0', GRUB_TERM_HOME
, GRUB_TERM_PPAGE
, GRUB_TERM_DC
, GRUB_TERM_END
, GRUB_TERM_NPAGE
, GRUB_TERM_RIGHT
,
42 GRUB_TERM_LEFT
, GRUB_TERM_DOWN
, GRUB_TERM_UP
45 static char keyboard_map_shift
[128] =
47 '\0', '\0', '\0', '\0', 'A', 'B', 'C', 'D',
48 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
49 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
50 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
51 '#', '$', '%', '^', '&', '*', '(', ')',
52 '\n', '\0', '\0', '\0', ' ', '_', '+', '{',
53 '}', '|', '#', ':', '"', '`', '<', '>',
57 static grub_usb_device_t usbdev
;
59 /* Valid values for bmRequestType. See HID definition version 1.11 section
61 #define USB_HID_HOST_TO_DEVICE 0x21
62 #define USB_HID_DEVICE_TO_HOST 0xA1
64 /* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
65 #define USB_HID_GET_REPORT 0x01
66 #define USB_HID_GET_IDLE 0x02
67 #define USB_HID_GET_PROTOCOL 0x03
68 #define USB_HID_SET_REPORT 0x09
69 #define USB_HID_SET_IDLE 0x0A
70 #define USB_HID_SET_PROTOCOL 0x0B
75 struct grub_usb_desc_device
*descdev
;
77 auto int usb_iterate (grub_usb_device_t dev
);
78 int usb_iterate (grub_usb_device_t dev
)
80 descdev
= &dev
->descdev
;
82 grub_dprintf ("usb_keyboard", "%x %x %x\n",
83 descdev
->class, descdev
->subclass
, descdev
->protocol
);
86 if (descdev
->class != 0x09
87 || descdev
->subclass
== 0x01
88 || descdev
->protocol
!= 0x02)
92 if (descdev
->class != 0 || descdev
->subclass
!= 0 || descdev
->protocol
!= 0)
95 grub_printf ("HID found!\n");
101 grub_usb_iterate (usb_iterate
);
103 /* Place the device in boot mode. */
104 grub_usb_control_msg (usbdev
, USB_HID_HOST_TO_DEVICE
, USB_HID_SET_PROTOCOL
,
107 /* Reports every time an event occurs and not more often than that. */
108 grub_usb_control_msg (usbdev
, USB_HID_HOST_TO_DEVICE
, USB_HID_SET_IDLE
,
113 grub_usb_keyboard_getreport (grub_usb_device_t dev
, grub_uint8_t
*report
)
115 return grub_usb_control_msg (dev
, USB_HID_DEVICE_TO_HOST
, USB_HID_GET_REPORT
,
116 0, 0, 8, (char *) report
);
122 grub_usb_keyboard_checkkey (void)
124 grub_uint8_t data
[8];
127 grub_uint64_t currtime
;
131 currtime
= grub_get_time_ms ();
135 err
= grub_usb_keyboard_getreport (usbdev
, data
);
137 /* Implement a timeout. */
138 if (grub_get_time_ms () > currtime
+ timeout
)
141 while (err
|| !data
[2]);
146 grub_dprintf ("usb_keyboard",
147 "report: 0x%02x 0x%02x 0x%02x 0x%02x"
148 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
149 data
[0], data
[1], data
[2], data
[3],
150 data
[4], data
[5], data
[6], data
[7]);
152 /* Check if the Control or Shift key was pressed. */
153 if (data
[0] & 0x01 || data
[0] & 0x10)
154 key
= keyboard_map
[data
[2]] - 'a' + 1;
155 else if (data
[0] & 0x02 || data
[0] & 0x20)
156 key
= keyboard_map_shift
[data
[2]];
158 key
= keyboard_map
[data
[2]];
161 grub_printf ("Unknown key 0x%x detected\n", data
[2]);
164 /* Wait until the key is released. */
165 while (!err
&& data
[2])
167 err
= grub_usb_control_msg (usbdev
, USB_HID_DEVICE_TO_HOST
,
168 USB_HID_GET_REPORT
, 0, 0,
169 sizeof (data
), (char *) data
);
170 grub_dprintf ("usb_keyboard",
171 "report2: 0x%02x 0x%02x 0x%02x 0x%02x"
172 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
173 data
[0], data
[1], data
[2], data
[3],
174 data
[4], data
[5], data
[6], data
[7]);
178 grub_errno
= GRUB_ERR_NONE
;
185 GRUB_HIDBOOT_REPEAT_NONE
,
186 GRUB_HIDBOOT_REPEAT_FIRST
,
188 } grub_usb_keyboard_repeat_t
;
191 grub_usb_keyboard_getkey (void)
195 grub_uint8_t data
[8];
196 grub_uint64_t currtime
;
198 static grub_usb_keyboard_repeat_t repeat
= GRUB_HIDBOOT_REPEAT_NONE
;
204 key
= grub_usb_keyboard_checkkey ();
207 data
[2] = !0; /* Or whatever. */
212 case GRUB_HIDBOOT_REPEAT_FIRST
:
215 case GRUB_HIDBOOT_REPEAT
:
223 /* Wait until the key is released. */
224 currtime
= grub_get_time_ms ();
225 while (!err
&& data
[2])
227 /* Implement a timeout. */
228 if (grub_get_time_ms () > currtime
+ timeout
)
235 grub_errno
= GRUB_ERR_NONE
;
239 err
= grub_usb_keyboard_getreport (usbdev
, data
);
250 grub_errno
= GRUB_ERR_NONE
;
256 grub_usb_keyboard_getkeystatus (void)
258 grub_uint8_t data
[8];
261 grub_uint64_t currtime
;
264 /* Set idle time to the minimum offered by the spec (4 milliseconds) so
265 that we can find out the current state. */
266 grub_usb_control_msg (usbdev
, USB_HID_HOST_TO_DEVICE
, USB_HID_SET_IDLE
,
269 currtime
= grub_get_time_ms ();
273 err
= grub_usb_keyboard_getreport (usbdev
, data
);
275 /* Implement a timeout. */
276 if (grub_get_time_ms () > currtime
+ timeout
)
279 while (err
|| !data
[0]);
281 /* Go back to reporting every time an event occurs and not more often than
283 grub_usb_control_msg (usbdev
, USB_HID_HOST_TO_DEVICE
, USB_HID_SET_IDLE
,
286 /* We allowed a while for modifiers to show up in the report, but it is
287 not an error if they never did. */
291 grub_dprintf ("usb_keyboard",
292 "report: 0x%02x 0x%02x 0x%02x 0x%02x"
293 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
294 data
[0], data
[1], data
[2], data
[3],
295 data
[4], data
[5], data
[6], data
[7]);
297 /* Check Shift, Control, and Alt status. */
298 if (data
[0] & 0x02 || data
[0] & 0x20)
299 mods
|= GRUB_TERM_STATUS_SHIFT
;
300 if (data
[0] & 0x01 || data
[0] & 0x10)
301 mods
|= GRUB_TERM_STATUS_CTRL
;
302 if (data
[0] & 0x04 || data
[0] & 0x40)
303 mods
|= GRUB_TERM_STATUS_ALT
;
305 grub_errno
= GRUB_ERR_NONE
;
310 static struct grub_term_input grub_usb_keyboard_term
=
312 .name
= "usb_keyboard",
313 .checkkey
= grub_usb_keyboard_checkkey
,
314 .getkey
= grub_usb_keyboard_getkey
,
315 .getkeystatus
= grub_usb_keyboard_getkeystatus
,
319 GRUB_MOD_INIT(usb_keyboard
)
322 grub_term_register_input ("usb_keyboard", &grub_usb_keyboard_term
);
325 GRUB_MOD_FINI(usb_keyboard
)
327 grub_term_unregister_input (&grub_usb_keyboard_term
);