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
)
270 static s32
__read_csw(usbstorage_handle
*dev
, u8
*status
, u32
*dataResidue
)
274 u32 signature
, tag
, _dataResidue
, _status
;
276 csw
= iosAlloc(hId
, CSW_SIZE
);
280 retval
= IPC_ENOHEAP
;
281 goto free_and_return
;
284 memset(csw
, 0, CSW_SIZE
);
286 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, CSW_SIZE
, csw
);
287 if(retval
!= CSW_SIZE
)
292 goto free_and_return
;
295 signature
= __read32(csw
);
296 tag
= __read32(csw
+ 4);
297 _dataResidue
= __read32(csw
+ 8);
300 if(signature
!= CSW_SIGNATURE
)
304 goto free_and_return
;
307 if(dataResidue
!= NULL
)
308 *dataResidue
= _dataResidue
;
316 goto free_and_return
;
321 LWP_MutexUnlock(dev
->lock
);
329 static s32
__read_cycle(usbstorage_handle
*dev
, u8
*buffer
, u32 len
, u8
*cb
, u8 cbLen
)
337 bfr
= iosAlloc(hId
, dev
->ep_in_size
);
341 retval
= IPC_ENOHEAP
;
345 retval
= __send_cbw(dev
, len
, CBW_IN
, cb
, cbLen
);
352 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, dev
->ep_in_size
, bfr
);
359 memcpy(buffer
, bfr
, retval
);
363 if(retval
!= dev
->ep_in_size
)
367 retval
= __read_csw(dev
, &status
, &dataResidue
);
374 if(status
|| dataResidue
)
377 // TODO error handling
389 static s32
__write_cycle(usbstorage_handle
*dev
, const u8
*buffer
, u32 len
, u8
*cb
, u8 cbLen
)
398 bfr
= iosAlloc(hId
, dev
->ep_out_size
);
402 retval
= IPC_ENOHEAP
;
406 retval
= __send_cbw(dev
, len
, CBW_OUT
, cb
, cbLen
);
413 thisLen
= len
> dev
->ep_out_size
? dev
->ep_out_size
: len
;
414 memset(bfr
, 0, dev
->ep_out_size
);
415 memcpy(bfr
, buffer
, thisLen
);
416 retval
= USB_WriteBlkMsg(dev
->usb_fd
, dev
->ep_out
, thisLen
, bfr
);
426 if(retval
!= thisLen
&& len
> 0)
433 retval
= __read_csw(dev
, &status
, &dataResidue
);
440 if(status
|| dataResidue
)
443 // TODO error handling
455 usbstorage_handle
*USBStorage_Open(const char *bus
, u16 vid
, u16 pid
)
457 usbstorage_handle
*dev
= NULL
;
463 u32 iConf
, iInterface
, iEp
;
465 usb_configurationdesc
*ucd
;
466 usb_interfacedesc
*uid
;
467 usb_endpointdesc
*ued
;
469 dev
= calloc(1, sizeof(*dev
));
473 goto free_and_return
;
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 goto free_and_return
;
539 USB_FreeDescriptors(&udd
);
541 /* some devices needs this (TEST_UNIT_READY -> REQUEST_SENSE
542 * to be working... */
543 memset(cmd
, 0, sizeof(cmd
));
544 cmd
[0] = SCSI_TEST_UNIT_READY
;
547 retval
= __send_cbw(dev
, 0, CBW_IN
, cmd
, 1);
554 goto free_and_return
;
557 retval
= __read_csw(dev
, &status
, NULL
);
561 cmd
[0] = SCSI_REQUEST_SENSE
;
562 cmd
[4] = SCSI_SENSE_REPLY_SIZE
;
564 retval
= __send_cbw(dev
, 0, CBW_IN
, cmd
, 6);
570 goto free_and_return
;
573 memset(sense
, 0, SCSI_SENSE_REPLY_SIZE
);
574 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, SCSI_SENSE_REPLY_SIZE
, sense
);
576 if(retval
< SCSI_SENSE_REPLY_SIZE
)
578 if(retval
< 0) /* this retruns 0 on one of my sticks.. :/ */
581 /* fatal error again */
584 goto free_and_return
;
587 retval
= __read_csw(dev
, NULL
, NULL
);
590 /* fatal error again */
593 goto free_and_return
;
597 memset(cmd
, 0, sizeof(cmd
));
599 cmd
[0] = SCSI_READ_CAPACITY
;
600 retval
= __read_cycle(dev
, sense
, 8, cmd
, 1);
604 goto free_and_return
;
606 memcpy(&dev
->n_sectors
, sense
, 4);
607 memcpy(&dev
->sector_size
, sense
+ 4, 4);
610 LWP_MutexUnlock(dev
->lock
);
616 LWP_MutexDestroy(dev
->lock
);
623 s32
USBStorage_Close(usbstorage_handle
*dev
)
625 USB_CloseDevice(&dev
->usb_fd
);
626 LWP_MutexDestroy(dev
->lock
);
631 s32
USBStorage_Read(usbstorage_handle
*dev
, u32 sector
, u8
*buffer
, u16 n_sectors
)
645 return __read_cycle(dev
, buffer
, n_sectors
* dev
->sector_size
, cmd
, sizeof(cmd
));
649 s32
USBStorage_Write(usbstorage_handle
*dev
, u32 sector
, const u8
*buffer
, u16 n_sectors
)
663 return __write_cycle(dev
, buffer
, n_sectors
* dev
->sector_size
, cmd
, sizeof(cmd
));