usb - wake up drive before attempting read/write/mount (tueidj)
[libogc.git] / libogc / usbstorage.c
blob06e131f67c5277be9370d1da2f480567605a739a
1 /*-------------------------------------------------------------
3 usbstorage.c -- Bulk-only USB mass storage support
5 Copyright (C) 2008
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
25 distribution.
27 -------------------------------------------------------------*/
28 #if defined(HW_RVL)
30 #include <gccore.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/time.h>
35 #include <errno.h>
36 #include <lwp_heap.h>
37 #include <malloc.h>
39 #include "asm.h"
40 #include "processor.h"
41 #include "disc_io.h"
43 #define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f)
45 #define HEAP_SIZE (18*1024)
46 #define TAG_START 0x0BADC0DE
48 #define CBW_SIZE 31
49 #define CBW_SIGNATURE 0x43425355
50 #define CBW_IN (1 << 7)
51 #define CBW_OUT 0
53 #define CSW_SIZE 13
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
91 as used by libfat
94 static usbstorage_handle __usbfd;
95 static u8 __lun = 0;
96 static bool __mounted = false;
97 static u16 __vid = 0;
98 static u16 __pid = 0;
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);
115 return 0;
118 static s32 __usb_deviceremoved_cb(s32 retval,void *arg)
120 __mounted = false;
121 return 0;
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)
133 struct timespec ts;
135 ts.tv_sec = secs;
136 ts.tv_nsec = 0;
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)
142 u32 level;
143 s32 retval;
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);
152 do {
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);
159 if (retval<0)
160 USB_ClearHalt(dev->usb_fd, bEndpoint);
162 return retval;
165 static s32 __USB_CtrlMsgTimeout(usbstorage_handle *dev, u8 bmRequestType, u8 bmRequest, u16 wValue, u16 wIndex, u16 wLength, void *rpData)
167 u32 level;
168 s32 retval;
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);
177 do {
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);
184 return retval;
187 s32 USBStorage_Initialize()
189 u8 *ptr;
190 u32 level;
192 if(__inited)
193 return IPC_OK;
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);
201 return IPC_ENOMEM;
204 SYS_SetArena2Hi(ptr);
205 __lwp_heap_init(&__heap, ptr, HEAP_SIZE, 32);
206 __inited = true;
207 _CPU_ISR_Restore(level);
208 return IPC_OK;
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)
216 return IPC_EINVAL;
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);
232 dev->suspended = 0;
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;
240 return retval;
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;
263 if(status != NULL)
264 *status = _status;
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;
275 u8 status=0;
276 u32 dataResidue = 0;
277 u16 max_size;
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;
283 else
284 max_size=MAX_TRANSFER_SIZE_V0;
286 if (len==0) return 0;
288 LWP_MutexLock(dev->lock);
291 u8 *_buffer = buffer;
292 u32 _len = len;
293 retries--;
295 if(retval == USBSTORAGE_ETIMEDOUT)
296 break;
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);
310 } else
311 retval = __USB_BlkMsgTimeout(dev, ep, thisLen, _buffer, USBSTORAGE_TIMEOUT);
313 if (retval == thisLen) {
314 _len -= retval;
315 _buffer += retval;
317 else if (retval != USBSTORAGE_ETIMEDOUT)
318 retval = USBSTORAGE_EDATARESIDUE;
321 if (retval >= 0)
322 __read_csw(dev, &status, &dataResidue, USBSTORAGE_TIMEOUT);
324 if (retval < 0) {
325 if (__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
326 retval = USBSTORAGE_ETIMEDOUT;
329 } while (retval < 0 && retries > 0);
331 LWP_MutexUnlock(dev->lock);
333 if(_status != NULL)
334 *_status = status;
335 if(_dataResidue != NULL)
336 *_dataResidue = dataResidue;
338 return retval;
341 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun)
343 s32 retval;
344 u8 cmd[6];
345 u8 sense[SCSI_SENSE_REPLY_SIZE];
346 u8 status = 0;
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;
354 if (status)
356 cmd[0] = SCSI_REQUEST_SENSE;
357 cmd[1] = lun << 5;
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);
361 if (retval>=0) {
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;
372 return retval;
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)
380 goto end;
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);
388 usleep(100);
390 end:
391 return retval;
394 s32 USBStorage_Open(usbstorage_handle *dev, s32 device_id, u16 vid, u16 pid)
396 s32 retval = -1;
397 u8 conf;
398 u8 *max_lun;
399 u32 iConf, iInterface, iEp;
400 usb_devdesc udd;
401 usb_configurationdesc *ucd;
402 usb_interfacedesc *uid;
403 usb_endpointdesc *ued;
405 max_lun = __lwp_heap_allocate(&__heap, 1);
406 if (!max_lun)
407 return IPC_ENOMEM;
409 memset(dev, 0, sizeof(*dev));
410 dev->usb_fd = -1;
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);
421 if (retval < 0)
422 goto free_and_return;
424 retval = USB_GetDescriptors(dev->usb_fd, &udd);
425 if (retval < 0)
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)
437 continue;
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)
443 continue;
445 if (ued->bEndpointAddress & USB_ENDPOINT_IN)
446 dev->ep_in = ued->bEndpointAddress;
447 else
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;
455 goto found;
461 USB_FreeDescriptors(&udd);
462 retval = USBSTORAGE_ENOINTERFACE;
463 goto free_and_return;
465 found:
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;
478 dev->suspended = 0;
480 retval = USBStorage_Reset(dev);
481 if (retval < 0)
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);
488 if (retval < 0)
489 dev->max_lun = 1;
490 else
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) {
499 retval = IPC_ENOMEM;
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);
516 if(!dev->buffer)
517 dev->buffer = __lwp_heap_allocate(&__heap, MAX_TRANSFER_SIZE_V5);
519 if(!dev->buffer) {
520 retval = IPC_ENOMEM;
521 } else {
522 USB_DeviceRemovalNotifyAsync(dev->usb_fd,__usb_deviceremoved_cb,dev);
523 retval = USBSTORAGE_OK;
526 free_and_return:
527 if (max_lun)
528 __lwp_heap_free(&__heap, max_lun);
530 if (retval < 0) {
531 USBStorage_Close(dev);
532 return retval;
535 return 0;
538 s32 USBStorage_Close(usbstorage_handle *dev)
540 __mounted = false;
541 __lun = 0;
542 __vid = 0;
543 __pid = 0;
545 if (dev->usb_fd != -1)
546 USB_CloseDevice(&dev->usb_fd);
548 LWP_MutexDestroy(dev->lock);
549 SYS_RemoveAlarm(dev->alarm);
551 if(dev->sector_size)
552 free(dev->sector_size);
554 if (dev->buffer)
555 __lwp_heap_free(&__heap, dev->buffer);
557 memset(dev, 0, sizeof(*dev));
558 dev->usb_fd = -1;
559 return 0;
562 s32 USBStorage_Reset(usbstorage_handle *dev)
564 s32 retval;
566 LWP_MutexLock(dev->lock);
567 retval = __usbstorage_reset(dev);
568 LWP_MutexUnlock(dev->lock);
570 return retval;
573 s32 USBStorage_GetMaxLUN(usbstorage_handle *dev)
575 return dev->max_lun;
578 s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun)
580 s32 retval;
582 if(lun >= dev->max_lun)
583 return IPC_EINVAL;
585 retval = __usbstorage_clearerrors(dev, lun);
586 if (retval<0)
587 return retval;
589 retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], NULL);
590 return retval;
593 s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors)
595 s32 retval;
596 u8 cmd[10] = {SCSI_READ_CAPACITY, lun<<5};
597 u8 response[8];
599 retval = __cycle(dev, lun, response, sizeof(response), cmd, sizeof(cmd), 0, NULL, NULL);
600 if(retval >= 0)
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;
609 return retval;
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)
619 u8 status = 0;
620 s32 retval = USBSTORAGE_OK;
621 u8 cmd[] = {
622 SCSI_START_STOP,
623 (lun << 5) | (imm&1),
626 ((lo_ej&1)<<1) | (start&1),
630 if(lun >= dev->max_lun)
631 return IPC_EINVAL;
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
638 if (retval >= 0)
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;
646 return retval;
649 s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer)
651 u8 status = 0;
652 s32 retval;
653 u8 cmd[] = {
654 SCSI_READ_10,
655 lun << 5,
656 sector >> 24,
657 sector >> 16,
658 sector >> 8,
659 sector,
661 n_sectors >> 8,
662 n_sectors,
666 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
667 return IPC_EINVAL;
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);
675 else if (retval<0)
676 return retval;
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;
682 return retval;
685 s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer)
687 u8 status = 0;
688 s32 retval;
689 u8 cmd[] = {
690 SCSI_WRITE_10,
691 lun << 5,
692 sector >> 24,
693 sector >> 16,
694 sector >> 8,
695 sector,
697 n_sectors >> 8,
698 n_sectors,
702 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
703 return IPC_EINVAL;
705 retval = __usbstorage_clearerrors(dev, lun);
706 if (retval==USBSTORAGE_EINIT)
707 retval = USBStorage_StartStop(dev, lun, 0, 1, 0);
708 else if (retval<0)
709 return retval;
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;
714 return retval;
717 s32 USBStorage_Suspend(usbstorage_handle *dev)
719 if(dev->suspended == 1)
720 return USBSTORAGE_OK;
722 USB_SuspendDevice(dev->usb_fd);
723 dev->suspended = 1;
725 return USBSTORAGE_OK;
729 The following is for implementing a DISC_INTERFACE
730 as used by libfat
733 static bool __usbstorage_Startup(void)
735 if(__inited)
736 return true;
738 if(USB_Initialize() < 0 || USBStorage_Initialize() < 0)
739 return false;
741 return true;
744 static bool __usbstorage_IsInserted(void)
746 usb_device_entry *buffer;
747 u8 device_count;
748 u8 i, j;
749 u16 vid, pid;
750 s32 maxLun;
751 s32 retval;
753 if(__mounted)
754 return true;
756 if(!__inited)
757 return false;
759 buffer = (usb_device_entry*)__lwp_heap_allocate(&__heap, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
760 if (!buffer)
761 return false;
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);
771 return false;
774 usleep(100);
776 if (__vid != 0 || __pid != 0) {
777 for(i = 0; i < device_count; i++) {
778 vid = buffer[i].vid;
779 pid = buffer[i].pid;
780 if(vid != 0 || pid != 0) {
781 if((vid == __vid) && (pid == __pid)) {
782 __mounted = true;
783 __lwp_heap_free(&__heap,buffer);
784 usleep(50); // I don't know why I have to wait but it's needed
785 return true;
789 USBStorage_Close(&__usbfd);
790 __lwp_heap_free(&__heap,buffer);
791 return false;
794 for (i = 0; i < device_count; i++) {
795 vid = buffer[i].vid;
796 pid = buffer[i].pid;
797 if (vid == 0 || pid == 0)
798 continue;
800 if (USBStorage_Open(&__usbfd, buffer[i].device_id, vid, pid) < 0)
801 continue;
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)
809 break;
811 if (retval < 0)
812 continue;
814 __mounted = true;
815 __lun = j;
816 __vid = vid;
817 __pid = pid;
818 break;
821 if (__mounted)
822 break;
824 USBStorage_Close(&__usbfd);
826 __lwp_heap_free(&__heap, buffer);
827 return __mounted;
830 static bool __usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer)
832 s32 retval;
834 if (!__mounted)
835 return false;
837 retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer);
839 return retval >= 0;
842 static bool __usbstorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer)
844 s32 retval;
846 if (!__mounted)
847 return false;
849 retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer);
851 return retval >= 0;
854 static bool __usbstorage_ClearStatus(void)
856 return true;
859 static bool __usbstorage_Shutdown(void)
861 if (__vid != 0 || __pid != 0)
862 USBStorage_Close(&__usbfd);
864 return true;
867 DISC_INTERFACE __io_usbstorage = {
868 DEVICE_TYPE_WII_USB,
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
878 #endif /* HW_RVL */