1 /*-------------------------------------------------------------
6 Michael Wiedenbauer (shagkur)
7 Dave Murphy (WinterMute)
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
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.
51 #include <processor.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
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
{
96 usb_device_entry attached_devices
[USB_MAX_DEVICES
];
97 struct _usb_cb_removalnotify_list remove_cb
[USB_MAX_DEVICES
];
101 static struct _usbv5_host
* ven_host
= NULL
;
102 static struct _usbv5_host
* hid_host
= NULL
;
136 // not completely sure about this but this field must be 0 for reads
139 u32 align_pad
[4]; // pad to 24 bytes
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
;
154 IOS_IoctlAsync(host
->fd
, USBV5_IOCTL_GETDEVICECHANGE
, NULL
, 0, host
->attached_devices
, 0x180, __usbv5_devicechangeCB
, host
);
159 static s32
__usbv5_devicechangeCB(s32 result
, void *p
)
162 struct _usbv5_host
* host
= (struct _usbv5_host
*)p
;
164 if(host
==NULL
) return IPC_EINVAL
;
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
)
172 for (j
=0; j
<result
; j
++) {
173 if (host
->remove_cb
[i
].device_id
== host
->attached_devices
[j
].device_id
)
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
);
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
);
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
);
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
);
241 static s32
__find_device_on_host(struct _usbv5_host
*host
, s32 device_id
)
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
)
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
));
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
;
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
;
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
;
316 ret
= IOS_Ioctlv(device_id
, USBV0_IOCTL_CTRLMSG
, 6, 1, msg
->vec
);
318 return IOS_IoctlvAsync(device_id
, USBV0_IOCTL_CTRLMSG
, 6, 1, msg
->vec
, __usbv0_control_messageCB
, msg
);
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
);
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
;
345 ret
= IOS_Ioctlv(fd
, USBV5_IOCTL_CTRLMSG
, 2-adjust
, adjust
, msg
->vec
);
347 return IOS_IoctlvAsync(fd
, USBV5_IOCTL_CTRLMSG
, 2-adjust
, adjust
, msg
->vec
, __usbv5_messageCB
, msg
);
350 if(msg
!=NULL
) iosFree(hId
,msg
);
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
));
371 msg
->userdata
= userdata
;
373 if (device_id
>=0 && device_id
<0x20) {
377 pEndP
= (u8
*)iosAlloc(hId
,32);
378 if(pEndP
==NULL
) goto done
;
381 pLength
= (u16
*)iosAlloc(hId
,32);
382 if(pLength
==NULL
) goto done
;
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
;
393 ret
= IOS_Ioctlv(device_id
,ioctl
,2,1,msg
->vec
);
395 return IOS_IoctlvAsync(device_id
,ioctl
,2,1,msg
->vec
,__usbv0_intrbulk_messageCB
,msg
);
398 if(pEndP
!=NULL
) iosFree(hId
,pEndP
);
399 if(pLength
!=NULL
) iosFree(hId
,pLength
);
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
412 msg
->hid_intr_write
= !adjust
;
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
;
426 ret
= IOS_Ioctlv(fd
, ioctl
, 2-adjust
, adjust
, msg
->vec
);
428 return IOS_IoctlvAsync(fd
, ioctl
, 2-adjust
, adjust
, msg
->vec
, __usbv5_messageCB
, msg
);
431 if(msg
!=NULL
) iosFree(hId
,msg
);
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
)
446 if(buffer
[1]==USB_DT_ENDPOINT
|| buffer
[1]==USB_DT_INTERFACE
)
453 return (buffer
- ptr
);
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
);
464 ven_host
= (struct _usbv5_host
*)iosAlloc(hId
, sizeof(*ven_host
));
465 if (ven_host
==NULL
) {
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
);
479 iosFree(hId
, ven_host
);
483 iosFree(hId
, ven_ver
);
487 if (hid_host
==NULL
) {
488 s32 hid_fd
= IOS_Open(__hid_path
, IPC_OPEN_NONE
);
490 hid_host
= (struct _usbv5_host
*)iosAlloc(hId
, sizeof(*hid_host
));
491 if (hid_host
==NULL
) {
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) {
505 iosFree(hId
, hid_host
);
508 IOS_IoctlAsync(hid_fd
, USBV5_IOCTL_GETDEVICECHANGE
, NULL
, 0, hid_host
->attached_devices
, 0x180, __usbv5_devicechangeCB
, hid_host
);
510 iosFree(hId
, hid_ver
);
521 s32
USB_Deinitialize()
525 IOS_Ioctl(hid_host
->fd
, USBV5_IOCTL_SHUTDOWN
, NULL
, 0, NULL
, 0);
526 IOS_Close(hid_host
->fd
);
528 iosFree(hId
, hid_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
);
549 s32
USB_OpenDevice(s32 device_id
,u16 vid
,u16 pid
,s32
*fd
)
552 char *devicepath
= NULL
;
555 if (device_id
&& device_id
!=USB_OH1_DEVICE_ID
) {
558 i
= __find_device_on_host(ven_host
, device_id
);
560 i
= __find_device_on_host(hid_host
, device_id
);
563 USB_ResumeDevice(device_id
);
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
);
574 snprintf(devicepath
,USB_MAXPATH
,"/dev/usb/oh0/%x/%x",vid
,pid
);
576 *fd
= IOS_Open(devicepath
,0);
579 if (devicepath
!=NULL
) iosFree(hId
,devicepath
);
583 s32
USBV5_CloseDevice(s32 device_id
)
586 struct _usbv5_host
* host
;
588 if (__find_device_on_host(ven_host
, device_id
)>=0)
590 else if (__find_device_on_host(hid_host
, device_id
)>=0)
595 for (i
=0; i
< USB_MAX_DEVICES
; i
++) {
596 if (host
->remove_cb
[i
].cb
==NULL
)
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
;
605 //return USB_SuspendDevice(device_id);
609 s32
USB_CloseDevice(s32
*fd
)
611 s32 ret
= IPC_EINVAL
;
613 ret
= USBV5_CloseDevice(*fd
);
614 if (ret
==IPC_EINVAL
&& *fd
>0)
615 ret
= IOS_Close(*fd
);
616 if (ret
>=0) *fd
= -1;
622 s32
USB_CloseDeviceAsync(s32
*fd
,usbcallback cb
,void *userdata
)
624 s32 ret
= IPC_EINVAL
;
626 ret
= USBV5_CloseDevice(*fd
);
627 if (ret
!=IPC_EINVAL
) {
629 return cb(ret
, userdata
);
634 return IOS_CloseAsync(*fd
,cb
,userdata
);
640 s32
USB_GetDeviceDescription(s32 fd
,usb_devdesc
*devdesc
)
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
);
656 static s32
USBV5_GetDescriptors(s32 device_id
, usb_devdesc
*udd
)
658 s32 retval
= IPC_ENOMEM
;
659 u32
*io_buffer
= NULL
;
661 usb_configurationdesc
*ucd
= NULL
;
662 usb_interfacedesc
*uid
= NULL
;
663 usb_endpointdesc
*ued
= NULL
;
664 u32 iConf
, iEndpoint
;
666 u32 desc_out_size
, desc_start_offset
;
668 if (__find_device_on_host(ven_host
, device_id
)>=0) {
670 desc_out_size
= 0xC0;
671 desc_start_offset
= 20;
672 } else if (__find_device_on_host(hid_host
, device_id
)>=0) {
674 desc_out_size
= 0x60;
675 desc_start_offset
= 36;
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
;
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;
726 iosFree(hId
, io_buffer
);
728 iosFree(hId
, buffer
);
732 s32
USB_GetDescriptors(s32 fd
, usb_devdesc
*udd
)
736 usb_configurationdesc
*ucd
= NULL
;
737 usb_interfacedesc
*uid
= NULL
;
738 usb_endpointdesc
*ued
= NULL
;
741 u32 iConf
, iInterface
, iEndpoint
;
743 if (fd
>=0x20 || fd
<-1)
744 return USBV5_GetDescriptors(fd
, udd
);
746 buffer
= iosAlloc(hId
, sizeof(*udd
));
749 retval
= IPC_ENOHEAP
;
753 retval
= __usb_getdesc(fd
, buffer
, USB_DT_DEVICE
, 0, USB_DT_DEVICE_SIZE
);
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
)
770 for(iConf
= 0; iConf
< udd
->bNumConfigurations
; iConf
++)
772 buffer
= iosAlloc(hId
, USB_DT_CONFIG_SIZE
);
775 retval
= IPC_ENOHEAP
;
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
);
789 retval
= IPC_ENOHEAP
;
793 retval
= __usb_getdesc(fd
, buffer
, USB_DT_CONFIG
, iConf
, ucd
->wTotalLength
);
799 size
-= ucd
->bLength
;
802 ucd
->interfaces
= calloc(ucd
->bNumInterfaces
, sizeof(*ucd
->interfaces
));
803 if(ucd
->interfaces
== NULL
)
805 for(iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++)
807 uid
= &ucd
->interfaces
[iInterface
];
808 memcpy(uid
, ptr
, USB_DT_INTERFACE_SIZE
);
810 size
-= uid
->bLength
;
812 if (uid
->bNumEndpoints
==0)
814 uid
->endpoints
= calloc(uid
->bNumEndpoints
, sizeof(*uid
->endpoints
));
815 if(uid
->endpoints
== NULL
)
818 /* This skips vendor and class specific descriptors */
819 i
= __find_next_endpoint(ptr
, size
);
823 uid
->extra
= malloc(i
);
824 if(uid
->extra
== NULL
)
826 memcpy(uid
->extra
, ptr
, 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
);
836 ued
->wMaxPacketSize
= bswap16(ued
->wMaxPacketSize
);
839 iosFree(hId
, buffer
);
846 iosFree(hId
, buffer
);
848 USB_FreeDescriptors(udd
);
852 //TODO: Fix for /dev/usb/hid
853 s32
USB_GetHIDDescriptor(s32 fd
,usb_hiddesc
*uhd
)
858 buffer
= iosAlloc(hId
,sizeof(usb_hiddesc
));
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
);
874 if(buffer
!=NULL
) iosFree(hId
,buffer
);
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
)
898 free(ucd
->interfaces
);
901 free(udd
->configurations
);
905 s32
USB_GetAsciiString(s32 fd
,u16 wIndex
,u16 wLangID
,u16 wLength
,void *rpData
)
910 u8
*rp
= (u8
*)rpData
;
915 buf
= iosAlloc(hId
, 255); /* 255 is the highest possible length of a descriptor */
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 */
925 memcpy(rpData
, buf
, wLength
);
934 while(ro
< (wLength
- 1) && bo
< buf
[0])
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
)
1014 // check to make sure the device is present
1015 if (__find_device_on_host(host
, device_id
)<0)
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
)
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
;
1036 s32
USB_DeviceRemovalNotifyAsync(s32 fd
,usbcallback cb
,void *userdata
)
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
);
1049 static s32
USBV5_SuspendResume(s32 device_id
, s32 resumed
)
1054 if (__find_device_on_host(ven_host
, device_id
)>=0)
1056 else if (__find_device_on_host(hid_host
, device_id
)>=0)
1061 s32
*buf
= (s32
*)iosAlloc(hId
, 32);
1062 if (buf
==NULL
) return IPC_ENOMEM
;
1066 ret
= IOS_Ioctl(fd
, USBV5_IOCTL_SUSPEND_RESUME
, buf
, 32, NULL
, 0);
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
)
1092 struct _usb_msg
*msg
;
1095 fd
= IOS_Open(__oh0_path
,IPC_OPEN_NONE
);
1096 if (fd
<0) return fd
;
1098 msg
= iosAlloc(hId
,sizeof(*msg
));
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
);
1114 if (ret
<0) iosFree(hId
, msg
);
1119 s32
USB_GetDeviceList(usb_device_entry
*descr_buffer
,u8 num_descr
,u8 interface_class
,u8
*cnt_descr
)
1124 if (ven_host
==NULL
) {
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
);
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
;
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];
1150 // for ven_host, we can only exclude usb_hid class devices
1151 if (interface_class
!= USB_CLASS_HID
&& ven_host
) {
1153 while (cntdevs
<num_descr
&& ven_host
->attached_devices
[i
].device_id
) {
1154 descr_buffer
[cntdevs
++] = ven_host
->attached_devices
[i
++];
1159 if ((!interface_class
|| interface_class
==USB_CLASS_HID
) && hid_host
) {
1161 while (cntdevs
<num_descr
&& hid_host
->attached_devices
[i
].device_id
) {
1162 descr_buffer
[cntdevs
++] = hid_host
->attached_devices
[i
++];
1167 if (cnt_descr
) *cnt_descr
= cntdevs
;
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
)
1182 _configuration
= iosAlloc(hId
, 1);
1183 if(_configuration
== NULL
)
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
);
1188 *configuration
= *_configuration
;
1189 iosFree(hId
, _configuration
);
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
)
1204 if (__find_device_on_host(ven_host
, device_id
)>=0)
1206 else if (__find_device_on_host(hid_host
, device_id
)>=0)
1211 s32
*buf
= (s32
*)iosAlloc(hId
, 32);
1212 if (buf
==NULL
) return IPC_ENOMEM
;
1216 ret
= IOS_Ioctl(fd
, USBV5_IOCTL_CANCELENDPOINT
, buf
, 32, NULL
, 0);
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) */