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
);