blob: b88d684f483b6c9518b06a870c01dc106d0cb56e [file] [log] [blame]
Kevin O'Connor114592f2009-09-28 21:32:08 -04001// Code for handling USB Human Interface Devices (HID).
2//
3// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
4//
5// This file may be distributed under the terms of the GNU LGPLv3 license.
6
7#include "util.h" // dprintf
8#include "usb-hid.h" // usb_keyboard_setup
9#include "config.h" // CONFIG_*
10#include "usb.h" // usb_ctrlrequest
11#include "biosvar.h" // GET_GLOBAL
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040012#include "ps2port.h" // ATKBD_CMD_GETID
Kevin O'Connor114592f2009-09-28 21:32:08 -040013
Kevin O'Connor59f02832009-10-12 10:09:15 -040014struct usb_pipe *keyboard_pipe VAR16VISIBLE;
Kevin O'Connor0e885762010-05-01 22:14:40 -040015struct usb_pipe *mouse_pipe VAR16VISIBLE;
Kevin O'Connor114592f2009-09-28 21:32:08 -040016
17
18/****************************************************************
19 * Setup
20 ****************************************************************/
21
Kevin O'Connor5718d562010-05-01 19:25:41 -040022// Send USB HID protocol message.
Kevin O'Connor114592f2009-09-28 21:32:08 -040023static int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050024set_protocol(struct usb_pipe *pipe, u16 val)
Kevin O'Connor114592f2009-09-28 21:32:08 -040025{
26 struct usb_ctrlrequest req;
27 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
28 req.bRequest = HID_REQ_SET_PROTOCOL;
29 req.wValue = val;
30 req.wIndex = 0;
31 req.wLength = 0;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050032 return send_default_control(pipe, &req, NULL);
Kevin O'Connor114592f2009-09-28 21:32:08 -040033}
34
Kevin O'Connor5718d562010-05-01 19:25:41 -040035// Send USB HID SetIdle request.
Kevin O'Connor114592f2009-09-28 21:32:08 -040036static int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050037set_idle(struct usb_pipe *pipe, int ms)
Kevin O'Connor114592f2009-09-28 21:32:08 -040038{
39 struct usb_ctrlrequest req;
40 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
41 req.bRequest = HID_REQ_SET_IDLE;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -050042 req.wValue = (ms/4)<<8;
Kevin O'Connor114592f2009-09-28 21:32:08 -040043 req.wIndex = 0;
44 req.wLength = 0;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050045 return send_default_control(pipe, &req, NULL);
Kevin O'Connor114592f2009-09-28 21:32:08 -040046}
47
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -050048#define KEYREPEATWAITMS 500
49#define KEYREPEATMS 33
50
Kevin O'Connor0e885762010-05-01 22:14:40 -040051static int
52usb_kbd_init(struct usb_pipe *pipe, struct usb_endpoint_descriptor *epdesc)
Kevin O'Connor114592f2009-09-28 21:32:08 -040053{
54 if (! CONFIG_USB_KEYBOARD)
55 return -1;
56 if (keyboard_pipe)
Kevin O'Connora5826b52009-10-24 17:57:29 -040057 // XXX - this enables the first found keyboard (could be random)
Kevin O'Connor114592f2009-09-28 21:32:08 -040058 return -1;
Kevin O'Connor114592f2009-09-28 21:32:08 -040059
Kevin O'Connor0e885762010-05-01 22:14:40 -040060 if (epdesc->wMaxPacketSize != 8)
Kevin O'Connor3c160dd2010-02-17 23:06:52 -050061 return -1;
Kevin O'Connor114592f2009-09-28 21:32:08 -040062
63 // Enable "boot" protocol.
Kevin O'Connore5cd9452010-05-01 22:45:56 -040064 int ret = set_protocol(pipe, 0);
Kevin O'Connor114592f2009-09-28 21:32:08 -040065 if (ret)
66 return -1;
Kevin O'Connor3c160dd2010-02-17 23:06:52 -050067 // Periodically send reports to enable key repeat.
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050068 ret = set_idle(pipe, KEYREPEATMS);
Kevin O'Connor114592f2009-09-28 21:32:08 -040069 if (ret)
70 return -1;
71
Kevin O'Connor4547eb92010-02-28 02:17:28 -050072 keyboard_pipe = alloc_intr_pipe(pipe, epdesc);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050073 if (!keyboard_pipe)
Kevin O'Connor114592f2009-09-28 21:32:08 -040074 return -1;
Kevin O'Connor114592f2009-09-28 21:32:08 -040075
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050076 dprintf(1, "USB keyboard initialized\n");
Kevin O'Connor114592f2009-09-28 21:32:08 -040077 return 0;
78}
79
Kevin O'Connor0e885762010-05-01 22:14:40 -040080static int
81usb_mouse_init(struct usb_pipe *pipe, struct usb_endpoint_descriptor *epdesc)
Kevin O'Connor114592f2009-09-28 21:32:08 -040082{
Kevin O'Connor0e885762010-05-01 22:14:40 -040083 if (! CONFIG_USB_MOUSE)
84 return -1;
85 if (mouse_pipe)
86 // XXX - this enables the first found mouse (could be random)
87 return -1;
88
89 if (epdesc->wMaxPacketSize < 3 || epdesc->wMaxPacketSize > 8)
90 return -1;
91
92 // Enable "boot" protocol.
Kevin O'Connore5cd9452010-05-01 22:45:56 -040093 int ret = set_protocol(pipe, 0);
Kevin O'Connor0e885762010-05-01 22:14:40 -040094 if (ret)
95 return -1;
96
97 mouse_pipe = alloc_intr_pipe(pipe, epdesc);
98 if (!mouse_pipe)
99 return -1;
100
101 dprintf(1, "USB mouse initialized\n");
102 return 0;
103}
104
105// Initialize a found USB HID device (if applicable).
106int
107usb_hid_init(struct usb_pipe *pipe
108 , struct usb_interface_descriptor *iface, int imax)
109{
110 if (! CONFIG_USB_KEYBOARD || ! CONFIG_USB_MOUSE)
111 return -1;
112 dprintf(2, "usb_hid_init %p\n", pipe);
113
114 if (iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT)
115 // Doesn't support boot protocol.
116 return -1;
117
118 // Find intr in endpoint.
119 struct usb_endpoint_descriptor *epdesc = findEndPointDesc(
120 iface, imax, USB_ENDPOINT_XFER_INT, USB_DIR_IN);
121 if (!epdesc) {
122 dprintf(1, "No usb hid intr in?\n");
123 return -1;
124 }
125
126 if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD)
127 return usb_kbd_init(pipe, epdesc);
128 if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
129 return usb_mouse_init(pipe, epdesc);
130 return -1;
131}
132
133void
134usb_hid_setup(void)
135{
136 if (CONFIG_USB_KEYBOARD)
137 keyboard_pipe = NULL;
138 if (CONFIG_USB_MOUSE)
139 mouse_pipe = NULL;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400140}
141
142
143/****************************************************************
144 * Keyboard events
145 ****************************************************************/
146
Kevin O'Connor5718d562010-05-01 19:25:41 -0400147// Mapping from USB key id to ps2 key sequence.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400148static u16 KeyToScanCode[] VAR16 = {
149 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020,
150 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026,
151 0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014,
152 0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003,
153 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b,
154 0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a,
155 0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034,
156 0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040,
157 0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046,
158 0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d,
159 0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e,
160 0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047,
161 0x0048, 0x0049, 0x0052, 0x0053
162};
163
Kevin O'Connor5718d562010-05-01 19:25:41 -0400164// Mapping from USB modifier id to ps2 key sequence.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400165static u16 ModifierToScanCode[] VAR16 = {
166 //lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui
167 0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c
168};
169
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500170#define RELEASEBIT 0x80
171
Kevin O'Connor0e885762010-05-01 22:14:40 -0400172// Format of USB keyboard event data
Kevin O'Connor114592f2009-09-28 21:32:08 -0400173struct keyevent {
174 u8 modifiers;
175 u8 reserved;
176 u8 keys[6];
177};
178
Kevin O'Connor5718d562010-05-01 19:25:41 -0400179// Translate data from KeyToScanCode[] to calls to process_key().
Kevin O'Connor114592f2009-09-28 21:32:08 -0400180static void
181prockeys(u16 keys)
182{
183 if (keys > 0xff) {
184 u8 key = keys>>8;
185 if (key == 0xe1) {
186 // Pause key
187 process_key(0xe1);
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500188 process_key(0x1d | (keys & RELEASEBIT));
189 process_key(0x45 | (keys & RELEASEBIT));
Kevin O'Connor114592f2009-09-28 21:32:08 -0400190 return;
191 }
192 process_key(key);
193 }
194 process_key(keys);
195}
196
Kevin O'Connor5718d562010-05-01 19:25:41 -0400197// Handle a USB key press/release event.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400198static void
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500199procscankey(u8 key, u8 flags)
200{
201 if (key >= ARRAY_SIZE(KeyToScanCode))
202 return;
203 u16 keys = GET_GLOBAL(KeyToScanCode[key]);
204 if (keys)
205 prockeys(keys | flags);
206}
207
Kevin O'Connor5718d562010-05-01 19:25:41 -0400208// Handle a USB modifier press/release event.
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500209static void
210procmodkey(u8 mods, u8 flags)
211{
212 int i;
213 for (i=0; mods; i++)
214 if (mods & (1<<i)) {
215 // Modifier key change.
216 prockeys(GET_GLOBAL(ModifierToScanCode[i]) | flags);
217 mods &= ~(1<<i);
218 }
219}
220
Kevin O'Connor5718d562010-05-01 19:25:41 -0400221// Process USB keyboard data.
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500222static void noinline
Kevin O'Connor114592f2009-09-28 21:32:08 -0400223handle_key(struct keyevent *data)
224{
Kevin O'Connor78523312010-02-14 19:07:43 -0500225 dprintf(9, "Got key %x %x\n", data->modifiers, data->keys[0]);
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500226
227 // Load old keys.
228 u16 ebda_seg = get_ebda_seg();
229 struct usbkeyinfo old;
230 old.data = GET_EBDA2(ebda_seg, usbkey_last.data);
231
232 // Check for keys no longer pressed.
233 int addpos = 0;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400234 int i;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500235 for (i=0; i<ARRAY_SIZE(old.keys); i++) {
236 u8 key = old.keys[i];
237 if (!key)
238 break;
239 int j;
240 for (j=0;; j++) {
241 if (j>=ARRAY_SIZE(data->keys)) {
242 // Key released.
243 procscankey(key, RELEASEBIT);
244 if (i+1 >= ARRAY_SIZE(old.keys) || !old.keys[i+1])
245 // Last pressed key released - disable repeat.
246 old.repeatcount = 0xff;
247 break;
248 }
249 if (data->keys[j] == key) {
250 // Key still pressed.
251 data->keys[j] = 0;
252 old.keys[addpos++] = key;
253 break;
254 }
255 }
256 }
257 procmodkey(old.modifiers & ~data->modifiers, RELEASEBIT);
258
259 // Process new keys
260 procmodkey(data->modifiers & ~old.modifiers, 0);
261 old.modifiers = data->modifiers;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400262 for (i=0; i<ARRAY_SIZE(data->keys); i++) {
263 u8 key = data->keys[i];
Kevin O'Connor114592f2009-09-28 21:32:08 -0400264 if (!key)
265 continue;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500266 // New key pressed.
267 procscankey(key, 0);
268 old.keys[addpos++] = key;
269 old.repeatcount = KEYREPEATWAITMS / KEYREPEATMS + 1;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400270 }
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500271 if (addpos < ARRAY_SIZE(old.keys))
272 old.keys[addpos] = 0;
273
274 // Check for key repeat event.
275 if (addpos) {
276 if (!old.repeatcount)
277 procscankey(old.keys[addpos-1], 0);
278 else if (old.repeatcount != 0xff)
279 old.repeatcount--;
280 }
281
282 // Update old keys
283 SET_EBDA2(ebda_seg, usbkey_last.data, old.data);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400284}
285
Kevin O'Connor0e885762010-05-01 22:14:40 -0400286// Check if a USB keyboard event is pending and process it if so.
287static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500288usb_check_key(void)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400289{
290 if (! CONFIG_USB_KEYBOARD)
291 return;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400292 struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400293 if (!pipe)
294 return;
295
296 for (;;) {
297 struct keyevent data;
298 int ret = usb_poll_intr(pipe, &data);
299 if (ret)
300 break;
301 handle_key(&data);
302 }
303}
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400304
305// Test if USB keyboard is active.
306inline int
307usb_kbd_active(void)
308{
Kevin O'Connor0e885762010-05-01 22:14:40 -0400309 if (! CONFIG_USB_KEYBOARD)
310 return 0;
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400311 return GET_GLOBAL(keyboard_pipe) != NULL;
312}
313
314// Handle a ps2 style keyboard command.
315inline int
316usb_kbd_command(int command, u8 *param)
317{
Kevin O'Connor0e885762010-05-01 22:14:40 -0400318 if (! CONFIG_USB_KEYBOARD)
319 return -1;
320 dprintf(9, "usb keyboard cmd=%x\n", command);
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400321 switch (command) {
322 case ATKBD_CMD_GETID:
323 // Return the id of a standard AT keyboard.
324 param[0] = 0xab;
325 param[1] = 0x83;
326 return 0;
327 default:
328 return -1;
329 }
330}
Kevin O'Connor0e885762010-05-01 22:14:40 -0400331
332
333/****************************************************************
334 * Mouse events
335 ****************************************************************/
336
337// Format of USB mouse event data
338struct mouseevent {
339 u8 buttons;
340 u8 x, y;
341 u8 reserved[5];
342};
343
344// Process USB mouse data.
345static void
346handle_mouse(struct mouseevent *data)
347{
348 dprintf(9, "Got mouse b=%x x=%x y=%x\n", data->buttons, data->x, data->y);
349
350 s8 x = data->x, y = -data->y;
351 u8 flag = ((data->buttons & 0x7) | (1<<3)
352 | (x & 0x80 ? (1<<4) : 0) | (y & 0x80 ? (1<<5) : 0));
353 process_mouse(flag);
354 process_mouse(x);
355 process_mouse(y);
356}
357
358// Check if a USB mouse event is pending and process it if so.
359static void
360usb_check_mouse(void)
361{
362 if (! CONFIG_USB_MOUSE)
363 return;
364 struct usb_pipe *pipe = GET_GLOBAL(mouse_pipe);
365 if (!pipe)
366 return;
367
368 for (;;) {
369 struct mouseevent data;
370 int ret = usb_poll_intr(pipe, &data);
371 if (ret)
372 break;
373 handle_mouse(&data);
374 }
375}
376
377// Test if USB mouse is active.
378inline int
379usb_mouse_active(void)
380{
381 if (! CONFIG_USB_MOUSE)
382 return 0;
383 return GET_GLOBAL(mouse_pipe) != NULL;
384}
385
386// Handle a ps2 style mouse command.
387inline int
388usb_mouse_command(int command, u8 *param)
389{
390 if (! CONFIG_USB_MOUSE)
391 return -1;
392 dprintf(9, "usb mouse cmd=%x\n", command);
393 switch (command) {
394 case PSMOUSE_CMD_ENABLE:
395 case PSMOUSE_CMD_DISABLE:
396 case PSMOUSE_CMD_SETSCALE11:
397 return 0;
398 case PSMOUSE_CMD_SETSCALE21:
399 case PSMOUSE_CMD_SETRATE:
400 case PSMOUSE_CMD_SETRES:
401 // XXX
402 return 0;
403 case PSMOUSE_CMD_RESET_BAT:
404 case PSMOUSE_CMD_GETID:
405 // Return the id of a standard AT mouse.
406 param[0] = 0xaa;
407 param[1] = 0x00;
408 return 0;
409
410 case PSMOUSE_CMD_GETINFO:
411 param[0] = 0x00;
412 param[1] = 4;
413 param[2] = 100;
414 return 0;
415
416 default:
417 return -1;
418 }
419}
420
421// Check for USB events pending - called periodically from timer interrupt.
422void
423usb_check_event(void)
424{
425 usb_check_key();
426 usb_check_mouse();
427}