1 /*-------------------------------------------------------------
3 usbstorage.c -- Bulk-only USB mass storage support
6 Sven Peter (svpe) <svpe@gmx.net>
8 This software is provided 'as-is', without any express or implied
9 warranty. In no event will the authors be held liable for any
10 damages arising from the use of this software.
12 Permission is granted to anyone to use this software for any
13 purpose, including commercial applications, and to alter it and
14 redistribute it freely, subject to the following restrictions:
16 1. The origin of this software must not be misrepresented; you
17 must not claim that you wrote the original software. If you use
18 this software in a product, an acknowledgment in the product
19 documentation would be appreciated but is not required.
21 2. Altered source versions must be plainly marked as such, and
22 must not be misrepresented as being the original software.
24 3. This notice may not be removed or altered from any source
27 -------------------------------------------------------------*/
32 #include "usbstorage.h"
34 #define HEAP_SIZE 4096
35 #define TAG_START 0x0BADC0DE
38 #define CBW_SIGNATURE 0x43425355
39 #define CBW_IN (1 << 7)
43 #define CSW_SIGNATURE 0x53425355
45 #define SCSI_TEST_UNIT_READY 0x00
46 #define SCSI_REQUEST_SENSE 0x03
47 #define SCSI_READ_CAPACITY 0x25
48 #define SCSI_READ_10 0x28
49 #define SCSI_WRITE_10 0x2A
51 #define SCSI_SENSE_REPLY_SIZE 18
53 #define USB_CLASS_MASS_STORAGE 0x08
54 #define MASS_STORAGE_SCSI_COMMANDS 0x06
55 #define MASS_STORAGE_BULK_ONLY 0x50
57 #define USB_ENDPOINT_BULK 0x02
61 #define TRACE printf(" %d@%s\n", __LINE__, __FUNCTION__);
68 static inline s32
__usb_getdesc(s32 fd
, u8
*buffer
, u8 type
, u8 index
, u8 size
)
70 return USB_WriteCtrlMsg(fd
, USB_ENDPOINT_IN
, USB_REQ_GETDESCRIPTOR
, (type
<< 8) | index
, 0, size
, buffer
);
73 static inline u16
bswap16(u16 s
)
75 return (s
>> 8) | ((s
& 0xFF) << 8);
78 s32
USB_GetDescriptors(s32 fd
, _usb_devdesc
*udd
)
82 usb_configurationdesc
*ucd
= NULL
;
83 usb_interfacedesc
*uid
= NULL
;
84 usb_endpointdesc
*ued
= NULL
;
86 u32 iConf
, iInterface
, iEndpoint
;
88 buffer
= iosAlloc(hId
, sizeof(*udd
));
95 retval
= __usb_getdesc(fd
, buffer
, USB_DT_DEVICE
, 0, USB_DT_DEVICE_SIZE
);
98 memcpy(udd
, buffer
, USB_DT_DEVICE_SIZE
);
101 udd
->bcdUSB
= bswap16(udd
->bcdUSB
);
102 udd
->idVendor
= bswap16(udd
->idVendor
);
103 udd
->idProduct
= bswap16(udd
->idProduct
);
104 udd
->bcdDevice
= bswap16(udd
->bcdDevice
);
106 udd
->configurations
= calloc(udd
->bNumConfigurations
, sizeof(*udd
->configurations
));
107 if(udd
->configurations
== NULL
)
112 for(iConf
= 0; iConf
< udd
->bNumConfigurations
; iConf
++)
114 buffer
= iosAlloc(hId
, USB_DT_CONFIG_SIZE
);
117 retval
= IPC_ENOHEAP
;
121 retval
= __usb_getdesc(fd
, buffer
, USB_DT_CONFIG
, iConf
, USB_DT_CONFIG_SIZE
);
122 ucd
= &udd
->configurations
[iConf
];
123 memcpy(ucd
, buffer
, USB_DT_CONFIG_SIZE
);
124 iosFree(hId
, buffer
);
126 ucd
->wTotalLength
= bswap16(ucd
->wTotalLength
);
127 buffer
= iosAlloc(hId
, ucd
->wTotalLength
);
130 retval
= IPC_ENOHEAP
;
134 retval
= __usb_getdesc(fd
, buffer
, USB_DT_CONFIG
, iConf
, ucd
->wTotalLength
);
142 ucd
->interfaces
= calloc(ucd
->bNumInterfaces
, sizeof(*ucd
->interfaces
));
143 if(ucd
->interfaces
== NULL
)
145 for(iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++)
147 uid
= &ucd
->interfaces
[iInterface
];
148 memcpy(uid
, ptr
, USB_DT_INTERFACE_SIZE
);
151 uid
->endpoints
= calloc(uid
->bNumEndpoints
, sizeof(*uid
->endpoints
));
152 if(uid
->endpoints
== NULL
)
154 for(iEndpoint
= 0; iEndpoint
< uid
->bNumEndpoints
; iEndpoint
++)
156 ued
= &uid
->endpoints
[iEndpoint
];
157 memcpy(ued
, ptr
, USB_DT_ENDPOINT_SIZE
);
159 ued
->wMaxPacketSize
= bswap16(ued
->wMaxPacketSize
);
169 iosFree(hId
, buffer
);
171 USB_FreeDescriptors(udd
);
175 void USB_FreeDescriptors(_usb_devdesc
*udd
)
177 int iConf
, iInterface
;
178 usb_configurationdesc
*ucd
;
179 usb_interfacedesc
*uid
;
180 if(udd
->configurations
!= NULL
)
182 for(iConf
= 0; iConf
< udd
->bNumConfigurations
; iConf
++)
184 ucd
= &udd
->configurations
[iConf
];
185 if(ucd
->interfaces
!= NULL
)
187 for(iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++)
189 uid
= &ucd
->interfaces
[iInterface
];
190 if(uid
->endpoints
!= NULL
)
191 free(uid
->endpoints
);
193 free(ucd
->interfaces
);
196 free(udd
->configurations
);
200 s32
USBStorage_Initialize()
202 hId
= iosCreateHeap(HEAP_SIZE
);
211 s32
USBStorage_Deinitialize()
213 return iosDestroyHeap(hId
);
216 static inline void __write32(u8
*p
, u32 v
)
224 static inline u32
__read32(u8
*p
)
226 return p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
229 static s32
__send_cbw(usbstorage_handle
*dev
, u32 len
, u8 flags
, const u8
*cb
, u8 cbLen
)
234 if(cbLen
== 0 || cbLen
> 16)
237 cbw
= iosAlloc(hId
, CBW_SIZE
);
241 retval
= IPC_ENOHEAP
;
242 goto free_and_return
;
245 memset(cbw
, 0, CBW_SIZE
);
247 __write32(cbw
, CBW_SIGNATURE
);
248 __write32(cbw
+ 4, dev
->tag
);
249 __write32(cbw
+ 8, len
);
251 cbw
[13] = 0; /* TODO: LUN */
254 memcpy(cbw
+ 15, cb
, cbLen
);
256 LWP_MutexLock(dev
->usb_fd
);
257 retval
= USB_WriteBlkMsg(dev
->usb_fd
, dev
->ep_out
, CBW_SIZE
, (void *)cbw
);
259 if(retval
== CBW_SIZE
)
261 else if(retval
> CBW_SIZE
)
269 USBStorage_Reset(dev
);
270 LWP_MutexUnlock(dev
->lock
);
275 static s32
__read_csw(usbstorage_handle
*dev
, u8
*status
, u32
*dataResidue
)
279 u32 signature
, tag
, _dataResidue
, _status
;
281 csw
= iosAlloc(hId
, CSW_SIZE
);
285 retval
= IPC_ENOHEAP
;
286 goto free_and_return
;
289 memset(csw
, 0, CSW_SIZE
);
291 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, CSW_SIZE
, csw
);
292 if(retval
!= CSW_SIZE
)
297 goto free_and_return
;
300 signature
= __read32(csw
);
301 tag
= __read32(csw
+ 4);
302 _dataResidue
= __read32(csw
+ 8);
305 if(signature
!= CSW_SIGNATURE
)
309 goto free_and_return
;
312 if(dataResidue
!= NULL
)
313 *dataResidue
= _dataResidue
;
321 goto free_and_return
;
331 USBStorage_Reset(dev
);
332 LWP_MutexUnlock(dev
->lock
);
336 static s32
__read_cycle(usbstorage_handle
*dev
, u8
*buffer
, u32 len
, u8
*cb
, u8 cbLen
)
344 bfr
= iosAlloc(hId
, dev
->ep_in_size
);
348 retval
= IPC_ENOHEAP
;
352 retval
= __send_cbw(dev
, len
, CBW_IN
, cb
, cbLen
);
359 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, dev
->ep_in_size
, bfr
);
366 memcpy(buffer
, bfr
, retval
);
370 if(retval
!= dev
->ep_in_size
)
374 retval
= __read_csw(dev
, &status
, &dataResidue
);
381 if(status
|| dataResidue
)
384 // TODO error handling
396 static s32
__write_cycle(usbstorage_handle
*dev
, const u8
*buffer
, u32 len
, u8
*cb
, u8 cbLen
)
405 bfr
= iosAlloc(hId
, dev
->ep_out_size
);
409 retval
= IPC_ENOHEAP
;
413 retval
= __send_cbw(dev
, len
, CBW_OUT
, cb
, cbLen
);
420 thisLen
= len
> dev
->ep_out_size
? dev
->ep_out_size
: len
;
421 memset(bfr
, 0, dev
->ep_out_size
);
422 memcpy(bfr
, buffer
, thisLen
);
423 retval
= USB_WriteBlkMsg(dev
->usb_fd
, dev
->ep_out
, thisLen
, bfr
);
433 if(retval
!= thisLen
&& len
> 0)
440 retval
= __read_csw(dev
, &status
, &dataResidue
);
447 if(status
|| dataResidue
)
450 // TODO error handling
462 usbstorage_handle
*USBStorage_Open(const char *bus
, u16 vid
, u16 pid
)
464 usbstorage_handle
*dev
= NULL
;
470 u32 iConf
, iInterface
, iEp
;
472 usb_configurationdesc
*ucd
;
473 usb_interfacedesc
*uid
;
474 usb_endpointdesc
*ued
;
476 dev
= calloc(1, sizeof(*dev
));
480 goto free_and_return
;
483 dev
->tag
= TAG_START
;
484 if(LWP_MutexInit(&dev
->lock
, true) < 0)
485 goto free_and_return
;
486 LWP_MutexLock(dev
->lock
);
488 retval
= USB_OpenDevice(bus
, vid
, pid
, &dev
->usb_fd
);
490 goto free_and_return
;
492 sense
= iosAlloc(hId
, SCSI_SENSE_REPLY_SIZE
);
494 goto free_and_return
;
496 retval
= USB_GetDescriptors(dev
->usb_fd
, &udd
);
498 goto free_and_return
;
500 for(iConf
= 0; iConf
< udd
.bNumConfigurations
; iConf
++)
502 ucd
= &udd
.configurations
[iConf
];
503 for(iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++)
505 uid
= &ucd
->interfaces
[iInterface
];
506 if(uid
->bInterfaceClass
== USB_CLASS_MASS_STORAGE
&&
507 uid
->bInterfaceSubClass
== MASS_STORAGE_SCSI_COMMANDS
&&
508 uid
->bInterfaceProtocol
== MASS_STORAGE_BULK_ONLY
)
510 if(uid
->bNumEndpoints
< 2)
513 dev
->ep_in
= dev
->ep_in_size
= dev
->ep_out
= dev
->ep_out_size
= 0;
514 for(iEp
= 0; iEp
< uid
->bNumEndpoints
; iEp
++)
516 ued
= &uid
->endpoints
[iEp
];
517 if(ued
->bmAttributes
!= USB_ENDPOINT_BULK
)
520 if(ued
->bEndpointAddress
& USB_ENDPOINT_IN
)
522 dev
->ep_in
= ued
->bEndpointAddress
;
523 dev
->ep_in_size
= ued
->wMaxPacketSize
;
527 dev
->ep_out
= ued
->bEndpointAddress
;
528 dev
->ep_out_size
= ued
->wMaxPacketSize
;
531 if(dev
->ep_in
!= 0 && dev
->ep_out
!= 0)
533 dev
->configuration
= ucd
->bConfigurationValue
;
534 dev
->interface
= uid
->bInterfaceNumber
;
535 dev
->altInterface
= uid
->bAlternateSetting
;
542 USB_FreeDescriptors(&udd
);
543 goto free_and_return
;
546 USB_FreeDescriptors(&udd
);
548 // TODO support for non-default configurations
550 /* some devices needs this (TEST_UNIT_READY -> REQUEST_SENSE
551 * to be working... */
552 memset(cmd
, 0, sizeof(cmd
));
553 cmd
[0] = SCSI_TEST_UNIT_READY
;
556 retval
= __send_cbw(dev
, 0, CBW_IN
, cmd
, 1);
563 goto free_and_return
;
566 retval
= __read_csw(dev
, &status
, NULL
);
570 cmd
[0] = SCSI_REQUEST_SENSE
;
571 cmd
[4] = SCSI_SENSE_REPLY_SIZE
;
573 retval
= __send_cbw(dev
, 0, CBW_IN
, cmd
, 6);
579 goto free_and_return
;
582 memset(sense
, 0, SCSI_SENSE_REPLY_SIZE
);
583 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, SCSI_SENSE_REPLY_SIZE
, sense
);
585 if(retval
< SCSI_SENSE_REPLY_SIZE
)
587 if(retval
< 0) /* this retruns 0 on one of my sticks.. :/ */
590 /* fatal error again */
593 goto free_and_return
;
596 retval
= __read_csw(dev
, NULL
, NULL
);
599 /* fatal error again */
602 goto free_and_return
;
606 memset(cmd
, 0, sizeof(cmd
));
608 cmd
[0] = SCSI_READ_CAPACITY
;
609 retval
= __read_cycle(dev
, sense
, 8, cmd
, 1);
613 goto free_and_return
;
615 memcpy(&dev
->n_sectors
, sense
, 4);
616 memcpy(&dev
->sector_size
, sense
+ 4, 4);
619 LWP_MutexUnlock(dev
->lock
);
625 LWP_MutexDestroy(dev
->lock
);
632 s32
USBStorage_Close(usbstorage_handle
*dev
)
634 USB_CloseDevice(&dev
->usb_fd
);
635 LWP_MutexDestroy(dev
->lock
);
640 // FIXME: remove the (void *)32 stuff when libogc gets updated
641 s32
USBStorage_Reset(usbstorage_handle
*dev
)
644 LWP_MutexLock(dev
->lock
);
646 retval
= USB_WriteCtrlMsg(dev
->usb_fd
, 33, 0xFF, dev
->interface
, 0, 0, NULL
);
648 retval
= USB_WriteCtrlMsg(dev
->usb_fd
, 33, 0xFF, dev
->interface
, 0, 0, (void *)32);
653 retval
= USB_WriteCtrlMsg(dev
->usb_fd
, 2, 0x1, 0, dev
->ep_in
, 0, NULL
);
655 retval
= USB_WriteCtrlMsg(dev
->usb_fd
, 2, 0x1, 0, dev
->ep_in
, 0, (void *)32);
660 retval
= USB_WriteCtrlMsg(dev
->usb_fd
, 2, 0x1, 0, dev
->ep_out
, 0, NULL
);
662 retval
= USB_WriteCtrlMsg(dev
->usb_fd
, 2, 0x1, 0, dev
->ep_out
, 0, (void *)32);
666 LWP_MutexUnlock(dev
->lock
);
670 s32
USBStorage_Read(usbstorage_handle
*dev
, u32 sector
, u8
*buffer
, u16 n_sectors
)
684 return __read_cycle(dev
, buffer
, n_sectors
* dev
->sector_size
, cmd
, sizeof(cmd
));
688 s32
USBStorage_Write(usbstorage_handle
*dev
, u32 sector
, const u8
*buffer
, u16 n_sectors
)
702 return __write_cycle(dev
, buffer
, n_sectors
* dev
->sector_size
, cmd
, sizeof(cmd
));