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 static inline u16
bswap16(u16 s
)
63 return (s
>> 8) | ((s
& 0xFF) << 8);
66 static inline s32
__usb_getdesc(s32 fd
, u8
*buffer
, u8 type
, u8 index
, u8 size
)
68 return USB_WriteCtrlMsg(fd
, USB_ENDPOINT_IN
, USB_REQ_GETDESCRIPTOR
, (type
<< 8) | index
, 0, size
, buffer
);
71 s32
USB_GetDescriptors(s32 fd
, _usb_devdesc
*udd
)
75 usb_configurationdesc
*ucd
= NULL
;
76 usb_interfacedesc
*uid
= NULL
;
77 usb_endpointdesc
*ued
= NULL
;
79 u32 iConf
, iInterface
, iEndpoint
;
81 buffer
= iosAlloc(hId
, sizeof(*udd
));
88 retval
= __usb_getdesc(fd
, buffer
, USB_DT_DEVICE
, 0, USB_DT_DEVICE_SIZE
);
91 memcpy(udd
, buffer
, USB_DT_DEVICE_SIZE
);
94 udd
->bcdUSB
= bswap16(udd
->bcdUSB
);
95 udd
->idVendor
= bswap16(udd
->idVendor
);
96 udd
->idProduct
= bswap16(udd
->idProduct
);
97 udd
->bcdDevice
= bswap16(udd
->bcdDevice
);
99 udd
->configurations
= calloc(udd
->bNumConfigurations
, sizeof(*udd
->configurations
));
100 if(udd
->configurations
== NULL
)
105 for(iConf
= 0; iConf
< udd
->bNumConfigurations
; iConf
++)
107 buffer
= iosAlloc(hId
, USB_DT_CONFIG_SIZE
);
110 retval
= IPC_ENOHEAP
;
114 retval
= __usb_getdesc(fd
, buffer
, USB_DT_CONFIG
, iConf
, USB_DT_CONFIG_SIZE
);
115 ucd
= &udd
->configurations
[iConf
];
116 memcpy(ucd
, buffer
, USB_DT_CONFIG_SIZE
);
117 iosFree(hId
, buffer
);
119 ucd
->wTotalLength
= bswap16(ucd
->wTotalLength
);
120 buffer
= iosAlloc(hId
, ucd
->wTotalLength
);
123 retval
= IPC_ENOHEAP
;
127 retval
= __usb_getdesc(fd
, buffer
, USB_DT_CONFIG
, iConf
, ucd
->wTotalLength
);
135 ucd
->interfaces
= calloc(ucd
->bNumInterfaces
, sizeof(*ucd
->interfaces
));
136 if(ucd
->interfaces
== NULL
)
138 for(iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++)
140 uid
= &ucd
->interfaces
[iInterface
];
141 memcpy(uid
, ptr
, USB_DT_INTERFACE_SIZE
);
144 uid
->endpoints
= calloc(uid
->bNumEndpoints
, sizeof(*uid
->endpoints
));
145 if(uid
->endpoints
== NULL
)
147 for(iEndpoint
= 0; iEndpoint
< uid
->bNumEndpoints
; iEndpoint
++)
149 ued
= &uid
->endpoints
[iEndpoint
];
150 memcpy(ued
, ptr
, USB_DT_ENDPOINT_SIZE
);
152 ued
->wMaxPacketSize
= bswap16(ued
->wMaxPacketSize
);
161 iosFree(hId
, buffer
);
163 USB_FreeDescriptors(udd
);
167 void USB_FreeDescriptors(_usb_devdesc
*udd
)
169 int iConf
, iInterface
;
170 usb_configurationdesc
*ucd
;
171 usb_interfacedesc
*uid
;
172 if(udd
->configurations
!= NULL
)
174 for(iConf
= 0; iConf
< udd
->bNumConfigurations
; iConf
++)
176 ucd
= &udd
->configurations
[iConf
];
177 if(ucd
->interfaces
!= NULL
)
179 for(iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++)
181 uid
= &ucd
->interfaces
[iInterface
];
182 if(uid
->endpoints
!= NULL
)
183 free(uid
->endpoints
);
185 free(ucd
->interfaces
);
188 free(udd
->configurations
);
191 s32
USBStorage_Initialize()
193 hId
= iosCreateHeap(HEAP_SIZE
);
202 s32
USBStorage_Deinitialize()
204 return iosDestroyHeap(hId
);
207 static inline void __write32(u8
*p
, u32 v
)
215 static inline u32
__read32(u8
*p
)
217 return p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
220 static s32
__send_cbw(usbstorage_handle
*dev
, u32 len
, u8 flags
, const u8
*cb
, u8 cbLen
)
223 s32 retval
= USBSTORAGE_OK
;
225 if(cbLen
== 0 || cbLen
> 16)
228 cbw
= iosAlloc(hId
, CBW_SIZE
);
231 retval
= IPC_ENOHEAP
;
232 goto free_and_return
;
235 memset(cbw
, 0, CBW_SIZE
);
237 __write32(cbw
, CBW_SIGNATURE
);
238 __write32(cbw
+ 4, dev
->tag
);
239 __write32(cbw
+ 8, len
);
241 cbw
[13] = 0; /* TODO: LUN */
244 memcpy(cbw
+ 15, cb
, cbLen
);
246 LWP_MutexLock(dev
->usb_fd
);
247 retval
= USB_WriteBlkMsg(dev
->usb_fd
, dev
->ep_out
, CBW_SIZE
, (void *)cbw
);
249 if(retval
== CBW_SIZE
)
250 retval
= USBSTORAGE_OK
;
251 else if(retval
> CBW_SIZE
)
252 retval
= USBSTORAGE_ESHORTWRITE
;
259 USBStorage_Reset(dev
);
260 LWP_MutexUnlock(dev
->lock
);
265 static s32
__read_csw(usbstorage_handle
*dev
, u8
*status
, u32
*dataResidue
)
268 s32 retval
= USBSTORAGE_OK
;
269 u32 signature
, tag
, _dataResidue
, _status
;
271 csw
= iosAlloc(hId
, CSW_SIZE
);
274 retval
= IPC_ENOHEAP
;
275 goto free_and_return
;
278 memset(csw
, 0, CSW_SIZE
);
280 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, CSW_SIZE
, csw
);
281 if(retval
!= CSW_SIZE
)
286 retval
= USBSTORAGE_ESHORTREAD
;
287 goto free_and_return
;
290 retval
= USBSTORAGE_OK
;
292 signature
= __read32(csw
);
293 tag
= __read32(csw
+ 4);
294 _dataResidue
= __read32(csw
+ 8);
297 if(signature
!= CSW_SIGNATURE
)
299 retval
= USBSTORAGE_ESIGNATURE
;
300 goto free_and_return
;
303 if(dataResidue
!= NULL
)
304 *dataResidue
= _dataResidue
;
310 retval
= USBSTORAGE_ETAG
;
311 goto free_and_return
;
321 USBStorage_Reset(dev
);
322 LWP_MutexUnlock(dev
->lock
);
326 static s32
__read_cycle(usbstorage_handle
*dev
, u8
*buffer
, u32 len
, u8
*cb
, u8 cbLen
)
328 s32 retval
= USBSTORAGE_OK
;
334 bfr
= iosAlloc(hId
, dev
->ep_in_size
);
337 retval
= IPC_ENOHEAP
;
341 retval
= __send_cbw(dev
, len
, CBW_IN
, cb
, cbLen
);
347 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, dev
->ep_in_size
, bfr
);
353 memcpy(buffer
, bfr
, retval
);
357 if(retval
!= dev
->ep_in_size
)
361 retval
= __read_csw(dev
, &status
, &dataResidue
);
369 retval
= USBSTORAGE_ESTATUS
;
370 USBStorage_Reset(dev
);
376 retval
= USBSTORAGE_EDATARESIDUE
;
377 USBStorage_Reset(dev
);
381 retval
= USBSTORAGE_OK
;
389 static s32
__write_cycle(usbstorage_handle
*dev
, const u8
*buffer
, u32 len
, u8
*cb
, u8 cbLen
)
391 s32 retval
= USBSTORAGE_OK
;
398 bfr
= iosAlloc(hId
, dev
->ep_out_size
);
401 retval
= IPC_ENOHEAP
;
405 retval
= __send_cbw(dev
, len
, CBW_OUT
, cb
, cbLen
);
411 thisLen
= len
> dev
->ep_out_size
? dev
->ep_out_size
: len
;
412 memset(bfr
, 0, dev
->ep_out_size
);
413 memcpy(bfr
, buffer
, thisLen
);
414 retval
= USB_WriteBlkMsg(dev
->usb_fd
, dev
->ep_out
, thisLen
, bfr
);
417 retval
= USBSTORAGE_EDATARESIDUE
;
418 USBStorage_Reset(dev
);
425 if(retval
!= thisLen
&& len
> 0)
427 retval
= USBSTORAGE_EDATARESIDUE
;
428 USBStorage_Reset(dev
);
433 retval
= __read_csw(dev
, &status
, &dataResidue
);
436 USBStorage_Reset(dev
);
442 retval
= USBSTORAGE_ESTATUS
;
443 USBStorage_Reset(dev
);
449 retval
= USBSTORAGE_EDATARESIDUE
;
450 USBStorage_Reset(dev
);
454 retval
= USBSTORAGE_OK
;
462 s32
USBStorage_Open(usbstorage_handle
*dev
, const char *bus
, u16 vid
, u16 pid
)
468 u32 iConf
, iInterface
, iEp
;
470 usb_configurationdesc
*ucd
;
471 usb_interfacedesc
*uid
;
472 usb_endpointdesc
*ued
;
474 memset(dev
, 0, sizeof(*dev
));
476 dev
->tag
= TAG_START
;
477 if(LWP_MutexInit(&dev
->lock
, true) < 0)
478 goto free_and_return
;
479 LWP_MutexLock(dev
->lock
);
481 retval
= USB_OpenDevice(bus
, vid
, pid
, &dev
->usb_fd
);
483 goto free_and_return
;
485 sense
= iosAlloc(hId
, SCSI_SENSE_REPLY_SIZE
);
487 goto free_and_return
;
489 retval
= USB_GetDescriptors(dev
->usb_fd
, &udd
);
491 goto free_and_return
;
493 for(iConf
= 0; iConf
< udd
.bNumConfigurations
; iConf
++)
495 ucd
= &udd
.configurations
[iConf
];
496 for(iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++)
498 uid
= &ucd
->interfaces
[iInterface
];
499 if(uid
->bInterfaceClass
== USB_CLASS_MASS_STORAGE
&&
500 uid
->bInterfaceSubClass
== MASS_STORAGE_SCSI_COMMANDS
&&
501 uid
->bInterfaceProtocol
== MASS_STORAGE_BULK_ONLY
)
503 if(uid
->bNumEndpoints
< 2)
506 dev
->ep_in
= dev
->ep_in_size
= dev
->ep_out
= dev
->ep_out_size
= 0;
507 for(iEp
= 0; iEp
< uid
->bNumEndpoints
; iEp
++)
509 ued
= &uid
->endpoints
[iEp
];
510 if(ued
->bmAttributes
!= USB_ENDPOINT_BULK
)
513 if(ued
->bEndpointAddress
& USB_ENDPOINT_IN
)
515 dev
->ep_in
= ued
->bEndpointAddress
;
516 dev
->ep_in_size
= ued
->wMaxPacketSize
;
520 dev
->ep_out
= ued
->bEndpointAddress
;
521 dev
->ep_out_size
= ued
->wMaxPacketSize
;
524 if(dev
->ep_in
!= 0 && dev
->ep_out
!= 0)
526 dev
->configuration
= ucd
->bConfigurationValue
;
527 dev
->interface
= uid
->bInterfaceNumber
;
528 dev
->altInterface
= uid
->bAlternateSetting
;
535 USB_FreeDescriptors(&udd
);
536 retval
= USBSTORAGE_ENOINTERFACE
;
537 goto free_and_return
;
540 USB_FreeDescriptors(&udd
);
542 // TODO support for non-default configurations
543 retval
= USBStorage_Reset(dev
);
545 goto free_and_return
;
547 memset(cmd
, 0, sizeof(cmd
));
549 cmd
[0] = SCSI_READ_CAPACITY
;
550 retval
= __read_cycle(dev
, sense
, 8, cmd
, 1);
553 retval
= USBSTORAGE_ECAPACITY
;
554 goto free_and_return
;
556 memcpy(&dev
->n_sectors
, sense
, 4);
557 memcpy(&dev
->sector_size
, sense
+ 4, 4);
560 LWP_MutexUnlock(dev
->lock
);
565 LWP_MutexDestroy(dev
->lock
);
571 s32
USBStorage_Close(usbstorage_handle
*dev
)
573 USB_CloseDevice(&dev
->usb_fd
);
574 LWP_MutexDestroy(dev
->lock
);
575 memset(dev
, 0, sizeof(*dev
));
579 s32
USBStorage_Reset(usbstorage_handle
*dev
)
586 LWP_MutexLock(dev
->lock
);
587 retval
= USB_WriteCtrlMsg(dev
->usb_fd
, 33, 0xFF, dev
->interface
, 0, 0, NULL
);
590 retval
= USB_WriteCtrlMsg(dev
->usb_fd
, 2, 0x1, 0, dev
->ep_in
, 0, NULL
);
593 retval
= USB_WriteCtrlMsg(dev
->usb_fd
, 2, 0x1, 0, dev
->ep_out
, 0, NULL
);
595 /* some devices needs this (TEST_UNIT_READY -> REQUEST_SENSE
596 * to be working... */
597 sense
= iosAlloc(hId
, SCSI_SENSE_REPLY_SIZE
);
604 memset(cmd
, 0, sizeof(cmd
));
605 cmd
[0] = SCSI_TEST_UNIT_READY
;
607 retval
= __send_cbw(dev
, 0, CBW_IN
, cmd
, 1);
615 retval
= __read_csw(dev
, &status
, NULL
);
618 cmd
[0] = SCSI_REQUEST_SENSE
;
619 cmd
[4] = SCSI_SENSE_REPLY_SIZE
;
621 retval
= __send_cbw(dev
, 0, CBW_IN
, cmd
, 6);
628 memset(sense
, 0, SCSI_SENSE_REPLY_SIZE
);
629 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, SCSI_SENSE_REPLY_SIZE
, sense
);
631 if(retval
< SCSI_SENSE_REPLY_SIZE
)
633 if(retval
< 0) /* this retruns 0 on one of my sticks.. :/ */
636 /* fatal error again */
637 retval
= USBSTORAGE_ESENSE
;
641 retval
= __read_csw(dev
, NULL
, NULL
);
644 /* fatal error again */
651 LWP_MutexUnlock(dev
->lock
);
655 s32
USBStorage_Read(usbstorage_handle
*dev
, u32 sector
, u8
*buffer
, u16 n_sectors
)
669 return __read_cycle(dev
, buffer
, n_sectors
* dev
->sector_size
, cmd
, sizeof(cmd
));
673 s32
USBStorage_Write(usbstorage_handle
*dev
, u32 sector
, const u8
*buffer
, u16 n_sectors
)
687 return __write_cycle(dev
, buffer
, n_sectors
* dev
->sector_size
, cmd
, sizeof(cmd
));