usbstorage - don't kill the 0 handle! (dhewg)
[libogc.git] / libogc / usbstorage.c
blob670e7ee6e6b41f7aa84cf980c9cb15ab07580bba
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 (32*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_READ_CAPACITY 0x25
59 #define SCSI_READ_10 0x28
60 #define SCSI_WRITE_10 0x2A
62 #define SCSI_SENSE_REPLY_SIZE 18
63 #define SCSI_SENSE_NOT_READY 0x02
64 #define SCSI_SENSE_MEDIUM_ERROR 0x03
65 #define SCSI_SENSE_HARDWARE_ERROR 0x04
67 #define USB_CLASS_MASS_STORAGE 0x08
68 #define MASS_STORAGE_SCSI_COMMANDS 0x06
69 #define MASS_STORAGE_BULK_ONLY 0x50
71 #define USBSTORAGE_GET_MAX_LUN 0xFE
72 #define USBSTORAGE_RESET 0xFF
74 #define USB_ENDPOINT_BULK 0x02
76 #define USBSTORAGE_CYCLE_RETRIES 3
78 #define MAX_TRANSFER_SIZE 4096
80 #define DEVLIST_MAXSIZE 8
82 static heap_cntrl __heap;
83 static u8 __heap_created = 0;
84 static lwpq_t __usbstorage_waitq = 0;
87 The following is for implementing a DISC_INTERFACE
88 as used by libfat
91 static usbstorage_handle __usbfd;
92 static u8 __lun = 0;
93 static bool __mounted = false;
94 static u16 __vid = 0;
95 static u16 __pid = 0;
97 static s32 __usbstorage_reset(usbstorage_handle *dev);
98 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun);
100 /* XXX: this is a *really* dirty and ugly way to send a bulkmessage with a timeout
101 * but there's currently no other known way of doing this and it's in my humble
102 * opinion still better than having a function blocking forever while waiting
103 * for the USB data/IOS reply..
106 static s32 __usb_blkmsg_cb(s32 retval, void *dummy)
108 usbstorage_handle *dev = (usbstorage_handle *)dummy;
109 dev->retval = retval;
110 SYS_CancelAlarm(dev->alarm);
111 LWP_ThreadBroadcast(__usbstorage_waitq);
112 return 0;
115 static s32 __usb_deviceremoved_cb(s32 retval,void *arg)
117 return 0;
120 static void __usb_timeouthandler(syswd_t alarm,void *cbarg)
122 usbstorage_handle *dev = (usbstorage_handle*)cbarg;
123 dev->retval = USBSTORAGE_ETIMEDOUT;
124 LWP_ThreadBroadcast(__usbstorage_waitq);
127 static void __usb_settimeout(usbstorage_handle *dev)
129 struct timespec ts;
131 ts.tv_sec = 2;
132 ts.tv_nsec = 0;
133 SYS_SetAlarm(dev->alarm,&ts,__usb_timeouthandler,dev);
136 static s32 __USB_BlkMsgTimeout(usbstorage_handle *dev, u8 bEndpoint, u16 wLength, void *rpData)
138 u32 level;
139 s32 retval;
141 dev->retval = USBSTORAGE_PROCESSING;
142 retval = USB_WriteBlkMsgAsync(dev->usb_fd, bEndpoint, wLength, rpData, __usb_blkmsg_cb, (void *)dev);
143 if(retval < 0) return retval;
145 __usb_settimeout(dev);
147 _CPU_ISR_Disable(level);
148 do {
149 retval = dev->retval;
150 if(retval!=USBSTORAGE_PROCESSING) break;
151 else LWP_ThreadSleep(__usbstorage_waitq);
152 } while(retval==USBSTORAGE_PROCESSING);
153 _CPU_ISR_Restore(level);
155 if(retval==USBSTORAGE_ETIMEDOUT) USBStorage_Close(dev);
156 return retval;
159 static s32 __USB_CtrlMsgTimeout(usbstorage_handle *dev, u8 bmRequestType, u8 bmRequest, u16 wValue, u16 wIndex, u16 wLength, void *rpData)
161 u32 level;
162 s32 retval;
163 struct timespec ts;
165 ts.tv_sec = 2;
166 ts.tv_nsec = 0;
168 dev->retval = USBSTORAGE_PROCESSING;
169 retval = USB_WriteCtrlMsgAsync(dev->usb_fd, bmRequestType, bmRequest, wValue, wIndex, wLength, rpData, __usb_blkmsg_cb, (void *)dev);
170 if(retval < 0) return retval;
172 __usb_settimeout(dev);
174 _CPU_ISR_Disable(level);
175 do {
176 retval = dev->retval;
177 if(retval!=USBSTORAGE_PROCESSING) break;
178 else LWP_ThreadSleep(__usbstorage_waitq);
179 } while(retval==USBSTORAGE_PROCESSING);
180 _CPU_ISR_Restore(level);
182 if(retval==USBSTORAGE_ETIMEDOUT) USBStorage_Close(dev);
183 return retval;
186 s32 USBStorage_Initialize()
188 u8 *ptr;
189 u32 level;
191 _CPU_ISR_Disable(level);
192 if(__heap_created != 0) {
193 _CPU_ISR_Restore(level);
194 return IPC_OK;
197 LWP_InitQueue(&__usbstorage_waitq);
199 ptr = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - HEAP_SIZE));
200 if((u32)ptr < (u32)SYS_GetArena2Lo()) {
201 _CPU_ISR_Restore(level);
202 return IPC_ENOMEM;
205 SYS_SetArena2Hi(ptr);
207 __lwp_heap_init(&__heap, ptr, HEAP_SIZE, 32);
208 __heap_created = 1;
209 _CPU_ISR_Restore(level);
211 return IPC_OK;
215 static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u8 *cb, u8 cbLen)
217 s32 retval = USBSTORAGE_OK;
219 if(cbLen == 0 || cbLen > 16)
220 return IPC_EINVAL;
222 memset(dev->buffer, 0, CBW_SIZE);
224 __stwbrx(dev->buffer, 0, CBW_SIGNATURE);
225 __stwbrx(dev->buffer, 4, dev->tag);
226 __stwbrx(dev->buffer, 8, len);
227 dev->buffer[12] = flags;
228 dev->buffer[13] = lun;
229 dev->buffer[14] = (cbLen > 6 ? 10 : 6);
231 memcpy(dev->buffer + 15, cb, cbLen);
233 if(dev->suspended == 1)
235 USB_ResumeDevice(dev->usb_fd);
236 dev->suspended = 0;
239 retval = __USB_BlkMsgTimeout(dev, dev->ep_out, CBW_SIZE, (void *)dev->buffer);
241 if(retval == CBW_SIZE) return USBSTORAGE_OK;
242 else if(retval > 0) return USBSTORAGE_ESHORTWRITE;
244 return retval;
247 static s32 __read_csw(usbstorage_handle *dev, u8 *status, u32 *dataResidue)
249 s32 retval = USBSTORAGE_OK;
250 u32 signature, tag, _dataResidue, _status;
252 memset(dev->buffer, 0, CSW_SIZE);
254 retval = __USB_BlkMsgTimeout(dev, dev->ep_in, CSW_SIZE, dev->buffer);
255 if(retval > 0 && retval != CSW_SIZE) return USBSTORAGE_ESHORTREAD;
256 else if(retval < 0) return retval;
258 signature = __lwbrx(dev->buffer, 0);
259 tag = __lwbrx(dev->buffer, 4);
260 _dataResidue = __lwbrx(dev->buffer, 8);
261 _status = dev->buffer[12];
263 if(signature != CSW_SIGNATURE) return USBSTORAGE_ESIGNATURE;
265 if(dataResidue != NULL)
266 *dataResidue = _dataResidue;
267 if(status != NULL)
268 *status = _status;
270 if(tag != dev->tag) return USBSTORAGE_ETAG;
271 dev->tag++;
273 return USBSTORAGE_OK;
276 static s32 __cycle(usbstorage_handle *dev, u8 lun, u8 *buffer, u32 len, u8 *cb, u8 cbLen, u8 write, u8 *_status, u32 *_dataResidue)
278 s32 retval = USBSTORAGE_OK;
280 u8 status = 0;
281 u32 dataResidue = 0;
282 u32 thisLen;
284 s8 retries = USBSTORAGE_CYCLE_RETRIES + 1;
286 LWP_MutexLock(dev->lock);
289 retries--;
291 if(retval == USBSTORAGE_ETIMEDOUT)
292 break;
294 if(write)
296 retval = __send_cbw(dev, lun, len, CBW_OUT, cb, cbLen);
297 if(retval == USBSTORAGE_ETIMEDOUT)
298 break;
299 if(retval < 0)
301 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
302 retval = USBSTORAGE_ETIMEDOUT;
303 continue;
305 while(len > 0)
307 thisLen = len > MAX_TRANSFER_SIZE ? MAX_TRANSFER_SIZE : len;
308 memset(dev->buffer, 0, MAX_TRANSFER_SIZE);
309 memcpy(dev->buffer, buffer, thisLen);
310 retval = __USB_BlkMsgTimeout(dev, dev->ep_out, thisLen, dev->buffer);
312 if(retval == USBSTORAGE_ETIMEDOUT)
313 break;
315 if(retval < 0)
317 retval = USBSTORAGE_EDATARESIDUE;
318 break;
321 if(retval != thisLen && len > 0)
323 retval = USBSTORAGE_EDATARESIDUE;
324 break;
326 len -= retval;
327 buffer += retval;
330 if(retval < 0)
332 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
333 retval = USBSTORAGE_ETIMEDOUT;
334 continue;
337 else
339 retval = __send_cbw(dev, lun, len, CBW_IN, cb, cbLen);
341 if(retval == USBSTORAGE_ETIMEDOUT)
342 break;
344 if(retval < 0)
346 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
347 retval = USBSTORAGE_ETIMEDOUT;
348 continue;
350 while(len > 0)
352 thisLen = len > MAX_TRANSFER_SIZE ? MAX_TRANSFER_SIZE : len;
353 retval = __USB_BlkMsgTimeout(dev, dev->ep_in, thisLen, dev->buffer);
354 if(retval < 0)
355 break;
357 memcpy(buffer, dev->buffer, retval);
358 len -= retval;
359 buffer += retval;
361 if(retval != thisLen)
362 break;
365 if(retval < 0)
367 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
368 retval = USBSTORAGE_ETIMEDOUT;
369 continue;
373 retval = __read_csw(dev, &status, &dataResidue);
375 if(retval == USBSTORAGE_ETIMEDOUT)
376 break;
378 if(retval < 0)
380 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
381 retval = USBSTORAGE_ETIMEDOUT;
382 continue;
385 retval = USBSTORAGE_OK;
386 } while(retval < 0 && retries > 0);
388 if(retval < 0 && retval != USBSTORAGE_ETIMEDOUT)
390 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
391 retval = USBSTORAGE_ETIMEDOUT;
393 LWP_MutexUnlock(dev->lock);
396 if(_status != NULL)
397 *_status = status;
398 if(_dataResidue != NULL)
399 *_dataResidue = dataResidue;
401 return retval;
404 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun)
406 s32 retval;
407 u8 cmd[16];
408 u8 sense[SCSI_SENSE_REPLY_SIZE];
409 u8 status = 0;
411 memset(cmd, 0, sizeof(cmd));
412 cmd[0] = SCSI_TEST_UNIT_READY;
414 retval = __cycle(dev, lun, NULL, 0, cmd, 1, 0, &status, NULL);
415 if(retval < 0) return retval;
417 if(status != 0)
419 cmd[0] = SCSI_REQUEST_SENSE;
420 cmd[1] = lun << 5;
421 cmd[4] = SCSI_SENSE_REPLY_SIZE;
422 cmd[5] = 0;
423 memset(sense, 0, SCSI_SENSE_REPLY_SIZE);
424 retval = __cycle(dev, lun, sense, SCSI_SENSE_REPLY_SIZE, cmd, 6, 0, NULL, NULL);
425 if(retval < 0) return retval;
427 status = sense[2] & 0x0F;
428 if(status == SCSI_SENSE_NOT_READY || status == SCSI_SENSE_MEDIUM_ERROR || status == SCSI_SENSE_HARDWARE_ERROR) return USBSTORAGE_ESENSE;
431 return retval;
434 static s32 __usbstorage_reset(usbstorage_handle *dev)
436 s32 retval;
438 if(dev->suspended == 1)
440 USB_ResumeDevice(dev->usb_fd);
441 dev->suspended = 0;
444 retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_RESET, 0, dev->interface, 0, NULL);
446 /* FIXME?: some devices return -7004 here which definitely violates the usb ms protocol but they still seem to be working... */
447 if(retval < 0 && retval != -7004)
448 goto end;
450 /* gives device enough time to process the reset */
451 usleep(60);
453 retval = USB_ClearHalt(dev->usb_fd, dev->ep_in);
454 if(retval < 0)
455 goto end;
456 retval = USB_ClearHalt(dev->usb_fd, dev->ep_out);
458 end:
459 return retval;
462 s32 USBStorage_Open(usbstorage_handle *dev, const char *bus, u16 vid, u16 pid)
464 s32 retval = -1;
465 u8 conf;
466 u8 *max_lun;
467 u32 iConf, iInterface, iEp;
468 usb_devdesc udd;
469 usb_configurationdesc *ucd;
470 usb_interfacedesc *uid;
471 usb_endpointdesc *ued;
473 max_lun = __lwp_heap_allocate(&__heap, 1);
474 if (!max_lun)
475 return IPC_ENOMEM;
477 memset(dev, 0, sizeof(*dev));
478 dev->usb_fd = -1;
480 dev->tag = TAG_START;
482 if (LWP_MutexInit(&dev->lock, false) < 0)
483 goto free_and_return;
485 if (SYS_CreateAlarm(&dev->alarm) < 0)
486 goto free_and_return;
488 retval = USB_OpenDevice(bus, vid, pid, &dev->usb_fd);
489 if (retval < 0)
490 goto free_and_return;
492 retval = USB_GetDescriptors(dev->usb_fd, &udd);
493 if (retval < 0)
494 goto free_and_return;
496 for (iConf = 0; iConf < udd.bNumConfigurations; iConf++) {
497 ucd = &udd.configurations[iConf];
498 for (iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) {
499 uid = &ucd->interfaces[iInterface];
500 if (uid->bInterfaceClass == USB_CLASS_MASS_STORAGE &&
501 uid->bInterfaceSubClass == MASS_STORAGE_SCSI_COMMANDS &&
502 uid->bInterfaceProtocol == MASS_STORAGE_BULK_ONLY)
504 if (uid->bNumEndpoints < 2)
505 continue;
507 dev->ep_in = dev->ep_out = 0;
508 for (iEp = 0; iEp < uid->bNumEndpoints; iEp++) {
509 ued = &uid->endpoints[iEp];
510 if (ued->bmAttributes != USB_ENDPOINT_BULK)
511 continue;
513 if (ued->bEndpointAddress & USB_ENDPOINT_IN)
514 dev->ep_in = ued->bEndpointAddress;
515 else
516 dev->ep_out = ued->bEndpointAddress;
519 if (dev->ep_in != 0 && dev->ep_out != 0) {
520 dev->configuration = ucd->bConfigurationValue;
521 dev->interface = uid->bInterfaceNumber;
522 dev->altInterface = uid->bAlternateSetting;
523 goto found;
529 USB_FreeDescriptors(&udd);
530 retval = USBSTORAGE_ENOINTERFACE;
531 goto free_and_return;
533 found:
534 USB_FreeDescriptors(&udd);
536 retval = USBSTORAGE_EINIT;
537 if (USB_GetConfiguration(dev->usb_fd, &conf) < 0)
538 goto free_and_return;
540 if (conf != dev->configuration && USB_SetConfiguration(dev->usb_fd, dev->configuration) < 0)
541 goto free_and_return;
543 if (dev->altInterface != 0 && USB_SetAlternativeInterface(dev->usb_fd, dev->interface, dev->altInterface) < 0)
544 goto free_and_return;
546 dev->suspended = 0;
548 retval = USBStorage_Reset(dev);
549 if (retval < 0)
550 goto free_and_return;
552 LWP_MutexLock(dev->lock);
553 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);
554 LWP_MutexUnlock(dev->lock);
556 if (retval < 0)
557 dev->max_lun = 1;
558 else
559 dev->max_lun = *max_lun + 1;
561 if (retval == USBSTORAGE_ETIMEDOUT)
562 goto free_and_return;
564 retval = USBSTORAGE_OK;
565 dev->sector_size = (u32 *) calloc(dev->max_lun, sizeof(u32));
566 if(!dev->sector_size) {
567 retval = IPC_ENOMEM;
568 goto free_and_return;
571 /* taken from linux usbstorage module (drivers/usb/storage/transport.c) */
573 * Some devices (i.e. Iomega Zip100) need this -- apparently
574 * the bulk pipes get STALLed when the GetMaxLUN request is
575 * processed. This is, in theory, harmless to all other devices
576 * (regardless of if they stall or not).
578 USB_ClearHalt(dev->usb_fd, dev->ep_in);
579 USB_ClearHalt(dev->usb_fd, dev->ep_out);
581 dev->buffer = __lwp_heap_allocate(&__heap, MAX_TRANSFER_SIZE);
583 if(!dev->buffer) {
584 retval = IPC_ENOMEM;
585 } else {
586 USB_DeviceRemovalNotifyAsync(dev->usb_fd,__usb_deviceremoved_cb,dev);
587 retval = USBSTORAGE_OK;
590 free_and_return:
591 if (max_lun)
592 __lwp_heap_free(&__heap, max_lun);
594 if (retval < 0) {
595 USBStorage_Close(dev);
596 return retval;
599 return 0;
602 s32 USBStorage_Close(usbstorage_handle *dev)
604 if (dev->usb_fd >= 0)
605 USB_CloseDevice(&dev->usb_fd);
607 LWP_MutexDestroy(dev->lock);
608 SYS_RemoveAlarm(dev->alarm);
610 if (dev->sector_size)
611 free(dev->sector_size);
613 if (dev->buffer)
614 __lwp_heap_free(&__heap, dev->buffer);
616 memset(dev, 0, sizeof(*dev));
617 dev->usb_fd = -1;
619 return 0;
622 s32 USBStorage_Reset(usbstorage_handle *dev)
624 s32 retval;
626 LWP_MutexLock(dev->lock);
627 retval = __usbstorage_reset(dev);
628 LWP_MutexUnlock(dev->lock);
630 return retval;
633 s32 USBStorage_GetMaxLUN(usbstorage_handle *dev)
635 return dev->max_lun;
638 s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun)
640 s32 retval;
642 if(lun >= dev->max_lun)
643 return IPC_EINVAL;
645 retval = __usbstorage_clearerrors(dev, lun);
646 if(retval < 0)
647 return retval;
649 retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], NULL);
650 return retval;
653 s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors)
655 s32 retval;
656 u8 cmd[10] = {SCSI_READ_CAPACITY, lun<<5, 0,0,0,0,0,0,0,0};
657 u8 response[8];
659 retval = __cycle(dev, lun, response, sizeof(response), cmd, sizeof(cmd), 0, NULL, NULL);
660 if(retval >= 0)
662 if(n_sectors != NULL)
663 memcpy(n_sectors, response, 4);
664 if(sector_size != NULL)
665 memcpy(sector_size, response + 4, 4);
666 retval = USBSTORAGE_OK;
669 return retval;
672 s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer)
674 u8 status = 0;
675 s32 retval;
676 u8 cmd[] = {
677 SCSI_READ_10,
678 lun << 5,
679 sector >> 24,
680 sector >> 16,
681 sector >> 8,
682 sector,
684 n_sectors >> 8,
685 n_sectors,
689 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
690 return IPC_EINVAL;
692 retval = __cycle(dev, lun, buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 0, &status, NULL);
693 if(retval > 0 && status != 0)
694 retval = USBSTORAGE_ESTATUS;
696 return retval;
699 s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer)
701 u8 status = 0;
702 s32 retval;
703 u8 cmd[] = {
704 SCSI_WRITE_10,
705 lun << 5,
706 sector >> 24,
707 sector >> 16,
708 sector >> 8,
709 sector,
711 n_sectors >> 8,
712 n_sectors,
716 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
717 return IPC_EINVAL;
718 retval = __cycle(dev, lun, (u8 *)buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 1, &status, NULL);
719 if(retval > 0 && status != 0)
720 retval = USBSTORAGE_ESTATUS;
721 return retval;
724 s32 USBStorage_Suspend(usbstorage_handle *dev)
726 if(dev->suspended == 1)
727 return USBSTORAGE_OK;
729 USB_SuspendDevice(dev->usb_fd);
730 dev->suspended = 1;
732 return USBSTORAGE_OK;
736 The following is for implementing a DISC_INTERFACE
737 as used by libfat
740 static bool usb_inited=false;
741 static bool __usbstorage_ReadSectors(u32, u32, void *);
743 static bool __usbstorage_IsInserted(void);
745 static bool __usbstorage_Startup(void)
747 usb_inited=true;
748 USB_Initialize();
749 USBStorage_Initialize();
750 return __usbstorage_IsInserted();
753 static bool __usbstorage_IsInserted(void)
755 u8 *buffer;
756 u8 dummy;
757 u8 i, j;
758 u16 vid, pid;
759 s32 maxLun;
760 s32 retval;
762 if(!usb_inited) {
763 usb_inited = true;
764 USB_Initialize();
765 USBStorage_Initialize();
768 // reset it here and check if device is still attached
769 __mounted = false;
771 buffer = __lwp_heap_allocate(&__heap, DEVLIST_MAXSIZE << 3);
772 if (!buffer)
773 return false;
775 memset(buffer, 0, DEVLIST_MAXSIZE << 3);
777 if (USB_GetDeviceList("/dev/usb/oh0", buffer, DEVLIST_MAXSIZE, USB_CLASS_MASS_STORAGE, &dummy) < 0)
779 if (__vid != 0 || __pid != 0)
780 USBStorage_Close(&__usbfd);
782 __lun = 0;
783 __vid = 0;
784 __pid = 0;
786 __lwp_heap_free(&__heap,buffer);
788 return false;
791 usleep(100);
793 if (__vid != 0 || __pid != 0) {
794 for(i = 0; i < dummy; i++) {
795 memcpy(&vid, (buffer + (i << 3) + 4), 2);
796 memcpy(&pid, (buffer + (i << 3) + 6), 2);
797 if(vid != 0 || pid != 0) {
798 if((vid == __vid) && (pid == __pid)) {
799 __mounted = true;
800 __lwp_heap_free(&__heap,buffer);
802 // I don't know why I have to wait but it's needed
803 usleep(50);
805 return true;
810 USBStorage_Close(&__usbfd);
813 __lun = 0;
814 __vid = 0;
815 __pid = 0;
817 for (i = 0; i < dummy; i++) {
818 memcpy(&vid, (buffer + (i << 3) + 4), 2);
819 memcpy(&pid, (buffer + (i << 3) + 6), 2);
820 if (vid == 0 || pid == 0)
821 continue;
823 if (USBStorage_Open(&__usbfd, "oh0", vid, pid) < 0)
824 continue;
826 maxLun = USBStorage_GetMaxLUN(&__usbfd);
827 for (j = 0; j < maxLun; j++) {
828 retval = USBStorage_MountLUN(&__usbfd, j);
830 if (retval == USBSTORAGE_ETIMEDOUT)
831 break;
833 if (retval < 0)
834 continue;
836 __mounted = true;
837 __lun = j;
838 __vid = vid;
839 __pid = pid;
841 break;
844 if (__mounted)
845 break;
847 USBStorage_Close(&__usbfd);
850 __lwp_heap_free(&__heap,buffer);
852 return __mounted;
855 static bool __usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer)
857 s32 retval;
859 if (!__mounted)
860 return false;
862 retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer);
864 if (retval == USBSTORAGE_ETIMEDOUT)
865 __mounted = false;
867 return retval >= 0;
870 static bool __usbstorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer)
872 s32 retval;
874 if (!__mounted)
875 return false;
877 retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer);
879 if (retval == USBSTORAGE_ETIMEDOUT)
880 __mounted = false;
882 return retval >= 0;
885 static bool __usbstorage_ClearStatus(void)
887 return true;
890 static bool __usbstorage_Shutdown(void)
892 if (__vid != 0 || __pid != 0)
893 USBStorage_Close(&__usbfd);
895 __lun = 0;
896 __vid = 0;
897 __pid = 0;
898 __mounted = false;
900 return true;
903 DISC_INTERFACE __io_usbstorage = {
904 DEVICE_TYPE_WII_USB,
905 FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB,
906 (FN_MEDIUM_STARTUP)&__usbstorage_Startup,
907 (FN_MEDIUM_ISINSERTED)&__usbstorage_IsInserted,
908 (FN_MEDIUM_READSECTORS)&__usbstorage_ReadSectors,
909 (FN_MEDIUM_WRITESECTORS)&__usbstorage_WriteSectors,
910 (FN_MEDIUM_CLEARSTATUS)&__usbstorage_ClearStatus,
911 (FN_MEDIUM_SHUTDOWN)&__usbstorage_Shutdown
914 #endif /* HW_RVL */