Add simple cooperative threading scheme to allow parallel hw init.
[seabios.git] / src / usb-hid.c
1 // 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
13 struct usb_pipe *keyboard_pipe VAR16VISIBLE;
14
15
16 /****************************************************************
17  * Setup
18  ****************************************************************/
19
20 static int
21 set_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
32 static int
33 set_idle(u32 endp, u8 val)
34 {
35     struct usb_ctrlrequest req;
36     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
37     req.bRequest = HID_REQ_SET_IDLE;
38     req.wValue = val<<8;
39     req.wIndex = 0;
40     req.wLength = 0;
41     return send_default_control(endp, &req, NULL);
42 }
43
44 int
45 usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax)
46 {
47     if (! CONFIG_USB_KEYBOARD)
48         return -1;
49     if (keyboard_pipe)
50         // XXX - this enables the first found keyboard (could be random)
51         return -1;
52     dprintf(2, "usb_keyboard_setup %x\n", endp);
53
54     struct usb_endpoint_descriptor *epdesc = (void*)&iface[1];
55     for (;;) {
56         if ((void*)epdesc >= (void*)iface + imax
57             || epdesc->bDescriptorType == USB_DT_INTERFACE) {
58             dprintf(1, "No keyboard intr in?\n");
59             return -1;
60         }
61         if (epdesc->bDescriptorType == USB_DT_ENDPOINT
62             && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN
63             && ((epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
64                 == USB_ENDPOINT_XFER_INT)
65             && epdesc->wMaxPacketSize == 8)
66             break;
67         epdesc = (void*)epdesc + epdesc->bLength;
68     }
69     u32 inendp = mkendp(endp2cntl(endp), endp2devaddr(endp)
70                         , epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK
71                         , endp2speed(endp), epdesc->wMaxPacketSize);
72
73     // Enable "boot" protocol.
74     int ret = set_protocol(endp, 1);
75     if (ret)
76         return -1;
77     // Only send reports on a new key event.
78     ret = set_idle(endp, 0);
79     if (ret)
80         return -1;
81
82     struct usb_pipe *pipe = alloc_intr_pipe(inendp, epdesc->bInterval);
83     if (!pipe)
84         return -1;
85     keyboard_pipe = pipe;
86
87     return 0;
88 }
89
90 void
91 usb_keyboard_setup()
92 {
93     if (! CONFIG_USB_KEYBOARD)
94         return;
95     keyboard_pipe = NULL;
96 }
97
98
99 /****************************************************************
100  * Keyboard events
101  ****************************************************************/
102
103 static u16 KeyToScanCode[] VAR16 = {
104     0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020,
105     0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026,
106     0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014,
107     0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003,
108     0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b,
109     0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a,
110     0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034,
111     0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040,
112     0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046,
113     0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d,
114     0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e,
115     0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047,
116     0x0048, 0x0049, 0x0052, 0x0053
117 };
118
119 static u16 ModifierToScanCode[] VAR16 = {
120     //lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui
121     0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c
122 };
123
124 struct keyevent {
125     u8 modifiers;
126     u8 reserved;
127     u8 keys[6];
128 };
129
130 static void
131 prockeys(u16 keys)
132 {
133     if (keys > 0xff) {
134         u8 key = keys>>8;
135         if (key == 0xe1) {
136             // Pause key
137             process_key(0xe1);
138             process_key(0x1d | (keys & 0x80));
139             process_key(0x45 | (keys & 0x80));
140             return;
141         }
142         process_key(key);
143     }
144     process_key(keys);
145 }
146
147 static void
148 handle_key(struct keyevent *data)
149 {
150     dprintf(5, "Got key %x %x\n", data->modifiers, data->keys[0]);
151     // XXX
152     int i;
153     for (i=0; i<8; i++)
154         if (data->modifiers & (1<<i))
155             prockeys(GET_GLOBAL(ModifierToScanCode[i]));
156     for (i=0; i<ARRAY_SIZE(data->keys); i++) {
157         u8 key = data->keys[i];
158         if (key >= ARRAY_SIZE(KeyToScanCode))
159             continue;
160         key = GET_GLOBAL(KeyToScanCode[key]);
161         if (!key)
162             continue;
163         prockeys(key);
164     }
165     for (i=0; i<8; i++)
166         if (data->modifiers & (1<<i))
167             prockeys(GET_GLOBAL(ModifierToScanCode[i]) | 0x80);
168 }
169
170 void
171 usb_check_key()
172 {
173     if (! CONFIG_USB_KEYBOARD)
174         return;
175     struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe);
176     if (!pipe)
177         return;
178
179     for (;;) {
180         struct keyevent data;
181         int ret = usb_poll_intr(pipe, &data);
182         if (ret)
183             break;
184         handle_key(&data);
185     }
186 }