blob: b714685f26514d8544c55f47dae541ed3d1e666b [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
12
Kevin O'Connor59f02832009-10-12 10:09:15 -040013struct usb_pipe *keyboard_pipe VAR16VISIBLE;
Kevin O'Connor114592f2009-09-28 21:32:08 -040014
15
16/****************************************************************
17 * Setup
18 ****************************************************************/
19
20static int
21set_protocol(u32 endp, u16 val)
22{
23 struct usb_ctrlrequest req;
24 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
25 req.bRequest = HID_REQ_SET_PROTOCOL;
26 req.wValue = val;
27 req.wIndex = 0;
28 req.wLength = 0;
29 return send_default_control(endp, &req, NULL);
30}
31
32static int
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -050033set_idle(u32 endp, int ms)
Kevin O'Connor114592f2009-09-28 21:32:08 -040034{
35 struct usb_ctrlrequest req;
36 req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
37 req.bRequest = HID_REQ_SET_IDLE;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -050038 req.wValue = (ms/4)<<8;
Kevin O'Connor114592f2009-09-28 21:32:08 -040039 req.wIndex = 0;
40 req.wLength = 0;
41 return send_default_control(endp, &req, NULL);
42}
43
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -050044#define KEYREPEATWAITMS 500
45#define KEYREPEATMS 33
46
Kevin O'Connor114592f2009-09-28 21:32:08 -040047int
48usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax)
49{
50 if (! CONFIG_USB_KEYBOARD)
51 return -1;
52 if (keyboard_pipe)
Kevin O'Connora5826b52009-10-24 17:57:29 -040053 // XXX - this enables the first found keyboard (could be random)
Kevin O'Connor114592f2009-09-28 21:32:08 -040054 return -1;
55 dprintf(2, "usb_keyboard_setup %x\n", endp);
56
57 struct usb_endpoint_descriptor *epdesc = (void*)&iface[1];
58 for (;;) {
59 if ((void*)epdesc >= (void*)iface + imax
60 || epdesc->bDescriptorType == USB_DT_INTERFACE) {
61 dprintf(1, "No keyboard intr in?\n");
62 return -1;
63 }
64 if (epdesc->bDescriptorType == USB_DT_ENDPOINT
65 && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN
66 && ((epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
67 == USB_ENDPOINT_XFER_INT)
68 && epdesc->wMaxPacketSize == 8)
69 break;
70 epdesc = (void*)epdesc + epdesc->bLength;
71 }
72 u32 inendp = mkendp(endp2cntl(endp), endp2devaddr(endp)
73 , epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK
74 , endp2speed(endp), epdesc->wMaxPacketSize);
75
76 // Enable "boot" protocol.
77 int ret = set_protocol(endp, 1);
78 if (ret)
79 return -1;
80 // Only send reports on a new key event.
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -050081 ret = set_idle(endp, KEYREPEATMS);
Kevin O'Connor114592f2009-09-28 21:32:08 -040082 if (ret)
83 return -1;
84
Kevin O'Connor59f02832009-10-12 10:09:15 -040085 struct usb_pipe *pipe = alloc_intr_pipe(inendp, epdesc->bInterval);
Kevin O'Connor114592f2009-09-28 21:32:08 -040086 if (!pipe)
87 return -1;
88 keyboard_pipe = pipe;
89
90 return 0;
91}
92
93void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050094usb_keyboard_setup(void)
Kevin O'Connor114592f2009-09-28 21:32:08 -040095{
96 if (! CONFIG_USB_KEYBOARD)
97 return;
98 keyboard_pipe = NULL;
99}
100
101
102/****************************************************************
103 * Keyboard events
104 ****************************************************************/
105
106static u16 KeyToScanCode[] VAR16 = {
107 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020,
108 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026,
109 0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014,
110 0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003,
111 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b,
112 0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a,
113 0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034,
114 0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040,
115 0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046,
116 0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d,
117 0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e,
118 0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047,
119 0x0048, 0x0049, 0x0052, 0x0053
120};
121
122static u16 ModifierToScanCode[] VAR16 = {
123 //lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui
124 0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c
125};
126
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500127#define RELEASEBIT 0x80
128
Kevin O'Connor114592f2009-09-28 21:32:08 -0400129struct keyevent {
130 u8 modifiers;
131 u8 reserved;
132 u8 keys[6];
133};
134
135static void
136prockeys(u16 keys)
137{
138 if (keys > 0xff) {
139 u8 key = keys>>8;
140 if (key == 0xe1) {
141 // Pause key
142 process_key(0xe1);
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500143 process_key(0x1d | (keys & RELEASEBIT));
144 process_key(0x45 | (keys & RELEASEBIT));
Kevin O'Connor114592f2009-09-28 21:32:08 -0400145 return;
146 }
147 process_key(key);
148 }
149 process_key(keys);
150}
151
152static void
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500153procscankey(u8 key, u8 flags)
154{
155 if (key >= ARRAY_SIZE(KeyToScanCode))
156 return;
157 u16 keys = GET_GLOBAL(KeyToScanCode[key]);
158 if (keys)
159 prockeys(keys | flags);
160}
161
162static void
163procmodkey(u8 mods, u8 flags)
164{
165 int i;
166 for (i=0; mods; i++)
167 if (mods & (1<<i)) {
168 // Modifier key change.
169 prockeys(GET_GLOBAL(ModifierToScanCode[i]) | flags);
170 mods &= ~(1<<i);
171 }
172}
173
174static void noinline
Kevin O'Connor114592f2009-09-28 21:32:08 -0400175handle_key(struct keyevent *data)
176{
177 dprintf(5, "Got key %x %x\n", data->modifiers, data->keys[0]);
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500178
179 // Load old keys.
180 u16 ebda_seg = get_ebda_seg();
181 struct usbkeyinfo old;
182 old.data = GET_EBDA2(ebda_seg, usbkey_last.data);
183
184 // Check for keys no longer pressed.
185 int addpos = 0;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400186 int i;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500187 for (i=0; i<ARRAY_SIZE(old.keys); i++) {
188 u8 key = old.keys[i];
189 if (!key)
190 break;
191 int j;
192 for (j=0;; j++) {
193 if (j>=ARRAY_SIZE(data->keys)) {
194 // Key released.
195 procscankey(key, RELEASEBIT);
196 if (i+1 >= ARRAY_SIZE(old.keys) || !old.keys[i+1])
197 // Last pressed key released - disable repeat.
198 old.repeatcount = 0xff;
199 break;
200 }
201 if (data->keys[j] == key) {
202 // Key still pressed.
203 data->keys[j] = 0;
204 old.keys[addpos++] = key;
205 break;
206 }
207 }
208 }
209 procmodkey(old.modifiers & ~data->modifiers, RELEASEBIT);
210
211 // Process new keys
212 procmodkey(data->modifiers & ~old.modifiers, 0);
213 old.modifiers = data->modifiers;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400214 for (i=0; i<ARRAY_SIZE(data->keys); i++) {
215 u8 key = data->keys[i];
Kevin O'Connor114592f2009-09-28 21:32:08 -0400216 if (!key)
217 continue;
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500218 // New key pressed.
219 procscankey(key, 0);
220 old.keys[addpos++] = key;
221 old.repeatcount = KEYREPEATWAITMS / KEYREPEATMS + 1;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400222 }
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500223 if (addpos < ARRAY_SIZE(old.keys))
224 old.keys[addpos] = 0;
225
226 // Check for key repeat event.
227 if (addpos) {
228 if (!old.repeatcount)
229 procscankey(old.keys[addpos-1], 0);
230 else if (old.repeatcount != 0xff)
231 old.repeatcount--;
232 }
233
234 // Update old keys
235 SET_EBDA2(ebda_seg, usbkey_last.data, old.data);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400236}
237
238void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500239usb_check_key(void)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400240{
241 if (! CONFIG_USB_KEYBOARD)
242 return;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400243 struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400244 if (!pipe)
245 return;
246
247 for (;;) {
248 struct keyevent data;
249 int ret = usb_poll_intr(pipe, &data);
250 if (ret)
251 break;
252 handle_key(&data);
253 }
254}