smb improvements (rodries)
[libogc.git] / libogc / usbstorage.c
blob634f18e9117da27f773f52fb341c4be7e10a40f2
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_INQUIRY 0x12
62 #define SCSI_START_STOP 0x1B
63 #define SCSI_READ_CAPACITY 0x25
64 #define SCSI_READ_10 0x28
65 #define SCSI_WRITE_10 0x2A
68 #define SCSI_SENSE_REPLY_SIZE 18
69 #define SCSI_SENSE_NOT_READY 0x02
70 #define SCSI_SENSE_MEDIUM_ERROR 0x03
71 #define SCSI_SENSE_HARDWARE_ERROR 0x04
73 #define USB_CLASS_MASS_STORAGE 0x08
74 #define MASS_STORAGE_RBC_COMMANDS 0x01
75 #define MASS_STORAGE_ATA_COMMANDS 0x02
76 #define MASS_STORAGE_QIC_COMMANDS 0x03
77 #define MASS_STORAGE_UFI_COMMANDS 0x04
78 #define MASS_STORAGE_SFF8070_COMMANDS 0x05
79 #define MASS_STORAGE_SCSI_COMMANDS 0x06
80 #define MASS_STORAGE_BULK_ONLY 0x50
82 #define USBSTORAGE_GET_MAX_LUN 0xFE
83 #define USBSTORAGE_RESET 0xFF
85 #define USB_ENDPOINT_BULK 0x02
87 #define USBSTORAGE_CYCLE_RETRIES 3
88 #define USBSTORAGE_TIMEOUT 2
90 #define MAX_TRANSFER_SIZE_V0 4096
91 #define MAX_TRANSFER_SIZE_V5 (16*1024)
93 #define DEVLIST_MAXSIZE 8
95 static heap_cntrl __heap;
96 static bool __inited = false;
97 static u64 usb_last_used = 0;
98 static lwpq_t __usbstorage_waitq = 0;
99 static u32 usbtimeout = USBSTORAGE_TIMEOUT;
102 The following is for implementing a DISC_INTERFACE
103 as used by libfat
106 static usbstorage_handle __usbfd;
107 static u8 __lun = 0;
108 static bool __mounted = false;
109 static u16 __vid = 0;
110 static u16 __pid = 0;
111 static bool usb2_mode=true;
113 static s32 __usbstorage_reset(usbstorage_handle *dev);
114 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun);
115 s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun);
117 /* XXX: this is a *really* dirty and ugly way to send a bulkmessage with a timeout
118 * but there's currently no other known way of doing this and it's in my humble
119 * opinion still better than having a function blocking forever while waiting
120 * for the USB data/IOS reply..
123 static s32 __usb_blkmsg_cb(s32 retval, void *dummy)
125 usbstorage_handle *dev = (usbstorage_handle *)dummy;
126 dev->retval = retval;
127 SYS_CancelAlarm(dev->alarm);
128 LWP_ThreadBroadcast(__usbstorage_waitq);
129 return 0;
132 static s32 __usb_deviceremoved_cb(s32 retval,void *arg)
134 __mounted = false;
135 if(__vid != 0) USBStorage_Close(&__usbfd);
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 static u8 *arena_ptr=NULL;
198 s32 USBStorage_Initialize()
200 u32 level;
202 if(__inited)
203 return IPC_OK;
205 _CPU_ISR_Disable(level);
206 LWP_InitQueue(&__usbstorage_waitq);
207 if(!arena_ptr) {
208 arena_ptr = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - HEAP_SIZE));
209 if((u32)arena_ptr < (u32)SYS_GetArena2Lo()) {
210 _CPU_ISR_Restore(level);
211 return IPC_ENOMEM;
213 SYS_SetArena2Hi(arena_ptr);
215 __lwp_heap_init(&__heap, arena_ptr, HEAP_SIZE, 32);
216 __inited = true;
217 _CPU_ISR_Restore(level);
218 return IPC_OK;
221 static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u8 *cb, u8 cbLen)
223 s32 retval = USBSTORAGE_OK;
225 if(cbLen == 0 || cbLen > 16)
226 return IPC_EINVAL;
228 memset(dev->buffer, 0, CBW_SIZE);
230 __stwbrx(dev->buffer, 0, CBW_SIGNATURE);
231 __stwbrx(dev->buffer, 4, ++dev->tag);
232 __stwbrx(dev->buffer, 8, len);
233 dev->buffer[12] = flags;
234 dev->buffer[13] = lun;
235 dev->buffer[14] = (cbLen > 6 ? 10 : 6);
237 memcpy(dev->buffer + 15, cb, cbLen);
239 if(dev->suspended == 1)
241 USB_ResumeDevice(dev->usb_fd);
242 dev->suspended = 0;
245 retval = __USB_BlkMsgTimeout(dev, dev->ep_out, CBW_SIZE, (void *)dev->buffer, usbtimeout);
247 if(retval == CBW_SIZE) return USBSTORAGE_OK;
248 else if(retval > 0) return USBSTORAGE_ESHORTWRITE;
250 return retval;
253 static s32 __read_csw(usbstorage_handle *dev, u8 *status, u32 *dataResidue, u32 timeout)
255 s32 retval = USBSTORAGE_OK;
256 u32 signature, tag, _dataResidue, _status;
258 memset(dev->buffer, 0, CSW_SIZE);
260 retval = __USB_BlkMsgTimeout(dev, dev->ep_in, CSW_SIZE, dev->buffer, timeout);
261 if(retval > 0 && retval != CSW_SIZE) return USBSTORAGE_ESHORTREAD;
262 else if(retval < 0) return retval;
264 signature = __lwbrx(dev->buffer, 0);
265 tag = __lwbrx(dev->buffer, 4);
266 _dataResidue = __lwbrx(dev->buffer, 8);
267 _status = dev->buffer[12];
269 if(signature != CSW_SIGNATURE) return USBSTORAGE_ESIGNATURE;
271 if(dataResidue != NULL)
272 *dataResidue = _dataResidue;
273 if(status != NULL)
274 *status = _status;
276 if(tag != dev->tag) return USBSTORAGE_ETAG;
278 return USBSTORAGE_OK;
281 static s32 __cycle(usbstorage_handle *dev, u8 lun, u8 *buffer, u32 len, u8 *cb, u8 cbLen, u8 write, u8 *_status, u32 *_dataResidue)
283 s32 retval = USBSTORAGE_OK;
285 u8 status=0;
286 u32 dataResidue = 0;
287 u16 max_size;
288 u8 ep = write ? dev->ep_out : dev->ep_in;
289 s8 retries = USBSTORAGE_CYCLE_RETRIES + 1;
291 if(usb2_mode)
292 max_size=MAX_TRANSFER_SIZE_V5;
293 else
294 max_size=MAX_TRANSFER_SIZE_V0;
296 LWP_MutexLock(dev->lock);
299 u8 *_buffer = buffer;
300 u32 _len = len;
301 retries--;
303 if(retval == USBSTORAGE_ETIMEDOUT)
304 break;
306 retval = __send_cbw(dev, lun, len, (write ? CBW_OUT:CBW_IN), cb, cbLen);
308 while(_len > 0 && retval >= 0)
310 u32 thisLen = _len > max_size ? max_size : _len;
312 if ((u32)_buffer&0x1F || !((u32)_buffer&0x10000000)) {
313 if (write) memcpy(dev->buffer, _buffer, thisLen);
314 retval = __USB_BlkMsgTimeout(dev, ep, thisLen, dev->buffer, usbtimeout);
315 if (!write && retval > 0)
316 memcpy(_buffer, dev->buffer, retval);
317 } else
318 retval = __USB_BlkMsgTimeout(dev, ep, thisLen, _buffer, usbtimeout);
320 if (retval == thisLen) {
321 _len -= retval;
322 _buffer += retval;
324 else if (retval != USBSTORAGE_ETIMEDOUT)
325 retval = USBSTORAGE_EDATARESIDUE;
328 if (retval >= 0)
329 __read_csw(dev, &status, &dataResidue, usbtimeout);
331 if (retval < 0) {
332 if (__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
333 retval = USBSTORAGE_ETIMEDOUT;
336 } while (retval < 0 && retries > 0);
338 LWP_MutexUnlock(dev->lock);
340 if(_status != NULL)
341 *_status = status;
342 if(_dataResidue != NULL)
343 *_dataResidue = dataResidue;
345 return retval;
348 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun)
350 s32 retval;
351 u8 cmd[6];
352 u8 sense[SCSI_SENSE_REPLY_SIZE];
353 u8 status = 0;
355 memset(cmd, 0, sizeof(cmd));
356 cmd[0] = SCSI_TEST_UNIT_READY;
358 retval = __cycle(dev, lun, NULL, 0, cmd, 1, 0, &status, NULL);
359 if (retval < 0) return retval;
361 if (status)
363 cmd[0] = SCSI_REQUEST_SENSE;
364 cmd[1] = lun << 5;
365 cmd[4] = SCSI_SENSE_REPLY_SIZE;
366 memset(sense, 0, SCSI_SENSE_REPLY_SIZE);
367 retval = __cycle(dev, lun, sense, SCSI_SENSE_REPLY_SIZE, cmd, 6, 0, NULL, NULL);
368 if (retval>=0) {
369 switch (sense[2]&0xF) {
370 case SCSI_SENSE_NOT_READY:
371 return USBSTORAGE_EINIT;
372 case SCSI_SENSE_MEDIUM_ERROR:
373 case SCSI_SENSE_HARDWARE_ERROR:
374 return USBSTORAGE_ESENSE;
379 return retval;
382 static s32 __usbstorage_reset(usbstorage_handle *dev)
384 u32 t = usbtimeout;
385 usbtimeout = 1;
386 s32 retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_RESET, 0, dev->interface, 0, NULL);
387 usleep(100);
388 usbtimeout = t;
389 USB_ClearHalt(dev->usb_fd, dev->ep_in);usleep(100); //from http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
390 USB_ClearHalt(dev->usb_fd, dev->ep_out);usleep(100);
391 return retval;
394 s32 USBStorage_Open(usbstorage_handle *dev, s32 device_id, u16 vid, u16 pid)
396 s32 retval = -1;
397 u8 conf = -1;
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;
404 bool reset_flag = false;
406 max_lun = __lwp_heap_allocate(&__heap, 1);
407 if (!max_lun)
408 return IPC_ENOMEM;
410 memset(dev, 0, sizeof(*dev));
411 dev->usb_fd = -1;
413 dev->tag = TAG_START;
415 if (LWP_MutexInit(&dev->lock, false) < 0)
416 goto free_and_return;
418 if (SYS_CreateAlarm(&dev->alarm) < 0)
419 goto free_and_return;
421 retry_init:
423 retval = USB_OpenDevice(device_id, vid, pid, &dev->usb_fd);
424 if (retval < 0)
425 goto free_and_return;
427 retval = USB_GetDescriptors(dev->usb_fd, &udd);
428 if (retval < 0)
429 goto free_and_return;
431 for (iConf = 0; iConf < udd.bNumConfigurations; iConf++) {
432 ucd = &udd.configurations[iConf];
433 for (iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) {
434 uid = &ucd->interfaces[iInterface];
435 if(uid->bInterfaceClass == USB_CLASS_MASS_STORAGE &&
436 uid->bInterfaceProtocol == MASS_STORAGE_BULK_ONLY)
438 if (uid->bNumEndpoints < 2)
439 continue;
441 dev->ep_in = dev->ep_out = 0;
442 for (iEp = 0; iEp < uid->bNumEndpoints; iEp++) {
443 ued = &uid->endpoints[iEp];
444 if (ued->bmAttributes != USB_ENDPOINT_BULK)
445 continue;
447 if (ued->bEndpointAddress & USB_ENDPOINT_IN) {
448 dev->ep_in = ued->bEndpointAddress;
450 else {
451 dev->ep_out = ued->bEndpointAddress;
452 if(ued->wMaxPacketSize > 64 && (dev->usb_fd>=0x20 || dev->usb_fd<-1))
453 usb2_mode=true;
454 else
455 usb2_mode=false;
459 if (dev->ep_in != 0 && dev->ep_out != 0) {
460 dev->configuration = ucd->bConfigurationValue;
461 dev->interface = uid->bInterfaceNumber;
462 dev->altInterface = uid->bAlternateSetting;
463 goto found;
469 USB_FreeDescriptors(&udd);
470 retval = USBSTORAGE_ENOINTERFACE;
471 goto free_and_return;
473 found:
474 dev->bInterfaceSubClass = uid->bInterfaceSubClass;
476 USB_FreeDescriptors(&udd);
478 retval = USBSTORAGE_EINIT;
479 // some devices return an error, ignore it
480 USB_GetConfiguration(dev->usb_fd, &conf);
482 if(!usb2_mode)
483 retval = USBStorage_Reset(dev);
485 dev->suspended = 0;
487 LWP_MutexLock(dev->lock);
488 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);
489 LWP_MutexUnlock(dev->lock);
491 if (retval < 0)
492 dev->max_lun = 1;
493 else
494 dev->max_lun = *max_lun + 1;
496 if (retval == USBSTORAGE_ETIMEDOUT)
497 goto free_and_return;
499 retval = USBSTORAGE_OK;
500 dev->sector_size = (u32 *) calloc(dev->max_lun, sizeof(u32));
501 if(!dev->sector_size) {
502 retval = IPC_ENOMEM;
503 goto free_and_return;
506 /* taken from linux usbstorage module (drivers/usb/storage/transport.c)
508 * Some devices (i.e. Iomega Zip100) need this -- apparently
509 * the bulk pipes get STALLed when the GetMaxLUN request is
510 * processed. This is, in theory, harmless to all other devices
511 * (regardless of if they stall or not).
513 * 8/9/10: If anyone wants to actually use a Zip100, they can add this back.
514 * But for now, it seems to be breaking things more than it is helping.
516 //USB_ClearHalt(dev->usb_fd, dev->ep_in);
517 //USB_ClearHalt(dev->usb_fd, dev->ep_out);
519 if(!dev->buffer)
520 dev->buffer = __lwp_heap_allocate(&__heap, MAX_TRANSFER_SIZE_V5);
522 if(!dev->buffer) {
523 retval = IPC_ENOMEM;
524 } else {
525 USB_DeviceRemovalNotifyAsync(dev->usb_fd,__usb_deviceremoved_cb,dev);
526 retval = USBSTORAGE_OK;
529 free_and_return:
530 if (max_lun)
531 __lwp_heap_free(&__heap, max_lun);
533 if (retval < 0) {
534 USBStorage_Close(dev);
535 return retval;
538 return 0;
541 s32 USBStorage_Close(usbstorage_handle *dev)
543 __mounted = false;
544 __lun = 0;
545 __vid = 0;
546 __pid = 0;
548 if (dev->usb_fd != -1)
549 USB_CloseDevice(&dev->usb_fd);
551 LWP_MutexDestroy(dev->lock);
552 SYS_RemoveAlarm(dev->alarm);
554 if(dev->sector_size)
555 free(dev->sector_size);
557 if (dev->buffer)
558 __lwp_heap_free(&__heap, dev->buffer);
560 memset(dev, 0, sizeof(*dev));
561 dev->usb_fd = -1;
562 return 0;
565 s32 USBStorage_Reset(usbstorage_handle *dev)
567 s32 retval;
569 LWP_MutexLock(dev->lock);
570 retval = __usbstorage_reset(dev);
571 LWP_MutexUnlock(dev->lock);
573 return retval;
576 s32 USBStorage_GetMaxLUN(usbstorage_handle *dev)
578 return dev->max_lun;
581 s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun)
583 s32 retval;
585 if(lun >= dev->max_lun)
586 return IPC_EINVAL;
588 usleep(50);
589 retval = __usbstorage_clearerrors(dev, lun);
590 if (retval<0)
592 USBStorage_Reset(dev);
593 retval = __usbstorage_clearerrors(dev, lun);
596 retval = USBStorage_Inquiry(dev, lun);
597 retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], NULL);
598 return retval;
601 s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun)
603 int n;
604 s32 retval;
605 u8 cmd[] = {SCSI_INQUIRY, lun << 5,0,0,36,0};
606 u8 response[36];
608 for(n=0;n<2;n++)
610 memset(response,0,36);
611 retval = __cycle(dev, lun, response, 36, cmd, 6, 0, NULL, NULL);
612 if(retval>=0) break;
614 if(retval>=0) retval=*response & 31;
616 if(retval>=0)
618 switch(*response & 31)
620 // info from http://en.wikipedia.org/wiki/SCSI_Peripheral_Device_Type
621 case 5: // CDROM
622 case 7: // optical memory device (e.g., some optical disks)
623 is_dvd=1;
624 break;
625 default:
626 is_dvd=0;
627 break;
631 return retval;
634 s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors)
636 s32 retval;
637 u8 cmd[10] = {SCSI_READ_CAPACITY, lun<<5};
638 u8 response[8];
640 retval = __cycle(dev, lun, response, sizeof(response), cmd, sizeof(cmd), 0, NULL, NULL);
641 if(retval >= 0)
643 if(n_sectors != NULL)
644 memcpy(n_sectors, response, 4);
645 if(sector_size != NULL)
646 memcpy(sector_size, response + 4, 4);
647 retval = USBSTORAGE_OK;
650 return retval;
653 /* lo_ej = load/eject, controls the tray
654 * start = start(1) or stop(0) the motor (or eject(0), load(1))
655 * imm = return before the command has completed
656 * it might be a good idea to call this before STM_ShutdownToStandby() so the USB HDD doesn't stay on
658 s32 USBStorage_StartStop(usbstorage_handle *dev, u8 lun, u8 lo_ej, u8 start, u8 imm)
660 u8 status = 0;
661 s32 retval = USBSTORAGE_OK;
662 u8 cmd[] = {
663 SCSI_START_STOP,
664 (lun << 5) | (imm&1),
667 ((lo_ej&1)<<1) | (start&1),
671 if(lun >= dev->max_lun)
672 return IPC_EINVAL;
674 LWP_MutexLock(dev->lock);
676 retval = __send_cbw(dev, lun, 0, CBW_IN, cmd, sizeof(cmd));
678 // if imm==0, wait up to 10secs for spinup to finish
679 if (retval >= 0)
680 retval = __read_csw(dev, &status, NULL, (imm ? USBSTORAGE_TIMEOUT : 10));
682 LWP_MutexUnlock(dev->lock);
684 if(retval >=0 && status != 0)
685 retval = USBSTORAGE_ESTATUS;
687 return retval;
690 s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer)
692 u8 status = 0;
693 s32 retval;
694 u8 cmd[] = {
695 SCSI_READ_10,
696 lun << 5,
697 sector >> 24,
698 sector >> 16,
699 sector >> 8,
700 sector,
702 n_sectors >> 8,
703 n_sectors,
707 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
708 return IPC_EINVAL;
710 // more than 60s since last use - make sure drive is awake
711 if(ticks_to_secs(gettime() - usb_last_used) > 60)
713 usbtimeout = 10;
716 retval = __cycle(dev, lun, buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 0, &status, NULL);
718 if(retval > 0 && status != 0)
719 retval = USBSTORAGE_ESTATUS;
721 usb_last_used = gettime();
722 usbtimeout = USBSTORAGE_TIMEOUT;
724 return retval;
727 s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer)
729 u8 status = 0;
730 s32 retval;
731 u8 cmd[] = {
732 SCSI_WRITE_10,
733 lun << 5,
734 sector >> 24,
735 sector >> 16,
736 sector >> 8,
737 sector,
739 n_sectors >> 8,
740 n_sectors,
744 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
745 return IPC_EINVAL;
747 // more than 60s since last use - make sure drive is awake
748 if(ticks_to_secs(gettime() - usb_last_used) > 60)
750 usbtimeout = 10;
753 retval = __cycle(dev, lun, (u8 *)buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 1, &status, NULL);
754 if(retval > 0 && status != 0)
755 retval = USBSTORAGE_ESTATUS;
757 usb_last_used = gettime();
758 usbtimeout = USBSTORAGE_TIMEOUT;
760 return retval;
763 s32 USBStorage_Suspend(usbstorage_handle *dev)
765 if(dev->suspended == 1)
766 return USBSTORAGE_OK;
768 USB_SuspendDevice(dev->usb_fd);
769 dev->suspended = 1;
771 return USBSTORAGE_OK;
775 The following is for implementing a DISC_INTERFACE
776 as used by libfat
779 static bool __usbstorage_Startup(void)
781 if(USB_Initialize() < 0 || USBStorage_Initialize() < 0)
782 return false;
784 return true;
787 static bool __usbstorage_IsInserted(void)
789 usb_device_entry *buffer;
790 u8 device_count;
791 u8 i, j;
792 u16 vid, pid;
793 s32 maxLun;
794 s32 retval;
796 if(__mounted)
797 return true;
799 if(!__inited)
800 return false;
802 buffer = (usb_device_entry*)__lwp_heap_allocate(&__heap, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
803 if (!buffer)
804 return false;
806 memset(buffer, 0, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
808 if (USB_GetDeviceList(buffer, DEVLIST_MAXSIZE, USB_CLASS_MASS_STORAGE, &device_count) < 0)
810 if (__vid != 0 || __pid != 0)
811 USBStorage_Close(&__usbfd);
813 __lwp_heap_free(&__heap, buffer);
814 return false;
817 usleep(100);
819 if (__vid != 0 || __pid != 0) {
820 for(i = 0; i < device_count; i++) {
821 vid = buffer[i].vid;
822 pid = buffer[i].pid;
823 if(vid != 0 || pid != 0) {
824 if((vid == __vid) && (pid == __pid)) {
825 __mounted = true;
826 __lwp_heap_free(&__heap,buffer);
827 usleep(50); // I don't know why I have to wait but it's needed
828 return true;
832 USBStorage_Close(&__usbfd);
833 __lwp_heap_free(&__heap,buffer);
834 return false;
836 for (i = 0; i < device_count; i++) {
837 vid = buffer[i].vid;
838 pid = buffer[i].pid;
839 if (vid == 0 || pid == 0)
840 continue;
842 if (USBStorage_Open(&__usbfd, buffer[i].device_id, vid, pid) < 0)
843 continue;
845 maxLun = USBStorage_GetMaxLUN(&__usbfd);
847 for (j = 0; j < maxLun; j++) {
848 retval = USBStorage_MountLUN(&__usbfd, j);
850 if (retval < 0)
852 __usbstorage_reset(&__usbfd);
853 continue;
856 __mounted = true;
857 __lun = j;
858 __vid = vid;
859 __pid = pid;
860 usb_last_used = gettime()-secs_to_ticks(100);
861 usleep(100);
862 break;
865 if (__mounted)
866 break;
868 USBStorage_Close(&__usbfd);
870 __lwp_heap_free(&__heap, buffer);
871 return __mounted;
874 static bool __usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer)
876 s32 retval;
878 if (!__mounted)
879 return false;
881 retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer);
883 return retval >= 0;
886 static bool __usbstorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer)
888 s32 retval;
890 if (!__mounted)
891 return false;
893 retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer);
895 return retval >= 0;
898 static bool __usbstorage_ClearStatus(void)
900 return true;
903 static bool __usbstorage_Shutdown(void)
905 if (__vid != 0 || __pid != 0)
906 USBStorage_Close(&__usbfd);
908 return true;
911 void USBStorage_Deinitialize()
913 __usbstorage_Shutdown();
914 LWP_CloseQueue(__usbstorage_waitq);
915 __inited = false;
918 DISC_INTERFACE __io_usbstorage = {
919 DEVICE_TYPE_WII_USB,
920 FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB,
921 (FN_MEDIUM_STARTUP)&__usbstorage_Startup,
922 (FN_MEDIUM_ISINSERTED)&__usbstorage_IsInserted,
923 (FN_MEDIUM_READSECTORS)&__usbstorage_ReadSectors,
924 (FN_MEDIUM_WRITESECTORS)&__usbstorage_WriteSectors,
925 (FN_MEDIUM_CLEARSTATUS)&__usbstorage_ClearStatus,
926 (FN_MEDIUM_SHUTDOWN)&__usbstorage_Shutdown
929 #endif /* HW_RVL */