]> git.proxmox.com Git - grub2.git/blob - grub-core/term/usb_keyboard.c
merge with mainline
[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
29 \f
30 static char keyboard_map[128] =
31 {
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
43 };
44
45 static char keyboard_map_shift[128] =
46 {
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 '}', '|', '#', ':', '"', '`', '<', '>',
54 '?'
55 };
56
57 static grub_usb_device_t usbdev;
58
59 /* Valid values for bmRequestType. See HID definition version 1.11 section
60 7.2. */
61 #define USB_HID_HOST_TO_DEVICE 0x21
62 #define USB_HID_DEVICE_TO_HOST 0xA1
63
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
71
72 static void
73 grub_usb_hid (void)
74 {
75 struct grub_usb_desc_device *descdev;
76
77 auto int usb_iterate (grub_usb_device_t dev);
78 int usb_iterate (grub_usb_device_t dev)
79 {
80 descdev = &dev->descdev;
81
82 grub_dprintf ("usb_keyboard", "%x %x %x\n",
83 descdev->class, descdev->subclass, descdev->protocol);
84
85 #if 0
86 if (descdev->class != 0x09
87 || descdev->subclass == 0x01
88 || descdev->protocol != 0x02)
89 return 0;
90 #endif
91
92 if (descdev->class != 0 || descdev->subclass != 0 || descdev->protocol != 0)
93 return 0;
94
95 grub_printf ("HID found!\n");
96
97 usbdev = dev;
98
99 return 1;
100 }
101 grub_usb_iterate (usb_iterate);
102
103 /* Place the device in boot mode. */
104 grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_PROTOCOL,
105 0, 0, 0, 0);
106
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,
109 0<<8, 0, 0, 0);
110 }
111
112 static grub_err_t
113 grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report)
114 {
115 return grub_usb_control_msg (dev, USB_HID_DEVICE_TO_HOST, USB_HID_GET_REPORT,
116 0, 0, 8, (char *) report);
117 }
118
119 \f
120
121 static int
122 grub_usb_keyboard_checkkey (void)
123 {
124 grub_uint8_t data[8];
125 int key;
126 grub_err_t err;
127 grub_uint64_t currtime;
128 int timeout = 50;
129
130 data[2] = 0;
131 currtime = grub_get_time_ms ();
132 do
133 {
134 /* Get_Report. */
135 err = grub_usb_keyboard_getreport (usbdev, data);
136
137 /* Implement a timeout. */
138 if (grub_get_time_ms () > currtime + timeout)
139 break;
140 }
141 while (err || !data[2]);
142
143 if (err || !data[2])
144 return -1;
145
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]);
151
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]];
157 else
158 key = keyboard_map[data[2]];
159
160 if (key == 0)
161 grub_printf ("Unknown key 0x%x detected\n", data[2]);
162
163 #if 0
164 /* Wait until the key is released. */
165 while (!err && data[2])
166 {
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]);
175 }
176 #endif
177
178 grub_errno = GRUB_ERR_NONE;
179
180 return key;
181 }
182
183 typedef enum
184 {
185 GRUB_HIDBOOT_REPEAT_NONE,
186 GRUB_HIDBOOT_REPEAT_FIRST,
187 GRUB_HIDBOOT_REPEAT
188 } grub_usb_keyboard_repeat_t;
189
190 static int
191 grub_usb_keyboard_getkey (void)
192 {
193 int key;
194 grub_err_t err;
195 grub_uint8_t data[8];
196 grub_uint64_t currtime;
197 int timeout;
198 static grub_usb_keyboard_repeat_t repeat = GRUB_HIDBOOT_REPEAT_NONE;
199
200 again:
201
202 do
203 {
204 key = grub_usb_keyboard_checkkey ();
205 } while (key == -1);
206
207 data[2] = !0; /* Or whatever. */
208 err = 0;
209
210 switch (repeat)
211 {
212 case GRUB_HIDBOOT_REPEAT_FIRST:
213 timeout = 500;
214 break;
215 case GRUB_HIDBOOT_REPEAT:
216 timeout = 50;
217 break;
218 default:
219 timeout = 100;
220 break;
221 }
222
223 /* Wait until the key is released. */
224 currtime = grub_get_time_ms ();
225 while (!err && data[2])
226 {
227 /* Implement a timeout. */
228 if (grub_get_time_ms () > currtime + timeout)
229 {
230 if (repeat == 0)
231 repeat = 1;
232 else
233 repeat = 2;
234
235 grub_errno = GRUB_ERR_NONE;
236 return key;
237 }
238
239 err = grub_usb_keyboard_getreport (usbdev, data);
240 }
241
242 if (repeat)
243 {
244 repeat = 0;
245 goto again;
246 }
247
248 repeat = 0;
249
250 grub_errno = GRUB_ERR_NONE;
251
252 return key;
253 }
254
255 static int
256 grub_usb_keyboard_getkeystatus (void)
257 {
258 grub_uint8_t data[8];
259 int mods = 0;
260 grub_err_t err;
261 grub_uint64_t currtime;
262 int timeout = 50;
263
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,
267 0<<8, 0, 0, 0);
268
269 currtime = grub_get_time_ms ();
270 do
271 {
272 /* Get_Report. */
273 err = grub_usb_keyboard_getreport (usbdev, data);
274
275 /* Implement a timeout. */
276 if (grub_get_time_ms () > currtime + timeout)
277 break;
278 }
279 while (err || !data[0]);
280
281 /* Go back to reporting every time an event occurs and not more often than
282 that. */
283 grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
284 0<<8, 0, 0, 0);
285
286 /* We allowed a while for modifiers to show up in the report, but it is
287 not an error if they never did. */
288 if (err)
289 return -1;
290
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]);
296
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;
304
305 grub_errno = GRUB_ERR_NONE;
306
307 return mods;
308 }
309
310 static struct grub_term_input grub_usb_keyboard_term =
311 {
312 .name = "usb_keyboard",
313 .checkkey = grub_usb_keyboard_checkkey,
314 .getkey = grub_usb_keyboard_getkey,
315 .getkeystatus = grub_usb_keyboard_getkeystatus,
316 .next = 0
317 };
318
319 GRUB_MOD_INIT(usb_keyboard)
320 {
321 grub_usb_hid ();
322 grub_term_register_input ("usb_keyboard", &grub_usb_keyboard_term);
323 }
324
325 GRUB_MOD_FINI(usb_keyboard)
326 {
327 grub_term_unregister_input (&grub_usb_keyboard_term);
328 }