2009-10-12 Robert Millan <rmh.grub@aybabtu.com>
[grub2/phcoder/solaris.git] / term / usb_keyboard.c
blob76b9bc3d4119ad375bc586448278e95f15cbd420
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/machine/machine.h>
22 #include <grub/machine/console.h>
23 #include <grub/time.h>
24 #include <grub/cpu/io.h>
25 #include <grub/misc.h>
26 #include <grub/term.h>
27 #include <grub/usb.h>
28 #include <grub/dl.h>
29 #include <grub/time.h>
32 static char keyboard_map[128] =
34 '\0', '\0', '\0', '\0', 'a', 'b', 'c', 'd',
35 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
36 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
37 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
38 '3', '4', '5', '6', '7', '8', '9', '0',
39 '\n', GRUB_TERM_ESC, GRUB_TERM_BACKSPACE, GRUB_TERM_TAB, ' ', '-', '=', '[',
40 ']', '\\', '#', ';', '\'', '`', ',', '.',
41 '/', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
42 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
43 '\0', '\0', GRUB_TERM_HOME, GRUB_TERM_PPAGE, GRUB_TERM_DC, GRUB_TERM_END, GRUB_TERM_NPAGE, GRUB_TERM_RIGHT,
44 GRUB_TERM_LEFT, GRUB_TERM_DOWN, GRUB_TERM_UP
47 static char keyboard_map_shift[128] =
49 '\0', '\0', '\0', '\0', 'A', 'B', 'C', 'D',
50 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
51 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
52 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
53 '#', '$', '%', '^', '&', '*', '(', ')',
54 '\n', '\0', '\0', '\0', ' ', '_', '+', '{',
55 '}', '|', '#', ':', '"', '`', '<', '>',
56 '?'
59 static grub_usb_device_t usbdev;
61 /* Valid values for bmRequestType. See HID definition version 1.11 section
62 7.2. */
63 #define USB_HID_HOST_TO_DEVICE 0x21
64 #define USB_HID_DEVICE_TO_HOST 0xA1
66 /* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
67 #define USB_HID_GET_REPORT 0x01
68 #define USB_HID_GET_IDLE 0x02
69 #define USB_HID_GET_PROTOCOL 0x03
70 #define USB_HID_SET_REPORT 0x09
71 #define USB_HID_SET_IDLE 0x0A
72 #define USB_HID_SET_PROTOCOL 0x0B
74 static void
75 grub_usb_hid (void)
77 struct grub_usb_desc_device *descdev;
79 auto int usb_iterate (grub_usb_device_t dev);
80 int usb_iterate (grub_usb_device_t dev)
82 descdev = &dev->descdev;
84 grub_dprintf ("usb_keyboard", "%x %x %x\n",
85 descdev->class, descdev->subclass, descdev->protocol);
87 #if 0
88 if (descdev->class != 0x09
89 || descdev->subclass == 0x01
90 || descdev->protocol != 0x02)
91 return 0;
92 #endif
94 if (descdev->class != 0 || descdev->subclass != 0 || descdev->protocol != 0)
95 return 0;
97 grub_printf ("HID found!\n");
99 usbdev = dev;
101 return 1;
103 grub_usb_iterate (usb_iterate);
105 /* Place the device in boot mode. */
106 grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_PROTOCOL,
107 0, 0, 0, 0);
109 /* Reports every time an event occurs and not more often than that. */
110 grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
111 0<<8, 0, 0, 0);
114 static grub_err_t
115 grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report)
117 return grub_usb_control_msg (dev, USB_HID_DEVICE_TO_HOST, USB_HID_GET_REPORT,
118 0, 0, 8, (char *) report);
123 static int
124 grub_usb_keyboard_checkkey (void)
126 grub_uint8_t data[8];
127 int key;
128 grub_err_t err;
129 grub_uint64_t currtime;
130 int timeout = 50;
132 data[2] = 0;
133 currtime = grub_get_time_ms ();
136 /* Get_Report. */
137 err = grub_usb_keyboard_getreport (usbdev, data);
139 /* Implement a timeout. */
140 if (grub_get_time_ms () > currtime + timeout)
141 break;
143 while (err || !data[2]);
145 if (err || !data[2])
146 return -1;
148 grub_dprintf ("usb_keyboard",
149 "report: 0x%02x 0x%02x 0x%02x 0x%02x"
150 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
151 data[0], data[1], data[2], data[3],
152 data[4], data[5], data[6], data[7]);
154 /* Check if the Control or Shift key was pressed. */
155 if (data[0] & 0x01 || data[0] & 0x10)
156 key = keyboard_map[data[2]] - 'a' + 1;
157 else if (data[0] & 0x02 || data[0] & 0x20)
158 key = keyboard_map_shift[data[2]];
159 else
160 key = keyboard_map[data[2]];
162 if (key == 0)
163 grub_printf ("Unknown key 0x%x detected\n", data[2]);
165 #if 0
166 /* Wait until the key is released. */
167 while (!err && data[2])
169 err = grub_usb_control_msg (usbdev, USB_HID_DEVICE_TO_HOST,
170 USB_HID_GET_REPORT, 0, 0,
171 sizeof (data), (char *) data);
172 grub_dprintf ("usb_keyboard",
173 "report2: 0x%02x 0x%02x 0x%02x 0x%02x"
174 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
175 data[0], data[1], data[2], data[3],
176 data[4], data[5], data[6], data[7]);
178 #endif
180 grub_errno = GRUB_ERR_NONE;
182 return key;
185 typedef enum
187 GRUB_HIDBOOT_REPEAT_NONE,
188 GRUB_HIDBOOT_REPEAT_FIRST,
189 GRUB_HIDBOOT_REPEAT
190 } grub_usb_keyboard_repeat_t;
192 static int
193 grub_usb_keyboard_getkey (void)
195 int key;
196 grub_err_t err;
197 grub_uint8_t data[8];
198 grub_uint64_t currtime;
199 int timeout;
200 static grub_usb_keyboard_repeat_t repeat = GRUB_HIDBOOT_REPEAT_NONE;
202 again:
206 key = grub_usb_keyboard_checkkey ();
207 } while (key == -1);
209 data[2] = !0; /* Or whatever. */
210 err = 0;
212 switch (repeat)
214 case GRUB_HIDBOOT_REPEAT_FIRST:
215 timeout = 500;
216 break;
217 case GRUB_HIDBOOT_REPEAT:
218 timeout = 50;
219 break;
220 default:
221 timeout = 100;
222 break;
225 /* Wait until the key is released. */
226 currtime = grub_get_time_ms ();
227 while (!err && data[2])
229 /* Implement a timeout. */
230 if (grub_get_time_ms () > currtime + timeout)
232 if (repeat == 0)
233 repeat = 1;
234 else
235 repeat = 2;
237 grub_errno = GRUB_ERR_NONE;
238 return key;
241 err = grub_usb_keyboard_getreport (usbdev, data);
244 if (repeat)
246 repeat = 0;
247 goto again;
250 repeat = 0;
252 grub_errno = GRUB_ERR_NONE;
254 return key;
257 static int
258 grub_usb_keyboard_getkeystatus (void)
260 grub_uint8_t data[8];
261 int mods = 0;
262 grub_err_t err;
263 grub_uint64_t currtime;
264 int timeout = 50;
266 /* Set idle time to the minimum offered by the spec (4 milliseconds) so
267 that we can find out the current state. */
268 grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
269 0<<8, 0, 0, 0);
271 currtime = grub_get_time_ms ();
274 /* Get_Report. */
275 err = grub_usb_keyboard_getreport (usbdev, data);
277 /* Implement a timeout. */
278 if (grub_get_time_ms () > currtime + timeout)
279 break;
281 while (err || !data[0]);
283 /* Go back to reporting every time an event occurs and not more often than
284 that. */
285 grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
286 0<<8, 0, 0, 0);
288 /* We allowed a while for modifiers to show up in the report, but it is
289 not an error if they never did. */
290 if (err)
291 return -1;
293 grub_dprintf ("usb_keyboard",
294 "report: 0x%02x 0x%02x 0x%02x 0x%02x"
295 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
296 data[0], data[1], data[2], data[3],
297 data[4], data[5], data[6], data[7]);
299 /* Check Shift, Control, and Alt status. */
300 if (data[0] & 0x02 || data[0] & 0x20)
301 mods |= GRUB_TERM_STATUS_SHIFT;
302 if (data[0] & 0x01 || data[0] & 0x10)
303 mods |= GRUB_TERM_STATUS_CTRL;
304 if (data[0] & 0x04 || data[0] & 0x40)
305 mods |= GRUB_TERM_STATUS_ALT;
307 grub_errno = GRUB_ERR_NONE;
309 return mods;
312 static struct grub_term_input grub_usb_keyboard_term =
314 .name = "usb_keyboard",
315 .checkkey = grub_usb_keyboard_checkkey,
316 .getkey = grub_usb_keyboard_getkey,
317 .getkeystatus = grub_usb_keyboard_getkeystatus,
318 .next = 0
321 GRUB_MOD_INIT(usb_keyboard)
323 grub_usb_hid ();
324 grub_term_register_input ("usb_keyboard", &grub_usb_keyboard_term);
327 GRUB_MOD_FINI(usb_keyboard)
329 grub_term_unregister_input (&grub_usb_keyboard_term);