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 '}', '|', '#', ':', '"', '`', '<', '>',
58 /* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
59 #define USB_HID_GET_REPORT 0x01
60 #define USB_HID_GET_IDLE 0x02
61 #define USB_HID_GET_PROTOCOL 0x03
62 #define USB_HID_SET_REPORT 0x09
63 #define USB_HID_SET_IDLE 0x0A
64 #define USB_HID_SET_PROTOCOL 0x0B
66 #define USB_HID_BOOT_SUBCLASS 0x01
67 #define USB_HID_KBD_PROTOCOL 0x01
69 static int grub_usb_keyboard_checkkey (struct grub_term_input
*term
);
70 static int grub_usb_keyboard_getkey (struct grub_term_input
*term
);
71 static int grub_usb_keyboard_getkeystatus (struct grub_term_input
*term
);
73 static struct grub_term_input grub_usb_keyboard_term
=
75 .checkkey
= grub_usb_keyboard_checkkey
,
76 .getkey
= grub_usb_keyboard_getkey
,
77 .getkeystatus
= grub_usb_keyboard_getkeystatus
,
81 struct grub_usb_keyboard_data
83 grub_usb_device_t usbdev
;
86 struct grub_usb_desc_endp
*endp
;
89 static struct grub_term_input grub_usb_keyboards
[16];
92 grub_usb_keyboard_detach (grub_usb_device_t usbdev
,
93 int config
__attribute__ ((unused
)),
94 int interface
__attribute__ ((unused
)))
97 for (i
= 0; i
< ARRAY_SIZE (grub_usb_keyboards
); i
++)
99 struct grub_usb_keyboard_data
*data
= grub_usb_keyboards
[i
].data
;
104 if (data
->usbdev
!= usbdev
)
107 grub_term_unregister_input (&grub_usb_keyboards
[i
]);
108 grub_free ((char *) grub_usb_keyboards
[i
].name
);
109 grub_usb_keyboards
[i
].name
= NULL
;
110 grub_free (grub_usb_keyboards
[i
].data
);
111 grub_usb_keyboards
[i
].data
= 0;
116 grub_usb_keyboard_attach (grub_usb_device_t usbdev
, int configno
, int interfno
)
119 struct grub_usb_keyboard_data
*data
;
120 struct grub_usb_desc_endp
*endp
= NULL
;
123 grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
124 usbdev
->descdev
.class, usbdev
->descdev
.subclass
,
125 usbdev
->descdev
.protocol
, configno
, interfno
);
127 for (curnum
= 0; curnum
< ARRAY_SIZE (grub_usb_keyboards
); curnum
++)
128 if (!grub_usb_keyboards
[curnum
].data
)
131 if (curnum
== ARRAY_SIZE (grub_usb_keyboards
))
134 if (usbdev
->descdev
.class != 0
135 || usbdev
->descdev
.subclass
!= 0 || usbdev
->descdev
.protocol
!= 0)
138 if (usbdev
->config
[configno
].interf
[interfno
].descif
->subclass
139 != USB_HID_BOOT_SUBCLASS
140 || usbdev
->config
[configno
].interf
[interfno
].descif
->protocol
141 != USB_HID_KBD_PROTOCOL
)
144 for (j
= 0; j
< usbdev
->config
[configno
].interf
[interfno
].descif
->endpointcnt
;
147 endp
= &usbdev
->config
[configno
].interf
[interfno
].descendp
[j
];
149 if ((endp
->endp_addr
& 128) && grub_usb_get_ep_type(endp
)
150 == GRUB_USB_EP_INTERRUPT
)
153 if (j
== usbdev
->config
[configno
].interf
[interfno
].descif
->endpointcnt
)
156 grub_dprintf ("usb_keyboard", "HID found!\n");
158 data
= grub_malloc (sizeof (*data
));
165 data
->usbdev
= usbdev
;
168 /* Place the device in boot mode. */
169 grub_usb_control_msg (usbdev
, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT
,
170 USB_HID_SET_PROTOCOL
, 0, 0, 0, 0);
172 /* Reports every time an event occurs and not more often than that. */
173 grub_usb_control_msg (usbdev
, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT
,
174 USB_HID_SET_IDLE
, 0<<8, 0, 0, 0);
176 grub_memcpy (&grub_usb_keyboards
[curnum
], &grub_usb_keyboard_term
,
177 sizeof (grub_usb_keyboards
[curnum
]));
178 grub_usb_keyboards
[curnum
].data
= data
;
179 usbdev
->config
[configno
].interf
[interfno
].detach_hook
180 = grub_usb_keyboard_detach
;
181 grub_usb_keyboards
[curnum
].name
= grub_xasprintf ("usb_keyboard%d", curnum
);
182 if (!grub_usb_keyboards
[curnum
].name
)
189 grub_uint8_t report
[8];
191 grub_memset (report
, 0, sizeof (report
));
192 err
= grub_usb_control_msg (usbdev
, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN
,
193 USB_HID_GET_REPORT
, 0x0000, interfno
,
194 sizeof (report
), (char *) report
);
202 data
->status
= report
[0];
203 data
->key
= report
[2] ? : -1;
207 grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards
[curnum
]);
215 grub_usb_keyboard_checkkey (struct grub_term_input
*term
)
217 grub_uint8_t data
[8];
219 struct grub_usb_keyboard_data
*termdata
= term
->data
;
222 if (termdata
->key
!= -1)
223 return termdata
->key
;
226 /* Poll interrupt pipe. */
227 err
= grub_usb_bulk_read_extended (termdata
->usbdev
,
228 termdata
->endp
->endp_addr
, sizeof (data
),
229 (char *) data
, 10, &actual
);
230 if (err
|| actual
< 1)
233 termdata
->status
= data
[0];
235 if (actual
< 3 || !data
[2])
238 grub_dprintf ("usb_keyboard",
239 "report: 0x%02x 0x%02x 0x%02x 0x%02x"
240 " 0x%02x 0x%02x 0x%02x 0x%02x\n",
241 data
[0], data
[1], data
[2], data
[3],
242 data
[4], data
[5], data
[6], data
[7]);
244 /* Check if the Control or Shift key was pressed. */
245 if (data
[0] & 0x01 || data
[0] & 0x10)
246 termdata
->key
= keyboard_map
[data
[2]] - 'a' + 1;
247 else if (data
[0] & 0x02 || data
[0] & 0x20)
248 termdata
->key
= keyboard_map_shift
[data
[2]];
250 termdata
->key
= keyboard_map
[data
[2]];
252 if (termdata
->key
== 0)
253 grub_printf ("Unknown key 0x%x detected\n", data
[2]);
255 grub_errno
= GRUB_ERR_NONE
;
257 return termdata
->key
;
261 grub_usb_keyboard_getkey (struct grub_term_input
*term
)
264 struct grub_usb_keyboard_data
*termdata
= term
->data
;
266 while (termdata
->key
== -1)
267 grub_usb_keyboard_checkkey (term
);
277 grub_usb_keyboard_getkeystatus (struct grub_term_input
*term
)
279 struct grub_usb_keyboard_data
*termdata
= term
->data
;
282 /* Check Shift, Control, and Alt status. */
283 if (termdata
->status
& 0x02 || termdata
->status
& 0x20)
284 mods
|= GRUB_TERM_STATUS_SHIFT
;
285 if (termdata
->status
& 0x01 || termdata
->status
& 0x10)
286 mods
|= GRUB_TERM_STATUS_CTRL
;
287 if (termdata
->status
& 0x04 || termdata
->status
& 0x40)
288 mods
|= GRUB_TERM_STATUS_ALT
;
293 struct grub_usb_attach_desc attach_hook
=
295 .class = GRUB_USB_CLASS_HID
,
296 .hook
= grub_usb_keyboard_attach
299 GRUB_MOD_INIT(usb_keyboard
)
301 grub_usb_register_attach_hook_class (&attach_hook
);
304 GRUB_MOD_FINI(usb_keyboard
)
307 for (i
= 0; i
< ARRAY_SIZE (grub_usb_keyboards
); i
++)
308 if (grub_usb_keyboards
[i
].data
)
310 grub_term_unregister_input (&grub_usb_keyboards
[i
]);
311 grub_free ((char *) grub_usb_keyboards
[i
].name
);
312 grub_usb_keyboards
[i
].name
= NULL
;
313 grub_usb_keyboards
[i
].data
= 0;
315 grub_usb_unregister_attach_hook_class (&attach_hook
);