commit missed changes
[libogc.git] / libwiikeyboard / usbkeyboard.c
blob1b2d45a6e3f85168a0c86f28e6b20ee283f768d5
1 /*-------------------------------------------------------------
3 usbkeyboard.c -- Usb keyboard support(boot protocol)
5 Copyright (C) 2008, 2009
6 DAVY Guillaume davyg2@gmail.com
7 dhewg
9 This software is provided 'as-is', without any express or implied
10 warranty. In no event will the authors be held liable for any
11 damages arising from the use of this software.
13 Permission is granted to anyone to use this software for any
14 purpose, including commercial applications, and to alter it and
15 redistribute it freely, subject to the following restrictions:
17 1. The origin of this software must not be misrepresented; you
18 must not claim that you wrote the original software. If you use
19 this software in a product, an acknowledgment in the product
20 documentation would be appreciated but is not required.
22 2. Altered source versions must be plainly marked as such, and
23 must not be misrepresented as being the original software.
25 3. This notice may not be removed or altered from any source
26 distribution.
28 -------------------------------------------------------------*/
30 #include <string.h>
31 #include <malloc.h>
33 #include <gccore.h>
34 #include <ogc/usb.h>
36 #include <wiikeyboard/usbkeyboard.h>
38 #define HEAP_SIZE 4096
39 #define DEVLIST_MAXSIZE 8
40 #define KEY_ERROR 0x01
41 #define MAXKEYCODE 6
43 #define USB_MOD_CTRL_L 0x01
44 #define USB_MOD_SHIFT_L 0x02
45 #define USB_MOD_ALT_L 0x04
46 #define USB_MOD_META_L 0x08
47 #define USB_MOD_CTRL_R 0x10
48 #define USB_MOD_SHIFT_R 0x20
49 #define USB_MOD_ALT_R 0x40
50 #define USB_MOD_META_R 0x80
52 struct ukbd_data {
53 u16 modifiers;
54 u8 keycode[MAXKEYCODE];
55 } ATTRIBUTE_PACKED;
57 struct ukbd {
58 bool connected;
60 s32 fd;
62 struct ukbd_data sc_ndata;
63 struct ukbd_data sc_odata;
65 u8 leds;
67 eventcallback cb;
69 u8 configuration;
70 u32 interface;
71 u32 altInterface;
73 u8 ep;
74 u32 ep_size;
77 static s32 hId = -1;
78 static struct ukbd *_kbd = NULL;
80 static u8 _ukbd_mod_map[][2] = {
81 { USB_MOD_CTRL_L, 224 },
82 { USB_MOD_SHIFT_L, 225 },
83 { USB_MOD_ALT_L, 226 },
84 { USB_MOD_META_L, 227 },
85 { USB_MOD_CTRL_R, 228 },
86 { USB_MOD_SHIFT_R, 229 },
87 { USB_MOD_ALT_R, 230 },
88 { USB_MOD_META_R, 231 }
91 #define MODMAPSIZE (sizeof(_ukbd_mod_map)/sizeof(_ukbd_mod_map[0]))
93 static void _submit(USBKeyboard_eventType type, u8 code)
95 if (!_kbd->cb)
96 return;
98 USBKeyboard_event ev;
99 ev.type = type;
100 ev.keyCode = code;
102 _kbd->cb(ev);
105 //Callback when the keyboard is disconnected
106 static s32 _disconnect(s32 retval, void *data)
108 (void) data;
110 _kbd->connected = false;
112 _submit(USBKEYBOARD_DISCONNECTED, 0);
114 return 1;
117 //Get the protocol, 0=boot protocol and 1=report protocol
118 static s32 _get_protocol(void)
120 s32 protocol;
121 u8 *buffer = 0;
123 if(!_kbd || _kbd->fd==-1) return -1;
125 buffer = iosAlloc(hId, 1);
127 if (buffer == NULL)
128 return -1;
130 USB_WriteCtrlMsg(_kbd->fd, USB_REQTYPE_INTERFACE_GET, USB_REQ_GETPROTOCOL, 0, _kbd->interface, 1, buffer);
132 protocol = *buffer;
133 iosFree(hId, buffer);
135 return protocol;
138 //Modify the protocol, 0=boot protocol and 1=report protocol
139 static s32 _set_protocol(u8 protocol)
141 if(!_kbd || _kbd->fd==-1) return -1;
142 return USB_WriteCtrlMsg(_kbd->fd, USB_REQTYPE_INTERFACE_SET, USB_REQ_SETPROTOCOL, protocol, _kbd->interface, 0, NULL);
145 //Get an input report from interrupt pipe
146 static s32 _get_input_report(void)
148 u8 *buffer = 0;
150 if(!_kbd || _kbd->fd==-1) return -1;
151 buffer = iosAlloc(hId, 8);
153 if (buffer == NULL)
154 return -1;
156 s32 ret = USB_ReadIntrMsg(_kbd->fd, _kbd->ep, 8, buffer);
158 memcpy(&_kbd->sc_ndata, buffer, 8);
159 iosFree(hId, buffer);
161 _kbd->sc_ndata.modifiers = (_kbd->sc_ndata.modifiers << 8) | (_kbd->sc_ndata.modifiers >> 8);
163 return ret;
166 #if 0
167 //Get an input report from control pipe
168 static s32 _get_output_report(u8 *leds)
170 u8 *buffer = 0;
171 if(!_kbd || _kbd->fd==-1) return -1;
172 buffer = iosAlloc(hId, 1);
174 if (buffer == NULL)
175 return -1;
177 s32 ret = USB_WriteCtrlMsg(_kbd->fd, USB_REQTYPE_INTERFACE_GET, USB_REQ_GETREPORT, USB_REPTYPE_OUTPUT << 8, _kbd->interface, 1, buffer);
179 memcpy(leds, buffer, 1);
180 iosFree(hId, buffer);
182 return ret;
184 #endif
186 //Set an input report to control pipe
187 static s32 _set_output_report(void)
189 u8 *buffer = 0;
190 if(!_kbd || _kbd->fd==-1) return -1;
191 buffer = iosAlloc(hId, 1);
193 if (buffer == NULL)
194 return -1;
196 memcpy(buffer, &_kbd->leds, 1);
197 s32 ret = USB_WriteCtrlMsg(_kbd->fd, USB_REQTYPE_INTERFACE_SET, USB_REQ_SETREPORT, USB_REPTYPE_OUTPUT << 8, _kbd->interface, 1, buffer);
199 iosFree(hId, buffer);
201 return ret;
204 //init the ioheap
205 s32 USBKeyboard_Initialize(void)
207 if (hId > 0)
208 return 0;
210 hId = iosCreateHeap(HEAP_SIZE);
212 if (hId < 0)
213 return IPC_ENOHEAP;
215 return IPC_OK;
218 s32 USBKeyboard_Deinitialize(void)
220 return IPC_OK;
223 //Search for a keyboard connected to the wii usb port
224 //Thanks to Sven Peter usbstorage support
225 s32 USBKeyboard_Open(const eventcallback cb)
227 usb_device_entry *buffer;
228 u8 device_count, i, conf;
229 u16 vid, pid;
230 bool found = false;
231 u32 iConf, iInterface, iEp;
232 usb_devdesc udd;
233 usb_configurationdesc *ucd;
234 usb_interfacedesc *uid;
235 usb_endpointdesc *ued;
237 buffer = (usb_device_entry*)iosAlloc(hId, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
238 if(buffer == NULL)
239 return -1;
241 memset(buffer, 0, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
243 if (USB_GetDeviceList(buffer, DEVLIST_MAXSIZE, USB_CLASS_HID, &device_count) < 0)
245 iosFree(hId, buffer);
246 return -2;
249 if (_kbd) {
250 if (_kbd->fd != -1) USB_CloseDevice(&_kbd->fd);
251 } else {
252 _kbd = (struct ukbd *) malloc(sizeof(struct ukbd));
254 if (!_kbd)
255 return -1;
258 memset(_kbd, 0, sizeof(struct ukbd));
259 _kbd->fd = -1;
261 for (i = 0; i < device_count; i++)
263 vid = buffer[i].vid;;
264 pid = buffer[i].pid;
266 if ((vid == 0) || (pid == 0))
267 continue;
269 s32 fd = 0;
270 if (USB_OpenDevice(buffer[i].device_id, vid, pid, &fd) < 0)
271 continue;
273 if (USB_GetDescriptors(fd, &udd) < 0) {
274 USB_CloseDevice(&fd);
275 continue;
278 for(iConf = 0; iConf < udd.bNumConfigurations; iConf++)
280 ucd = &udd.configurations[iConf];
282 for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++)
284 uid = &ucd->interfaces[iInterface];
286 if ((uid->bInterfaceClass == USB_CLASS_HID) &&
287 (uid->bInterfaceSubClass == USB_SUBCLASS_BOOT) &&
288 (uid->bInterfaceProtocol== USB_PROTOCOL_KEYBOARD))
290 for(iEp = 0; iEp < uid->bNumEndpoints; iEp++)
292 ued = &uid->endpoints[iEp];
294 if (ued->bmAttributes != USB_ENDPOINT_INTERRUPT)
295 continue;
297 if (!(ued->bEndpointAddress & USB_ENDPOINT_IN))
298 continue;
300 _kbd->fd = fd;
301 _kbd->cb = cb;
303 _kbd->configuration = ucd->bConfigurationValue;
304 _kbd->interface = uid->bInterfaceNumber;
305 _kbd->altInterface = uid->bAlternateSetting;
307 _kbd->ep = ued->bEndpointAddress;
308 _kbd->ep_size = ued->wMaxPacketSize;
310 found = true;
312 break;
316 if (found)
317 break;
320 if (found)
321 break;
324 USB_FreeDescriptors(&udd);
326 if (found)
327 break;
328 else
329 USB_CloseDevice(&fd);
332 iosFree(hId, buffer);
334 if (!found)
335 return -3;
337 if (USB_GetConfiguration(_kbd->fd, &conf) < 0)
339 USBKeyboard_Close();
340 return -4;
343 if (conf != _kbd->configuration &&
344 USB_SetConfiguration(_kbd->fd, _kbd->configuration) < 0)
346 USBKeyboard_Close();
347 return -5;
350 if (_kbd->altInterface != 0 &&
351 USB_SetAlternativeInterface(_kbd->fd, _kbd->interface, _kbd->altInterface) < 0)
353 USBKeyboard_Close();
354 return -6;
357 if (_get_protocol() != 0)
359 if (_set_protocol(0) < 0)
361 USBKeyboard_Close();
362 return -6;
365 if (_get_protocol() == 1)
367 USBKeyboard_Close();
368 return -7;
372 if (USB_DeviceRemovalNotifyAsync(_kbd->fd, &_disconnect, NULL) < 0)
374 USBKeyboard_Close();
375 return -8;
378 _kbd->connected = true;
380 return 1;
383 //Close the device
384 void USBKeyboard_Close(void)
386 if (!_kbd)
387 return;
389 if(_kbd->fd != -1)
390 USB_CloseDevice(&_kbd->fd);
392 free(_kbd);
393 _kbd = NULL;
395 return;
398 bool USBKeyboard_IsConnected(void) {
399 if (!_kbd)
400 return false;
402 return _kbd->connected;
405 //Scan for key presses and generate events for the callback function
406 s32 USBKeyboard_Scan(void)
408 int i, j, index;
410 if (!_kbd)
411 return -1;
413 if (_get_input_report() < 0)
414 return -2;
416 if (_kbd->sc_ndata.keycode[0] == KEY_ERROR)
417 return 0;
419 if (_kbd->sc_ndata.modifiers != _kbd->sc_odata.modifiers) {
420 for (i = 0; i < MODMAPSIZE; ++i) {
421 if ((_kbd->sc_odata.modifiers & _ukbd_mod_map[i][0])
422 && !(_kbd->sc_ndata.modifiers & _ukbd_mod_map[i][0]))
423 _submit(USBKEYBOARD_RELEASED, _ukbd_mod_map[i][1]);
424 else if ((_kbd->sc_ndata.modifiers & _ukbd_mod_map[i][0])
425 && !(_kbd->sc_odata.modifiers & _ukbd_mod_map[i][0]))
426 _submit(USBKEYBOARD_PRESSED, _ukbd_mod_map[i][1]);
430 for (i = 0; i < MAXKEYCODE; i++) {
431 if (_kbd->sc_odata.keycode[i] > 3) {
432 index = -1;
434 for (j = 0; j < MAXKEYCODE; j++) {
435 if (_kbd->sc_odata.keycode[i] == _kbd->sc_ndata.keycode[j]) {
436 index = j;
437 break;
441 if (index == -1)
442 _submit(USBKEYBOARD_RELEASED, _kbd->sc_odata.keycode[i]);
445 if (_kbd->sc_ndata.keycode[i] > 3) {
446 index = -1;
448 for (j = 0; j < MAXKEYCODE; j++) {
449 if (_kbd->sc_ndata.keycode[i] == _kbd->sc_odata.keycode[j]) {
450 index = j;
451 break;
455 if (index == -1)
456 _submit(USBKEYBOARD_PRESSED, _kbd->sc_ndata.keycode[i]);
460 _kbd->sc_odata = _kbd->sc_ndata;
462 return 0;
465 //Turn on/off a led
466 s32 USBKeyboard_SetLed(const USBKeyboard_led led, bool on)
468 if (!_kbd)
469 return -1;
471 if (on)
472 _kbd->leds = _kbd->leds | (1 << led );
473 else
474 _kbd->leds = _kbd->leds & (255 ^ (1 << led));
476 if (_set_output_report() < 0)
477 return -2;
479 return 1;
482 //Toggle a led
483 s32 USBKeyboard_ToggleLed(const USBKeyboard_led led)
485 if (!_kbd)
486 return -1;
488 _kbd->leds = _kbd->leds ^ (1 << led);
490 if (_set_output_report() < 0)
491 return -2;
493 return 1;