blob: 168b7fabb209588fffe89bdfd79fdfc8c3f98233 [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
Kevin O'Connor114592f2009-09-28 21:32:08 -0400133
134/****************************************************************
135 * Keyboard events
136 ****************************************************************/
137
Kevin O'Connor5718d562010-05-01 19:25:41 -0400138// Mapping from USB key id to ps2 key sequence.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400139static u16 KeyToScanCode[] VAR16 = {
140 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020,
141 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026,
142 0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014,
143 0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003,
144 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b,
145 0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a,
146 0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034,
147 0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040,
148 0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046,
149 0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d,
150 0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e,
151 0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047,
152 0x0048, 0x0049, 0x0052, 0x0053
153};
154
Kevin O'Connor5718d562010-05-01 19:25:41 -0400155// Mapping from USB modifier id to ps2 key sequence.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400156static u16 ModifierToScanCode[] VAR16 = {
157 //lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui
158 0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c
159};
160
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500161#define RELEASEBIT 0x80
162
Kevin O'Connor0e885762010-05-01 22:14:40 -0400163// Format of USB keyboard event data
Kevin O'Connor114592f2009-09-28 21:32:08 -0400164struct keyevent {
165 u8 modifiers;
166 u8 reserved;
167 u8 keys[6];
168};
169
Kevin O'Connor5718d562010-05-01 19:25:41 -0400170// Translate data from KeyToScanCode[] to calls to process_key().
Kevin O'Connor114592f2009-09-28 21:32:08 -0400171static void
172prockeys(u16 keys)
173{
174 if (keys > 0xff) {
175 u8 key = keys>>8;
176 if (key == 0xe1) {
177 // Pause key
178 process_key(0xe1);
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500179 process_key(0x1d | (keys & RELEASEBIT));
180 process_key(0x45 | (keys & RELEASEBIT));
Kevin O'Connor114592f2009-09-28 21:32:08 -0400181 return;
182 }
183 process_key(key);
184 }
185 process_key(keys);
186}
187
Kevin O'Connor5718d562010-05-01 19:25:41 -0400188// Handle a USB key press/release event.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400189static void
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500190procscankey(u8 key, u8 flags)
191{
192 if (key >= ARRAY_SIZE(KeyToScanCode))
193 return;
194 u16 keys = GET_GLOBAL(KeyToScanCode[key]);
195 if (keys)
196 prockeys(keys | flags);
197}
198
Kevin O'Connor5718d562010-05-01 19:25:41 -0400199// Handle a USB modifier press/release event.
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500200static void
201procmodkey(u8 mods, u8 flags)
202{
203 int i;
204 for (i=0; mods; i++)
205 if (mods & (1<<i)) {
206 // Modifier key change.
207 prockeys(GET_GLOBAL(ModifierToScanCode[i]) | flags);
208 mods &= ~(1<<i);
209 }
210}
211
Kevin O'Connor5718d562010-05-01 19:25:41 -0400212// Process USB keyboard data.
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500213static void noinline
Kevin O'Connor114592f2009-09-28 21:32:08 -0400214handle_key(struct keyevent *data)
215{
Kevin O'Connor78523312010-02-14 19:07:43 -0500216 dprintf(9, "Got key %x %x\n", data->modifiers, data->keys[0]);
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500217
218 // Load old keys.
219 u16 ebda_seg = get_ebda_seg();
220 struct usbkeyinfo old;
221 old.data = GET_EBDA2(ebda_seg, usbkey_last.data);
222
223 // Check for keys no longer pressed.
224 int addpos = 0;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400225 int i;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500226 for (i=0; i<ARRAY_SIZE(old.keys); i++) {
227 u8 key = old.keys[i];
228 if (!key)
229 break;
230 int j;
231 for (j=0;; j++) {
232 if (j>=ARRAY_SIZE(data->keys)) {
233 // Key released.
234 procscankey(key, RELEASEBIT);
235 if (i+1 >= ARRAY_SIZE(old.keys) || !old.keys[i+1])
236 // Last pressed key released - disable repeat.
237 old.repeatcount = 0xff;
238 break;
239 }
240 if (data->keys[j] == key) {
241 // Key still pressed.
242 data->keys[j] = 0;
243 old.keys[addpos++] = key;
244 break;
245 }
246 }
247 }
248 procmodkey(old.modifiers & ~data->modifiers, RELEASEBIT);
249
250 // Process new keys
251 procmodkey(data->modifiers & ~old.modifiers, 0);
252 old.modifiers = data->modifiers;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400253 for (i=0; i<ARRAY_SIZE(data->keys); i++) {
254 u8 key = data->keys[i];
Kevin O'Connor114592f2009-09-28 21:32:08 -0400255 if (!key)
256 continue;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500257 // New key pressed.
258 procscankey(key, 0);
259 old.keys[addpos++] = key;
260 old.repeatcount = KEYREPEATWAITMS / KEYREPEATMS + 1;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400261 }
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500262 if (addpos < ARRAY_SIZE(old.keys))
263 old.keys[addpos] = 0;
264
265 // Check for key repeat event.
266 if (addpos) {
267 if (!old.repeatcount)
268 procscankey(old.keys[addpos-1], 0);
269 else if (old.repeatcount != 0xff)
270 old.repeatcount--;
271 }
272
273 // Update old keys
274 SET_EBDA2(ebda_seg, usbkey_last.data, old.data);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400275}
276
Kevin O'Connor0e885762010-05-01 22:14:40 -0400277// Check if a USB keyboard event is pending and process it if so.
278static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500279usb_check_key(void)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400280{
281 if (! CONFIG_USB_KEYBOARD)
282 return;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400283 struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400284 if (!pipe)
285 return;
286
287 for (;;) {
288 struct keyevent data;
289 int ret = usb_poll_intr(pipe, &data);
290 if (ret)
291 break;
292 handle_key(&data);
293 }
294}
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400295
296// Test if USB keyboard is active.
297inline int
298usb_kbd_active(void)
299{
Kevin O'Connor0e885762010-05-01 22:14:40 -0400300 if (! CONFIG_USB_KEYBOARD)
301 return 0;
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400302 return GET_GLOBAL(keyboard_pipe) != NULL;
303}
304
305// Handle a ps2 style keyboard command.
306inline int
307usb_kbd_command(int command, u8 *param)
308{
Kevin O'Connor0e885762010-05-01 22:14:40 -0400309 if (! CONFIG_USB_KEYBOARD)
310 return -1;
311 dprintf(9, "usb keyboard cmd=%x\n", command);
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400312 switch (command) {
313 case ATKBD_CMD_GETID:
314 // Return the id of a standard AT keyboard.
315 param[0] = 0xab;
316 param[1] = 0x83;
317 return 0;
318 default:
319 return -1;
320 }
321}
Kevin O'Connor0e885762010-05-01 22:14:40 -0400322
323
324/****************************************************************
325 * Mouse events
326 ****************************************************************/
327
328// Format of USB mouse event data
329struct mouseevent {
330 u8 buttons;
331 u8 x, y;
332 u8 reserved[5];
333};
334
335// Process USB mouse data.
336static void
337handle_mouse(struct mouseevent *data)
338{
339 dprintf(9, "Got mouse b=%x x=%x y=%x\n", data->buttons, data->x, data->y);
340
341 s8 x = data->x, y = -data->y;
342 u8 flag = ((data->buttons & 0x7) | (1<<3)
343 | (x & 0x80 ? (1<<4) : 0) | (y & 0x80 ? (1<<5) : 0));
344 process_mouse(flag);
345 process_mouse(x);
346 process_mouse(y);
347}
348
349// Check if a USB mouse event is pending and process it if so.
350static void
351usb_check_mouse(void)
352{
353 if (! CONFIG_USB_MOUSE)
354 return;
355 struct usb_pipe *pipe = GET_GLOBAL(mouse_pipe);
356 if (!pipe)
357 return;
358
359 for (;;) {
360 struct mouseevent data;
361 int ret = usb_poll_intr(pipe, &data);
362 if (ret)
363 break;
364 handle_mouse(&data);
365 }
366}
367
368// Test if USB mouse is active.
369inline int
370usb_mouse_active(void)
371{
372 if (! CONFIG_USB_MOUSE)
373 return 0;
374 return GET_GLOBAL(mouse_pipe) != NULL;
375}
376
377// Handle a ps2 style mouse command.
378inline int
379usb_mouse_command(int command, u8 *param)
380{
381 if (! CONFIG_USB_MOUSE)
382 return -1;
383 dprintf(9, "usb mouse cmd=%x\n", command);
384 switch (command) {
385 case PSMOUSE_CMD_ENABLE:
386 case PSMOUSE_CMD_DISABLE:
387 case PSMOUSE_CMD_SETSCALE11:
388 return 0;
389 case PSMOUSE_CMD_SETSCALE21:
390 case PSMOUSE_CMD_SETRATE:
391 case PSMOUSE_CMD_SETRES:
392 // XXX
393 return 0;
394 case PSMOUSE_CMD_RESET_BAT:
395 case PSMOUSE_CMD_GETID:
396 // Return the id of a standard AT mouse.
397 param[0] = 0xaa;
398 param[1] = 0x00;
399 return 0;
400
401 case PSMOUSE_CMD_GETINFO:
402 param[0] = 0x00;
403 param[1] = 4;
404 param[2] = 100;
405 return 0;
406
407 default:
408 return -1;
409 }
410}
411
412// Check for USB events pending - called periodically from timer interrupt.
413void
414usb_check_event(void)
415{
416 usb_check_key();
417 usb_check_mouse();
418}