Fix libogc hardware lighting (GX_SetChanCtrl) - patch from https://sourceforge.net...
[libogc.git] / libogc / usbstorage.c
blob6170276eace5217e95f32cf09bc1563f580411d3
1 /*-------------------------------------------------------------
3 usbstorage.c -- Bulk-only USB mass storage support
5 Copyright (C) 2008
6 Sven Peter (svpe) <svpe@gmx.net>
7 Copyright (C) 2009-2010
8 tueidj, rodries, Tantric
10 This software is provided 'as-is', without any express or implied
11 warranty. In no event will the authors be held liable for any
12 damages arising from the use of this software.
14 Permission is granted to anyone to use this software for any
15 purpose, including commercial applications, and to alter it and
16 redistribute it freely, subject to the following restrictions:
18 1. The origin of this software must not be misrepresented; you
19 must not claim that you wrote the original software. If you use
20 this software in a product, an acknowledgment in the product
21 documentation would be appreciated but is not required.
23 2. Altered source versions must be plainly marked as such, and
24 must not be misrepresented as being the original software.
26 3. This notice may not be removed or altered from any source
27 distribution.
29 -------------------------------------------------------------*/
30 #if defined(HW_RVL)
32 #include <gccore.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/time.h>
37 #include <errno.h>
38 #include <lwp_heap.h>
39 #include <malloc.h>
41 #include "asm.h"
42 #include "processor.h"
43 #include "disc_io.h"
44 #include "lwp_watchdog.h"
46 #define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f)
48 #define HEAP_SIZE (18*1024)
49 #define TAG_START 0x0BADC0DE
51 #define CBW_SIZE 31
52 #define CBW_SIGNATURE 0x43425355
53 #define CBW_IN (1 << 7)
54 #define CBW_OUT 0
56 #define CSW_SIZE 13
57 #define CSW_SIGNATURE 0x53425355
59 #define SCSI_TEST_UNIT_READY 0x00
60 #define SCSI_REQUEST_SENSE 0x03
61 #define SCSI_START_STOP 0x1B
62 #define SCSI_READ_CAPACITY 0x25
63 #define SCSI_READ_10 0x28
64 #define SCSI_WRITE_10 0x2A
66 #define SCSI_SENSE_REPLY_SIZE 18
67 #define SCSI_SENSE_NOT_READY 0x02
68 #define SCSI_SENSE_MEDIUM_ERROR 0x03
69 #define SCSI_SENSE_HARDWARE_ERROR 0x04
71 #define USB_CLASS_MASS_STORAGE 0x08
72 #define MASS_STORAGE_RBC_COMMANDS 0x01
73 #define MASS_STORAGE_ATA_COMMANDS 0x02
74 #define MASS_STORAGE_QIC_COMMANDS 0x03
75 #define MASS_STORAGE_UFI_COMMANDS 0x04
76 #define MASS_STORAGE_SFF8070_COMMANDS 0x05
77 #define MASS_STORAGE_SCSI_COMMANDS 0x06
78 #define MASS_STORAGE_BULK_ONLY 0x50
80 #define USBSTORAGE_GET_MAX_LUN 0xFE
81 #define USBSTORAGE_RESET 0xFF
83 #define USB_ENDPOINT_BULK 0x02
85 #define USBSTORAGE_CYCLE_RETRIES 3
86 #define USBSTORAGE_TIMEOUT 2
88 #define MAX_TRANSFER_SIZE_V0 4096
89 #define MAX_TRANSFER_SIZE_V5 (16*1024)
91 #define DEVLIST_MAXSIZE 8
96 static heap_cntrl __heap;
97 static bool __inited = false;
98 static u64 usb_last_used = 0;
99 static lwpq_t __usbstorage_waitq = 0;
100 static u32 usbtimeout = USBSTORAGE_TIMEOUT;
103 The following is for implementing a DISC_INTERFACE
104 as used by libfat
108 static usbstorage_handle __usbfd;
109 static u8 __lun = 0;
110 static bool __mounted = false;
111 static u16 __vid = 0;
112 static u16 __pid = 0;
113 static bool usb2_mode=true;
115 static s32 __usbstorage_reset(usbstorage_handle *dev);
116 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun);
118 /* XXX: this is a *really* dirty and ugly way to send a bulkmessage with a timeout
119 * but there's currently no other known way of doing this and it's in my humble
120 * opinion still better than having a function blocking forever while waiting
121 * for the USB data/IOS reply..
124 static s32 __usb_blkmsg_cb(s32 retval, void *dummy)
126 usbstorage_handle *dev = (usbstorage_handle *)dummy;
127 dev->retval = retval;
128 SYS_CancelAlarm(dev->alarm);
129 LWP_ThreadBroadcast(__usbstorage_waitq);
130 return 0;
133 static s32 __usb_deviceremoved_cb(s32 retval,void *arg)
135 __mounted = false;
136 return 0;
139 static void __usb_timeouthandler(syswd_t alarm,void *cbarg)
141 usbstorage_handle *dev = (usbstorage_handle*)cbarg;
142 dev->retval = USBSTORAGE_ETIMEDOUT;
143 LWP_ThreadBroadcast(__usbstorage_waitq);
146 static void __usb_settimeout(usbstorage_handle *dev, u32 secs)
148 struct timespec ts;
150 ts.tv_sec = secs;
151 ts.tv_nsec = 0;
152 SYS_SetAlarm(dev->alarm,&ts,__usb_timeouthandler,dev);
155 static s32 __USB_BlkMsgTimeout(usbstorage_handle *dev, u8 bEndpoint, u16 wLength, void *rpData, u32 timeout)
157 s32 retval;
159 dev->retval = USBSTORAGE_PROCESSING;
160 retval = USB_WriteBlkMsgAsync(dev->usb_fd, bEndpoint, wLength, rpData, __usb_blkmsg_cb, (void *)dev);
161 if(retval < 0) return retval;
163 __usb_settimeout(dev, timeout);
165 do {
166 retval = dev->retval;
167 if(retval!=USBSTORAGE_PROCESSING) break;
168 else LWP_ThreadSleep(__usbstorage_waitq);
169 } while(retval==USBSTORAGE_PROCESSING);
171 if (retval<0)
172 USB_ClearHalt(dev->usb_fd, bEndpoint);
174 return retval;
177 static s32 __USB_CtrlMsgTimeout(usbstorage_handle *dev, u8 bmRequestType, u8 bmRequest, u16 wValue, u16 wIndex, u16 wLength, void *rpData)
179 s32 retval;
181 dev->retval = USBSTORAGE_PROCESSING;
182 retval = USB_WriteCtrlMsgAsync(dev->usb_fd, bmRequestType, bmRequest, wValue, wIndex, wLength, rpData, __usb_blkmsg_cb, (void *)dev);
183 if(retval < 0) return retval;
185 __usb_settimeout(dev, usbtimeout);
187 do {
188 retval = dev->retval;
189 if(retval!=USBSTORAGE_PROCESSING) break;
190 else LWP_ThreadSleep(__usbstorage_waitq);
191 } while(retval==USBSTORAGE_PROCESSING);
193 return retval;
196 s32 USBStorage_Initialize()
198 u8 *ptr;
199 u32 level;
201 if(__inited)
202 return IPC_OK;
204 _CPU_ISR_Disable(level);
205 LWP_InitQueue(&__usbstorage_waitq);
207 ptr = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - HEAP_SIZE));
208 if((u32)ptr < (u32)SYS_GetArena2Lo()) {
209 _CPU_ISR_Restore(level);
210 return IPC_ENOMEM;
213 SYS_SetArena2Hi(ptr);
214 __lwp_heap_init(&__heap, ptr, HEAP_SIZE, 32);
215 __inited = true;
216 _CPU_ISR_Restore(level);
217 return IPC_OK;
220 static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u8 *cb, u8 cbLen)
222 s32 retval = USBSTORAGE_OK;
224 if(cbLen == 0 || cbLen > 16)
225 return IPC_EINVAL;
227 memset(dev->buffer, 0, CBW_SIZE);
229 __stwbrx(dev->buffer, 0, CBW_SIGNATURE);
230 __stwbrx(dev->buffer, 4, ++dev->tag);
231 __stwbrx(dev->buffer, 8, len);
232 dev->buffer[12] = flags;
233 dev->buffer[13] = lun;
234 dev->buffer[14] = (cbLen > 6 ? 10 : 6);
236 memcpy(dev->buffer + 15, cb, cbLen);
238 if(dev->suspended == 1)
240 USB_ResumeDevice(dev->usb_fd);
241 dev->suspended = 0;
244 retval = __USB_BlkMsgTimeout(dev, dev->ep_out, CBW_SIZE, (void *)dev->buffer, usbtimeout);
246 if(retval == CBW_SIZE) return USBSTORAGE_OK;
247 else if(retval > 0) return USBSTORAGE_ESHORTWRITE;
249 return retval;
252 static s32 __read_csw(usbstorage_handle *dev, u8 *status, u32 *dataResidue, u32 timeout)
254 s32 retval = USBSTORAGE_OK;
255 u32 signature, tag, _dataResidue, _status;
257 memset(dev->buffer, 0, CSW_SIZE);
259 retval = __USB_BlkMsgTimeout(dev, dev->ep_in, CSW_SIZE, dev->buffer, timeout);
260 if(retval > 0 && retval != CSW_SIZE) return USBSTORAGE_ESHORTREAD;
261 else if(retval < 0) return retval;
263 signature = __lwbrx(dev->buffer, 0);
264 tag = __lwbrx(dev->buffer, 4);
265 _dataResidue = __lwbrx(dev->buffer, 8);
266 _status = dev->buffer[12];
268 if(signature != CSW_SIGNATURE) return USBSTORAGE_ESIGNATURE;
270 if(dataResidue != NULL)
271 *dataResidue = _dataResidue;
272 if(status != NULL)
273 *status = _status;
275 if(tag != dev->tag) return USBSTORAGE_ETAG;
277 return USBSTORAGE_OK;
280 static s32 __cycle(usbstorage_handle *dev, u8 lun, u8 *buffer, u32 len, u8 *cb, u8 cbLen, u8 write, u8 *_status, u32 *_dataResidue)
282 s32 retval = USBSTORAGE_OK;
284 u8 status=0;
285 u32 dataResidue = 0;
286 u16 max_size;
287 u8 ep = write ? dev->ep_out : dev->ep_in;
288 s8 retries = USBSTORAGE_CYCLE_RETRIES + 1;
290 if(usb2_mode)
291 max_size=MAX_TRANSFER_SIZE_V5;
292 else
293 max_size=MAX_TRANSFER_SIZE_V0;
295 LWP_MutexLock(dev->lock);
298 u8 *_buffer = buffer;
299 u32 _len = len;
300 retries--;
302 if(retval == USBSTORAGE_ETIMEDOUT)
303 break;
305 retval = __send_cbw(dev, lun, len, (write ? CBW_OUT:CBW_IN), cb, cbLen);
307 while(_len > 0 && retval >= 0)
309 u32 thisLen = _len > max_size ? max_size : _len;
311 if ((u32)_buffer&0x1F || !((u32)_buffer&0x10000000)) {
312 if (write) memcpy(dev->buffer, _buffer, thisLen);
313 retval = __USB_BlkMsgTimeout(dev, ep, thisLen, dev->buffer, usbtimeout);
314 if (!write && retval > 0)
315 memcpy(_buffer, dev->buffer, retval);
316 } else
317 retval = __USB_BlkMsgTimeout(dev, ep, thisLen, _buffer, usbtimeout);
319 if (retval == thisLen) {
320 _len -= retval;
321 _buffer += retval;
323 else if (retval != USBSTORAGE_ETIMEDOUT)
324 retval = USBSTORAGE_EDATARESIDUE;
327 if (retval >= 0)
328 __read_csw(dev, &status, &dataResidue, usbtimeout);
330 if (retval < 0) {
331 if (__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
332 retval = USBSTORAGE_ETIMEDOUT;
335 } while (retval < 0 && retries > 0);
337 LWP_MutexUnlock(dev->lock);
339 if(_status != NULL)
340 *_status = status;
341 if(_dataResidue != NULL)
342 *_dataResidue = dataResidue;
344 return retval;
347 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun)
349 s32 retval;
350 u8 cmd[6];
351 u8 sense[SCSI_SENSE_REPLY_SIZE];
352 u8 status = 0;
354 memset(cmd, 0, sizeof(cmd));
355 cmd[0] = SCSI_TEST_UNIT_READY;
357 retval = __cycle(dev, lun, NULL, 0, cmd, 1, 0, &status, NULL);
358 if (retval < 0) return retval;
360 if (status)
362 cmd[0] = SCSI_REQUEST_SENSE;
363 cmd[1] = lun << 5;
364 cmd[4] = SCSI_SENSE_REPLY_SIZE;
365 memset(sense, 0, SCSI_SENSE_REPLY_SIZE);
366 retval = __cycle(dev, lun, sense, SCSI_SENSE_REPLY_SIZE, cmd, 6, 0, NULL, NULL);
367 if (retval>=0) {
368 switch (sense[2]&0xF) {
369 case SCSI_SENSE_NOT_READY:
370 return USBSTORAGE_EINIT;
371 case SCSI_SENSE_MEDIUM_ERROR:
372 case SCSI_SENSE_HARDWARE_ERROR:
373 return USBSTORAGE_ESENSE;
378 return retval;
381 static s32 __usbstorage_reset(usbstorage_handle *dev)
383 u32 t = usbtimeout;
384 usbtimeout = 1;
385 s32 retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_RESET, 0, dev->interface, 0, NULL);
386 usleep(100);
387 usbtimeout = t;
388 return retval;
391 s32 USBStorage_Open(usbstorage_handle *dev, s32 device_id, u16 vid, u16 pid)
393 s32 retval = -1;
394 u8 conf = -1;
395 u8 *max_lun;
396 u32 iConf, iInterface, iEp;
397 usb_devdesc udd;
398 usb_configurationdesc *ucd;
399 usb_interfacedesc *uid;
400 usb_endpointdesc *ued;
401 bool reset_flag = false;
403 max_lun = __lwp_heap_allocate(&__heap, 1);
404 if (!max_lun)
405 return IPC_ENOMEM;
407 memset(dev, 0, sizeof(*dev));
408 dev->usb_fd = -1;
410 dev->tag = TAG_START;
412 if (LWP_MutexInit(&dev->lock, false) < 0)
413 goto free_and_return;
415 if (SYS_CreateAlarm(&dev->alarm) < 0)
416 goto free_and_return;
418 retry_init:
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->bInterfaceSubClass == MASS_STORAGE_RBC_COMMANDS
435 || uid->bInterfaceSubClass == MASS_STORAGE_ATA_COMMANDS
436 || uid->bInterfaceSubClass == MASS_STORAGE_QIC_COMMANDS
437 || uid->bInterfaceSubClass == MASS_STORAGE_UFI_COMMANDS
438 || uid->bInterfaceSubClass == MASS_STORAGE_SFF8070_COMMANDS) &&
439 uid->bInterfaceProtocol == MASS_STORAGE_BULK_ONLY)
441 if (uid->bNumEndpoints < 2)
442 continue;
445 dev->ep_in = dev->ep_out = 0;
446 for (iEp = 0; iEp < uid->bNumEndpoints; iEp++) {
447 ued = &uid->endpoints[iEp];
448 if (ued->bmAttributes != USB_ENDPOINT_BULK)
449 continue;
451 if (ued->bEndpointAddress & USB_ENDPOINT_IN) {
452 dev->ep_in = ued->bEndpointAddress;
454 else {
455 dev->ep_out = ued->bEndpointAddress;
456 if(ued->wMaxPacketSize > 64 && (dev->usb_fd>=0x20 || dev->usb_fd<-1))
457 usb2_mode=true;
458 else
459 usb2_mode=false;
463 if (dev->ep_in != 0 && dev->ep_out != 0) {
464 dev->configuration = ucd->bConfigurationValue;
465 dev->interface = uid->bInterfaceNumber;
466 dev->altInterface = uid->bAlternateSetting;
467 goto found;
474 USB_FreeDescriptors(&udd);
475 retval = USBSTORAGE_ENOINTERFACE;
476 goto free_and_return;
478 found:
479 dev->bInterfaceSubClass = uid->bInterfaceSubClass;
481 USB_FreeDescriptors(&udd);
483 retval = USBSTORAGE_EINIT;
484 // some devices return an error, ignore it
485 USB_GetConfiguration(dev->usb_fd, &conf);
487 if (conf != dev->configuration && USB_SetConfiguration(dev->usb_fd, dev->configuration) < 0)
488 goto free_and_return;
490 if (dev->altInterface !=0 && USB_SetAlternativeInterface(dev->usb_fd, dev->interface, dev->altInterface) < 0)
491 goto free_and_return;
493 dev->suspended = 0;
495 if(!reset_flag && dev->bInterfaceSubClass == MASS_STORAGE_SCSI_COMMANDS)
497 reset_flag = true;
498 retval = USBStorage_Reset(dev);
499 if (retval < 0)
501 USB_CloseDevice(&dev->usb_fd);
502 goto retry_init;
506 LWP_MutexLock(dev->lock);
507 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);
508 LWP_MutexUnlock(dev->lock);
510 if (retval < 0)
511 dev->max_lun = 1;
512 else
513 dev->max_lun = *max_lun + 1;
515 if (retval == USBSTORAGE_ETIMEDOUT)
516 goto free_and_return;
518 retval = USBSTORAGE_OK;
519 dev->sector_size = (u32 *) calloc(dev->max_lun, sizeof(u32));
520 if(!dev->sector_size) {
521 retval = IPC_ENOMEM;
522 goto free_and_return;
525 /* taken from linux usbstorage module (drivers/usb/storage/transport.c)
527 * Some devices (i.e. Iomega Zip100) need this -- apparently
528 * the bulk pipes get STALLed when the GetMaxLUN request is
529 * processed. This is, in theory, harmless to all other devices
530 * (regardless of if they stall or not).
532 * 8/9/10: If anyone wants to actually use a Zip100, they can add this back.
533 * But for now, it seems to be breaking things more than it is helping.
535 //USB_ClearHalt(dev->usb_fd, dev->ep_in);
536 //USB_ClearHalt(dev->usb_fd, dev->ep_out);
538 if(!dev->buffer)
539 dev->buffer = __lwp_heap_allocate(&__heap, MAX_TRANSFER_SIZE_V5);
541 if(!dev->buffer) {
542 retval = IPC_ENOMEM;
543 } else {
544 USB_DeviceRemovalNotifyAsync(dev->usb_fd,__usb_deviceremoved_cb,dev);
545 retval = USBSTORAGE_OK;
548 free_and_return:
549 if (max_lun)
550 __lwp_heap_free(&__heap, max_lun);
552 if (retval < 0) {
553 USBStorage_Close(dev);
554 return retval;
557 return 0;
560 s32 USBStorage_Close(usbstorage_handle *dev)
562 __mounted = false;
563 __lun = 0;
564 __vid = 0;
565 __pid = 0;
567 if (dev->usb_fd != -1)
568 USB_CloseDevice(&dev->usb_fd);
570 LWP_MutexDestroy(dev->lock);
571 SYS_RemoveAlarm(dev->alarm);
573 if(dev->sector_size)
574 free(dev->sector_size);
576 if (dev->buffer)
577 __lwp_heap_free(&__heap, dev->buffer);
579 memset(dev, 0, sizeof(*dev));
580 dev->usb_fd = -1;
581 return 0;
584 s32 USBStorage_Reset(usbstorage_handle *dev)
586 s32 retval;
588 LWP_MutexLock(dev->lock);
589 retval = __usbstorage_reset(dev);
590 LWP_MutexUnlock(dev->lock);
592 return retval;
595 s32 USBStorage_GetMaxLUN(usbstorage_handle *dev)
597 return dev->max_lun;
600 s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun)
602 s32 retval;
604 if(lun >= dev->max_lun)
605 return IPC_EINVAL;
607 retval = __usbstorage_clearerrors(dev, lun);
608 if (retval<0)
609 return retval;
611 retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], NULL);
613 return retval;
616 s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors)
618 s32 retval;
619 u8 cmd[10] = {SCSI_READ_CAPACITY, lun<<5};
620 u8 response[8];
622 retval = __cycle(dev, lun, response, sizeof(response), cmd, sizeof(cmd), 0, NULL, NULL);
623 if(retval >= 0)
625 if(n_sectors != NULL)
626 memcpy(n_sectors, response, 4);
627 if(sector_size != NULL)
628 memcpy(sector_size, response + 4, 4);
629 retval = USBSTORAGE_OK;
632 return retval;
635 /* lo_ej = load/eject, controls the tray
636 * start = start(1) or stop(0) the motor (or eject(0), load(1))
637 * imm = return before the command has completed
638 * it might be a good idea to call this before STM_ShutdownToStandby() so the USB HDD doesn't stay on
640 s32 USBStorage_StartStop(usbstorage_handle *dev, u8 lun, u8 lo_ej, u8 start, u8 imm)
642 u8 status = 0;
643 s32 retval = USBSTORAGE_OK;
644 u8 cmd[] = {
645 SCSI_START_STOP,
646 (lun << 5) | (imm&1),
649 ((lo_ej&1)<<1) | (start&1),
653 if(lun >= dev->max_lun)
654 return IPC_EINVAL;
656 LWP_MutexLock(dev->lock);
658 retval = __send_cbw(dev, lun, 0, CBW_IN, cmd, sizeof(cmd));
660 // if imm==0, wait up to 10secs for spinup to finish
661 if (retval >= 0)
662 retval = __read_csw(dev, &status, NULL, (imm ? USBSTORAGE_TIMEOUT : 10));
664 LWP_MutexUnlock(dev->lock);
666 if(retval >=0 && status != 0)
667 retval = USBSTORAGE_ESTATUS;
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 // more than 60s since last use - make sure drive is awake
693 if(ticks_to_secs(gettime() - usb_last_used) > 60)
695 usbtimeout = 10;
696 USB_ResumeDevice(dev->usb_fd);
698 if(dev->bInterfaceSubClass == MASS_STORAGE_SCSI_COMMANDS)
700 retval = __usbstorage_clearerrors(dev, lun);
702 if (retval < 0)
703 return retval;
705 retval = USBStorage_StartStop(dev, lun, 0, 1, 0);
707 if (retval < 0)
708 return retval;
712 retval = __cycle(dev, lun, buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 0, &status, NULL);
713 if(retval > 0 && status != 0)
714 retval = USBSTORAGE_ESTATUS;
716 usb_last_used = gettime();
717 usbtimeout = USBSTORAGE_TIMEOUT;
719 return retval;
722 s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer)
724 u8 status = 0;
725 s32 retval;
726 u8 cmd[] = {
727 SCSI_WRITE_10,
728 lun << 5,
729 sector >> 24,
730 sector >> 16,
731 sector >> 8,
732 sector,
734 n_sectors >> 8,
735 n_sectors,
739 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
740 return IPC_EINVAL;
742 // more than 60s since last use - make sure drive is awake
743 if(ticks_to_secs(gettime() - usb_last_used) > 60)
745 usbtimeout = 10;
746 USB_ResumeDevice(dev->usb_fd);
748 if(dev->bInterfaceSubClass == MASS_STORAGE_SCSI_COMMANDS)
750 retval = __usbstorage_clearerrors(dev, lun);
752 if (retval < 0)
753 return retval;
754 retval = USBStorage_StartStop(dev, lun, 0, 1, 0);
756 if (retval < 0)
757 return retval;
761 retval = __cycle(dev, lun, (u8 *)buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 1, &status, NULL);
762 if(retval > 0 && status != 0)
763 retval = USBSTORAGE_ESTATUS;
765 usb_last_used = gettime();
766 usbtimeout = USBSTORAGE_TIMEOUT;
768 return retval;
771 s32 USBStorage_Suspend(usbstorage_handle *dev)
773 if(dev->suspended == 1)
774 return USBSTORAGE_OK;
776 USB_SuspendDevice(dev->usb_fd);
777 dev->suspended = 1;
779 return USBSTORAGE_OK;
783 The following is for implementing a DISC_INTERFACE
784 as used by libfat
787 static bool __usbstorage_Startup(void)
789 if(USB_Initialize() < 0 || USBStorage_Initialize() < 0)
790 return false;
792 return true;
795 static bool __usbstorage_IsInserted(void)
797 usb_device_entry *buffer;
798 u8 device_count;
799 u8 i, j;
800 u16 vid, pid;
801 s32 maxLun;
802 s32 retval;
804 if(__mounted)
805 return true;
807 if(!__inited)
808 return false;
810 buffer = (usb_device_entry*)__lwp_heap_allocate(&__heap, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
811 if (!buffer)
812 return false;
814 memset(buffer, 0, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
816 if (USB_GetDeviceList(buffer, DEVLIST_MAXSIZE, USB_CLASS_MASS_STORAGE, &device_count) < 0)
818 if (__vid != 0 || __pid != 0)
819 USBStorage_Close(&__usbfd);
821 __lwp_heap_free(&__heap, buffer);
822 return false;
825 usleep(100);
827 if (__vid != 0 || __pid != 0) {
828 for(i = 0; i < device_count; i++) {
829 vid = buffer[i].vid;
830 pid = buffer[i].pid;
831 if(vid != 0 || pid != 0) {
832 if((vid == __vid) && (pid == __pid)) {
833 __mounted = true;
834 __lwp_heap_free(&__heap,buffer);
835 usleep(50); // I don't know why I have to wait but it's needed
836 return true;
840 USBStorage_Close(&__usbfd);
841 __lwp_heap_free(&__heap,buffer);
842 return false;
845 for (i = 0; i < device_count; i++) {
846 vid = buffer[i].vid;
847 pid = buffer[i].pid;
848 if (vid == 0 || pid == 0)
849 continue;
851 if (USBStorage_Open(&__usbfd, buffer[i].device_id, vid, pid) < 0)
852 continue;
854 maxLun = USBStorage_GetMaxLUN(&__usbfd);
855 for (j = 0; j < maxLun; j++) {
856 retval = USBStorage_MountLUN(&__usbfd, j);
858 if (retval == USBSTORAGE_ETIMEDOUT)
859 break;
861 if (retval < 0)
862 continue;
864 __mounted = true;
865 __lun = j;
866 __vid = vid;
867 __pid = pid;
868 usb_last_used = gettime()-secs_to_ticks(100);
870 break;
873 if (__mounted)
874 break;
876 USBStorage_Close(&__usbfd);
878 __lwp_heap_free(&__heap, buffer);
879 return __mounted;
882 static bool __usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer)
884 s32 retval;
886 if (!__mounted)
887 return false;
889 retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer);
891 return retval >= 0;
894 static bool __usbstorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer)
896 s32 retval;
898 if (!__mounted)
899 return false;
901 retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer);
903 return retval >= 0;
906 static bool __usbstorage_ClearStatus(void)
908 return true;
911 static bool __usbstorage_Shutdown(void)
913 if (__vid != 0 || __pid != 0)
914 USBStorage_Close(&__usbfd);
916 return true;
919 DISC_INTERFACE __io_usbstorage = {
920 DEVICE_TYPE_WII_USB,
921 FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB,
922 (FN_MEDIUM_STARTUP)&__usbstorage_Startup,
923 (FN_MEDIUM_ISINSERTED)&__usbstorage_IsInserted,
924 (FN_MEDIUM_READSECTORS)&__usbstorage_ReadSectors,
925 (FN_MEDIUM_WRITESECTORS)&__usbstorage_WriteSectors,
926 (FN_MEDIUM_CLEARSTATUS)&__usbstorage_ClearStatus,
927 (FN_MEDIUM_SHUTDOWN)&__usbstorage_Shutdown
930 #endif /* HW_RVL */