remove const from TPL_OpenTPLFromMemory since the memory is altered
[libogc.git] / libwiikeyboard / usbkeyboard.c
blob6ccfd4b29edfca59d4f2545153f7ffef35656763
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_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_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_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_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 //Destroy the io heap
219 s32 USBKeyboard_Deinitialize(void)
221 if (hId < 0)
222 return -1;
224 s32 retval;
225 retval = iosDestroyHeap(hId);
226 hId = -1;
228 return retval;
231 //Search for a keyboard connected to the wii usb port
232 //Thanks to Sven Peter usbstorage support
233 s32 USBKeyboard_Open(const eventcallback cb)
235 usb_device_entry *buffer;
236 u8 device_count, i, conf;
237 u16 vid, pid;
238 bool found = false;
239 u32 iConf, iInterface, iEp;
240 usb_devdesc udd;
241 usb_configurationdesc *ucd;
242 usb_interfacedesc *uid;
243 usb_endpointdesc *ued;
245 buffer = (usb_device_entry*)iosAlloc(hId, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
246 if(buffer == NULL)
247 return -1;
249 memset(buffer, 0, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
251 if (USB_GetDeviceList(buffer, DEVLIST_MAXSIZE, USB_CLASS_HID, &device_count) < 0)
253 iosFree(hId, buffer);
254 return -2;
257 if (_kbd) {
258 if (_kbd->fd != -1) USB_CloseDevice(&_kbd->fd);
259 } else {
260 _kbd = (struct ukbd *) malloc(sizeof(struct ukbd));
262 if (!_kbd)
263 return -1;
266 memset(_kbd, 0, sizeof(struct ukbd));
267 _kbd->fd = -1;
269 for (i = 0; i < device_count; i++)
271 vid = buffer[i].vid;;
272 pid = buffer[i].pid;
274 if ((vid == 0) || (pid == 0))
275 continue;
277 s32 fd = 0;
278 if (USB_OpenDevice(buffer[i].device_id, vid, pid, &fd) < 0)
279 continue;
281 if (USB_GetDescriptors(fd, &udd) < 0) {
282 USB_CloseDevice(&fd);
283 continue;
286 for(iConf = 0; iConf < udd.bNumConfigurations; iConf++)
288 ucd = &udd.configurations[iConf];
290 for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++)
292 uid = &ucd->interfaces[iInterface];
294 if ((uid->bInterfaceClass == USB_CLASS_HID) &&
295 (uid->bInterfaceSubClass == USB_SUBCLASS_BOOT) &&
296 (uid->bInterfaceProtocol== USB_PROTOCOL_KEYBOARD))
298 for(iEp = 0; iEp < uid->bNumEndpoints; iEp++)
300 ued = &uid->endpoints[iEp];
302 if (ued->bmAttributes != USB_ENDPOINT_INTERRUPT)
303 continue;
305 if (!(ued->bEndpointAddress & USB_ENDPOINT_IN))
306 continue;
308 _kbd->fd = fd;
309 _kbd->cb = cb;
311 _kbd->configuration = ucd->bConfigurationValue;
312 _kbd->interface = uid->bInterfaceNumber;
313 _kbd->altInterface = uid->bAlternateSetting;
315 _kbd->ep = ued->bEndpointAddress;
316 _kbd->ep_size = ued->wMaxPacketSize;
318 found = true;
320 break;
324 if (found)
325 break;
328 if (found)
329 break;
332 USB_FreeDescriptors(&udd);
334 if (found)
335 break;
336 else
337 USB_CloseDevice(&fd);
340 iosFree(hId, buffer);
342 if (!found)
343 return -3;
345 if (USB_GetConfiguration(_kbd->fd, &conf) < 0)
347 USBKeyboard_Close();
348 return -4;
351 if (conf != _kbd->configuration &&
352 USB_SetConfiguration(_kbd->fd, _kbd->configuration) < 0)
354 USBKeyboard_Close();
355 return -5;
358 if (_kbd->altInterface != 0 &&
359 USB_SetAlternativeInterface(_kbd->fd, _kbd->interface, _kbd->altInterface) < 0)
361 USBKeyboard_Close();
362 return -6;
365 if (_get_protocol() != 0)
367 if (_set_protocol(0) < 0)
369 USBKeyboard_Close();
370 return -6;
373 if (_get_protocol() == 1)
375 USBKeyboard_Close();
376 return -7;
380 if (USB_DeviceRemovalNotifyAsync(_kbd->fd, &_disconnect, NULL) < 0)
382 USBKeyboard_Close();
383 return -8;
386 _kbd->connected = true;
388 return 1;
391 //Close the device
392 void USBKeyboard_Close(void)
394 if (!_kbd)
395 return;
397 if(_kbd->fd != -1)
398 USB_CloseDevice(&_kbd->fd);
400 free(_kbd);
401 _kbd = NULL;
403 return;
406 bool USBKeyboard_IsConnected(void) {
407 if (!_kbd)
408 return false;
410 return _kbd->connected;
413 //Scan for key presses and generate events for the callback function
414 s32 USBKeyboard_Scan(void)
416 int i, j, index;
418 if (!_kbd)
419 return -1;
421 if (_get_input_report() < 0)
422 return -2;
424 if (_kbd->sc_ndata.keycode[0] == KEY_ERROR)
425 return 0;
427 if (_kbd->sc_ndata.modifiers != _kbd->sc_odata.modifiers) {
428 for (i = 0; i < MODMAPSIZE; ++i) {
429 if ((_kbd->sc_odata.modifiers & _ukbd_mod_map[i][0])
430 && !(_kbd->sc_ndata.modifiers & _ukbd_mod_map[i][0]))
431 _submit(USBKEYBOARD_RELEASED, _ukbd_mod_map[i][1]);
432 else if ((_kbd->sc_ndata.modifiers & _ukbd_mod_map[i][0])
433 && !(_kbd->sc_odata.modifiers & _ukbd_mod_map[i][0]))
434 _submit(USBKEYBOARD_PRESSED, _ukbd_mod_map[i][1]);
438 for (i = 0; i < MAXKEYCODE; i++) {
439 if (_kbd->sc_odata.keycode[i] > 3) {
440 index = -1;
442 for (j = 0; j < MAXKEYCODE; j++) {
443 if (_kbd->sc_odata.keycode[i] == _kbd->sc_ndata.keycode[j]) {
444 index = j;
445 break;
449 if (index == -1)
450 _submit(USBKEYBOARD_RELEASED, _kbd->sc_odata.keycode[i]);
453 if (_kbd->sc_ndata.keycode[i] > 3) {
454 index = -1;
456 for (j = 0; j < MAXKEYCODE; j++) {
457 if (_kbd->sc_ndata.keycode[i] == _kbd->sc_odata.keycode[j]) {
458 index = j;
459 break;
463 if (index == -1)
464 _submit(USBKEYBOARD_PRESSED, _kbd->sc_ndata.keycode[i]);
468 _kbd->sc_odata = _kbd->sc_ndata;
470 return 0;
473 //Turn on/off a led
474 s32 USBKeyboard_SetLed(const USBKeyboard_led led, bool on)
476 if (!_kbd)
477 return -1;
479 if (on)
480 _kbd->leds = _kbd->leds | (1 << led );
481 else
482 _kbd->leds = _kbd->leds & (255 ^ (1 << led));
484 if (_set_output_report() < 0)
485 return -2;
487 return 1;
490 //Toggle a led
491 s32 USBKeyboard_ToggleLed(const USBKeyboard_led led)
493 if (!_kbd)
494 return -1;
496 _kbd->leds = _kbd->leds ^ (1 << led);
498 if (_set_output_report() < 0)
499 return -2;
501 return 1;