clear SBSS section
[libogc.git] / libogc / usb.c
blobc9562f2404c56d3e14189a27b662d48bf844c068
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 __usbv5_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 __usbv0_control_messageCB(s32 result,void *usrdata)
207 struct _usb_msg *msg = (struct _usb_msg*)usrdata;
209 if(msg==NULL) return IPC_EINVAL;
211 if(msg->cb!=NULL) msg->cb(result, msg->userdata);
213 if(msg->vec[0].data!=NULL) iosFree(hId,msg->vec[0].data);
214 if(msg->vec[1].data!=NULL) iosFree(hId,msg->vec[1].data);
215 if(msg->vec[2].data!=NULL) iosFree(hId,msg->vec[2].data);
216 if(msg->vec[3].data!=NULL) iosFree(hId,msg->vec[3].data);
217 if(msg->vec[4].data!=NULL) iosFree(hId,msg->vec[4].data);
218 if(msg->vec[5].data!=NULL) iosFree(hId,msg->vec[5].data);
220 iosFree(hId,msg);
222 return IPC_OK;
225 static s32 __usbv0_intrbulk_messageCB(s32 result,void *usrdata)
227 struct _usb_msg *msg = (struct _usb_msg*)usrdata;
229 if(msg==NULL) return IPC_EINVAL;
231 if(msg->cb!=NULL) msg->cb(result, msg->userdata);
233 if(msg->vec[0].data!=NULL) iosFree(hId,msg->vec[0].data);
234 if(msg->vec[1].data!=NULL) iosFree(hId,msg->vec[1].data);
236 iosFree(hId,msg);
238 return IPC_OK;
241 static s32 __find_device_on_host(struct _usbv5_host *host, s32 device_id)
243 int i;
244 if (host==NULL) return -1;
246 for (i=0; host->attached_devices[i].device_id; i++) {
247 if (host->attached_devices[i].device_id == device_id)
248 return i;
251 return -1;
254 static s32 __usb_control_message(s32 device_id,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *userdata)
256 s32 ret = IPC_ENOMEM;
257 struct _usb_msg *msg;
259 if(((s32)rpData%32)!=0) return IPC_EINVAL;
260 if(wLength && !rpData) return IPC_EINVAL;
261 if(!wLength && rpData) return IPC_EINVAL;
263 msg = (struct _usb_msg*)iosAlloc(hId,sizeof(struct _usb_msg));
264 if(msg==NULL) return IPC_ENOMEM;
266 memset(msg, 0, sizeof(struct _usb_msg));
268 msg->fd = device_id;
269 msg->cb = cb;
270 msg->userdata = userdata;
272 if (device_id>=0 && device_id<0x20) {
273 u8 *pRqType = NULL,*pRq = NULL,*pNull = NULL;
274 u16 *pValue = NULL,*pIndex = NULL,*pLength = NULL;
276 pRqType = (u8*)iosAlloc(hId,32);
277 if(pRqType==NULL) goto done;
278 *pRqType = bmRequestType;
280 pRq = (u8*)iosAlloc(hId,32);
281 if(pRq==NULL) goto done;
282 *pRq = bmRequest;
284 pValue = (u16*)iosAlloc(hId,32);
285 if(pValue==NULL) goto done;
286 *pValue = bswap16(wValue);
288 pIndex = (u16*)iosAlloc(hId,32);
289 if(pIndex==NULL) goto done;
290 *pIndex = bswap16(wIndex);
292 pLength = (u16*)iosAlloc(hId,32);
293 if(pLength==NULL) goto done;
294 *pLength = bswap16(wLength);
296 pNull = (u8*)iosAlloc(hId,32);
297 if(pNull==NULL) goto done;
298 *pNull = 0;
300 msg->vec[0].data = pRqType;
301 msg->vec[0].len = sizeof(u8);
302 msg->vec[1].data = pRq;
303 msg->vec[1].len = sizeof(u8);
304 msg->vec[2].data = pValue;
305 msg->vec[2].len = sizeof(u16);
306 msg->vec[3].data = pIndex;
307 msg->vec[3].len = sizeof(u16);
308 msg->vec[4].data = pLength;
309 msg->vec[4].len = sizeof(u16);
310 msg->vec[5].data = pNull;
311 msg->vec[5].len = sizeof(u8);
312 msg->vec[6].data = rpData;
313 msg->vec[6].len = wLength;
315 if (cb==NULL)
316 ret = IOS_Ioctlv(device_id, USBV0_IOCTL_CTRLMSG, 6, 1, msg->vec);
317 else
318 return IOS_IoctlvAsync(device_id, USBV0_IOCTL_CTRLMSG, 6, 1, msg->vec, __usbv0_control_messageCB, msg);
320 done:
321 if(pRqType!=NULL) iosFree(hId,pRqType);
322 if(pRq!=NULL) iosFree(hId,pRq);
323 if(pValue!=NULL) iosFree(hId,pValue);
324 if(pIndex!=NULL) iosFree(hId,pIndex);
325 if(pLength!=NULL) iosFree(hId,pLength);
326 if(pNull!=NULL) iosFree(hId,pNull);
328 } else {
329 u8 adjust = bmRequestType >> 7;
330 s32 fd = (device_id<0) ? ven_host->fd : hid_host->fd;
332 msg->ctrl.bmRequestType = bmRequestType;
333 msg->ctrl.bmRequest = bmRequest;
334 msg->ctrl.wValue = wValue;
335 msg->ctrl.wIndex = wIndex;
336 msg->ctrl.wLength = wLength;
337 msg->ctrl.rpData = rpData;
339 msg->vec[0].data = msg;
340 msg->vec[0].len = 64;
341 msg->vec[1].data = rpData;
342 msg->vec[1].len = wLength;
344 if (cb==NULL)
345 ret = IOS_Ioctlv(fd, USBV5_IOCTL_CTRLMSG, 2-adjust, adjust, msg->vec);
346 else
347 return IOS_IoctlvAsync(fd, USBV5_IOCTL_CTRLMSG, 2-adjust, adjust, msg->vec, __usbv5_messageCB, msg);
350 if(msg!=NULL) iosFree(hId,msg);
352 return ret;
355 static inline s32 __usb_interrupt_bulk_message(s32 device_id,u8 ioctl,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
357 s32 ret = IPC_ENOMEM;
358 struct _usb_msg *msg;
360 if(((s32)rpData%32)!=0) return IPC_EINVAL;
361 if(wLength && !rpData) return IPC_EINVAL;
362 if(!wLength && rpData) return IPC_EINVAL;
364 msg = (struct _usb_msg*)iosAlloc(hId,sizeof(struct _usb_msg));
365 if(msg==NULL) return IPC_ENOMEM;
367 memset(msg, 0, sizeof(struct _usb_msg));
369 msg->fd = device_id;
370 msg->cb = cb;
371 msg->userdata = userdata;
373 if (device_id>=0 && device_id<0x20) {
374 u8 *pEndP = NULL;
375 u16 *pLength = NULL;
377 pEndP = (u8*)iosAlloc(hId,32);
378 if(pEndP==NULL) goto done;
379 *pEndP = bEndpoint;
381 pLength = (u16*)iosAlloc(hId,32);
382 if(pLength==NULL) goto done;
383 *pLength = wLength;
385 msg->vec[0].data = pEndP;
386 msg->vec[0].len = sizeof(u8);
387 msg->vec[1].data = pLength;
388 msg->vec[1].len = sizeof(u16);
389 msg->vec[2].data = rpData;
390 msg->vec[2].len = wLength;
392 if (cb==NULL)
393 ret = IOS_Ioctlv(device_id,ioctl,2,1,msg->vec);
394 else
395 return IOS_IoctlvAsync(device_id,ioctl,2,1,msg->vec,__usbv0_intrbulk_messageCB,msg);
397 done:
398 if(pEndP!=NULL) iosFree(hId,pEndP);
399 if(pLength!=NULL) iosFree(hId,pLength);
401 } else {
402 u8 adjust = bEndpoint >> 7;
403 s32 fd = (device_id<0) ? ven_host->fd : hid_host->fd;
405 if (ioctl == USBV0_IOCTL_INTRMSG) {
406 msg->intr.rpData = rpData;
407 msg->intr.wLength = wLength;
408 msg->intr.bEndpoint = bEndpoint;
409 ioctl = USBV5_IOCTL_INTRMSG;
410 // HID does this a little bit differently
411 if (device_id>=0)
412 msg->hid_intr_write = !adjust;
413 } else {
414 msg->bulk.rpData = rpData;
415 msg->bulk.wLength = wLength;
416 msg->bulk.bEndpoint = bEndpoint;
417 ioctl = USBV5_IOCTL_BULKMSG;
420 msg->vec[0].data = msg;
421 msg->vec[0].len = 64;
422 msg->vec[1].data = rpData;
423 msg->vec[1].len = wLength;
425 if (cb==NULL)
426 ret = IOS_Ioctlv(fd, ioctl, 2-adjust, adjust, msg->vec);
427 else
428 return IOS_IoctlvAsync(fd, ioctl, 2-adjust, adjust, msg->vec, __usbv5_messageCB, msg);
431 if(msg!=NULL) iosFree(hId,msg);
433 return ret;
436 static inline s32 __usb_getdesc(s32 fd, u8 *buffer, u8 type, u8 index, u8 size)
438 return __usb_control_message(fd, USB_ENDPOINT_IN ,USB_REQ_GETDESCRIPTOR, (type << 8) | index, 0, size, buffer, NULL, NULL);
441 static u32 __find_next_endpoint(u8 *buffer,u32 size)
443 u8 *ptr = buffer;
445 while(size>0) {
446 if(buffer[1]==USB_DT_ENDPOINT || buffer[1]==USB_DT_INTERFACE)
447 break;
449 size -= buffer[0];
450 buffer += buffer[0];
453 return (buffer - ptr);
456 s32 USB_Initialize()
458 if(hId==-1) hId = iosCreateHeap(USB_HEAPSIZE);
459 if(hId<0) return IPC_ENOMEM;
461 if (ven_host==NULL) {
462 s32 ven_fd = IOS_Open(__ven_path, IPC_OPEN_NONE);
463 if (ven_fd>=0) {
464 ven_host = (struct _usbv5_host*)iosAlloc(hId, sizeof(*ven_host));
465 if (ven_host==NULL) {
466 IOS_Close(ven_fd);
467 return IPC_ENOMEM;
469 memset(ven_host, 0, sizeof(*ven_host));
470 ven_host->fd = ven_fd;
472 u32 *ven_ver = (u32*)iosAlloc(hId, 0x20);
473 if (ven_ver==NULL) goto mem_error;
474 if (IOS_Ioctl(ven_fd, USBV5_IOCTL_GETVERSION, NULL, 0, ven_ver, 0x20)==0 && ven_ver[0]==0x50001)
475 IOS_IoctlAsync(ven_fd, USBV5_IOCTL_GETDEVICECHANGE, NULL, 0, ven_host->attached_devices, 0x180, __usbv5_devicechangeCB, ven_host);
476 else {
477 // wrong ven version
478 IOS_Close(ven_fd);
479 iosFree(hId, ven_host);
480 ven_host = NULL;
483 iosFree(hId, ven_ver);
487 if (hid_host==NULL) {
488 s32 hid_fd = IOS_Open(__hid_path, IPC_OPEN_NONE);
489 if (hid_fd>=0) {
490 hid_host = (struct _usbv5_host*)iosAlloc(hId, sizeof(*hid_host));
491 if (hid_host==NULL) {
492 IOS_Close(hid_fd);
493 goto mem_error;
495 memset(hid_host, 0, sizeof(*hid_host));
496 hid_host->fd = hid_fd;
498 u32 *hid_ver = (u32*)iosAlloc(hId, 0x20);
499 if (hid_ver==NULL) goto mem_error;
500 // have to call the USB4 version first, to be safe
501 if (IOS_Ioctl(hid_fd, USBV4_IOCTL_GETVERSION, NULL, 0, NULL, 0)==0x40001 || \
502 IOS_Ioctl(hid_fd, USBV5_IOCTL_GETVERSION, NULL, 0, hid_ver, 0x20) || hid_ver[0]!=0x50001) {
503 // wrong hid version
504 IOS_Close(hid_fd);
505 iosFree(hId, hid_host);
506 hid_host = NULL;
507 } else
508 IOS_IoctlAsync(hid_fd, USBV5_IOCTL_GETDEVICECHANGE, NULL, 0, hid_host->attached_devices, 0x180, __usbv5_devicechangeCB, hid_host);
510 iosFree(hId, hid_ver);
514 return IPC_OK;
516 mem_error:
517 USB_Deinitialize();
518 return IPC_ENOMEM;
521 s32 USB_Deinitialize()
523 if (hid_host) {
524 if (hid_host->fd) {
525 IOS_Ioctl(hid_host->fd, USBV5_IOCTL_SHUTDOWN, NULL, 0, NULL, 0);
526 IOS_Close(hid_host->fd);
528 iosFree(hId, hid_host);
529 hid_host = NULL;
532 if (ven_host) {
533 if (ven_host->fd>=0) {
534 IOS_Ioctl(ven_host->fd, USBV5_IOCTL_SHUTDOWN, NULL, 0, NULL, 0);
535 IOS_Close(ven_host->fd);
537 iosFree(hId, ven_host);
538 ven_host = NULL;
541 if (hId>=0) {
542 iosDestroyHeap(hId);
543 hId = -1;
546 return IPC_OK;
549 s32 USB_OpenDevice(s32 device_id,u16 vid,u16 pid,s32 *fd)
551 s32 ret = USB_OK;
552 char *devicepath = NULL;
553 *fd = -1;
555 if (device_id && device_id!=USB_OH1_DEVICE_ID) {
556 int i;
558 i = __find_device_on_host(ven_host, device_id);
559 if (i<0)
560 i = __find_device_on_host(hid_host, device_id);
561 if (i>=0) {
562 *fd = device_id;
563 USB_ResumeDevice(device_id);
564 return 0;
568 devicepath = iosAlloc(hId,USB_MAXPATH);
569 if(devicepath==NULL) return IPC_ENOMEM;
571 if (device_id==USB_OH1_DEVICE_ID)
572 snprintf(devicepath,USB_MAXPATH,"/dev/usb/oh1/%x/%x",vid,pid);
573 else
574 snprintf(devicepath,USB_MAXPATH,"/dev/usb/oh0/%x/%x",vid,pid);
576 *fd = IOS_Open(devicepath,0);
577 if(*fd<0) ret = *fd;
579 if (devicepath!=NULL) iosFree(hId,devicepath);
580 return ret;
583 s32 USBV5_CloseDevice(s32 device_id)
585 int i;
586 struct _usbv5_host* host;
588 if (__find_device_on_host(ven_host, device_id)>=0)
589 host = ven_host;
590 else if (__find_device_on_host(hid_host, device_id)>=0)
591 host = hid_host;
592 else
593 return IPC_EINVAL;
595 for (i=0; i < USB_MAX_DEVICES; i++) {
596 if (host->remove_cb[i].cb==NULL)
597 continue;
599 if (host->remove_cb[i].device_id==device_id) {
600 host->remove_cb[i].cb(0, host->remove_cb[i].userdata);
601 host->remove_cb[i].cb = NULL;
602 break;
605 //return USB_SuspendDevice(device_id);
606 return 0;
609 s32 USB_CloseDevice(s32 *fd)
611 s32 ret = IPC_EINVAL;
612 if (fd) {
613 ret = USBV5_CloseDevice(*fd);
614 if (ret==IPC_EINVAL && *fd>0)
615 ret = IOS_Close(*fd);
616 if (ret>=0) *fd = -1;
619 return ret;
622 s32 USB_CloseDeviceAsync(s32 *fd,usbcallback cb,void *userdata)
624 s32 ret = IPC_EINVAL;
625 if(fd) {
626 ret = USBV5_CloseDevice(*fd);
627 if (ret!=IPC_EINVAL) {
628 if (cb)
629 return cb(ret, userdata);
630 else
631 return ret;
633 if (*fd>0)
634 return IOS_CloseAsync(*fd,cb,userdata);
637 return ret;
640 s32 USB_GetDeviceDescription(s32 fd,usb_devdesc *devdesc)
642 s32 ret;
643 usb_devdesc *p;
645 p = iosAlloc(hId,USB_DT_DEVICE_SIZE);
646 if(p==NULL) return IPC_ENOMEM;
648 ret = __usb_control_message(fd,USB_ENDPOINT_IN,USB_REQ_GETDESCRIPTOR,(USB_DT_DEVICE<<8),0,USB_DT_DEVICE_SIZE,p,NULL,NULL);
649 if(ret>=0) memcpy(devdesc,p,USB_DT_DEVICE_SIZE);
650 devdesc->configurations = NULL;
652 if(p!=NULL) iosFree(hId,p);
653 return ret;
656 static s32 USBV5_GetDescriptors(s32 device_id, usb_devdesc *udd)
658 s32 retval = IPC_ENOMEM;
659 u32 *io_buffer = NULL;
660 u8 *buffer = NULL;
661 usb_configurationdesc *ucd = NULL;
662 usb_interfacedesc *uid = NULL;
663 usb_endpointdesc *ued = NULL;
664 u32 iConf, iEndpoint;
665 s32 fd;
666 u32 desc_out_size, desc_start_offset;
668 if (__find_device_on_host(ven_host, device_id)>=0) {
669 fd = ven_host->fd;
670 desc_out_size = 0xC0;
671 desc_start_offset = 20;
672 } else if (__find_device_on_host(hid_host, device_id)>=0) {
673 fd = hid_host->fd;
674 desc_out_size = 0x60;
675 desc_start_offset = 36;
676 } else
677 return IPC_EINVAL;
679 io_buffer = (u32*)iosAlloc(hId, 0x20);
680 buffer = (u8*)iosAlloc(hId, desc_out_size);
681 if (io_buffer==NULL || buffer==NULL) goto free_bufs;
683 io_buffer[0] = device_id;
684 io_buffer[2] = 0;
685 retval = IOS_Ioctl(fd, USBV5_IOCTL_GETDEVPARAMS, io_buffer, 0x20, buffer, desc_out_size);
686 if (retval==IPC_OK) {
687 u8 *next = buffer+desc_start_offset;
689 memcpy(udd, next, sizeof(*udd));
690 udd->configurations = calloc(udd->bNumConfigurations, sizeof(*udd->configurations));
691 if(udd->configurations == NULL) goto free_bufs;
693 next += (udd->bLength+3)&~3;
694 for (iConf = 0; iConf < udd->bNumConfigurations; iConf++)
696 ucd = &udd->configurations[iConf];
697 memcpy(ucd, next, USB_DT_CONFIG_SIZE);
698 next += (USB_DT_CONFIG_SIZE+3)&~3;
699 // IOS presents each interface as a different device
700 if (ucd->bNumInterfaces) ucd->bNumInterfaces = 1;
702 ucd->interfaces = calloc(ucd->bNumInterfaces, sizeof(*ucd->interfaces));
703 if (ucd->interfaces == NULL) goto free_bufs;
705 uid = ucd->interfaces;
706 memcpy(uid, next, USB_DT_INTERFACE_SIZE);
707 next += (uid->bLength+3)&~3;
709 uid->endpoints = calloc(uid->bNumEndpoints, sizeof(*uid->endpoints));
710 if (uid->endpoints == NULL) goto free_bufs;
712 memset(uid->endpoints,0,uid->bNumEndpoints*sizeof(*uid->endpoints));
713 for(iEndpoint = 0; iEndpoint < uid->bNumEndpoints; iEndpoint++)
715 ued = &uid->endpoints[iEndpoint];
716 memcpy(ued, next, USB_DT_ENDPOINT_SIZE);
717 next += (ued->bLength+3)&~3;
720 retval = IPC_OK;
724 free_bufs:
725 if (io_buffer!=NULL)
726 iosFree(hId, io_buffer);
727 if (buffer!=NULL)
728 iosFree(hId, buffer);
729 return retval;
732 s32 USB_GetDescriptors(s32 fd, usb_devdesc *udd)
734 u8 *buffer = NULL;
735 u8 *ptr = NULL;
736 usb_configurationdesc *ucd = NULL;
737 usb_interfacedesc *uid = NULL;
738 usb_endpointdesc *ued = NULL;
739 s32 retval = 0;
740 u32 size,i;
741 u32 iConf, iInterface, iEndpoint;
743 if (fd>=0x20 || fd<-1)
744 return USBV5_GetDescriptors(fd, udd);
746 buffer = iosAlloc(hId, sizeof(*udd));
747 if(buffer == NULL)
749 retval = IPC_ENOHEAP;
750 goto free_and_error;
753 retval = __usb_getdesc(fd, buffer, USB_DT_DEVICE, 0, USB_DT_DEVICE_SIZE);
754 if(retval < 0)
755 goto free_and_error;
756 memcpy(udd, buffer, USB_DT_DEVICE_SIZE);
757 iosFree(hId, buffer);
759 udd->bcdUSB = bswap16(udd->bcdUSB);
760 udd->idVendor = bswap16(udd->idVendor);
761 udd->idProduct = bswap16(udd->idProduct);
762 udd->bcdDevice = bswap16(udd->bcdDevice);
764 udd->configurations = calloc(udd->bNumConfigurations, sizeof(*udd->configurations));
765 if(udd->configurations == NULL)
767 retval = IPC_ENOMEM;
768 goto free_and_error;
770 for(iConf = 0; iConf < udd->bNumConfigurations; iConf++)
772 buffer = iosAlloc(hId, USB_DT_CONFIG_SIZE);
773 if(buffer == NULL)
775 retval = IPC_ENOHEAP;
776 goto free_and_error;
779 retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, USB_DT_CONFIG_SIZE);
780 ucd = &udd->configurations[iConf];
781 memcpy(ucd, buffer, USB_DT_CONFIG_SIZE);
782 iosFree(hId, buffer);
784 ucd->wTotalLength = bswap16(ucd->wTotalLength);
785 size = ucd->wTotalLength;
786 buffer = iosAlloc(hId, size);
787 if(buffer == NULL)
789 retval = IPC_ENOHEAP;
790 goto free_and_error;
793 retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, ucd->wTotalLength);
794 if(retval < 0)
795 goto free_and_error;
797 ptr = buffer;
798 ptr += ucd->bLength;
799 size -= ucd->bLength;
801 retval = IPC_ENOMEM;
802 ucd->interfaces = calloc(ucd->bNumInterfaces, sizeof(*ucd->interfaces));
803 if(ucd->interfaces == NULL)
804 goto free_and_error;
805 for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++)
807 uid = &ucd->interfaces[iInterface];
808 memcpy(uid, ptr, USB_DT_INTERFACE_SIZE);
809 ptr += uid->bLength;
810 size -= uid->bLength;
812 if (uid->bNumEndpoints==0)
813 continue;
814 uid->endpoints = calloc(uid->bNumEndpoints, sizeof(*uid->endpoints));
815 if(uid->endpoints == NULL)
816 goto free_and_error;
818 /* This skips vendor and class specific descriptors */
819 i = __find_next_endpoint(ptr, size);
820 uid->extra_size = i;
821 if(i>0)
823 uid->extra = malloc(i);
824 if(uid->extra == NULL)
825 goto free_and_error;
826 memcpy(uid->extra, ptr, i);
827 ptr += i;
828 size -= i;
830 memset(uid->endpoints,0,uid->bNumEndpoints* sizeof(*uid->endpoints));
831 for(iEndpoint = 0; iEndpoint < uid->bNumEndpoints; iEndpoint++)
833 ued = &uid->endpoints[iEndpoint];
834 memcpy(ued, ptr, USB_DT_ENDPOINT_SIZE);
835 ptr += ued->bLength;
836 ued->wMaxPacketSize = bswap16(ued->wMaxPacketSize);
839 iosFree(hId, buffer);
840 buffer = NULL;
842 retval = IPC_OK;
844 free_and_error:
845 if(buffer != NULL)
846 iosFree(hId, buffer);
847 if(retval < 0)
848 USB_FreeDescriptors(udd);
849 return retval;
852 //TODO: Fix for /dev/usb/hid
853 s32 USB_GetHIDDescriptor(s32 fd,usb_hiddesc *uhd)
855 u8 *buffer = NULL;
856 s32 retval = IPC_OK;
858 buffer = iosAlloc(hId,sizeof(usb_hiddesc));
859 if(buffer==NULL) {
860 retval = IPC_ENOMEM;
861 goto free_and_error;
864 retval = __usb_getdesc(fd,buffer,USB_DT_HID,0,USB_DT_HID_SIZE);
865 if(retval<0) goto free_and_error;
867 memcpy(uhd,buffer,USB_DT_HID_SIZE);
868 uhd->bcdHID = bswap16(uhd->bcdHID);
869 uhd->descr[0].wDescriptorLength = bswap16(uhd->descr[0].wDescriptorLength);
871 retval = IPC_OK;
873 free_and_error:
874 if(buffer!=NULL) iosFree(hId,buffer);
875 return retval;
878 void USB_FreeDescriptors(usb_devdesc *udd)
880 int iConf, iInterface;
881 usb_configurationdesc *ucd;
882 usb_interfacedesc *uid;
883 if(udd->configurations != NULL)
885 for(iConf = 0; iConf < udd->bNumConfigurations; iConf++)
887 ucd = &udd->configurations[iConf];
888 if(ucd->interfaces != NULL)
890 for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++)
892 uid = &ucd->interfaces[iInterface];
893 if(uid->endpoints != NULL)
894 free(uid->endpoints);
895 if(uid->extra != NULL)
896 free(uid->extra);
898 free(ucd->interfaces);
901 free(udd->configurations);
905 s32 USB_GetAsciiString(s32 fd,u16 wIndex,u16 wLangID,u16 wLength,void *rpData)
907 s32 ret;
908 u8 bo, ro;
909 u8 *buf;
910 u8 *rp = (u8 *)rpData;
912 if(wLength > 255)
913 wLength = 255;
915 buf = iosAlloc(hId, 255); /* 255 is the highest possible length of a descriptor */
916 if(buf == NULL)
917 return IPC_ENOMEM;
919 ret = __usb_control_message(fd, USB_ENDPOINT_IN, USB_REQ_GETDESCRIPTOR, ((USB_DT_STRING << 8) + wIndex), wLangID, 255, buf, NULL, NULL);
921 /* index 0 gets a list of supported languages */
922 if(wIndex == 0)
924 if(ret > 0)
925 memcpy(rpData, buf, wLength);
926 iosFree(hId, buf);
927 return ret;
930 if(ret > 0)
932 bo = 2;
933 ro = 0;
934 while(ro < (wLength - 1) && bo < buf[0])
936 if(buf[bo + 1])
937 rp[ro++] = '?';
938 else
939 rp[ro++] = buf[bo];
940 bo += 2;
942 rp[ro] = 0;
943 ret = ro - 1;
946 iosFree(hId, buf);
947 return ret;
950 s32 USB_ReadIntrMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData)
952 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,NULL,NULL);
955 s32 USB_ReadIntrMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
957 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,cb,userdata);
960 s32 USB_WriteIntrMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData)
962 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,NULL,NULL);
965 s32 USB_WriteIntrMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
967 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,cb,userdata);
970 s32 USB_ReadBlkMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData)
972 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,NULL,NULL);
975 s32 USB_ReadBlkMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
977 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,cb,userdata);
980 s32 USB_WriteBlkMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData)
982 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,NULL,NULL);
985 s32 USB_WriteBlkMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata)
987 return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,cb,userdata);
990 s32 USB_ReadCtrlMsg(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData)
992 return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,NULL,NULL);
995 s32 USB_ReadCtrlMsgAsync(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *userdata)
997 return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,cb,userdata);
1000 s32 USB_WriteCtrlMsg(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData)
1002 return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,NULL,NULL);
1005 s32 USB_WriteCtrlMsgAsync(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *userdata)
1007 return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,cb,userdata);
1010 static s32 USB5_RegisterDeviceRemoval(struct _usbv5_host *host, s32 device_id, usbcallback cb, void *userdata)
1012 int i;
1014 // check to make sure the device is present
1015 if (__find_device_on_host(host, device_id)<0)
1016 return IPC_ENOENT;
1018 // now make sure it's not hooked already
1019 for (i=0; i<USB_MAX_DEVICES; i++) {
1020 if (host->remove_cb[i].cb && host->remove_cb[i].device_id==device_id)
1021 return IPC_EINVAL;
1024 // find a free entry and add it
1025 for (i=0; i<USB_MAX_DEVICES; i++) {
1026 if (host->remove_cb[i].cb==NULL) {
1027 host->remove_cb[i].cb = cb;
1028 host->remove_cb[i].userdata = userdata;
1029 host->remove_cb[i].device_id = device_id;
1030 return IPC_OK;
1033 return IPC_EINVAL;
1036 s32 USB_DeviceRemovalNotifyAsync(s32 fd,usbcallback cb,void *userdata)
1038 s32 ret;
1039 if (fd>=0 && fd<0x20)
1040 return IOS_IoctlAsync(fd,USBV0_IOCTL_DEVREMOVALHOOK,NULL,0,NULL,0,cb,userdata);
1042 ret = USB5_RegisterDeviceRemoval(ven_host, fd, cb, userdata);
1043 if (ret == IPC_ENOENT)
1044 ret = USB5_RegisterDeviceRemoval(hid_host, fd, cb, userdata);
1046 return ret;
1049 static s32 USBV5_SuspendResume(s32 device_id, s32 resumed)
1051 s32 ret;
1052 s32 fd;
1054 if (__find_device_on_host(ven_host, device_id)>=0)
1055 fd = ven_host->fd;
1056 else if (__find_device_on_host(hid_host, device_id)>=0)
1057 fd = hid_host->fd;
1058 else
1059 return IPC_ENOENT;
1061 s32 *buf = (s32*)iosAlloc(hId, 32);
1062 if (buf==NULL) return IPC_ENOMEM;
1064 buf[0] = device_id;
1065 buf[2] = resumed;
1066 ret = IOS_Ioctl(fd, USBV5_IOCTL_SUSPEND_RESUME, buf, 32, NULL, 0);
1067 iosFree(hId, buf);
1069 return ret;
1072 s32 USB_SuspendDevice(s32 fd)
1074 if (fd>=0x20 || fd<-1)
1075 return USBV5_SuspendResume(fd, 0);
1077 return IOS_Ioctl(fd,USBV0_IOCTL_SUSPENDDEV,NULL,0,NULL,0);
1080 s32 USB_ResumeDevice(s32 fd)
1082 if (fd>=0x20 || fd<-1)
1083 return USBV5_SuspendResume(fd, 1);
1085 return IOS_Ioctl(fd,USBV0_IOCTL_RESUMEDEV,NULL,0,NULL,0);
1088 // TODO: Implement this for VEN and HID
1089 s32 USB_DeviceChangeNotifyAsync(u8 interface_class, usbcallback cb, void* userdata)
1091 s32 fd;
1092 struct _usb_msg *msg;
1093 s32 ret;
1095 fd = IOS_Open(__oh0_path,IPC_OPEN_NONE);
1096 if (fd<0) return fd;
1098 msg = iosAlloc(hId,sizeof(*msg));
1099 if (msg==NULL) {
1100 IOS_Close(fd);
1101 return IPC_ENOMEM;
1104 msg->cb = cb;
1105 msg->userdata = userdata;
1106 msg->class = interface_class;
1108 msg->vec[0].data = &msg->class;
1109 msg->vec[0].len = 1;
1111 ret = IOS_IoctlvAsync(fd,USBV0_IOCTL_DEVICECLASSCHANGE,1,0,msg->vec,__usbv5_messageCB,msg);
1112 IOS_Close(fd);
1114 if (ret<0) iosFree(hId, msg);
1116 return ret;
1119 s32 USB_GetDeviceList(usb_device_entry *descr_buffer,u8 num_descr,u8 interface_class,u8 *cnt_descr)
1121 int i;
1122 u8 cntdevs=0;
1124 if (ven_host==NULL) {
1125 s32 fd;
1126 u32 *buf = (u32*)iosAlloc(hId, num_descr<<3);
1127 if (buf==NULL) return IPC_ENOMEM;
1129 fd = IOS_Open(__oh0_path,IPC_OPEN_NONE);
1130 if (fd<0) {
1131 iosFree(hId, buf);
1132 return fd;
1135 cntdevs = 0;
1136 i = IOS_IoctlvFormat(hId,fd,USBV0_IOCTL_GETDEVLIST,"bb:dd",num_descr,interface_class,&cntdevs,sizeof(cntdevs),buf,(num_descr<<3));
1137 if (cnt_descr) *cnt_descr = cntdevs;
1139 while (cntdevs--) {
1140 descr_buffer[cntdevs].device_id = 0;
1141 descr_buffer[cntdevs].vid = (u16)(buf[cntdevs*2+1]>>16);
1142 descr_buffer[cntdevs].pid = (u16)buf[cntdevs*2+1];
1145 IOS_Close(fd);
1146 iosFree(hId, buf);
1147 return i;
1150 // for ven_host, we can only exclude usb_hid class devices
1151 if (interface_class != USB_CLASS_HID && ven_host) {
1152 i=0;
1153 while (cntdevs<num_descr && ven_host->attached_devices[i].device_id) {
1154 descr_buffer[cntdevs++] = ven_host->attached_devices[i++];
1155 if (i>=32) break;
1159 if ((!interface_class || interface_class==USB_CLASS_HID) && hid_host) {
1160 i=0;
1161 while (cntdevs<num_descr && hid_host->attached_devices[i].device_id) {
1162 descr_buffer[cntdevs++] = hid_host->attached_devices[i++];
1163 if (i>32) break;
1167 if (cnt_descr) *cnt_descr = cntdevs;
1169 return IPC_OK;
1172 s32 USB_SetConfiguration(s32 fd, u8 configuration)
1174 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);
1177 s32 USB_GetConfiguration(s32 fd, u8 *configuration)
1179 u8 *_configuration;
1180 s32 retval;
1182 _configuration = iosAlloc(hId, 1);
1183 if(_configuration == NULL)
1184 return IPC_ENOMEM;
1186 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);
1187 if(retval >= 0)
1188 *configuration = *_configuration;
1189 iosFree(hId, _configuration);
1191 return retval;
1194 s32 USB_SetAlternativeInterface(s32 fd, u8 interface, u8 alternateSetting)
1196 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);
1199 static s32 USBV5_CancelEndpoint(s32 device_id, u8 endpoint)
1201 s32 ret;
1202 s32 fd;
1204 if (__find_device_on_host(ven_host, device_id)>=0)
1205 fd = ven_host->fd;
1206 else if (__find_device_on_host(hid_host, device_id)>=0)
1207 fd = hid_host->fd;
1208 else
1209 return IPC_ENOENT;
1211 s32 *buf = (s32*)iosAlloc(hId, 32);
1212 if (buf==NULL) return IPC_ENOMEM;
1214 buf[0] = device_id;
1215 buf[2] = endpoint;
1216 ret = IOS_Ioctl(fd, USBV5_IOCTL_CANCELENDPOINT, buf, 32, NULL, 0);
1217 iosFree(hId, buf);
1219 return ret;
1222 s32 USB_ClearHalt(s32 fd, u8 endpoint)
1224 if (fd>=0x20 || fd<-1)
1225 return USBV5_CancelEndpoint(fd, endpoint);
1226 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);
1229 #endif /* defined(HW_RVL) */