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 -------------------------------------------------------------*/
40 #include "processor.h"
43 #define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f)
45 #define HEAP_SIZE (18*1024)
46 #define TAG_START 0x0BADC0DE
49 #define CBW_SIGNATURE 0x43425355
50 #define CBW_IN (1 << 7)
54 #define CSW_SIGNATURE 0x53425355
56 #define SCSI_TEST_UNIT_READY 0x00
57 #define SCSI_REQUEST_SENSE 0x03
58 #define SCSI_START_STOP 0x1B
59 #define SCSI_READ_CAPACITY 0x25
60 #define SCSI_READ_10 0x28
61 #define SCSI_WRITE_10 0x2A
63 #define SCSI_SENSE_REPLY_SIZE 18
64 #define SCSI_SENSE_NOT_READY 0x02
65 #define SCSI_SENSE_MEDIUM_ERROR 0x03
66 #define SCSI_SENSE_HARDWARE_ERROR 0x04
68 #define USB_CLASS_MASS_STORAGE 0x08
69 #define MASS_STORAGE_SCSI_COMMANDS 0x06
70 #define MASS_STORAGE_BULK_ONLY 0x50
72 #define USBSTORAGE_GET_MAX_LUN 0xFE
73 #define USBSTORAGE_RESET 0xFF
75 #define USB_ENDPOINT_BULK 0x02
77 #define USBSTORAGE_CYCLE_RETRIES 3
78 #define USBSTORAGE_TIMEOUT 2
80 #define MAX_TRANSFER_SIZE_V0 4096
81 #define MAX_TRANSFER_SIZE_V5 (16*1024)
83 #define DEVLIST_MAXSIZE 8
85 static heap_cntrl __heap
;
86 static bool __inited
= false;
87 static lwpq_t __usbstorage_waitq
= 0;
90 The following is for implementing a DISC_INTERFACE
94 static usbstorage_handle __usbfd
;
96 static bool __mounted
= false;
100 static s32
__usbstorage_reset(usbstorage_handle
*dev
);
101 static s32
__usbstorage_clearerrors(usbstorage_handle
*dev
, u8 lun
);
103 /* XXX: this is a *really* dirty and ugly way to send a bulkmessage with a timeout
104 * but there's currently no other known way of doing this and it's in my humble
105 * opinion still better than having a function blocking forever while waiting
106 * for the USB data/IOS reply..
109 static s32
__usb_blkmsg_cb(s32 retval
, void *dummy
)
111 usbstorage_handle
*dev
= (usbstorage_handle
*)dummy
;
112 dev
->retval
= retval
;
113 SYS_CancelAlarm(dev
->alarm
);
114 LWP_ThreadBroadcast(__usbstorage_waitq
);
118 static s32
__usb_deviceremoved_cb(s32 retval
,void *arg
)
124 static void __usb_timeouthandler(syswd_t alarm
,void *cbarg
)
126 usbstorage_handle
*dev
= (usbstorage_handle
*)cbarg
;
127 dev
->retval
= USBSTORAGE_ETIMEDOUT
;
128 LWP_ThreadBroadcast(__usbstorage_waitq
);
131 static void __usb_settimeout(usbstorage_handle
*dev
, u32 secs
)
137 SYS_SetAlarm(dev
->alarm
,&ts
,__usb_timeouthandler
,dev
);
140 static s32
__USB_BlkMsgTimeout(usbstorage_handle
*dev
, u8 bEndpoint
, u16 wLength
, void *rpData
, u32 timeout
)
145 dev
->retval
= USBSTORAGE_PROCESSING
;
146 retval
= USB_WriteBlkMsgAsync(dev
->usb_fd
, bEndpoint
, wLength
, rpData
, __usb_blkmsg_cb
, (void *)dev
);
147 if(retval
< 0) return retval
;
149 __usb_settimeout(dev
, timeout
);
151 _CPU_ISR_Disable(level
);
153 retval
= dev
->retval
;
154 if(retval
!=USBSTORAGE_PROCESSING
) break;
155 else LWP_ThreadSleep(__usbstorage_waitq
);
156 } while(retval
==USBSTORAGE_PROCESSING
);
157 _CPU_ISR_Restore(level
);
160 USB_ClearHalt(dev
->usb_fd
, bEndpoint
);
165 static s32
__USB_CtrlMsgTimeout(usbstorage_handle
*dev
, u8 bmRequestType
, u8 bmRequest
, u16 wValue
, u16 wIndex
, u16 wLength
, void *rpData
)
170 dev
->retval
= USBSTORAGE_PROCESSING
;
171 retval
= USB_WriteCtrlMsgAsync(dev
->usb_fd
, bmRequestType
, bmRequest
, wValue
, wIndex
, wLength
, rpData
, __usb_blkmsg_cb
, (void *)dev
);
172 if(retval
< 0) return retval
;
174 __usb_settimeout(dev
, USBSTORAGE_TIMEOUT
);
176 _CPU_ISR_Disable(level
);
178 retval
= dev
->retval
;
179 if(retval
!=USBSTORAGE_PROCESSING
) break;
180 else LWP_ThreadSleep(__usbstorage_waitq
);
181 } while(retval
==USBSTORAGE_PROCESSING
);
182 _CPU_ISR_Restore(level
);
187 s32
USBStorage_Initialize()
195 _CPU_ISR_Disable(level
);
196 LWP_InitQueue(&__usbstorage_waitq
);
198 ptr
= (u8
*)ROUNDDOWN32(((u32
)SYS_GetArena2Hi() - HEAP_SIZE
));
199 if((u32
)ptr
< (u32
)SYS_GetArena2Lo()) {
200 _CPU_ISR_Restore(level
);
204 SYS_SetArena2Hi(ptr
);
205 __lwp_heap_init(&__heap
, ptr
, HEAP_SIZE
, 32);
207 _CPU_ISR_Restore(level
);
211 static s32
__send_cbw(usbstorage_handle
*dev
, u8 lun
, u32 len
, u8 flags
, const u8
*cb
, u8 cbLen
)
213 s32 retval
= USBSTORAGE_OK
;
215 if(cbLen
== 0 || cbLen
> 16)
218 memset(dev
->buffer
, 0, CBW_SIZE
);
220 __stwbrx(dev
->buffer
, 0, CBW_SIGNATURE
);
221 __stwbrx(dev
->buffer
, 4, ++dev
->tag
);
222 __stwbrx(dev
->buffer
, 8, len
);
223 dev
->buffer
[12] = flags
;
224 dev
->buffer
[13] = lun
;
225 dev
->buffer
[14] = (cbLen
> 6 ? 10 : 6);
227 memcpy(dev
->buffer
+ 15, cb
, cbLen
);
229 if(dev
->suspended
== 1)
231 USB_ResumeDevice(dev
->usb_fd
);
235 retval
= __USB_BlkMsgTimeout(dev
, dev
->ep_out
, CBW_SIZE
, (void *)dev
->buffer
, USBSTORAGE_TIMEOUT
);
237 if(retval
== CBW_SIZE
) return USBSTORAGE_OK
;
238 else if(retval
> 0) return USBSTORAGE_ESHORTWRITE
;
243 static s32
__read_csw(usbstorage_handle
*dev
, u8
*status
, u32
*dataResidue
, u32 timeout
)
245 s32 retval
= USBSTORAGE_OK
;
246 u32 signature
, tag
, _dataResidue
, _status
;
248 memset(dev
->buffer
, 0, CSW_SIZE
);
250 retval
= __USB_BlkMsgTimeout(dev
, dev
->ep_in
, CSW_SIZE
, dev
->buffer
, timeout
);
251 if(retval
> 0 && retval
!= CSW_SIZE
) return USBSTORAGE_ESHORTREAD
;
252 else if(retval
< 0) return retval
;
254 signature
= __lwbrx(dev
->buffer
, 0);
255 tag
= __lwbrx(dev
->buffer
, 4);
256 _dataResidue
= __lwbrx(dev
->buffer
, 8);
257 _status
= dev
->buffer
[12];
259 if(signature
!= CSW_SIGNATURE
) return USBSTORAGE_ESIGNATURE
;
261 if(dataResidue
!= NULL
)
262 *dataResidue
= _dataResidue
;
266 if(tag
!= dev
->tag
) return USBSTORAGE_ETAG
;
268 return USBSTORAGE_OK
;
271 static s32
__cycle(usbstorage_handle
*dev
, u8 lun
, u8
*buffer
, u32 len
, u8
*cb
, u8 cbLen
, u8 write
, u8
*_status
, u32
*_dataResidue
)
273 s32 retval
= USBSTORAGE_OK
;
278 u8 ep
= write
? dev
->ep_out
: dev
->ep_in
;
279 s8 retries
= USBSTORAGE_CYCLE_RETRIES
+ 1;
281 if (dev
->usb_fd
>=0x20 || dev
->usb_fd
<-1)
282 max_size
=MAX_TRANSFER_SIZE_V5
;
284 max_size
=MAX_TRANSFER_SIZE_V0
;
286 if (len
==0) return 0;
288 LWP_MutexLock(dev
->lock
);
291 u8
*_buffer
= buffer
;
295 if(retval
== USBSTORAGE_ETIMEDOUT
)
298 retval
= __send_cbw(dev
, lun
, len
, (write
? CBW_OUT
:CBW_IN
), cb
, cbLen
);
301 while(_len
> 0 && retval
>= 0)
303 u32 thisLen
= _len
> max_size
? max_size
: _len
;
305 if ((u32
)_buffer
&0x1F || !((u32
)_buffer
&0x10000000)) {
306 if (write
) memcpy(dev
->buffer
, _buffer
, thisLen
);
307 retval
= __USB_BlkMsgTimeout(dev
, ep
, thisLen
, dev
->buffer
, USBSTORAGE_TIMEOUT
);
308 if (!write
&& retval
> 0)
309 memcpy(_buffer
, dev
->buffer
, retval
);
311 retval
= __USB_BlkMsgTimeout(dev
, ep
, thisLen
, _buffer
, USBSTORAGE_TIMEOUT
);
313 if (retval
== thisLen
) {
317 else if (retval
!= USBSTORAGE_ETIMEDOUT
)
318 retval
= USBSTORAGE_EDATARESIDUE
;
322 __read_csw(dev
, &status
, &dataResidue
, USBSTORAGE_TIMEOUT
);
325 if (__usbstorage_reset(dev
) == USBSTORAGE_ETIMEDOUT
)
326 retval
= USBSTORAGE_ETIMEDOUT
;
329 } while (retval
< 0 && retries
> 0);
331 LWP_MutexUnlock(dev
->lock
);
335 if(_dataResidue
!= NULL
)
336 *_dataResidue
= dataResidue
;
341 static s32
__usbstorage_clearerrors(usbstorage_handle
*dev
, u8 lun
)
345 u8 sense
[SCSI_SENSE_REPLY_SIZE
];
348 memset(cmd
, 0, sizeof(cmd
));
349 cmd
[0] = SCSI_TEST_UNIT_READY
;
351 retval
= __cycle(dev
, lun
, NULL
, 0, cmd
, 1, 0, &status
, NULL
);
352 if (retval
< 0) return retval
;
356 cmd
[0] = SCSI_REQUEST_SENSE
;
358 cmd
[4] = SCSI_SENSE_REPLY_SIZE
;
359 memset(sense
, 0, SCSI_SENSE_REPLY_SIZE
);
360 retval
= __cycle(dev
, lun
, sense
, SCSI_SENSE_REPLY_SIZE
, cmd
, 6, 0, NULL
, NULL
);
362 switch (sense
[2]&0xF) {
363 case SCSI_SENSE_NOT_READY
:
364 return USBSTORAGE_EINIT
;
365 case SCSI_SENSE_MEDIUM_ERROR
:
366 case SCSI_SENSE_HARDWARE_ERROR
:
367 return USBSTORAGE_ESENSE
;
375 static s32
__usbstorage_reset(usbstorage_handle
*dev
)
377 s32 retval
= __USB_CtrlMsgTimeout(dev
, (USB_CTRLTYPE_DIR_HOST2DEVICE
| USB_CTRLTYPE_TYPE_CLASS
| USB_CTRLTYPE_REC_INTERFACE
), USBSTORAGE_RESET
, 0, dev
->interface
, 0, NULL
);
379 if (retval
<0 && retval
!= -7004)
382 // don't clear the endpoints, it makes too many devices die
384 //retval = USB_ClearHalt(dev->usb_fd, dev->ep_in);
385 //if (retval < 0) goto end;
386 //retval = USB_ClearHalt(dev->usb_fd, dev->ep_out);
394 s32
USBStorage_Open(usbstorage_handle
*dev
, s32 device_id
, u16 vid
, u16 pid
)
399 u32 iConf
, iInterface
, iEp
;
401 usb_configurationdesc
*ucd
;
402 usb_interfacedesc
*uid
;
403 usb_endpointdesc
*ued
;
405 max_lun
= __lwp_heap_allocate(&__heap
, 1);
409 memset(dev
, 0, sizeof(*dev
));
412 dev
->tag
= TAG_START
;
414 if (LWP_MutexInit(&dev
->lock
, false) < 0)
415 goto free_and_return
;
417 if (SYS_CreateAlarm(&dev
->alarm
) < 0)
418 goto free_and_return
;
420 retval
= USB_OpenDevice(device_id
, vid
, pid
, &dev
->usb_fd
);
422 goto free_and_return
;
424 retval
= USB_GetDescriptors(dev
->usb_fd
, &udd
);
426 goto free_and_return
;
428 for (iConf
= 0; iConf
< udd
.bNumConfigurations
; iConf
++) {
429 ucd
= &udd
.configurations
[iConf
];
430 for (iInterface
= 0; iInterface
< ucd
->bNumInterfaces
; iInterface
++) {
431 uid
= &ucd
->interfaces
[iInterface
];
432 if (uid
->bInterfaceClass
== USB_CLASS_MASS_STORAGE
&&
433 uid
->bInterfaceSubClass
== MASS_STORAGE_SCSI_COMMANDS
&&
434 uid
->bInterfaceProtocol
== MASS_STORAGE_BULK_ONLY
)
436 if (uid
->bNumEndpoints
< 2)
439 dev
->ep_in
= dev
->ep_out
= 0;
440 for (iEp
= 0; iEp
< uid
->bNumEndpoints
; iEp
++) {
441 ued
= &uid
->endpoints
[iEp
];
442 if (ued
->bmAttributes
!= USB_ENDPOINT_BULK
)
445 if (ued
->bEndpointAddress
& USB_ENDPOINT_IN
)
446 dev
->ep_in
= ued
->bEndpointAddress
;
448 dev
->ep_out
= ued
->bEndpointAddress
;
451 if (dev
->ep_in
!= 0 && dev
->ep_out
!= 0) {
452 dev
->configuration
= ucd
->bConfigurationValue
;
453 dev
->interface
= uid
->bInterfaceNumber
;
454 dev
->altInterface
= uid
->bAlternateSetting
;
461 USB_FreeDescriptors(&udd
);
462 retval
= USBSTORAGE_ENOINTERFACE
;
463 goto free_and_return
;
466 USB_FreeDescriptors(&udd
);
468 retval
= USBSTORAGE_EINIT
;
469 // some devices return an error, ignore it
470 USB_GetConfiguration(dev
->usb_fd
, &conf
);
472 if (conf
!= dev
->configuration
&& USB_SetConfiguration(dev
->usb_fd
, dev
->configuration
) < 0)
473 goto free_and_return
;
475 if (dev
->altInterface
!=0 && USB_SetAlternativeInterface(dev
->usb_fd
, dev
->interface
, dev
->altInterface
) < 0)
476 goto free_and_return
;
480 retval
= USBStorage_Reset(dev
);
482 goto free_and_return
;
484 LWP_MutexLock(dev
->lock
);
485 retval
= __USB_CtrlMsgTimeout(dev
, (USB_CTRLTYPE_DIR_DEVICE2HOST
| USB_CTRLTYPE_TYPE_CLASS
| USB_CTRLTYPE_REC_INTERFACE
), USBSTORAGE_GET_MAX_LUN
, 0, dev
->interface
, 1, max_lun
);
486 LWP_MutexUnlock(dev
->lock
);
491 dev
->max_lun
= *max_lun
+ 1;
493 if (retval
== USBSTORAGE_ETIMEDOUT
)
494 goto free_and_return
;
496 retval
= USBSTORAGE_OK
;
497 dev
->sector_size
= (u32
*) calloc(dev
->max_lun
, sizeof(u32
));
498 if(!dev
->sector_size
) {
500 goto free_and_return
;
503 /* taken from linux usbstorage module (drivers/usb/storage/transport.c)
505 * Some devices (i.e. Iomega Zip100) need this -- apparently
506 * the bulk pipes get STALLed when the GetMaxLUN request is
507 * processed. This is, in theory, harmless to all other devices
508 * (regardless of if they stall or not).
510 * 8/9/10: If anyone wants to actually use a Zip100, they can add this back.
511 * But for now, it seems to be breaking things more than it is helping.
513 //USB_ClearHalt(dev->usb_fd, dev->ep_in);
514 //USB_ClearHalt(dev->usb_fd, dev->ep_out);
517 dev
->buffer
= __lwp_heap_allocate(&__heap
, MAX_TRANSFER_SIZE_V5
);
522 USB_DeviceRemovalNotifyAsync(dev
->usb_fd
,__usb_deviceremoved_cb
,dev
);
523 retval
= USBSTORAGE_OK
;
528 __lwp_heap_free(&__heap
, max_lun
);
531 USBStorage_Close(dev
);
538 s32
USBStorage_Close(usbstorage_handle
*dev
)
545 if (dev
->usb_fd
!= -1)
546 USB_CloseDevice(&dev
->usb_fd
);
548 LWP_MutexDestroy(dev
->lock
);
549 SYS_RemoveAlarm(dev
->alarm
);
552 free(dev
->sector_size
);
555 __lwp_heap_free(&__heap
, dev
->buffer
);
557 memset(dev
, 0, sizeof(*dev
));
562 s32
USBStorage_Reset(usbstorage_handle
*dev
)
566 LWP_MutexLock(dev
->lock
);
567 retval
= __usbstorage_reset(dev
);
568 LWP_MutexUnlock(dev
->lock
);
573 s32
USBStorage_GetMaxLUN(usbstorage_handle
*dev
)
578 s32
USBStorage_MountLUN(usbstorage_handle
*dev
, u8 lun
)
582 if(lun
>= dev
->max_lun
)
585 retval
= __usbstorage_clearerrors(dev
, lun
);
589 retval
= USBStorage_ReadCapacity(dev
, lun
, &dev
->sector_size
[lun
], NULL
);
593 s32
USBStorage_ReadCapacity(usbstorage_handle
*dev
, u8 lun
, u32
*sector_size
, u32
*n_sectors
)
596 u8 cmd
[10] = {SCSI_READ_CAPACITY
, lun
<<5};
599 retval
= __cycle(dev
, lun
, response
, sizeof(response
), cmd
, sizeof(cmd
), 0, NULL
, NULL
);
602 if(n_sectors
!= NULL
)
603 memcpy(n_sectors
, response
, 4);
604 if(sector_size
!= NULL
)
605 memcpy(sector_size
, response
+ 4, 4);
606 retval
= USBSTORAGE_OK
;
612 /* lo_ej = load/eject, controls the tray
613 * start = start(1) or stop(0) the motor (or eject(0), load(1))
614 * imm = return before the command has completed
615 * it might be a good idea to call this before STM_ShutdownToStandby() so the USB HDD doesn't stay on
617 s32
USBStorage_StartStop(usbstorage_handle
*dev
, u8 lun
, u8 lo_ej
, u8 start
, u8 imm
)
620 s32 retval
= USBSTORAGE_OK
;
623 (lun
<< 5) | (imm
&1),
626 ((lo_ej
&1)<<1) | (start
&1),
630 if(lun
>= dev
->max_lun
)
633 LWP_MutexLock(dev
->lock
);
635 retval
= __send_cbw(dev
, lun
, 0, CBW_IN
, cmd
, sizeof(cmd
));
637 // if imm==0, wait up to 10secs for spinup to finish
639 retval
= __read_csw(dev
, &status
, NULL
, (imm
? USBSTORAGE_TIMEOUT
: 10));
641 LWP_MutexUnlock(dev
->lock
);
643 if(retval
>=0 && status
!= 0)
644 retval
= USBSTORAGE_ESTATUS
;
649 s32
USBStorage_Read(usbstorage_handle
*dev
, u8 lun
, u32 sector
, u16 n_sectors
, u8
*buffer
)
666 if(lun
>= dev
->max_lun
|| dev
->sector_size
[lun
] == 0)
669 retval
= __usbstorage_clearerrors(dev
, lun
);
671 // it's gone to sleep, try and wake it up
672 // don't check the returned value, device may not support this command
673 if (retval
==USBSTORAGE_EINIT
)
674 retval
= USBStorage_StartStop(dev
, lun
, 0, 1, 0);
678 retval
= __cycle(dev
, lun
, buffer
, n_sectors
* dev
->sector_size
[lun
], cmd
, sizeof(cmd
), 0, &status
, NULL
);
679 if(retval
> 0 && status
!= 0)
680 retval
= USBSTORAGE_ESTATUS
;
685 s32
USBStorage_Write(usbstorage_handle
*dev
, u8 lun
, u32 sector
, u16 n_sectors
, const u8
*buffer
)
702 if(lun
>= dev
->max_lun
|| dev
->sector_size
[lun
] == 0)
705 retval
= __usbstorage_clearerrors(dev
, lun
);
706 if (retval
==USBSTORAGE_EINIT
)
707 retval
= USBStorage_StartStop(dev
, lun
, 0, 1, 0);
711 retval
= __cycle(dev
, lun
, (u8
*)buffer
, n_sectors
* dev
->sector_size
[lun
], cmd
, sizeof(cmd
), 1, &status
, NULL
);
712 if(retval
> 0 && status
!= 0)
713 retval
= USBSTORAGE_ESTATUS
;
717 s32
USBStorage_Suspend(usbstorage_handle
*dev
)
719 if(dev
->suspended
== 1)
720 return USBSTORAGE_OK
;
722 USB_SuspendDevice(dev
->usb_fd
);
725 return USBSTORAGE_OK
;
729 The following is for implementing a DISC_INTERFACE
733 static bool __usbstorage_Startup(void)
738 if(USB_Initialize() < 0 || USBStorage_Initialize() < 0)
744 static bool __usbstorage_IsInserted(void)
746 usb_device_entry
*buffer
;
759 buffer
= (usb_device_entry
*)__lwp_heap_allocate(&__heap
, DEVLIST_MAXSIZE
* sizeof(usb_device_entry
));
763 memset(buffer
, 0, DEVLIST_MAXSIZE
* sizeof(usb_device_entry
));
765 if (USB_GetDeviceList(buffer
, DEVLIST_MAXSIZE
, USB_CLASS_MASS_STORAGE
, &device_count
) < 0)
767 if (__vid
!= 0 || __pid
!= 0)
768 USBStorage_Close(&__usbfd
);
770 __lwp_heap_free(&__heap
, buffer
);
776 if (__vid
!= 0 || __pid
!= 0) {
777 for(i
= 0; i
< device_count
; i
++) {
780 if(vid
!= 0 || pid
!= 0) {
781 if((vid
== __vid
) && (pid
== __pid
)) {
783 __lwp_heap_free(&__heap
,buffer
);
784 usleep(50); // I don't know why I have to wait but it's needed
789 USBStorage_Close(&__usbfd
);
790 __lwp_heap_free(&__heap
,buffer
);
794 for (i
= 0; i
< device_count
; i
++) {
797 if (vid
== 0 || pid
== 0)
800 if (USBStorage_Open(&__usbfd
, buffer
[i
].device_id
, vid
, pid
) < 0)
803 maxLun
= USBStorage_GetMaxLUN(&__usbfd
);
804 for (j
= 0; j
< maxLun
; j
++) {
805 USBStorage_StartStop(&__usbfd
, j
, 0, 1, 0);
806 retval
= USBStorage_MountLUN(&__usbfd
, j
);
808 if (retval
== USBSTORAGE_ETIMEDOUT
)
824 USBStorage_Close(&__usbfd
);
826 __lwp_heap_free(&__heap
, buffer
);
830 static bool __usbstorage_ReadSectors(u32 sector
, u32 numSectors
, void *buffer
)
837 retval
= USBStorage_Read(&__usbfd
, __lun
, sector
, numSectors
, buffer
);
842 static bool __usbstorage_WriteSectors(u32 sector
, u32 numSectors
, const void *buffer
)
849 retval
= USBStorage_Write(&__usbfd
, __lun
, sector
, numSectors
, buffer
);
854 static bool __usbstorage_ClearStatus(void)
859 static bool __usbstorage_Shutdown(void)
861 if (__vid
!= 0 || __pid
!= 0)
862 USBStorage_Close(&__usbfd
);
867 DISC_INTERFACE __io_usbstorage
= {
869 FEATURE_MEDIUM_CANREAD
| FEATURE_MEDIUM_CANWRITE
| FEATURE_WII_USB
,
870 (FN_MEDIUM_STARTUP
)&__usbstorage_Startup
,
871 (FN_MEDIUM_ISINSERTED
)&__usbstorage_IsInserted
,
872 (FN_MEDIUM_READSECTORS
)&__usbstorage_ReadSectors
,
873 (FN_MEDIUM_WRITESECTORS
)&__usbstorage_WriteSectors
,
874 (FN_MEDIUM_CLEARSTATUS
)&__usbstorage_ClearStatus
,
875 (FN_MEDIUM_SHUTDOWN
)&__usbstorage_Shutdown