update DI disc code
[libogc.git] / libogc / usbstorage.c
blob392bbca86c6dfaf49d9298b5b17e2340be206d70
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 u8 __mounted = 0;
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;
169 dev->retval = USBSTORAGE_PROCESSING;
170 retval = USB_WriteCtrlMsgAsync(dev->usb_fd, bmRequestType, bmRequest, wValue, wIndex, wLength, rpData, __usb_blkmsg_cb, (void *)dev);
171 if(retval < 0) return retval;
173 __usb_settimeout(dev);
175 _CPU_ISR_Disable(level);
176 do {
177 retval = dev->retval;
178 if(retval!=USBSTORAGE_PROCESSING) break;
179 else LWP_ThreadSleep(__usbstorage_waitq);
180 } while(retval==USBSTORAGE_PROCESSING);
181 _CPU_ISR_Restore(level);
183 if(retval==USBSTORAGE_ETIMEDOUT) USBStorage_Close(dev);
184 return retval;
187 s32 USBStorage_Initialize()
189 u8 *ptr;
190 u32 level;
192 _CPU_ISR_Disable(level);
193 if(__heap_created != 0) {
194 _CPU_ISR_Restore(level);
195 return IPC_OK;
198 LWP_InitQueue(&__usbstorage_waitq);
200 ptr = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - HEAP_SIZE));
201 if((u32)ptr < (u32)SYS_GetArena2Lo()) {
202 _CPU_ISR_Restore(level);
203 return IPC_ENOMEM;
206 SYS_SetArena2Hi(ptr);
208 __lwp_heap_init(&__heap, ptr, HEAP_SIZE, 32);
209 __heap_created = 1;
210 _CPU_ISR_Restore(level);
212 return IPC_OK;
216 static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u8 *cb, u8 cbLen)
218 s32 retval = USBSTORAGE_OK;
220 if(cbLen == 0 || cbLen > 16)
221 return IPC_EINVAL;
223 memset(dev->buffer, 0, CBW_SIZE);
225 __stwbrx(dev->buffer, 0, CBW_SIGNATURE);
226 __stwbrx(dev->buffer, 4, dev->tag);
227 __stwbrx(dev->buffer, 8, len);
228 dev->buffer[12] = flags;
229 dev->buffer[13] = lun;
230 dev->buffer[14] = (cbLen > 6 ? 0x10 : 6);
232 memcpy(dev->buffer + 15, cb, cbLen);
234 if(dev->suspended == 1)
236 USB_ResumeDevice(dev->usb_fd);
237 dev->suspended = 0;
240 retval = __USB_BlkMsgTimeout(dev, dev->ep_out, CBW_SIZE, (void *)dev->buffer);
242 if(retval == CBW_SIZE) return USBSTORAGE_OK;
243 else if(retval > 0) return USBSTORAGE_ESHORTWRITE;
245 return retval;
248 static s32 __read_csw(usbstorage_handle *dev, u8 *status, u32 *dataResidue)
250 s32 retval = USBSTORAGE_OK;
251 u32 signature, tag, _dataResidue, _status;
253 memset(dev->buffer, 0, CSW_SIZE);
255 retval = __USB_BlkMsgTimeout(dev, dev->ep_in, CSW_SIZE, dev->buffer);
256 if(retval > 0 && retval != CSW_SIZE) return USBSTORAGE_ESHORTREAD;
257 else if(retval < 0) return retval;
259 signature = __lwbrx(dev->buffer, 0);
260 tag = __lwbrx(dev->buffer, 4);
261 _dataResidue = __lwbrx(dev->buffer, 8);
262 _status = dev->buffer[12];
264 if(signature != CSW_SIGNATURE) return USBSTORAGE_ESIGNATURE;
266 if(dataResidue != NULL)
267 *dataResidue = _dataResidue;
268 if(status != NULL)
269 *status = _status;
271 if(tag != dev->tag) return USBSTORAGE_ETAG;
272 dev->tag++;
274 return USBSTORAGE_OK;
277 static s32 __cycle(usbstorage_handle *dev, u8 lun, u8 *buffer, u32 len, u8 *cb, u8 cbLen, u8 write, u8 *_status, u32 *_dataResidue)
279 s32 retval = USBSTORAGE_OK;
281 u8 status = 0;
282 u32 dataResidue = 0;
283 u32 thisLen;
285 s8 retries = USBSTORAGE_CYCLE_RETRIES + 1;
287 LWP_MutexLock(dev->lock);
290 retries--;
292 if(retval == USBSTORAGE_ETIMEDOUT)
293 break;
295 if(write)
297 retval = __send_cbw(dev, lun, len, CBW_OUT, cb, cbLen);
298 if(retval == USBSTORAGE_ETIMEDOUT)
299 break;
300 if(retval < 0)
302 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
303 retval = USBSTORAGE_ETIMEDOUT;
304 continue;
306 while(len > 0)
308 thisLen = len > MAX_TRANSFER_SIZE ? MAX_TRANSFER_SIZE : len;
309 memset(dev->buffer, 0, MAX_TRANSFER_SIZE);
310 memcpy(dev->buffer, buffer, thisLen);
311 retval = __USB_BlkMsgTimeout(dev, dev->ep_out, thisLen, dev->buffer);
313 if(retval == USBSTORAGE_ETIMEDOUT)
314 break;
316 if(retval < 0)
318 retval = USBSTORAGE_EDATARESIDUE;
319 break;
323 if(retval != thisLen && len > 0)
325 retval = USBSTORAGE_EDATARESIDUE;
326 break;
328 len -= retval;
329 buffer += retval;
332 if(retval < 0)
334 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
335 retval = USBSTORAGE_ETIMEDOUT;
336 continue;
339 else
341 retval = __send_cbw(dev, lun, len, CBW_IN, cb, cbLen);
343 if(retval == USBSTORAGE_ETIMEDOUT)
344 break;
346 if(retval < 0)
348 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
349 retval = USBSTORAGE_ETIMEDOUT;
350 continue;
352 while(len > 0)
354 thisLen = len > MAX_TRANSFER_SIZE ? MAX_TRANSFER_SIZE : len;
355 retval = __USB_BlkMsgTimeout(dev, dev->ep_in, thisLen, dev->buffer);
356 if(retval < 0)
357 break;
359 memcpy(buffer, dev->buffer, retval);
360 len -= retval;
361 buffer += retval;
363 if(retval != thisLen)
364 break;
367 if(retval < 0)
369 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
370 retval = USBSTORAGE_ETIMEDOUT;
371 continue;
375 retval = __read_csw(dev, &status, &dataResidue);
377 if(retval == USBSTORAGE_ETIMEDOUT)
378 break;
380 if(retval < 0)
382 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
383 retval = USBSTORAGE_ETIMEDOUT;
384 continue;
387 retval = USBSTORAGE_OK;
388 } while(retval < 0 && retries > 0);
390 if(retval < 0 && retval != USBSTORAGE_ETIMEDOUT)
392 if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
393 retval = USBSTORAGE_ETIMEDOUT;
395 LWP_MutexUnlock(dev->lock);
398 if(_status != NULL)
399 *_status = status;
400 if(_dataResidue != NULL)
401 *_dataResidue = dataResidue;
403 return retval;
406 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun)
408 s32 retval;
409 u8 cmd[16];
410 u8 sense[SCSI_SENSE_REPLY_SIZE];
411 u8 status = 0;
413 memset(cmd, 0, sizeof(cmd));
414 cmd[0] = SCSI_TEST_UNIT_READY;
416 retval = __cycle(dev, lun, NULL, 0, cmd, 1, 0, &status, NULL);
417 if(retval < 0) return retval;
419 if(status != 0)
421 cmd[0] = SCSI_REQUEST_SENSE;
422 cmd[1] = lun << 5;
423 cmd[4] = SCSI_SENSE_REPLY_SIZE;
424 cmd[5] = 0;
425 memset(sense, 0, SCSI_SENSE_REPLY_SIZE);
426 retval = __cycle(dev, lun, sense, SCSI_SENSE_REPLY_SIZE, cmd, 6, 0, NULL, NULL);
427 if(retval < 0) return retval;
429 status = sense[2] & 0x0F;
430 if(status == SCSI_SENSE_NOT_READY || status == SCSI_SENSE_MEDIUM_ERROR || status == SCSI_SENSE_HARDWARE_ERROR) return USBSTORAGE_ESENSE;
433 return retval;
436 static s32 __usbstorage_reset(usbstorage_handle *dev)
438 s32 retval;
440 if(dev->suspended == 1)
442 USB_ResumeDevice(dev->usb_fd);
443 dev->suspended = 0;
446 retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_RESET, 0, dev->interface, 0, NULL);
448 /* FIXME?: some devices return -7004 here which definitely violates the usb ms protocol but they still seem to be working... */
449 if(retval < 0 && retval != -7004)
450 goto end;
452 /* gives device enough time to process the reset */
453 usleep(60);
455 retval = USB_ClearHalt(dev->usb_fd, dev->ep_in);
456 if(retval < 0)
457 goto end;
458 retval = USB_ClearHalt(dev->usb_fd, dev->ep_out);
460 end:
461 return retval;
464 s32 USBStorage_Open(usbstorage_handle *dev, const char *bus, u16 vid, u16 pid)
466 s32 retval = -1;
467 u8 conf,*max_lun = NULL;
468 u32 iConf, iInterface, iEp;
469 usb_devdesc udd;
470 usb_configurationdesc *ucd;
471 usb_interfacedesc *uid;
472 usb_endpointdesc *ued;
474 max_lun = __lwp_heap_allocate(&__heap, 1);
475 if(max_lun==NULL) return IPC_ENOMEM;
477 memset(dev, 0, sizeof(*dev));
479 dev->tag = TAG_START;
480 if(LWP_MutexInit(&dev->lock, false) < 0)
481 goto free_and_return;
482 if(SYS_CreateAlarm(&dev->alarm)<0)
483 goto free_and_return;
485 retval = USB_OpenDevice(bus, vid, pid, &dev->usb_fd);
486 if(retval < 0)
487 goto free_and_return;
489 retval = USB_GetDescriptors(dev->usb_fd, &udd);
490 if(retval < 0)
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)
504 continue;
506 dev->ep_in = dev->ep_out = 0;
507 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;
518 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;
539 if(conf != dev->configuration && USB_SetConfiguration(dev->usb_fd, dev->configuration) < 0)
540 goto free_and_return;
541 if(dev->altInterface != 0 && USB_SetAlternativeInterface(dev->usb_fd, dev->interface, dev->altInterface) < 0)
542 goto free_and_return;
543 dev->suspended = 0;
545 retval = USBStorage_Reset(dev);
546 if(retval < 0)
547 goto free_and_return;
549 LWP_MutexLock(dev->lock);
550 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);
551 LWP_MutexUnlock(dev->lock);
552 if(retval < 0)
553 dev->max_lun = 1;
554 else
555 dev->max_lun = *max_lun;
558 if(retval == USBSTORAGE_ETIMEDOUT)
559 goto free_and_return;
561 retval = USBSTORAGE_OK;
562 dev->sector_size = (u32 *)calloc(dev->max_lun, sizeof(u32));
563 if(dev->sector_size == NULL)
565 retval = IPC_ENOMEM;
566 goto free_and_return;
569 if(dev->max_lun == 0)
570 dev->max_lun++;
572 /* taken from linux usbstorage module (drivers/usb/storage/transport.c) */
574 * Some devices (i.e. Iomega Zip100) need this -- apparently
575 * the bulk pipes get STALLed when the GetMaxLUN request is
576 * processed. This is, in theory, harmless to all other devices
577 * (regardless of if they stall or not).
579 USB_ClearHalt(dev->usb_fd, dev->ep_in);
580 USB_ClearHalt(dev->usb_fd, dev->ep_out);
582 dev->buffer = __lwp_heap_allocate(&__heap, MAX_TRANSFER_SIZE);
584 if(dev->buffer == NULL) 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!=NULL) __lwp_heap_free(&__heap, max_lun);
592 if(retval < 0)
594 USB_CloseDevice(&dev->usb_fd);
595 LWP_MutexDestroy(dev->lock);
596 SYS_RemoveAlarm(dev->alarm);
597 if(dev->buffer != NULL)
598 __lwp_heap_free(&__heap, dev->buffer);
599 if(dev->sector_size != NULL)
600 free(dev->sector_size);
601 memset(dev, 0, sizeof(*dev));
602 return retval;
604 return 0;
607 s32 USBStorage_Close(usbstorage_handle *dev)
609 USB_CloseDevice(&dev->usb_fd);
610 LWP_MutexDestroy(dev->lock);
611 SYS_RemoveAlarm(dev->alarm);
612 if(dev->sector_size!=NULL)
613 free(dev->sector_size);
614 if(dev->buffer!=NULL)
615 __lwp_heap_free(&__heap, dev->buffer);
616 memset(dev, 0, sizeof(*dev));
617 return 0;
620 s32 USBStorage_Reset(usbstorage_handle *dev)
622 s32 retval;
624 LWP_MutexLock(dev->lock);
625 retval = __usbstorage_reset(dev);
626 LWP_MutexUnlock(dev->lock);
628 return retval;
631 s32 USBStorage_GetMaxLUN(usbstorage_handle *dev)
633 return dev->max_lun;
636 s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun)
638 s32 retval;
640 if(lun >= dev->max_lun)
641 return IPC_EINVAL;
643 retval = __usbstorage_clearerrors(dev, lun);
644 if(retval < 0)
645 return retval;
647 retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], NULL);
648 return retval;
651 s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors)
653 s32 retval;
654 u8 cmd[] = {SCSI_READ_CAPACITY, lun << 5};
655 u8 response[8];
657 retval = __cycle(dev, lun, response, 8, cmd, 2, 0, NULL, NULL);
658 if(retval >= 0)
660 if(n_sectors != NULL)
661 memcpy(n_sectors, response, 4);
662 if(sector_size != NULL)
663 memcpy(sector_size, response + 4, 4);
664 retval = USBSTORAGE_OK;
667 return retval;
670 s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer)
672 u8 status = 0;
673 s32 retval;
674 u8 cmd[] = {
675 SCSI_READ_10,
676 lun << 5,
677 sector >> 24,
678 sector >> 16,
679 sector >> 8,
680 sector,
682 n_sectors >> 8,
683 n_sectors,
686 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
687 return IPC_EINVAL;
688 retval = __cycle(dev, lun, buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 0, &status, NULL);
689 if(retval > 0 && status != 0)
690 retval = USBSTORAGE_ESTATUS;
691 return retval;
694 s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer)
696 u8 status = 0;
697 s32 retval;
698 u8 cmd[] = {
699 SCSI_WRITE_10,
700 lun << 5,
701 sector >> 24,
702 sector >> 16,
703 sector >> 8,
704 sector,
706 n_sectors >> 8,
707 n_sectors,
710 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
711 return IPC_EINVAL;
712 retval = __cycle(dev, lun, (u8 *)buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 1, &status, NULL);
713 if(retval > 0 && status != 0)
714 retval = USBSTORAGE_ESTATUS;
715 return retval;
718 s32 USBStorage_Suspend(usbstorage_handle *dev)
720 if(dev->suspended == 1)
721 return USBSTORAGE_OK;
723 USB_SuspendDevice(dev->usb_fd);
724 dev->suspended = 1;
726 return USBSTORAGE_OK;
731 The following is for implementing a DISC_INTERFACE
732 as used by libfat
735 static bool usb_inited=false;
736 static bool __usbstorage_ReadSectors(u32, u32, void *);
738 static bool __usbstorage_IsInserted(void);
740 static bool __usbstorage_Startup(void)
742 usb_inited=true;
743 USB_Initialize();
744 USBStorage_Initialize();
745 return __usbstorage_IsInserted();
748 static bool __usbstorage_IsInserted(void)
750 u8 *buffer;
751 u8 dummy;
752 u8 i, j;
753 u16 vid, pid;
754 s32 maxLun;
755 s32 retval;
757 if(!usb_inited)
759 usb_inited=true;
760 USB_Initialize();
761 USBStorage_Initialize();
764 __mounted = 0; //reset it here and check if device is still attached
766 buffer = __lwp_heap_allocate(&__heap, DEVLIST_MAXSIZE << 3);
767 if(buffer == NULL)
768 return false;
769 memset(buffer, 0, DEVLIST_MAXSIZE << 3);
771 if(USB_GetDeviceList("/dev/usb/oh0", buffer, DEVLIST_MAXSIZE, 0, &dummy) < 0)
773 if(__vid!=0 || __pid!=0)
775 USBStorage_Close(&__usbfd);
776 memset(&__usbfd, 0, sizeof(__usbfd));
779 __lun = 0;
780 __vid = 0;
781 __pid = 0;
783 __lwp_heap_free(&__heap,buffer);
784 return false;
787 usleep(100);
789 if(__vid!=0 || __pid!=0)
791 for(i = 0; i < DEVLIST_MAXSIZE; i++)
793 memcpy(&vid, (buffer + (i << 3) + 4), 2);
794 memcpy(&pid, (buffer + (i << 3) + 6), 2);
795 if(vid != 0 || pid != 0)
797 if( (vid == __vid) && (pid == __pid))
799 __mounted = 1;
800 __lwp_heap_free(&__heap,buffer);
801 usleep(50); // I don't know why I have to wait but it's needed
802 return true;
806 USBStorage_Close(&__usbfd);
807 memset(&__usbfd, 0, sizeof(__usbfd));
810 __lun = 0;
811 __vid = 0;
812 __pid = 0;
814 for(i = 0; i < DEVLIST_MAXSIZE; i++)
816 memcpy(&vid, (buffer + (i << 3) + 4), 2);
817 memcpy(&pid, (buffer + (i << 3) + 6), 2);
818 if(vid == 0 || pid == 0)
819 continue;
821 if(USBStorage_Open(&__usbfd, "oh0", vid, pid) < 0)
822 continue;
824 maxLun = USBStorage_GetMaxLUN(&__usbfd);
825 for(j = 0; j < maxLun; j++)
827 retval = USBStorage_MountLUN(&__usbfd, j);
829 if(retval == USBSTORAGE_ETIMEDOUT)
830 break;
831 if(retval < 0)
832 continue;
834 __mounted = 1;
835 __lun = j;
836 __vid = vid;
837 __pid = pid;
838 i = DEVLIST_MAXSIZE;
839 break;
842 __lwp_heap_free(&__heap,buffer);
843 if(__mounted == 1)
844 return true;
845 return false;
848 static bool __usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer)
850 s32 retval;
852 if(__mounted != 1)
853 return false;
855 retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer);
857 if(retval == USBSTORAGE_ETIMEDOUT)
858 __mounted = 0;
860 if(retval < 0)
861 return false;
862 return true;
865 static bool __usbstorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer)
867 s32 retval;
869 if(__mounted != 1)
870 return false;
872 retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer);
874 if(retval == USBSTORAGE_ETIMEDOUT)
875 __mounted = 0;
877 if(retval < 0)
878 return false;
879 return true;
882 static bool __usbstorage_ClearStatus(void)
884 return true;
887 static bool __usbstorage_Shutdown(void)
889 if(__vid!=0 || __pid!=0)
891 USBStorage_Close(&__usbfd);
892 memset(&__usbfd, 0, sizeof(__usbfd));
895 __lun = 0;
896 __vid = 0;
897 __pid = 0;
898 __mounted = 0;
899 return true;
902 DISC_INTERFACE __io_usbstorage = {
903 DEVICE_TYPE_WII_USB,
904 FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB,
905 (FN_MEDIUM_STARTUP)&__usbstorage_Startup,
906 (FN_MEDIUM_ISINSERTED)&__usbstorage_IsInserted,
907 (FN_MEDIUM_READSECTORS)&__usbstorage_ReadSectors,
908 (FN_MEDIUM_WRITESECTORS)&__usbstorage_WriteSectors,
909 (FN_MEDIUM_CLEARSTATUS)&__usbstorage_ClearStatus,
910 (FN_MEDIUM_SHUTDOWN)&__usbstorage_Shutdown
913 #endif /* HW_RVL */