blob: 09a6cf455e01668e805881c6afe3f044cb901d7c [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
Kevin O'Connor114592f2009-09-28 21:32:08 -04007#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor2d2fa312013-09-14 21:55:26 -04008#include "config.h" // CONFIG_*
9#include "output.h" // dprintf
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040010#include "ps2port.h" // ATKBD_CMD_GETID
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040011#include "usb.h" // usb_ctrlrequest
12#include "usb-hid.h" // usb_keyboard_setup
13#include "util.h" // process_key
Kevin O'Connor114592f2009-09-28 21:32:08 -040014
Kevin O'Connor89a2f962013-02-18 23:36:03 -050015struct usb_pipe *keyboard_pipe VARFSEG;
16struct usb_pipe *mouse_pipe VARFSEG;
Kevin O'Connor114592f2009-09-28 21:32:08 -040017
18
19/****************************************************************
20 * Setup
21 ****************************************************************/
22
Kevin O'Connor5718d562010-05-01 19:25:41 -040023// Send USB HID protocol message.
Kevin O'Connor114592f2009-09-28 21:32:08 -040024static int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050025set_protocol(struct usb_pipe *pipe, u16 val)
Kevin O'Connor114592f2009-09-28 21:32:08 -040026{
27 struct usb_ctrlrequest req;
28 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
29 req.bRequest = HID_REQ_SET_PROTOCOL;
30 req.wValue = val;
31 req.wIndex = 0;
32 req.wLength = 0;
Kevin O'Connor222bad42014-10-16 12:11:12 -040033 return usb_send_default_control(pipe, &req, NULL);
Kevin O'Connor114592f2009-09-28 21:32:08 -040034}
35
Kevin O'Connor5718d562010-05-01 19:25:41 -040036// Send USB HID SetIdle request.
Kevin O'Connor114592f2009-09-28 21:32:08 -040037static int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050038set_idle(struct usb_pipe *pipe, int ms)
Kevin O'Connor114592f2009-09-28 21:32:08 -040039{
40 struct usb_ctrlrequest req;
41 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
42 req.bRequest = HID_REQ_SET_IDLE;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -050043 req.wValue = (ms/4)<<8;
Kevin O'Connor114592f2009-09-28 21:32:08 -040044 req.wIndex = 0;
45 req.wLength = 0;
Kevin O'Connor222bad42014-10-16 12:11:12 -040046 return usb_send_default_control(pipe, &req, NULL);
Kevin O'Connor114592f2009-09-28 21:32:08 -040047}
48
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -050049#define KEYREPEATWAITMS 500
50#define KEYREPEATMS 33
51
Kevin O'Connor0e885762010-05-01 22:14:40 -040052static int
Kevin O'Connord83c87b2013-01-21 01:14:12 -050053usb_kbd_setup(struct usbdevice_s *usbdev
54 , struct usb_endpoint_descriptor *epdesc)
Kevin O'Connor114592f2009-09-28 21:32:08 -040055{
56 if (! CONFIG_USB_KEYBOARD)
57 return -1;
58 if (keyboard_pipe)
Kevin O'Connora5826b52009-10-24 17:57:29 -040059 // XXX - this enables the first found keyboard (could be random)
Kevin O'Connor114592f2009-09-28 21:32:08 -040060 return -1;
Kevin O'Connor114592f2009-09-28 21:32:08 -040061
Kevin O'Connor0e885762010-05-01 22:14:40 -040062 if (epdesc->wMaxPacketSize != 8)
Kevin O'Connor3c160dd2010-02-17 23:06:52 -050063 return -1;
Kevin O'Connor114592f2009-09-28 21:32:08 -040064
65 // Enable "boot" protocol.
Kevin O'Connor6a8e8952012-03-08 07:20:30 -050066 int ret = set_protocol(usbdev->defpipe, 0);
Kevin O'Connor114592f2009-09-28 21:32:08 -040067 if (ret)
68 return -1;
Kevin O'Connor3c160dd2010-02-17 23:06:52 -050069 // Periodically send reports to enable key repeat.
Kevin O'Connor6a8e8952012-03-08 07:20:30 -050070 ret = set_idle(usbdev->defpipe, KEYREPEATMS);
Kevin O'Connor114592f2009-09-28 21:32:08 -040071 if (ret)
72 return -1;
73
Kevin O'Connorc3d96c22012-03-10 09:03:25 -050074 keyboard_pipe = usb_alloc_pipe(usbdev, epdesc);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050075 if (!keyboard_pipe)
Kevin O'Connor114592f2009-09-28 21:32:08 -040076 return -1;
Kevin O'Connor114592f2009-09-28 21:32:08 -040077
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050078 dprintf(1, "USB keyboard initialized\n");
Kevin O'Connor114592f2009-09-28 21:32:08 -040079 return 0;
80}
81
Kevin O'Connor0e885762010-05-01 22:14:40 -040082static int
Kevin O'Connord83c87b2013-01-21 01:14:12 -050083usb_mouse_setup(struct usbdevice_s *usbdev
84 , struct usb_endpoint_descriptor *epdesc)
Kevin O'Connor114592f2009-09-28 21:32:08 -040085{
Kevin O'Connor0e885762010-05-01 22:14:40 -040086 if (! CONFIG_USB_MOUSE)
87 return -1;
88 if (mouse_pipe)
89 // XXX - this enables the first found mouse (could be random)
90 return -1;
91
92 if (epdesc->wMaxPacketSize < 3 || epdesc->wMaxPacketSize > 8)
93 return -1;
94
95 // Enable "boot" protocol.
Kevin O'Connor6a8e8952012-03-08 07:20:30 -050096 int ret = set_protocol(usbdev->defpipe, 0);
Kevin O'Connor0e885762010-05-01 22:14:40 -040097 if (ret)
98 return -1;
99
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500100 mouse_pipe = usb_alloc_pipe(usbdev, epdesc);
Kevin O'Connor0e885762010-05-01 22:14:40 -0400101 if (!mouse_pipe)
102 return -1;
103
104 dprintf(1, "USB mouse initialized\n");
105 return 0;
106}
107
108// Initialize a found USB HID device (if applicable).
109int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500110usb_hid_setup(struct usbdevice_s *usbdev)
Kevin O'Connor0e885762010-05-01 22:14:40 -0400111{
Kevin O'Connor48dc6a62014-03-31 23:50:42 -0400112 if (! CONFIG_USB_KEYBOARD && ! CONFIG_USB_MOUSE)
Kevin O'Connor0e885762010-05-01 22:14:40 -0400113 return -1;
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500114 dprintf(2, "usb_hid_setup %p\n", usbdev->defpipe);
Kevin O'Connor0e885762010-05-01 22:14:40 -0400115
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500116 struct usb_interface_descriptor *iface = usbdev->iface;
Kevin O'Connor0e885762010-05-01 22:14:40 -0400117 if (iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT)
118 // Doesn't support boot protocol.
119 return -1;
120
121 // Find intr in endpoint.
Kevin O'Connor674f1402014-10-16 12:08:00 -0400122 struct usb_endpoint_descriptor *epdesc = usb_find_desc(
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500123 usbdev, USB_ENDPOINT_XFER_INT, USB_DIR_IN);
Kevin O'Connor0e885762010-05-01 22:14:40 -0400124 if (!epdesc) {
125 dprintf(1, "No usb hid intr in?\n");
126 return -1;
127 }
128
129 if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD)
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500130 return usb_kbd_setup(usbdev, epdesc);
Kevin O'Connor0e885762010-05-01 22:14:40 -0400131 if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500132 return usb_mouse_setup(usbdev, epdesc);
Kevin O'Connor0e885762010-05-01 22:14:40 -0400133 return -1;
134}
135
Kevin O'Connor114592f2009-09-28 21:32:08 -0400136
137/****************************************************************
138 * Keyboard events
139 ****************************************************************/
140
Kevin O'Connor5718d562010-05-01 19:25:41 -0400141// Mapping from USB key id to ps2 key sequence.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400142static u16 KeyToScanCode[] VAR16 = {
143 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020,
144 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026,
145 0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014,
146 0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003,
147 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b,
148 0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a,
149 0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034,
150 0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040,
151 0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046,
152 0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d,
153 0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e,
154 0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047,
155 0x0048, 0x0049, 0x0052, 0x0053
156};
157
Kevin O'Connor5718d562010-05-01 19:25:41 -0400158// Mapping from USB modifier id to ps2 key sequence.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400159static u16 ModifierToScanCode[] VAR16 = {
160 //lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui
161 0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c
162};
163
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500164#define RELEASEBIT 0x80
165
Kevin O'Connor0e885762010-05-01 22:14:40 -0400166// Format of USB keyboard event data
Kevin O'Connor114592f2009-09-28 21:32:08 -0400167struct keyevent {
168 u8 modifiers;
169 u8 reserved;
170 u8 keys[6];
171};
172
Kevin O'Connor5718d562010-05-01 19:25:41 -0400173// Translate data from KeyToScanCode[] to calls to process_key().
Kevin O'Connor114592f2009-09-28 21:32:08 -0400174static void
175prockeys(u16 keys)
176{
177 if (keys > 0xff) {
178 u8 key = keys>>8;
179 if (key == 0xe1) {
180 // Pause key
181 process_key(0xe1);
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500182 process_key(0x1d | (keys & RELEASEBIT));
183 process_key(0x45 | (keys & RELEASEBIT));
Kevin O'Connor114592f2009-09-28 21:32:08 -0400184 return;
185 }
186 process_key(key);
187 }
188 process_key(keys);
189}
190
Kevin O'Connor5718d562010-05-01 19:25:41 -0400191// Handle a USB key press/release event.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400192static void
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500193procscankey(u8 key, u8 flags)
194{
195 if (key >= ARRAY_SIZE(KeyToScanCode))
196 return;
197 u16 keys = GET_GLOBAL(KeyToScanCode[key]);
198 if (keys)
199 prockeys(keys | flags);
200}
201
Kevin O'Connor5718d562010-05-01 19:25:41 -0400202// Handle a USB modifier press/release event.
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500203static void
204procmodkey(u8 mods, u8 flags)
205{
206 int i;
207 for (i=0; mods; i++)
208 if (mods & (1<<i)) {
209 // Modifier key change.
210 prockeys(GET_GLOBAL(ModifierToScanCode[i]) | flags);
211 mods &= ~(1<<i);
212 }
213}
214
Kevin O'Connor2d012ec2012-05-13 12:39:16 -0400215struct usbkeyinfo {
216 union {
217 struct {
218 u8 modifiers;
219 u8 repeatcount;
220 u8 keys[6];
221 };
222 u64 data;
223 };
224};
225struct usbkeyinfo LastUSBkey VARLOW;
226
Kevin O'Connor5718d562010-05-01 19:25:41 -0400227// Process USB keyboard data.
Kevin O'Connorac7eb5e2012-05-28 14:42:16 -0400228static void
Kevin O'Connor114592f2009-09-28 21:32:08 -0400229handle_key(struct keyevent *data)
230{
Kevin O'Connor78523312010-02-14 19:07:43 -0500231 dprintf(9, "Got key %x %x\n", data->modifiers, data->keys[0]);
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500232
233 // Load old keys.
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500234 struct usbkeyinfo old;
Kevin O'Connor2d012ec2012-05-13 12:39:16 -0400235 old.data = GET_LOW(LastUSBkey.data);
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500236
237 // Check for keys no longer pressed.
238 int addpos = 0;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400239 int i;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500240 for (i=0; i<ARRAY_SIZE(old.keys); i++) {
241 u8 key = old.keys[i];
242 if (!key)
243 break;
244 int j;
245 for (j=0;; j++) {
246 if (j>=ARRAY_SIZE(data->keys)) {
247 // Key released.
248 procscankey(key, RELEASEBIT);
249 if (i+1 >= ARRAY_SIZE(old.keys) || !old.keys[i+1])
250 // Last pressed key released - disable repeat.
251 old.repeatcount = 0xff;
252 break;
253 }
254 if (data->keys[j] == key) {
255 // Key still pressed.
256 data->keys[j] = 0;
257 old.keys[addpos++] = key;
258 break;
259 }
260 }
261 }
262 procmodkey(old.modifiers & ~data->modifiers, RELEASEBIT);
263
264 // Process new keys
265 procmodkey(data->modifiers & ~old.modifiers, 0);
266 old.modifiers = data->modifiers;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400267 for (i=0; i<ARRAY_SIZE(data->keys); i++) {
268 u8 key = data->keys[i];
Kevin O'Connor114592f2009-09-28 21:32:08 -0400269 if (!key)
270 continue;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500271 // New key pressed.
272 procscankey(key, 0);
273 old.keys[addpos++] = key;
274 old.repeatcount = KEYREPEATWAITMS / KEYREPEATMS + 1;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400275 }
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500276 if (addpos < ARRAY_SIZE(old.keys))
277 old.keys[addpos] = 0;
278
279 // Check for key repeat event.
280 if (addpos) {
281 if (!old.repeatcount)
282 procscankey(old.keys[addpos-1], 0);
283 else if (old.repeatcount != 0xff)
284 old.repeatcount--;
285 }
286
287 // Update old keys
Kevin O'Connor2d012ec2012-05-13 12:39:16 -0400288 SET_LOW(LastUSBkey.data, old.data);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400289}
290
Kevin O'Connor0e885762010-05-01 22:14:40 -0400291// Check if a USB keyboard event is pending and process it if so.
292static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500293usb_check_key(void)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400294{
295 if (! CONFIG_USB_KEYBOARD)
296 return;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400297 struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400298 if (!pipe)
299 return;
300
301 for (;;) {
302 struct keyevent data;
303 int ret = usb_poll_intr(pipe, &data);
304 if (ret)
305 break;
306 handle_key(&data);
307 }
308}
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400309
310// Test if USB keyboard is active.
311inline int
312usb_kbd_active(void)
313{
Kevin O'Connor0e885762010-05-01 22:14:40 -0400314 if (! CONFIG_USB_KEYBOARD)
315 return 0;
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400316 return GET_GLOBAL(keyboard_pipe) != NULL;
317}
318
319// Handle a ps2 style keyboard command.
320inline int
321usb_kbd_command(int command, u8 *param)
322{
Kevin O'Connor0e885762010-05-01 22:14:40 -0400323 if (! CONFIG_USB_KEYBOARD)
324 return -1;
325 dprintf(9, "usb keyboard cmd=%x\n", command);
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400326 switch (command) {
327 case ATKBD_CMD_GETID:
328 // Return the id of a standard AT keyboard.
329 param[0] = 0xab;
330 param[1] = 0x83;
331 return 0;
332 default:
333 return -1;
334 }
335}
Kevin O'Connor0e885762010-05-01 22:14:40 -0400336
337
338/****************************************************************
339 * Mouse events
340 ****************************************************************/
341
342// Format of USB mouse event data
343struct mouseevent {
344 u8 buttons;
345 u8 x, y;
346 u8 reserved[5];
347};
348
349// Process USB mouse data.
350static void
351handle_mouse(struct mouseevent *data)
352{
353 dprintf(9, "Got mouse b=%x x=%x y=%x\n", data->buttons, data->x, data->y);
354
355 s8 x = data->x, y = -data->y;
356 u8 flag = ((data->buttons & 0x7) | (1<<3)
357 | (x & 0x80 ? (1<<4) : 0) | (y & 0x80 ? (1<<5) : 0));
358 process_mouse(flag);
359 process_mouse(x);
360 process_mouse(y);
361}
362
363// Check if a USB mouse event is pending and process it if so.
364static void
365usb_check_mouse(void)
366{
367 if (! CONFIG_USB_MOUSE)
368 return;
369 struct usb_pipe *pipe = GET_GLOBAL(mouse_pipe);
370 if (!pipe)
371 return;
372
373 for (;;) {
374 struct mouseevent data;
375 int ret = usb_poll_intr(pipe, &data);
376 if (ret)
377 break;
378 handle_mouse(&data);
379 }
380}
381
382// Test if USB mouse is active.
383inline int
384usb_mouse_active(void)
385{
386 if (! CONFIG_USB_MOUSE)
387 return 0;
388 return GET_GLOBAL(mouse_pipe) != NULL;
389}
390
391// Handle a ps2 style mouse command.
392inline int
393usb_mouse_command(int command, u8 *param)
394{
395 if (! CONFIG_USB_MOUSE)
396 return -1;
397 dprintf(9, "usb mouse cmd=%x\n", command);
398 switch (command) {
399 case PSMOUSE_CMD_ENABLE:
400 case PSMOUSE_CMD_DISABLE:
401 case PSMOUSE_CMD_SETSCALE11:
402 return 0;
403 case PSMOUSE_CMD_SETSCALE21:
404 case PSMOUSE_CMD_SETRATE:
405 case PSMOUSE_CMD_SETRES:
406 // XXX
407 return 0;
408 case PSMOUSE_CMD_RESET_BAT:
409 case PSMOUSE_CMD_GETID:
410 // Return the id of a standard AT mouse.
411 param[0] = 0xaa;
412 param[1] = 0x00;
413 return 0;
414
415 case PSMOUSE_CMD_GETINFO:
416 param[0] = 0x00;
417 param[1] = 4;
418 param[2] = 100;
419 return 0;
420
421 default:
422 return -1;
423 }
424}
425
426// Check for USB events pending - called periodically from timer interrupt.
427void
428usb_check_event(void)
429{
430 usb_check_key();
431 usb_check_mouse();
432}