usb 2.0 support through ios 58, and other usb improvements/corrections. patch by...
[libogc.git] / libogc / usb.c
blob3ebf9d02114dc240ff126fa26f7588cb03f56142
1 /*-------------------------------------------------------------
3 usb.c -- USB lowlevel
5 Copyright (C) 2008
6 Michael Wiedenbauer (shagkur)
7 Dave Murphy (WinterMute)
8 tueidj
10 This software is provided 'as-is', without any express or implied
11 warranty. In no event will the authors be held liable for any
12 damages arising from the use of this software.
14 Permission is granted to anyone to use this software for any
15 purpose, including commercial applications, and to alter it and
16 redistribute it freely, subject to the following restrictions:
18 1. The origin of this software must not be misrepresented; you
19 must not claim that you wrote the original software. If you use
20 this software in a product, an acknowledgment in the product
21 documentation would be appreciated but is not required.
23 2. Altered source versions must be plainly marked as such, and
24 must not be misrepresented as being the original software.
26 3. This notice may not be removed or altered from any source
27 distribution.
29 -------------------------------------------------------------*/
32 /* Note: There are 3 types of USB interfaces here, the early ones
33 * (V0: /dev/usb/oh0 and /dev/usb/oh1) and two later ones (V5: /dev/usb/ven
34 * and /dev/usb/hid) which are similar but have some small
35 * differences. There is also an earlier version of /dev/usb/hid (V4)
36 * found in IOSes 37,61,56,etc. and /dev/usb/msc found in IOS 57.
37 * These interfaces aren't implemented here and you may find some
38 * devices don't show up if you're running under those IOSes.
41 #if defined(HW_RVL)
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <malloc.h>
46 #include <time.h>
47 #include <gcutil.h>
48 #include <ogcsys.h>
49 #include <ipc.h>
50 #include <asm.h>
51 #include <processor.h>
53 #include "usb.h"
55 #define USB_HEAPSIZE 16384
57 #define USBV0_IOCTL_CTRLMSG 0
58 #define USBV0_IOCTL_BLKMSG 1
59 #define USBV0_IOCTL_INTRMSG 2
60 #define USBV0_IOCTL_SUSPENDDEV 5
61 #define USBV0_IOCTL_RESUMEDEV 6
62 #define USBV0_IOCTL_GETDEVLIST 12
63 #define USBV0_IOCTL_DEVREMOVALHOOK 26
64 #define USBV0_IOCTL_DEVINSERTHOOK 27
65 #define USBV0_IOCTL_DEVICECLASSCHANGE 28
67 #define USBV4_IOCTL_GETVERSION 6 // returns 0x40001
69 #define USBV5_IOCTL_GETVERSION 0 // should return 0x50001
70 #define USBV5_IOCTL_GETDEVICECHANGE 1
71 #define USBV5_IOCTL_SHUTDOWN 2
72 #define USBV5_IOCTL_GETDEVPARAMS 3
73 #define USBV5_IOCTL_ATTACHFINISH 6
74 #define USBV5_IOCTL_SETALTERNATE 7
75 #define USBV5_IOCTL_SUSPEND_RESUME 16
76 #define USBV5_IOCTL_CANCELENDPOINT 17
77 #define USBV5_IOCTL_CTRLMSG 18
78 #define USBV5_IOCTL_INTRMSG 19
79 #define USBV5_IOCTL_ISOMSG 20
80 #define USBV5_IOCTL_BULKMSG 21
82 #define USB_MAX_DEVICES 32
84 static s32 hId = -1;
85 static const char __oh0_path[] ATTRIBUTE_ALIGN(32) = "/dev/usb/oh0";
86 static const char __ven_path[] ATTRIBUTE_ALIGN(32) = "/dev/usb/ven";
87 static const char __hid_path[] ATTRIBUTE_ALIGN(32) = "/dev/usb/hid";
89 struct _usb_cb_removalnotify_list {
90 usbcallback cb;
91 void *userdata;
92 s32 device_id;
95 struct _usbv5_host {
96 usb_device_entry attached_devices[USB_MAX_DEVICES];
97 struct _usb_cb_removalnotify_list remove_cb[USB_MAX_DEVICES];
98 s32 fd;
101 static struct _usbv5_host* ven_host = NULL;
102 static struct _usbv5_host* hid_host = NULL;
104 struct _usb_msg {
105 s32 fd;
106 s32 zero;
107 union {
108 struct {
109 u8 bmRequestType;
110 u8 bmRequest;
111 u16 wValue;
112 u16 wIndex;
113 u16 wLength;
114 void *rpData;
115 } ctrl;
117 struct {
118 void *rpData;
119 u16 wLength;
120 u8 pad[4];
121 u8 bEndpoint;
122 } bulk;
124 struct {
125 void *rpData;
126 u16 wLength;
127 u8 bEndpoint;
128 } intr;
130 struct {
131 u16 pid;
132 u16 vid;
133 } notify;
135 u8 class;
136 // not completely sure about this but this field must be 0 for reads
137 u32 hid_intr_write;
139 u32 align_pad[4]; // pad to 24 bytes
141 usbcallback cb;
142 void *userdata;
143 ioctlv vec[7];
146 static s32 __usbv5_devicechangeCB(s32 result, void *p);
148 static s32 __usbv5_attachfinishCB(s32 result, void *p)
150 struct _usbv5_host* host = (struct _usbv5_host*)p;
151 if(host==NULL) return IPC_EINVAL;
153 if (result==0)
154 IOS_IoctlAsync(host->fd, USBV5_IOCTL_GETDEVICECHANGE, NULL, 0, host->attached_devices, 0x180, __usbv5_devicechangeCB, host);
156 return IPC_OK;
159 static s32 __usbv5_devicechangeCB(s32 result, void *p)
161 int i, j;
162 struct _usbv5_host* host = (struct _usbv5_host*)p;
164 if(host==NULL) return IPC_EINVAL;
166 if (result>=0) {
167 // can't check the remove callbacks only if the number of devices has decreased,
168 // because devices may have been inserted as well as removed
169 for (i=0; i<USB_MAX_DEVICES; i++) {
170 if (host->remove_cb[i].cb==NULL)
171 continue;
172 for (j=0; j<result; j++) {
173 if (host->remove_cb[i].device_id == host->attached_devices[j].device_id)
174 break;
177 if (j==result) { // execute callback and remove it
178 host->remove_cb[i].cb(0, host->remove_cb[i].userdata);
179 host->remove_cb[i].cb = NULL;
182 // wipe unused device entries
183 memset(host->attached_devices+result, 0, sizeof(usb_device_entry)*(32-result));
185 IOS_IoctlAsync(host->fd, USBV5_IOCTL_ATTACHFINISH, NULL, 0, NULL, 0, __usbv5_attachfinishCB, host);
188 return IPC_OK;
192 static s32 __usb_messageCB(s32 result,void *_msg)
194 struct _usb_msg *msg = (struct _usb_msg*)_msg;
196 if(msg==NULL) return IPC_EINVAL;
198 if(msg->cb!=NULL) msg->cb(result, msg->userdata);
200 iosFree(hId,msg);
202 return IPC_OK;
205 static s32 __find_device_on_host(struct _usbv5_host *host, s32 device_id)
207 int i;
208 if (host==NULL) return -1;
210 for (i=0; host->attached_devices[i].device_id; i++) {
211 if (host->attached_devices[i].device_id == device_id)
212 return i;
215 return -1;
218 static s32 __usb_control_message(s32 device_id,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *userdata)
220 s32 ret;
221 struct _usb_msg *msg;
223 if(((s32)rpData%32)!=0) return IPC_EINVAL;
224 if(wLength && !rpData) return IPC_EINVAL;
225 if(!wLength && rpData) return IPC_EINVAL;
227 msg = iosAlloc(hId,sizeof(*msg));
228 if(msg==NULL) return IPC_ENOMEM;
230 memset(msg, 0, sizeof(*msg));
232 msg->fd = device_id;
233 msg->ctrl.bmRequestType = bmRequestType;
234 msg->ctrl.bmRequest = bmRequest;
235 msg->cb = cb;
236 msg->userdata = userdata;
238 if (device_id>=0 && device_id<0x20) {
239 msg->ctrl.wValue = bswap16(wValue);
240 msg->ctrl.wIndex = bswap16(wIndex);
241 msg->ctrl.wLength = bswap16(wLength);
243 msg->vec[0].data = &msg->ctrl.bmRequestType;
244 msg->vec[0].len = sizeof(u8);
245 msg->vec[1].data = &msg->ctrl.bmRequest;
246 msg->vec[1].len = sizeof(u8);
247 msg->vec[2].data = &msg->ctrl.wValue;
248 msg->vec[2].len = sizeof(u16);
249 msg->vec[3].data = &msg->ctrl.wIndex;
250 msg->vec[3].len = sizeof(u16);
251 msg->vec[4].data = &msg->ctrl.wLength;
252 msg->vec[4].len = sizeof(u16);
253 msg->vec[5].data = &msg->ctrl.rpData;
254 msg->vec[5].len = sizeof(u8);
255 msg->vec[6].data = rpData;
256 msg->vec[6].len = wLength;
258 if (cb==NULL)
259 ret = IOS_Ioctlv(device_id, USBV0_IOCTL_CTRLMSG, 6, 1, msg->vec);
260 else
261 return IOS_IoctlvAsync(device_id, USBV0_IOCTL_CTRLMSG, 6, 1, msg->vec, __usb_messageCB, msg);
263 } else {
264 u8 adjust = bmRequestType >> 7;
265 s32 fd = (device_id<0) ? ven_host->fd : hid_host->fd;
266 msg->ctrl.wValue = wValue;
267 msg->ctrl.wIndex = wIndex;
268 msg->ctrl.wLength = wLength;
269 msg->ctrl.rpData = rpData;
271 msg->vec[0].data = msg;
272 msg->vec[0].len = 64;
273 msg->vec[1].data = rpData;
274 msg->vec[1].len = wLength;
276 if (cb==NULL)
277 ret = IOS_Ioctlv(fd, USBV5_IOCTL_CTRLMSG, 2-adjust, adjust, msg->vec);
278 else
279 return IOS_IoctlvAsync(fd, USBV5_IOCTL_CTRLMSG, 2-adjust, adjust, msg->vec, __usb_messageCB, msg);
282 if(msg!=NULL) iosFree(hId,msg);
284 return ret;
287 static inline s32 __usb_interrupt_bulk_message(s32 device_id,u8 ioctl,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
289 s32 ret;
290 struct _usb_msg *msg;
292 if(((s32)rpData%32)!=0) return IPC_EINVAL;
293 if(wLength && !rpData) return IPC_EINVAL;
294 if(!wLength && rpData) return IPC_EINVAL;
296 msg = iosAlloc(hId,sizeof(*msg));
297 if(msg==NULL) return IPC_ENOMEM;
299 memset(msg, 0, sizeof(*msg));
301 msg->fd = device_id;
302 msg->bulk.wLength = wLength;
303 msg->bulk.rpData = rpData;
304 msg->cb = cb;
305 msg->userdata = userdata;
307 if (device_id>=0 && device_id<0x20) {
308 msg->bulk.bEndpoint = bEndpoint;
309 msg->vec[0].data = &msg->bulk.bEndpoint;
310 msg->vec[0].len = sizeof(u8);
311 msg->vec[1].data = &msg->bulk.wLength;
312 msg->vec[1].len = sizeof(u16);
313 msg->vec[2].data = rpData;
314 msg->vec[2].len = wLength;
316 if (cb==NULL)
317 ret = IOS_Ioctlv(device_id,ioctl,2,1,msg->vec);
318 else
319 return IOS_IoctlvAsync(device_id,ioctl,2,1,msg->vec,__usb_messageCB,msg);
321 } else {
322 u8 adjust = bEndpoint >> 7;
323 s32 fd = (device_id<0) ? ven_host->fd : hid_host->fd;
324 if (ioctl == USBV0_IOCTL_INTRMSG) {
325 msg->intr.bEndpoint = bEndpoint;
326 ioctl = USBV5_IOCTL_INTRMSG;
327 // HID does this a little bit differently
328 if (device_id>=0)
329 msg->hid_intr_write = !adjust;
330 } else {
331 msg->bulk.bEndpoint = bEndpoint;
332 ioctl = USBV5_IOCTL_BULKMSG;
335 msg->vec[0].data = msg;
336 msg->vec[0].len = 64;
337 msg->vec[1].data = rpData;
338 msg->vec[1].len = wLength;
340 if (cb==NULL)
341 ret = IOS_Ioctlv(fd, ioctl, 2-adjust, adjust, msg->vec);
342 else
343 return IOS_IoctlvAsync(fd, ioctl, 2-adjust, adjust, msg->vec, __usb_messageCB, msg);
346 if(msg!=NULL) iosFree(hId,msg);
348 return ret;
351 static inline s32 __usb_getdesc(s32 fd, u8 *buffer, u8 type, u8 index, u8 size)
353 return __usb_control_message(fd, USB_ENDPOINT_IN ,USB_REQ_GETDESCRIPTOR, (type << 8) | index, 0, size, buffer, NULL, NULL);
356 static u32 __find_next_endpoint(u8 *buffer,u32 size)
358 u8 *ptr = buffer;
360 while(size>0) {
361 if(buffer[1]==USB_DT_ENDPOINT || buffer[1]==USB_DT_INTERFACE)
362 break;
364 size -= buffer[0];
365 buffer += buffer[0];
368 return (buffer - ptr);
371 s32 USB_Initialize()
373 if(hId==-1) hId = iosCreateHeap(USB_HEAPSIZE);
374 if(hId<0) return IPC_ENOMEM;
376 if (ven_host==NULL) {
377 s32 ven_fd = IOS_Open(__ven_path, IPC_OPEN_NONE);
378 if (ven_fd>=0) {
379 ven_host = (struct _usbv5_host*)iosAlloc(hId, sizeof(*ven_host));
380 if (ven_host==NULL) {
381 IOS_Close(ven_fd);
382 return IPC_ENOMEM;
384 memset(ven_host, 0, sizeof(*ven_host));
385 ven_host->fd = ven_fd;
387 u32 *ven_ver = (u32*)iosAlloc(hId, 0x20);
388 if (ven_ver==NULL) goto mem_error;
389 if (IOS_Ioctl(ven_fd, USBV5_IOCTL_GETVERSION, NULL, 0, ven_ver, 0x20)==0 && ven_ver[0]==0x50001)
390 IOS_IoctlAsync(ven_fd, USBV5_IOCTL_GETDEVICECHANGE, NULL, 0, ven_host->attached_devices, 0x180, __usbv5_devicechangeCB, ven_host);
391 else {
392 // wrong ven version
393 IOS_Close(ven_fd);
394 iosFree(hId, ven_host);
395 ven_host = NULL;
398 iosFree(hId, ven_ver);
402 if (hid_host==NULL) {
403 s32 hid_fd = IOS_Open(__hid_path, IPC_OPEN_NONE);
404 if (hid_fd>=0) {
405 hid_host = (struct _usbv5_host*)iosAlloc(hId, sizeof(*hid_host));
406 if (hid_host==NULL) {
407 IOS_Close(hid_fd);
408 goto mem_error;
410 memset(hid_host, 0, sizeof(*hid_host));
411 hid_host->fd = hid_fd;
413 u32 *hid_ver = (u32*)iosAlloc(hId, 0x20);
414 if (hid_ver==NULL) goto mem_error;
415 // have to call the USB4 version first, to be safe
416 if (IOS_Ioctl(hid_fd, USBV4_IOCTL_GETVERSION, NULL, 0, NULL, 0)==0x40001 || \
417 IOS_Ioctl(hid_fd, USBV5_IOCTL_GETVERSION, NULL, 0, hid_ver, 0x20) || hid_ver[0]!=0x50001) {
418 // wrong hid version
419 IOS_Close(hid_fd);
420 iosFree(hId, hid_host);
421 hid_host = NULL;
422 } else
423 IOS_IoctlAsync(hid_fd, USBV5_IOCTL_GETDEVICECHANGE, NULL, 0, hid_host->attached_devices, 0x180, __usbv5_devicechangeCB, hid_host);
425 iosFree(hId, hid_ver);
429 return IPC_OK;
431 mem_error:
432 USB_Deinitialize();
433 return IPC_ENOMEM;
436 s32 USB_Deinitialize()
438 if (hid_host) {
439 if (hid_host->fd) {
440 IOS_Ioctl(hid_host->fd, USBV5_IOCTL_SHUTDOWN, NULL, 0, NULL, 0);
441 IOS_Close(hid_host->fd);
443 iosFree(hId, hid_host);
444 hid_host = NULL;
447 if (ven_host) {
448 if (ven_host->fd>=0) {
449 IOS_Ioctl(ven_host->fd, USBV5_IOCTL_SHUTDOWN, NULL, 0, NULL, 0);
450 IOS_Close(ven_host->fd);
452 iosFree(hId, ven_host);
453 ven_host = NULL;
456 if (hId>=0) {
457 iosDestroyHeap(hId);
458 hId = -1;
461 return IPC_OK;
464 s32 USB_OpenDevice(s32 device_id,u16 vid,u16 pid,s32 *fd)
466 s32 ret = USB_OK;
467 char *devicepath = NULL;
468 *fd = -1;
470 if (device_id && device_id!=USB_OH1_DEVICE_ID) {
471 int i;
473 i = __find_device_on_host(ven_host, device_id);
474 if (i<0)
475 i = __find_device_on_host(hid_host, device_id);
476 if (i>=0) {
477 *fd = device_id;
478 return USB_ResumeDevice(device_id);
482 devicepath = iosAlloc(hId,USB_MAXPATH);
483 if(devicepath==NULL) return IPC_ENOMEM;
485 if (device_id==USB_OH1_DEVICE_ID)
486 snprintf(devicepath,USB_MAXPATH,"/dev/usb/oh1/%x/%x",vid,pid);
487 else
488 snprintf(devicepath,USB_MAXPATH,"/dev/usb/oh0/%x/%x",vid,pid);
490 *fd = IOS_Open(devicepath,0);
491 if(*fd<0) ret = *fd;
493 if (devicepath!=NULL) iosFree(hId,devicepath);
494 return ret;
497 s32 USBV5_CloseDevice(s32 device_id)
499 int i;
500 struct _usbv5_host* host;
502 if (__find_device_on_host(ven_host, device_id)>=0)
503 host = ven_host;
504 else if (__find_device_on_host(hid_host, device_id)>=0)
505 host = hid_host;
506 else
507 return IPC_EINVAL;
509 for (i=0; i < USB_MAX_DEVICES; i++) {
510 if (host->remove_cb[i].cb==NULL)
511 continue;
513 if (host->remove_cb[i].device_id==device_id) {
514 host->remove_cb[i].cb(0, host->remove_cb[i].userdata);
515 host->remove_cb[i].cb = NULL;
516 break;
520 return USB_SuspendDevice(device_id);
523 s32 USB_CloseDevice(s32 *fd)
525 s32 ret = IPC_EINVAL;
526 if (fd) {
527 ret = USBV5_CloseDevice(*fd);
528 if (ret==IPC_EINVAL && *fd>0)
529 ret = IOS_Close(*fd);
530 if (ret>=0) *fd = -1;
533 return ret;
536 s32 USB_CloseDeviceAsync(s32 *fd,usbcallback cb,void *userdata)
538 s32 ret = IPC_EINVAL;
539 if(fd) {
540 ret = USBV5_CloseDevice(*fd);
541 if (ret!=IPC_EINVAL) {
542 if (cb)
543 return cb(ret, userdata);
544 else
545 return ret;
547 if (*fd>0)
548 return IOS_CloseAsync(*fd,cb,userdata);
551 return ret;
554 s32 USB_GetDeviceDescription(s32 fd,usb_devdesc *devdesc)
556 s32 ret;
557 usb_devdesc *p;
559 p = iosAlloc(hId,USB_DT_DEVICE_SIZE);
560 if(p==NULL) return IPC_ENOMEM;
562 ret = __usb_control_message(fd,USB_ENDPOINT_IN,USB_REQ_GETDESCRIPTOR,(USB_DT_DEVICE<<8),0,USB_DT_DEVICE_SIZE,p,NULL,NULL);
563 if(ret>=0) memcpy(devdesc,p,USB_DT_DEVICE_SIZE);
564 devdesc->configurations = NULL;
566 if(p!=NULL) iosFree(hId,p);
567 return ret;
570 static s32 USBV5_GetDescriptors(s32 device_id, usb_devdesc *udd)
572 s32 retval = IPC_ENOMEM;
573 u32 *io_buffer = NULL;
574 u8 *buffer = NULL;
575 usb_configurationdesc *ucd = NULL;
576 usb_interfacedesc *uid = NULL;
577 usb_endpointdesc *ued = NULL;
578 u32 iConf, iEndpoint;
579 s32 fd;
580 u32 desc_out_size, desc_start_offset;
582 if (__find_device_on_host(ven_host, device_id)>=0) {
583 fd = ven_host->fd;
584 desc_out_size = 0xC0;
585 desc_start_offset = 20;
586 } else if (__find_device_on_host(hid_host, device_id)>=0) {
587 fd = hid_host->fd;
588 desc_out_size = 0x60;
589 desc_start_offset = 36;
590 } else
591 return IPC_EINVAL;
593 io_buffer = (u32*)iosAlloc(hId, 0x20);
594 buffer = (u8*)iosAlloc(hId, desc_out_size);
595 if (io_buffer==NULL || buffer==NULL) goto free_bufs;
597 io_buffer[0] = device_id;
598 io_buffer[2] = 0;
599 retval = IOS_Ioctl(fd, USBV5_IOCTL_GETDEVPARAMS, io_buffer, 0x20, buffer, desc_out_size);
600 if (retval==IPC_OK) {
601 u8 *next = buffer+desc_start_offset;
603 memcpy(udd, next, sizeof(*udd));
604 udd->configurations = calloc(udd->bNumConfigurations, sizeof(*udd->configurations));
605 if(udd->configurations == NULL) goto free_bufs;
607 next += (udd->bLength+3)&~3;
608 for (iConf = 0; iConf < udd->bNumConfigurations; iConf++)
610 ucd = &udd->configurations[iConf];
611 memcpy(ucd, next, USB_DT_CONFIG_SIZE);
612 next += (USB_DT_CONFIG_SIZE+3)&~3;
613 // IOS presents each interface as a different device
614 if (ucd->bNumInterfaces) ucd->bNumInterfaces = 1;
616 ucd->interfaces = calloc(ucd->bNumInterfaces, sizeof(*ucd->interfaces));
617 if (ucd->interfaces == NULL) goto free_bufs;
619 uid = ucd->interfaces;
620 memcpy(uid, next, USB_DT_INTERFACE_SIZE);
621 next += (uid->bLength+3)&~3;
623 uid->endpoints = calloc(uid->bNumEndpoints, sizeof(*uid->endpoints));
624 if (uid->endpoints == NULL) goto free_bufs;
626 memset(uid->endpoints,0,uid->bNumEndpoints*sizeof(*uid->endpoints));
627 for(iEndpoint = 0; iEndpoint < uid->bNumEndpoints; iEndpoint++)
629 ued = &uid->endpoints[iEndpoint];
630 memcpy(ued, next, USB_DT_ENDPOINT_SIZE);
631 next += (ued->bLength+3)&~3;
634 retval = IPC_OK;
638 free_bufs:
639 if (io_buffer!=NULL)
640 iosFree(hId, io_buffer);
641 if (buffer!=NULL)
642 iosFree(hId, buffer);
643 return retval;
646 s32 USB_GetDescriptors(s32 fd, usb_devdesc *udd)
648 u8 *buffer = NULL;
649 u8 *ptr = NULL;
650 usb_configurationdesc *ucd = NULL;
651 usb_interfacedesc *uid = NULL;
652 usb_endpointdesc *ued = NULL;
653 s32 retval = 0;
654 u32 size,i;
655 u32 iConf, iInterface, iEndpoint;
657 if (fd>=0x20 || fd<-1)
658 return USBV5_GetDescriptors(fd, udd);
660 buffer = iosAlloc(hId, sizeof(*udd));
661 if(buffer == NULL)
663 retval = IPC_ENOHEAP;
664 goto free_and_error;
667 retval = __usb_getdesc(fd, buffer, USB_DT_DEVICE, 0, USB_DT_DEVICE_SIZE);
668 if(retval < 0)
669 goto free_and_error;
670 memcpy(udd, buffer, USB_DT_DEVICE_SIZE);
671 iosFree(hId, buffer);
673 udd->bcdUSB = bswap16(udd->bcdUSB);
674 udd->idVendor = bswap16(udd->idVendor);
675 udd->idProduct = bswap16(udd->idProduct);
676 udd->bcdDevice = bswap16(udd->bcdDevice);
678 udd->configurations = calloc(udd->bNumConfigurations, sizeof(*udd->configurations));
679 if(udd->configurations == NULL)
681 retval = IPC_ENOMEM;
682 goto free_and_error;
684 for(iConf = 0; iConf < udd->bNumConfigurations; iConf++)
686 buffer = iosAlloc(hId, USB_DT_CONFIG_SIZE);
687 if(buffer == NULL)
689 retval = IPC_ENOHEAP;
690 goto free_and_error;
693 retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, USB_DT_CONFIG_SIZE);
694 ucd = &udd->configurations[iConf];
695 memcpy(ucd, buffer, USB_DT_CONFIG_SIZE);
696 iosFree(hId, buffer);
698 ucd->wTotalLength = bswap16(ucd->wTotalLength);
699 size = ucd->wTotalLength;
700 buffer = iosAlloc(hId, size);
701 if(buffer == NULL)
703 retval = IPC_ENOHEAP;
704 goto free_and_error;
707 retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, ucd->wTotalLength);
708 if(retval < 0)
709 goto free_and_error;
711 ptr = buffer;
712 ptr += ucd->bLength;
713 size -= ucd->bLength;
715 retval = IPC_ENOMEM;
716 ucd->interfaces = calloc(ucd->bNumInterfaces, sizeof(*ucd->interfaces));
717 if(ucd->interfaces == NULL)
718 goto free_and_error;
719 for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++)
721 uid = &ucd->interfaces[iInterface];
722 memcpy(uid, ptr, USB_DT_INTERFACE_SIZE);
723 ptr += uid->bLength;
724 size -= uid->bLength;
726 if (uid->bNumEndpoints==0)
727 continue;
728 uid->endpoints = calloc(uid->bNumEndpoints, sizeof(*uid->endpoints));
729 if(uid->endpoints == NULL)
730 goto free_and_error;
732 /* This skips vendor and class specific descriptors */
733 i = __find_next_endpoint(ptr, size);
734 uid->extra_size = i;
735 if(i>0)
737 uid->extra = malloc(i);
738 if(uid->extra == NULL)
739 goto free_and_error;
740 memcpy(uid->extra, ptr, i);
741 ptr += i;
742 size -= i;
744 memset(uid->endpoints,0,uid->bNumEndpoints* sizeof(*uid->endpoints));
745 for(iEndpoint = 0; iEndpoint < uid->bNumEndpoints; iEndpoint++)
747 ued = &uid->endpoints[iEndpoint];
748 memcpy(ued, ptr, USB_DT_ENDPOINT_SIZE);
749 ptr += ued->bLength;
750 ued->wMaxPacketSize = bswap16(ued->wMaxPacketSize);
753 iosFree(hId, buffer);
754 buffer = NULL;
756 retval = IPC_OK;
758 free_and_error:
759 if(buffer != NULL)
760 iosFree(hId, buffer);
761 if(retval < 0)
762 USB_FreeDescriptors(udd);
763 return retval;
766 //TODO: Fix for /dev/usb/hid
767 s32 USB_GetHIDDescriptor(s32 fd,usb_hiddesc *uhd)
769 u8 *buffer = NULL;
770 s32 retval = IPC_OK;
772 buffer = iosAlloc(hId,sizeof(usb_hiddesc));
773 if(buffer==NULL) {
774 retval = IPC_ENOMEM;
775 goto free_and_error;
778 retval = __usb_getdesc(fd,buffer,USB_DT_HID,0,USB_DT_HID_SIZE);
779 if(retval<0) goto free_and_error;
781 memcpy(uhd,buffer,USB_DT_HID_SIZE);
782 uhd->bcdHID = bswap16(uhd->bcdHID);
783 uhd->descr[0].wDescriptorLength = bswap16(uhd->descr[0].wDescriptorLength);
785 retval = IPC_OK;
787 free_and_error:
788 if(buffer!=NULL) iosFree(hId,buffer);
789 return retval;
792 void USB_FreeDescriptors(usb_devdesc *udd)
794 int iConf, iInterface;
795 usb_configurationdesc *ucd;
796 usb_interfacedesc *uid;
797 if(udd->configurations != NULL)
799 for(iConf = 0; iConf < udd->bNumConfigurations; iConf++)
801 ucd = &udd->configurations[iConf];
802 if(ucd->interfaces != NULL)
804 for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++)
806 uid = &ucd->interfaces[iInterface];
807 if(uid->endpoints != NULL)
808 free(uid->endpoints);
809 if(uid->extra != NULL)
810 free(uid->extra);
812 free(ucd->interfaces);
815 free(udd->configurations);
819 s32 USB_GetAsciiString(s32 fd,u16 wIndex,u16 wLangID,u16 wLength,void *rpData)
821 s32 ret;
822 u8 bo, ro;
823 u8 *buf;
824 u8 *rp = (u8 *)rpData;
826 if(wLength > 255)
827 wLength = 255;
829 buf = iosAlloc(hId, 255); /* 255 is the highest possible length of a descriptor */
830 if(buf == NULL)
831 return IPC_ENOMEM;
833 ret = __usb_control_message(fd, USB_ENDPOINT_IN, USB_REQ_GETDESCRIPTOR, ((USB_DT_STRING << 8) + wIndex), wLangID, 255, buf, NULL, NULL);
835 /* index 0 gets a list of supported languages */
836 if(wIndex == 0)
838 if(ret > 0)
839 memcpy(rpData, buf, wLength);
840 iosFree(hId, buf);
841 return ret;
844 if(ret > 0)
846 bo = 2;
847 ro = 0;
848 while(ro < (wLength - 1) && bo < buf[0])
850 if(buf[bo + 1])
851 rp[ro++] = '?';
852 else
853 rp[ro++] = buf[bo];
854 bo += 2;
856 rp[ro] = 0;
857 ret = ro - 1;
860 iosFree(hId, buf);
861 return ret;
864 s32 USB_ReadIntrMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData)
866 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,NULL,NULL);
869 s32 USB_ReadIntrMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
871 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,cb,userdata);
874 s32 USB_WriteIntrMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData)
876 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,NULL,NULL);
879 s32 USB_WriteIntrMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
881 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,cb,userdata);
884 s32 USB_ReadBlkMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData)
886 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,NULL,NULL);
889 s32 USB_ReadBlkMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
891 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,cb,userdata);
894 s32 USB_WriteBlkMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData)
896 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,NULL,NULL);
899 s32 USB_WriteBlkMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
901 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,cb,userdata);
904 s32 USB_ReadCtrlMsg(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData)
906 return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,NULL,NULL);
909 s32 USB_ReadCtrlMsgAsync(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *userdata)
911 return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,cb,userdata);
914 s32 USB_WriteCtrlMsg(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData)
916 return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,NULL,NULL);
919 s32 USB_WriteCtrlMsgAsync(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *userdata)
921 return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,cb,userdata);
924 static s32 USB5_RegisterDeviceRemoval(struct _usbv5_host *host, s32 device_id, usbcallback cb, void *userdata)
926 int i;
928 // check to make sure the device is present
929 if (__find_device_on_host(host, device_id)<0)
930 return IPC_ENOENT;
932 // now make sure it's not hooked already
933 for (i=0; i<USB_MAX_DEVICES; i++) {
934 if (host->remove_cb[i].cb && host->remove_cb[i].device_id==device_id)
935 return IPC_EINVAL;
938 // find a free entry and add it
939 for (i=0; i<USB_MAX_DEVICES; i++) {
940 if (host->remove_cb[i].cb==NULL) {
941 host->remove_cb[i].cb = cb;
942 host->remove_cb[i].userdata = userdata;
943 host->remove_cb[i].device_id = device_id;
944 return IPC_OK;
947 return IPC_EINVAL;
950 s32 USB_DeviceRemovalNotifyAsync(s32 fd,usbcallback cb,void *userdata)
952 s32 ret;
953 if (fd>=0 && fd<0x20)
954 return IOS_IoctlAsync(fd,USBV0_IOCTL_DEVREMOVALHOOK,NULL,0,NULL,0,cb,userdata);
956 ret = USB5_RegisterDeviceRemoval(ven_host, fd, cb, userdata);
957 if (ret == IPC_ENOENT)
958 ret = USB5_RegisterDeviceRemoval(hid_host, fd, cb, userdata);
960 return ret;
963 static s32 USBV5_SuspendResume(s32 device_id, s32 resumed)
965 s32 ret;
966 s32 fd;
968 if (__find_device_on_host(ven_host, device_id)>=0)
969 fd = ven_host->fd;
970 else if (__find_device_on_host(hid_host, device_id)>=0)
971 fd = hid_host->fd;
972 else
973 return IPC_ENOENT;
975 s32 *buf = (s32*)iosAlloc(hId, 32);
976 if (buf==NULL) return IPC_ENOMEM;
978 buf[0] = device_id;
979 buf[2] = resumed;
980 ret = IOS_Ioctl(fd, USBV5_IOCTL_SUSPEND_RESUME, buf, 32, NULL, 0);
981 iosFree(hId, buf);
983 return ret;
986 s32 USB_SuspendDevice(s32 fd)
988 if (fd>=0x20 || fd<-1)
989 return USBV5_SuspendResume(fd, 0);
991 return IOS_Ioctl(fd,USBV0_IOCTL_SUSPENDDEV,NULL,0,NULL,0);
994 s32 USB_ResumeDevice(s32 fd)
996 if (fd>=0x20 || fd<-1)
997 return USBV5_SuspendResume(fd, 1);
999 return IOS_Ioctl(fd,USBV0_IOCTL_RESUMEDEV,NULL,0,NULL,0);
1002 // TODO: Implement this for VEN and HID
1003 s32 USB_DeviceChangeNotifyAsync(u8 interface_class, usbcallback cb, void* userdata)
1005 s32 fd;
1006 struct _usb_msg *msg;
1007 s32 ret;
1009 fd = IOS_Open(__oh0_path,IPC_OPEN_NONE);
1010 if (fd<0) return fd;
1012 msg = iosAlloc(hId,sizeof(*msg));
1013 if (msg==NULL) {
1014 IOS_Close(fd);
1015 return IPC_ENOMEM;
1018 msg->cb = cb;
1019 msg->userdata = userdata;
1020 msg->class = interface_class;
1022 msg->vec[0].data = &msg->class;
1023 msg->vec[0].len = 1;
1025 ret = IOS_IoctlvAsync(fd,USBV0_IOCTL_DEVICECLASSCHANGE,1,0,msg->vec,__usb_messageCB,msg);
1026 IOS_Close(fd);
1028 if (ret<0) iosFree(hId, msg);
1030 return ret;
1033 s32 USB_GetDeviceList(usb_device_entry *descr_buffer,u8 num_descr,u8 interface_class,u8 *cnt_descr)
1035 int i;
1036 u8 cntdevs=0;
1038 if (ven_host==NULL) {
1039 s32 fd;
1040 u32 *buf = (u32*)iosAlloc(hId, num_descr<<3);
1041 if (buf==NULL) return IPC_ENOMEM;
1043 fd = IOS_Open(__oh0_path,IPC_OPEN_NONE);
1044 if (fd<0) {
1045 iosFree(hId, buf);
1046 return fd;
1049 cntdevs = 0;
1050 i = IOS_IoctlvFormat(hId,fd,USBV0_IOCTL_GETDEVLIST,"bb:dd",num_descr,interface_class,&cntdevs,sizeof(cntdevs),buf,(num_descr<<3));
1051 if (cnt_descr) *cnt_descr = cntdevs;
1053 while (cntdevs--) {
1054 descr_buffer[cntdevs].device_id = 0;
1055 descr_buffer[cntdevs].vid = (u16)(buf[cntdevs*2+1]>>16);
1056 descr_buffer[cntdevs].pid = (u16)buf[cntdevs*2+1];
1059 IOS_Close(fd);
1060 iosFree(hId, buf);
1061 return i;
1064 // for ven_host, we can only exclude usb_hid class devices
1065 if (interface_class != USB_CLASS_HID && ven_host) {
1066 i=0;
1067 while (cntdevs<num_descr && ven_host->attached_devices[i].device_id) {
1068 descr_buffer[cntdevs++] = ven_host->attached_devices[i++];
1069 if (i>=32) break;
1073 if ((!interface_class || interface_class==USB_CLASS_HID) && hid_host) {
1074 i=0;
1075 while (cntdevs<num_descr && hid_host->attached_devices[i].device_id) {
1076 descr_buffer[cntdevs++] = hid_host->attached_devices[i++];
1077 if (i>32) break;
1081 if (cnt_descr) *cnt_descr = cntdevs;
1083 return IPC_OK;
1086 s32 USB_SetConfiguration(s32 fd, u8 configuration)
1088 return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_DEVICE), USB_REQ_SETCONFIG, configuration, 0, 0, NULL, NULL, NULL);
1091 s32 USB_GetConfiguration(s32 fd, u8 *configuration)
1093 u8 *_configuration;
1094 s32 retval;
1096 _configuration = iosAlloc(hId, 1);
1097 if(_configuration == NULL)
1098 return IPC_ENOMEM;
1100 retval = __usb_control_message(fd, (USB_CTRLTYPE_DIR_DEVICE2HOST | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_DEVICE), USB_REQ_GETCONFIG, 0, 0, 1, _configuration, NULL, NULL);
1101 if(retval >= 0)
1102 *configuration = *_configuration;
1103 iosFree(hId, _configuration);
1105 return retval;
1108 s32 USB_SetAlternativeInterface(s32 fd, u8 interface, u8 alternateSetting)
1110 if(alternateSetting == 0)
1111 return IPC_EINVAL;
1112 return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_INTERFACE), USB_REQ_SETINTERFACE, alternateSetting, interface, 0, NULL, NULL, NULL);
1115 static s32 USBV5_CancelEndpoint(s32 device_id, u8 endpoint)
1117 s32 ret;
1118 s32 fd;
1120 if (__find_device_on_host(ven_host, device_id)>=0)
1121 fd = ven_host->fd;
1122 else if (__find_device_on_host(hid_host, device_id)>=0)
1123 fd = hid_host->fd;
1124 else
1125 return IPC_ENOENT;
1127 s32 *buf = (s32*)iosAlloc(hId, 32);
1128 if (buf==NULL) return IPC_ENOMEM;
1130 buf[0] = device_id;
1131 buf[2] = endpoint;
1132 ret = IOS_Ioctl(fd, USBV5_IOCTL_CANCELENDPOINT, buf, 32, NULL, 0);
1133 iosFree(hId, buf);
1135 return ret;
1138 s32 USB_ClearHalt(s32 fd, u8 endpoint)
1140 if (fd>=0x20 || fd<-1)
1141 return USBV5_CancelEndpoint(fd, endpoint);
1142 return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_ENDPOINT), USB_REQ_CLEARFEATURE, USB_FEATURE_ENDPOINT_HALT, endpoint, 0, NULL, NULL, NULL);
1145 #endif /* defined(HW_RVL) */