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
55 #define TRACE printf(" %d@%s\n", __LINE__, __FUNCTION__);
62 static inline s32
__usb_getdesc(s32 fd
, u8
*buffer
, u8 type
, u8 index
, u8 size
)
64 return USB_WriteCtrlMsg(fd
, USB_ENDPOINT_IN
, USB_REQ_GETDESCRIPTOR
, (type
<< 8) | index
, 0, size
, buffer
);
67 static inline u16
bswap16(u16 s
)
69 return (s
>> 8) | ((s
& 0xFF) << 8);
72 s32
USB_GetDescriptors(s32 fd
, _usb_devdesc
*udd
)
76 usb_configurationdesc
*ucd
= NULL
;
77 usb_interfacedesc
*uid
= NULL
;
78 usb_endpointdesc
*ued
= NULL
;
80 u32 iConf
, iInterface
, iEndpoint
;
82 buffer
= iosAlloc(hId
, sizeof(*udd
));
89 retval
= __usb_getdesc(fd
, buffer
, USB_DT_DEVICE
, 0, USB_DT_DEVICE_SIZE
);
92 memcpy(udd
, buffer
, USB_DT_DEVICE_SIZE
);
95 udd
->bcdUSB
= bswap16(udd
->bcdUSB
);
96 udd
->idVendor
= bswap16(udd
->idVendor
);
97 udd
->idProduct
= bswap16(udd
->idProduct
);
98 udd
->bcdDevice
= bswap16(udd
->bcdDevice
);
100 udd
->configurations
= calloc(udd
->bNumConfigurations
, sizeof(*udd
->configurations
));
101 if(udd
->configurations
== NULL
)
106 for(iConf
= 0; iConf
< udd
->bNumConfigurations
; iConf
++)
108 buffer
= iosAlloc(hId
, USB_DT_CONFIG_SIZE
);
111 retval
= IPC_ENOHEAP
;
115 retval
= __usb_getdesc(fd
, buffer
, USB_DT_CONFIG
, iConf
, USB_DT_CONFIG_SIZE
);
116 ucd
= &udd
->configurations
[iConf
];
117 memcpy(ucd
, buffer
, USB_DT_CONFIG_SIZE
);
118 iosFree(hId
, buffer
);
120 ucd
->wTotalLength
= bswap16(ucd
->wTotalLength
);
121 buffer
= iosAlloc(hId
, ucd
->wTotalLength
);
124 retval
= IPC_ENOHEAP
;
128 retval
= __usb_getdesc(fd
, buffer
, USB_DT_CONFIG
, iConf
, ucd
->wTotalLength
);
136 ucd
->interfaces
= calloc(ucd
->bNumInterfaces
, sizeof(*ucd
->interfaces
));
137 if(ucd
->interfaces
== NULL
)
139 for(iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++)
141 uid
= &ucd
->interfaces
[iInterface
];
142 memcpy(uid
, ptr
, USB_DT_INTERFACE_SIZE
);
145 uid
->endpoints
= calloc(uid
->bNumEndpoints
, sizeof(*uid
->endpoints
));
146 if(uid
->endpoints
== NULL
)
148 for(iEndpoint
= 0; iEndpoint
< uid
->bNumEndpoints
; iEndpoint
++)
150 ued
= &uid
->endpoints
[iEndpoint
];
151 memcpy(ued
, ptr
, USB_DT_ENDPOINT_SIZE
);
153 ued
->wMaxPacketSize
= bswap16(ued
->wMaxPacketSize
);
163 iosFree(hId
, buffer
);
165 USB_FreeDescriptors(udd
);
169 void USB_FreeDescriptors(_usb_devdesc
*udd
)
171 int iConf
, iInterface
;
172 usb_configurationdesc
*ucd
;
173 usb_interfacedesc
*uid
;
174 if(udd
->configurations
!= NULL
)
176 for(iConf
= 0; iConf
< udd
->bNumConfigurations
; iConf
++)
178 ucd
= &udd
->configurations
[iConf
];
179 if(ucd
->interfaces
!= NULL
)
181 for(iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++)
183 uid
= &ucd
->interfaces
[iInterface
];
184 if(uid
->endpoints
!= NULL
)
185 free(uid
->endpoints
);
187 free(ucd
->interfaces
);
190 free(udd
->configurations
);
194 s32
USBStorage_Initialize()
196 hId
= iosCreateHeap(HEAP_SIZE
);
205 s32
USBStorage_Deinitialize()
207 return iosDestroyHeap(hId
);
210 static inline void __write32(u8
*p
, u32 v
)
218 static inline u32
__read32(u8
*p
)
220 return p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
223 static s32
__send_cbw(usbstorage_handle
*dev
, u32 len
, u8 flags
, const u8
*cb
, u8 cbLen
)
228 if(cbLen
== 0 || cbLen
> 16)
231 cbw
= iosAlloc(hId
, CBW_SIZE
);
235 retval
= IPC_ENOHEAP
;
236 goto free_and_return
;
239 memset(cbw
, 0, CBW_SIZE
);
241 __write32(cbw
, CBW_SIGNATURE
);
242 __write32(cbw
+ 4, dev
->tag
);
243 __write32(cbw
+ 8, len
);
245 cbw
[13] = 0; /* TODO: LUN */
248 memcpy(cbw
+ 15, cb
, cbLen
);
250 LWP_MutexLock(dev
->usb_fd
);
251 retval
= USB_WriteBlkMsg(dev
->usb_fd
, dev
->ep_out
, CBW_SIZE
, (void *)cbw
);
253 if(retval
== CBW_SIZE
)
255 else if(retval
> CBW_SIZE
)
264 static s32
__read_csw(usbstorage_handle
*dev
, u8
*status
, u32
*dataResidue
)
268 u32 signature
, tag
, _dataResidue
, _status
;
270 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 goto free_and_return
;
289 signature
= __read32(csw
);
290 tag
= __read32(csw
+ 4);
291 _dataResidue
= __read32(csw
+ 8);
294 if(signature
!= CSW_SIGNATURE
)
298 goto free_and_return
;
301 if(dataResidue
!= NULL
)
302 *dataResidue
= _dataResidue
;
310 goto free_and_return
;
315 LWP_MutexUnlock(dev
->lock
);
323 static s32
__read_cycle(usbstorage_handle
*dev
, u8
*buffer
, u32 len
, u8
*cb
, u8 cbLen
)
331 bfr
= iosAlloc(hId
, dev
->ep_in_size
);
335 retval
= IPC_ENOHEAP
;
339 retval
= __send_cbw(dev
, len
, CBW_IN
, cb
, cbLen
);
346 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
);
368 if(status
|| dataResidue
)
371 // TODO error handling
383 static s32
__write_cycle(usbstorage_handle
*dev
, const u8
*buffer
, u32 len
, u8
*cb
, u8 cbLen
)
392 bfr
= iosAlloc(hId
, dev
->ep_out_size
);
396 retval
= IPC_ENOHEAP
;
400 retval
= __send_cbw(dev
, len
, CBW_OUT
, cb
, cbLen
);
407 thisLen
= len
> dev
->ep_out_size
? dev
->ep_out_size
: len
;
408 memset(bfr
, 0, dev
->ep_out_size
);
409 memcpy(bfr
, buffer
, thisLen
);
410 retval
= USB_WriteBlkMsg(dev
->usb_fd
, dev
->ep_out
, thisLen
, bfr
);
420 if(retval
!= thisLen
&& len
> 0)
427 retval
= __read_csw(dev
, &status
, &dataResidue
);
434 if(status
|| dataResidue
)
437 // TODO error handling
449 usbstorage_handle
*USBStorage_Open(const char *bus
, u16 vid
, u16 pid
)
451 usbstorage_handle
*dev
= NULL
;
457 dev
= calloc(1, sizeof(*dev
));
461 goto free_and_return
;
464 dev
->tag
= TAG_START
;
465 if(LWP_MutexInit(&dev
->lock
, true) < 0)
466 goto free_and_return
;
467 LWP_MutexLock(dev
->lock
);
469 retval
= USB_OpenDevice(bus
, vid
, pid
, &dev
->usb_fd
);
471 goto free_and_return
;
473 sense
= iosAlloc(hId
, SCSI_SENSE_REPLY_SIZE
);
475 goto free_and_return
;
477 // TODO: parse descriptors here
479 dev
->ep_in_size
= 64;
481 dev
->ep_out_size
= 64;
483 /* some devices needs this (TEST_UNIT_READY -> REQUEST_SENSE
484 * to be working... */
485 memset(cmd
, 0, sizeof(cmd
));
486 cmd
[0] = SCSI_TEST_UNIT_READY
;
489 retval
= __send_cbw(dev
, 0, CBW_IN
, cmd
, 1);
496 goto free_and_return
;
499 retval
= __read_csw(dev
, &status
, NULL
);
503 cmd
[0] = SCSI_REQUEST_SENSE
;
504 cmd
[4] = SCSI_SENSE_REPLY_SIZE
;
506 retval
= __send_cbw(dev
, 0, CBW_IN
, cmd
, 6);
512 goto free_and_return
;
515 memset(sense
, 0, SCSI_SENSE_REPLY_SIZE
);
516 retval
= USB_ReadBlkMsg(dev
->usb_fd
, dev
->ep_in
, SCSI_SENSE_REPLY_SIZE
, sense
);
518 if(retval
< SCSI_SENSE_REPLY_SIZE
)
520 if(retval
< 0) /* this retruns 0 on one of my sticks.. :/ */
523 /* fatal error again */
526 goto free_and_return
;
529 retval
= __read_csw(dev
, NULL
, NULL
);
532 /* fatal error again */
535 goto free_and_return
;
539 memset(cmd
, 0, sizeof(cmd
));
541 cmd
[0] = SCSI_READ_CAPACITY
;
542 retval
= __read_cycle(dev
, sense
, 8, cmd
, 1);
546 goto free_and_return
;
548 memcpy(&dev
->n_sectors
, sense
, 4);
549 memcpy(&dev
->sector_size
, sense
+ 4, 4);
552 LWP_MutexUnlock(dev
->lock
);
558 LWP_MutexDestroy(dev
->lock
);
565 s32
USBStorage_Close(usbstorage_handle
*dev
)
567 USB_CloseDevice(&dev
->usb_fd
);
568 LWP_MutexDestroy(dev
->lock
);
573 s32
USBStorage_Read(usbstorage_handle
*dev
, u32 sector
, u8
*buffer
, u16 n_sectors
)
587 return __read_cycle(dev
, buffer
, n_sectors
* dev
->sector_size
, cmd
, sizeof(cmd
));
591 s32
USBStorage_Write(usbstorage_handle
*dev
, u32 sector
, const u8
*buffer
, u16 n_sectors
)
605 return __write_cycle(dev
, buffer
, n_sectors
* dev
->sector_size
, cmd
, sizeof(cmd
));