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/misc.h>
23 #include <grub/term.h>
26 #include <grub/time.h>
27 #include <grub/keyboard_layouts.h>
29 GRUB_MOD_LICENSE ("GPLv3+");
36 KEY_ERR_BUFFER
= 0x01,
49 /* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
50 #define USB_HID_GET_REPORT 0x01
51 #define USB_HID_GET_IDLE 0x02
52 #define USB_HID_GET_PROTOCOL 0x03
53 #define USB_HID_SET_REPORT 0x09
54 #define USB_HID_SET_IDLE 0x0A
55 #define USB_HID_SET_PROTOCOL 0x0B
57 #define USB_HID_BOOT_SUBCLASS 0x01
58 #define USB_HID_KBD_PROTOCOL 0x01
60 #define GRUB_USB_KEYBOARD_LEFT_CTRL 0x01
61 #define GRUB_USB_KEYBOARD_LEFT_SHIFT 0x02
62 #define GRUB_USB_KEYBOARD_LEFT_ALT 0x04
63 #define GRUB_USB_KEYBOARD_RIGHT_CTRL 0x10
64 #define GRUB_USB_KEYBOARD_RIGHT_SHIFT 0x20
65 #define GRUB_USB_KEYBOARD_RIGHT_ALT 0x40
67 struct grub_usb_keyboard_data
69 grub_usb_device_t usbdev
;
73 struct grub_usb_desc_endp
*endp
;
74 grub_usb_transfer_t transfer
;
75 grub_uint8_t report
[8];
78 grub_uint64_t repeat_time
;
79 grub_uint8_t current_report
[8];
80 grub_uint8_t last_report
[8];
85 static int grub_usb_keyboard_getkey (struct grub_term_input
*term
);
86 static int grub_usb_keyboard_getkeystatus (struct grub_term_input
*term
);
88 static struct grub_term_input grub_usb_keyboard_term
=
90 .getkey
= grub_usb_keyboard_getkey
,
91 .getkeystatus
= grub_usb_keyboard_getkeystatus
,
95 static struct grub_term_input grub_usb_keyboards
[16];
98 interpret_status (grub_uint8_t data0
)
102 /* Check Shift, Control, and Alt status. */
103 if (data0
& GRUB_USB_KEYBOARD_LEFT_SHIFT
)
104 mods
|= GRUB_TERM_STATUS_LSHIFT
;
105 if (data0
& GRUB_USB_KEYBOARD_RIGHT_SHIFT
)
106 mods
|= GRUB_TERM_STATUS_RSHIFT
;
107 if (data0
& GRUB_USB_KEYBOARD_LEFT_CTRL
)
108 mods
|= GRUB_TERM_STATUS_LCTRL
;
109 if (data0
& GRUB_USB_KEYBOARD_RIGHT_CTRL
)
110 mods
|= GRUB_TERM_STATUS_RCTRL
;
111 if (data0
& GRUB_USB_KEYBOARD_LEFT_ALT
)
112 mods
|= GRUB_TERM_STATUS_LALT
;
113 if (data0
& GRUB_USB_KEYBOARD_RIGHT_ALT
)
114 mods
|= GRUB_TERM_STATUS_RALT
;
120 grub_usb_keyboard_detach (grub_usb_device_t usbdev
,
121 int config
__attribute__ ((unused
)),
122 int interface
__attribute__ ((unused
)))
125 for (i
= 0; i
< ARRAY_SIZE (grub_usb_keyboards
); i
++)
127 struct grub_usb_keyboard_data
*data
= grub_usb_keyboards
[i
].data
;
132 if (data
->usbdev
!= usbdev
)
136 grub_usb_cancel_transfer (data
->transfer
);
138 grub_term_unregister_input (&grub_usb_keyboards
[i
]);
139 grub_free ((char *) grub_usb_keyboards
[i
].name
);
140 grub_usb_keyboards
[i
].name
= NULL
;
141 grub_free (grub_usb_keyboards
[i
].data
);
142 grub_usb_keyboards
[i
].data
= 0;
147 grub_usb_keyboard_attach (grub_usb_device_t usbdev
, int configno
, int interfno
)
150 struct grub_usb_keyboard_data
*data
;
151 struct grub_usb_desc_endp
*endp
= NULL
;
154 grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
155 usbdev
->descdev
.class, usbdev
->descdev
.subclass
,
156 usbdev
->descdev
.protocol
, configno
, interfno
);
158 for (curnum
= 0; curnum
< ARRAY_SIZE (grub_usb_keyboards
); curnum
++)
159 if (!grub_usb_keyboards
[curnum
].data
)
162 if (curnum
== ARRAY_SIZE (grub_usb_keyboards
))
165 if (usbdev
->descdev
.class != 0
166 || usbdev
->descdev
.subclass
!= 0 || usbdev
->descdev
.protocol
!= 0)
169 if (usbdev
->config
[configno
].interf
[interfno
].descif
->subclass
170 != USB_HID_BOOT_SUBCLASS
171 || usbdev
->config
[configno
].interf
[interfno
].descif
->protocol
172 != USB_HID_KBD_PROTOCOL
)
175 for (j
= 0; j
< usbdev
->config
[configno
].interf
[interfno
].descif
->endpointcnt
;
178 endp
= &usbdev
->config
[configno
].interf
[interfno
].descendp
[j
];
180 if ((endp
->endp_addr
& 128) && grub_usb_get_ep_type(endp
)
181 == GRUB_USB_EP_INTERRUPT
)
184 if (j
== usbdev
->config
[configno
].interf
[interfno
].descif
->endpointcnt
)
187 grub_dprintf ("usb_keyboard", "HID found!\n");
189 data
= grub_malloc (sizeof (*data
));
196 data
->usbdev
= usbdev
;
197 data
->interfno
= interfno
;
200 /* Configure device */
201 grub_usb_set_configuration (usbdev
, configno
+ 1);
203 /* Place the device in boot mode. */
204 grub_usb_control_msg (usbdev
, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT
,
205 USB_HID_SET_PROTOCOL
, 0, interfno
, 0, 0);
207 /* Reports every time an event occurs and not more often than that. */
208 grub_usb_control_msg (usbdev
, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT
,
209 USB_HID_SET_IDLE
, 0<<8, interfno
, 0, 0);
211 grub_memcpy (&grub_usb_keyboards
[curnum
], &grub_usb_keyboard_term
,
212 sizeof (grub_usb_keyboards
[curnum
]));
213 grub_usb_keyboards
[curnum
].data
= data
;
214 usbdev
->config
[configno
].interf
[interfno
].detach_hook
215 = grub_usb_keyboard_detach
;
216 grub_usb_keyboards
[curnum
].name
= grub_xasprintf ("usb_keyboard%d", curnum
);
217 if (!grub_usb_keyboards
[curnum
].name
)
223 /* Test showed that getting report may make the keyboard go nuts.
224 Moreover since we're reattaching keyboard it usually sends
225 an initial message on interrupt pipe and so we retrieve
230 grub_uint8_t report
[8];
232 grub_memset (report
, 0, sizeof (report
));
233 err
= grub_usb_control_msg (usbdev
, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN
,
234 USB_HID_GET_REPORT
, 0x0100, interfno
,
235 sizeof (report
), (char *) report
);
239 data
->status
= report
[0];
245 data
->transfer
= grub_usb_bulk_read_background (usbdev
,
247 sizeof (data
->report
),
248 (char *) data
->report
);
259 grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards
[curnum
]);
267 send_leds (struct grub_usb_keyboard_data
*termdata
)
271 if (termdata
->mods
& GRUB_TERM_STATUS_CAPS
)
272 report
[0] |= LED_CAPS_LOCK
;
273 if (termdata
->mods
& GRUB_TERM_STATUS_NUM
)
274 report
[0] |= LED_NUM_LOCK
;
275 grub_usb_control_msg (termdata
->usbdev
, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT
,
276 USB_HID_SET_REPORT
, 0x0200, termdata
->interfno
,
277 sizeof (report
), (char *) report
);
278 grub_errno
= GRUB_ERR_NONE
;
282 parse_keycode (struct grub_usb_keyboard_data
*termdata
)
284 int index
= termdata
->index
;
291 for ( ; index
< termdata
->max_index
; index
++)
293 keycode
= termdata
->current_report
[index
];
295 if (keycode
== KEY_NO_KEY
296 || keycode
== KEY_ERR_BUFFER
297 || keycode
== KEY_ERR_POST
298 || keycode
== KEY_ERR_UNDEF
)
300 /* Don't parse (rest of) this report */
302 if (keycode
!= KEY_NO_KEY
)
303 /* Don't replace last report with current faulty report
305 grub_memcpy (termdata
->current_report
,
306 termdata
->last_report
,
307 sizeof (termdata
->report
));
308 return GRUB_TERM_NO_KEY
;
311 /* Try to find current keycode in last report. */
312 for (i
= 2; i
< 8; i
++)
313 if (keycode
== termdata
->last_report
[i
])
316 /* Keycode is in last report, it means it was not released,
320 if (keycode
== KEY_CAPS_LOCK
)
322 termdata
->mods
^= GRUB_TERM_STATUS_CAPS
;
323 send_leds (termdata
);
327 if (keycode
== KEY_NUM_LOCK
)
329 termdata
->mods
^= GRUB_TERM_STATUS_NUM
;
330 send_leds (termdata
);
334 termdata
->last_key
= grub_term_map_key (keycode
,
335 interpret_status (termdata
->current_report
[0])
337 termdata
->repeat_time
= grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL
;
339 grub_errno
= GRUB_ERR_NONE
;
342 if (index
>= termdata
->max_index
)
345 termdata
->index
= index
;
347 return termdata
->last_key
;
350 /* All keycodes parsed */
352 return GRUB_TERM_NO_KEY
;
356 grub_usb_keyboard_getkey (struct grub_term_input
*term
)
359 struct grub_usb_keyboard_data
*termdata
= term
->data
;
361 int keycode
= GRUB_TERM_NO_KEY
;
364 return GRUB_TERM_NO_KEY
;
367 keycode
= parse_keycode (termdata
);
368 if (keycode
!= GRUB_TERM_NO_KEY
)
371 /* Poll interrupt pipe. */
372 err
= grub_usb_check_transfer (termdata
->transfer
, &actual
);
374 if (err
== GRUB_USB_ERR_WAIT
)
376 if (termdata
->last_key
!= -1
377 && grub_get_time_ms () > termdata
->repeat_time
)
379 termdata
->repeat_time
= grub_get_time_ms ()
380 + GRUB_TERM_REPEAT_INTERVAL
;
381 return termdata
->last_key
;
383 return GRUB_TERM_NO_KEY
;
386 if (!err
&& (actual
>= 3))
387 grub_memcpy (termdata
->last_report
,
388 termdata
->current_report
,
389 sizeof (termdata
->report
));
391 grub_memcpy (termdata
->current_report
,
393 sizeof (termdata
->report
));
395 termdata
->transfer
= grub_usb_bulk_read_background (termdata
->usbdev
,
397 sizeof (termdata
->report
),
398 (char *) termdata
->report
);
399 if (!termdata
->transfer
)
401 grub_printf ("%s failed. Stopped\n", term
->name
);
405 termdata
->last_key
= -1;
407 grub_dprintf ("usb_keyboard",
408 "err = %d, actual = %" PRIuGRUB_SIZE
409 " report: 0x%02x 0x%02x 0x%02x 0x%02x"
410 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
412 termdata
->current_report
[0], termdata
->current_report
[1],
413 termdata
->current_report
[2], termdata
->current_report
[3],
414 termdata
->current_report
[4], termdata
->current_report
[5],
415 termdata
->current_report
[6], termdata
->current_report
[7]);
417 if (err
|| actual
< 1)
418 return GRUB_TERM_NO_KEY
;
420 termdata
->status
= termdata
->current_report
[0];
423 return GRUB_TERM_NO_KEY
;
425 termdata
->index
= 2; /* New data received. */
426 termdata
->max_index
= actual
;
428 return parse_keycode (termdata
);
432 grub_usb_keyboard_getkeystatus (struct grub_term_input
*term
)
434 struct grub_usb_keyboard_data
*termdata
= term
->data
;
436 return interpret_status (termdata
->status
) | termdata
->mods
;
439 static struct grub_usb_attach_desc attach_hook
=
441 .class = GRUB_USB_CLASS_HID
,
442 .hook
= grub_usb_keyboard_attach
445 GRUB_MOD_INIT(usb_keyboard
)
447 grub_usb_register_attach_hook_class (&attach_hook
);
450 GRUB_MOD_FINI(usb_keyboard
)
453 for (i
= 0; i
< ARRAY_SIZE (grub_usb_keyboards
); i
++)
454 if (grub_usb_keyboards
[i
].data
)
456 struct grub_usb_keyboard_data
*data
= grub_usb_keyboards
[i
].data
;
462 grub_usb_cancel_transfer (data
->transfer
);
464 grub_term_unregister_input (&grub_usb_keyboards
[i
]);
465 grub_free ((char *) grub_usb_keyboards
[i
].name
);
466 grub_usb_keyboards
[i
].name
= NULL
;
467 grub_free (grub_usb_keyboards
[i
].data
);
468 grub_usb_keyboards
[i
].data
= 0;
470 grub_usb_unregister_attach_hook_class (&attach_hook
);