usbstorage - optimization
[libogc.git] / libogc / usbstorage.c
blobfd50fd4343e5ad9e63718e60e55745350a781d77
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
67 #define SCSI_SENSE_REPLY_SIZE 18
68 #define SCSI_SENSE_NOT_READY 0x02
69 #define SCSI_SENSE_MEDIUM_ERROR 0x03
70 #define SCSI_SENSE_HARDWARE_ERROR 0x04
72 #define USB_CLASS_MASS_STORAGE 0x08
73 #define MASS_STORAGE_RBC_COMMANDS 0x01
74 #define MASS_STORAGE_ATA_COMMANDS 0x02
75 #define MASS_STORAGE_QIC_COMMANDS 0x03
76 #define MASS_STORAGE_UFI_COMMANDS 0x04
77 #define MASS_STORAGE_SFF8070_COMMANDS 0x05
78 #define MASS_STORAGE_SCSI_COMMANDS 0x06
79 #define MASS_STORAGE_BULK_ONLY 0x50
81 #define USBSTORAGE_GET_MAX_LUN 0xFE
82 #define USBSTORAGE_RESET 0xFF
84 #define USB_ENDPOINT_BULK 0x02
86 #define USBSTORAGE_CYCLE_RETRIES 3
87 #define USBSTORAGE_TIMEOUT 2
89 #define INVALID_LUN -2
91 #define MAX_TRANSFER_SIZE_V0 4096
92 #define MAX_TRANSFER_SIZE_V5 (16*1024)
94 #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
107 static usbstorage_handle __usbfd;
108 static u8 __lun = 0;
109 static bool __mounted = false;
110 static u16 __vid = 0;
111 static u16 __pid = 0;
112 static bool usb2_mode=true;
114 static s32 __usbstorage_reset(usbstorage_handle *dev);
115 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun);
116 s32 USBStorage_Inquiry(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 if(__vid != 0) USBStorage_Close(&__usbfd);
137 return 0;
140 static void __usb_timeouthandler(syswd_t alarm,void *cbarg)
142 usbstorage_handle *dev = (usbstorage_handle*)cbarg;
143 dev->retval = USBSTORAGE_ETIMEDOUT;
144 LWP_ThreadBroadcast(__usbstorage_waitq);
147 static void __usb_settimeout(usbstorage_handle *dev, u32 secs)
149 struct timespec ts;
151 ts.tv_sec = secs;
152 ts.tv_nsec = 0;
153 SYS_SetAlarm(dev->alarm,&ts,__usb_timeouthandler,dev);
156 static s32 __USB_BlkMsgTimeout(usbstorage_handle *dev, u8 bEndpoint, u16 wLength, void *rpData, u32 timeout)
158 s32 retval;
160 dev->retval = USBSTORAGE_PROCESSING;
161 retval = USB_WriteBlkMsgAsync(dev->usb_fd, bEndpoint, wLength, rpData, __usb_blkmsg_cb, (void *)dev);
162 if(retval < 0) return retval;
164 __usb_settimeout(dev, timeout);
166 do {
167 retval = dev->retval;
168 if(retval!=USBSTORAGE_PROCESSING) break;
169 else LWP_ThreadSleep(__usbstorage_waitq);
170 } while(retval==USBSTORAGE_PROCESSING);
172 if (retval<0)
173 USB_ClearHalt(dev->usb_fd, bEndpoint);
175 return retval;
178 static s32 __USB_CtrlMsgTimeout(usbstorage_handle *dev, u8 bmRequestType, u8 bmRequest, u16 wValue, u16 wIndex, u16 wLength, void *rpData)
180 s32 retval;
182 dev->retval = USBSTORAGE_PROCESSING;
183 retval = USB_WriteCtrlMsgAsync(dev->usb_fd, bmRequestType, bmRequest, wValue, wIndex, wLength, rpData, __usb_blkmsg_cb, (void *)dev);
184 if(retval < 0) return retval;
186 __usb_settimeout(dev, usbtimeout);
188 do {
189 retval = dev->retval;
190 if(retval!=USBSTORAGE_PROCESSING) break;
191 else LWP_ThreadSleep(__usbstorage_waitq);
192 } while(retval==USBSTORAGE_PROCESSING);
194 return retval;
197 static u8 *arena_ptr=NULL;
198 static u8 *cbw_buffer=NULL;
200 s32 USBStorage_Initialize()
202 u32 level;
204 if(__inited)
205 return IPC_OK;
207 _CPU_ISR_Disable(level);
208 LWP_InitQueue(&__usbstorage_waitq);
209 if(!arena_ptr) {
210 arena_ptr = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - HEAP_SIZE));
211 if((u32)arena_ptr < (u32)SYS_GetArena2Lo()) {
212 _CPU_ISR_Restore(level);
213 return IPC_ENOMEM;
215 SYS_SetArena2Hi(arena_ptr);
217 __lwp_heap_init(&__heap, arena_ptr, HEAP_SIZE, 32);
218 cbw_buffer=(u8*)__lwp_heap_allocate(&__heap, 32);
219 __inited = true;
220 _CPU_ISR_Restore(level);
221 return IPC_OK;
224 static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u8 *cb, u8 cbLen)
226 s32 retval = USBSTORAGE_OK;
228 if(cbLen == 0 || cbLen > 16)
229 return IPC_EINVAL;
231 memset(cbw_buffer, 0, CBW_SIZE);
233 __stwbrx(cbw_buffer, 0, CBW_SIGNATURE);
234 __stwbrx(cbw_buffer, 4, ++dev->tag);
235 __stwbrx(cbw_buffer, 8, len);
236 cbw_buffer[12] = flags;
237 cbw_buffer[13] = lun;
238 cbw_buffer[14] = (cbLen > 6 ? 10 : 6);
240 memcpy(cbw_buffer + 15, cb, cbLen);
242 if(dev->suspended == 1)
244 USB_ResumeDevice(dev->usb_fd);
245 dev->suspended = 0;
248 retval = __USB_BlkMsgTimeout(dev, dev->ep_out, CBW_SIZE, (void *)cbw_buffer, usbtimeout);
250 if(retval == CBW_SIZE) return USBSTORAGE_OK;
251 else if(retval > 0) return USBSTORAGE_ESHORTWRITE;
253 return retval;
256 static s32 __read_csw(usbstorage_handle *dev, u8 *status, u32 *dataResidue, u32 timeout)
258 s32 retval = USBSTORAGE_OK;
259 u32 signature, tag, _dataResidue, _status;
261 memset(cbw_buffer, 0, CSW_SIZE);
263 retval = __USB_BlkMsgTimeout(dev, dev->ep_in, CSW_SIZE, cbw_buffer, timeout);
264 if(retval > 0 && retval != CSW_SIZE) return USBSTORAGE_ESHORTREAD;
265 else if(retval < 0) return retval;
267 signature = __lwbrx(cbw_buffer, 0);
268 tag = __lwbrx(cbw_buffer, 4);
269 _dataResidue = __lwbrx(cbw_buffer, 8);
270 _status = cbw_buffer[12];
272 if(signature != CSW_SIGNATURE) return USBSTORAGE_ESIGNATURE;
274 if(dataResidue != NULL)
275 *dataResidue = _dataResidue;
276 if(status != NULL)
277 *status = _status;
279 if(tag != dev->tag) return USBSTORAGE_ETAG;
281 return USBSTORAGE_OK;
284 static s32 __cycle(usbstorage_handle *dev, u8 lun, u8 *buffer, u32 len, u8 *cb, u8 cbLen, u8 write, u8 *_status, u32 *_dataResidue)
286 s32 retval = USBSTORAGE_OK;
288 u8 status=0;
289 u32 dataResidue = 0;
290 u16 max_size;
291 u8 ep = write ? dev->ep_out : dev->ep_in;
292 s8 retries = USBSTORAGE_CYCLE_RETRIES + 1;
294 if(usb2_mode)
295 max_size=MAX_TRANSFER_SIZE_V5;
296 else
297 max_size=MAX_TRANSFER_SIZE_V0;
299 LWP_MutexLock(dev->lock);
302 u8 *_buffer = buffer;
303 u32 _len = len;
304 retries--;
306 if(retval == USBSTORAGE_ETIMEDOUT)
307 break;
309 retval = __send_cbw(dev, lun, len, (write ? CBW_OUT:CBW_IN), cb, cbLen);
311 while(_len > 0 && retval >= 0)
313 u32 thisLen = _len > max_size ? max_size : _len;
315 if ((u32)_buffer&0x1F || !((u32)_buffer&0x10000000)) {
316 if (write) memcpy(dev->buffer, _buffer, thisLen);
317 retval = __USB_BlkMsgTimeout(dev, ep, thisLen, dev->buffer, usbtimeout);
318 if (!write && retval > 0)
319 memcpy(_buffer, dev->buffer, retval);
320 } else
321 retval = __USB_BlkMsgTimeout(dev, ep, thisLen, _buffer, usbtimeout);
323 if (retval == thisLen) {
324 _len -= retval;
325 _buffer += retval;
327 else if (retval != USBSTORAGE_ETIMEDOUT)
328 retval = USBSTORAGE_EDATARESIDUE;
331 if (retval >= 0)
332 retval = __read_csw(dev, &status, &dataResidue, usbtimeout);
334 if (retval < 0) {
335 if (__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
336 retval = USBSTORAGE_ETIMEDOUT;
338 } while (retval < 0 && retries > 0);
340 LWP_MutexUnlock(dev->lock);
342 if(_status != NULL)
343 *_status = status;
344 if(_dataResidue != NULL)
345 *_dataResidue = dataResidue;
347 return retval;
350 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun)
352 s32 retval;
353 u8 cmd[6];
354 u8 sense[SCSI_SENSE_REPLY_SIZE];
355 u8 status = 0;
357 memset(cmd, 0, sizeof(cmd));
358 cmd[0] = SCSI_TEST_UNIT_READY;
360 retval = __cycle(dev, lun, NULL, 0, cmd, 1, 0, &status, NULL);
361 if (retval < 0) return retval;
363 if (status)
365 cmd[0] = SCSI_REQUEST_SENSE;
366 cmd[1] = lun << 5;
367 cmd[4] = SCSI_SENSE_REPLY_SIZE;
368 memset(sense, 0, SCSI_SENSE_REPLY_SIZE);
369 retval = __cycle(dev, lun, sense, SCSI_SENSE_REPLY_SIZE, cmd, 6, 0, NULL, NULL);
370 if (retval>=0) {
371 switch (sense[2]&0xF) {
372 case SCSI_SENSE_NOT_READY:
373 return USBSTORAGE_EINIT;
374 case SCSI_SENSE_MEDIUM_ERROR:
375 case SCSI_SENSE_HARDWARE_ERROR:
376 return USBSTORAGE_ESENSE;
381 return retval;
384 static s32 __usbstorage_reset(usbstorage_handle *dev)
386 u32 t = usbtimeout;
387 usbtimeout = 1;
388 s32 retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_RESET, 0, dev->interface, 0, NULL);
389 usbtimeout = t;
390 usleep(60*1000);
391 USB_ClearHalt(dev->usb_fd, dev->ep_in);usleep(10000); //from http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
392 USB_ClearHalt(dev->usb_fd, dev->ep_out);usleep(10000);
393 return retval;
396 s32 USBStorage_Open(usbstorage_handle *dev, s32 device_id, u16 vid, u16 pid)
398 s32 retval = -1;
399 u8 conf = -1;
400 u8 *max_lun;
401 u32 iConf, iInterface, iEp;
402 usb_devdesc udd;
403 usb_configurationdesc *ucd;
404 usb_interfacedesc *uid;
405 usb_endpointdesc *ued;
407 max_lun = __lwp_heap_allocate(&__heap, 1);
408 if (!max_lun)
409 return IPC_ENOMEM;
411 memset(dev, 0, sizeof(*dev));
412 dev->usb_fd = -1;
414 dev->tag = TAG_START;
416 if (LWP_MutexInit(&dev->lock, false) < 0)
417 goto free_and_return;
419 if (SYS_CreateAlarm(&dev->alarm) < 0)
420 goto free_and_return;
422 retval = USB_OpenDevice(device_id, vid, pid, &dev->usb_fd);
423 if (retval < 0)
424 goto free_and_return;
426 retval = USB_GetDescriptors(dev->usb_fd, &udd);
427 if (retval < 0)
428 goto free_and_return;
430 for (iConf = 0; iConf < udd.bNumConfigurations; iConf++) {
431 ucd = &udd.configurations[iConf];
432 for (iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) {
433 uid = &ucd->interfaces[iInterface];
434 if(uid->bInterfaceClass == USB_CLASS_MASS_STORAGE && /*
435 (uid->bInterfaceSubClass == MASS_STORAGE_SCSI_COMMANDS
436 || uid->bInterfaceSubClass == MASS_STORAGE_RBC_COMMANDS
437 || uid->bInterfaceSubClass == MASS_STORAGE_ATA_COMMANDS
438 || uid->bInterfaceSubClass == MASS_STORAGE_QIC_COMMANDS
439 || uid->bInterfaceSubClass == MASS_STORAGE_UFI_COMMANDS
440 || uid->bInterfaceSubClass == MASS_STORAGE_SFF8070_COMMANDS) &&*/
441 uid->bInterfaceProtocol == MASS_STORAGE_BULK_ONLY)
444 if (uid->bNumEndpoints < 2)
445 continue;
447 dev->ep_in = dev->ep_out = 0;
448 for (iEp = 0; iEp < uid->bNumEndpoints; iEp++) {
449 ued = &uid->endpoints[iEp];
450 if (ued->bmAttributes != USB_ENDPOINT_BULK)
451 continue;
453 if (ued->bEndpointAddress & USB_ENDPOINT_IN) {
454 dev->ep_in = ued->bEndpointAddress;
456 else {
457 dev->ep_out = ued->bEndpointAddress;
458 if(ued->wMaxPacketSize > 64 && (dev->usb_fd>=0x20 || dev->usb_fd<-1))
459 usb2_mode=true;
460 else
461 usb2_mode=false;
465 if (dev->ep_in != 0 && dev->ep_out != 0) {
466 dev->configuration = ucd->bConfigurationValue;
467 dev->interface = uid->bInterfaceNumber;
468 dev->altInterface = uid->bAlternateSetting;
469 goto found;
475 USB_FreeDescriptors(&udd);
476 retval = USBSTORAGE_ENOINTERFACE;
477 goto free_and_return;
479 found:
480 dev->bInterfaceSubClass = uid->bInterfaceSubClass;
482 USB_FreeDescriptors(&udd);
484 retval = USBSTORAGE_EINIT;
485 // some devices return an error, ignore it
486 USB_GetConfiguration(dev->usb_fd, &conf);
488 if (conf != dev->configuration) USB_SetConfiguration(dev->usb_fd, dev->configuration);
489 if (dev->altInterface !=0) USB_SetAlternativeInterface(dev->usb_fd, dev->interface, dev->altInterface);
491 if(!usb2_mode)
492 retval = USBStorage_Reset(dev);
494 dev->suspended = 0;
496 LWP_MutexLock(dev->lock);
497 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);
498 LWP_MutexUnlock(dev->lock);
500 if (retval < 0)
501 dev->max_lun = 1;
502 else
503 dev->max_lun = *max_lun + 1;
505 if (retval == USBSTORAGE_ETIMEDOUT)
506 goto free_and_return;
508 retval = USBSTORAGE_OK;
509 dev->sector_size = (u32 *) calloc(dev->max_lun, sizeof(u32));
510 if(!dev->sector_size) {
511 retval = IPC_ENOMEM;
512 goto free_and_return;
515 /* taken from linux usbstorage module (drivers/usb/storage/transport.c)
517 * Some devices (i.e. Iomega Zip100) need this -- apparently
518 * the bulk pipes get STALLed when the GetMaxLUN request is
519 * processed. This is, in theory, harmless to all other devices
520 * (regardless of if they stall or not).
522 * 8/9/10: If anyone wants to actually use a Zip100, they can add this back.
523 * But for now, it seems to be breaking things more than it is helping.
525 //USB_ClearHalt(dev->usb_fd, dev->ep_in);
526 //USB_ClearHalt(dev->usb_fd, dev->ep_out);
528 if(!dev->buffer)
529 dev->buffer = __lwp_heap_allocate(&__heap, MAX_TRANSFER_SIZE_V5);
531 if(!dev->buffer) {
532 retval = IPC_ENOMEM;
533 } else {
534 USB_DeviceRemovalNotifyAsync(dev->usb_fd,__usb_deviceremoved_cb,dev);
535 retval = USBSTORAGE_OK;
538 free_and_return:
539 if (max_lun)
540 __lwp_heap_free(&__heap, max_lun);
542 if (retval < 0) {
543 USBStorage_Close(dev);
544 return retval;
547 return 0;
550 s32 USBStorage_Close(usbstorage_handle *dev)
552 __mounted = false;
553 __lun = 0;
554 __vid = 0;
555 __pid = 0;
557 if (dev->usb_fd != -1)
558 USB_CloseDevice(&dev->usb_fd);
560 LWP_MutexDestroy(dev->lock);
561 SYS_RemoveAlarm(dev->alarm);
563 if(dev->sector_size)
564 free(dev->sector_size);
566 if (dev->buffer)
567 __lwp_heap_free(&__heap, dev->buffer);
569 memset(dev, 0, sizeof(*dev));
570 dev->usb_fd = -1;
571 return 0;
574 s32 USBStorage_Reset(usbstorage_handle *dev)
576 s32 retval;
578 LWP_MutexLock(dev->lock);
579 retval = __usbstorage_reset(dev);
580 LWP_MutexUnlock(dev->lock);
582 return retval;
585 s32 USBStorage_GetMaxLUN(usbstorage_handle *dev)
587 return dev->max_lun;
590 s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun)
592 s32 retval;
593 u32 n_sectors;
595 if(lun >= dev->max_lun)
596 return IPC_EINVAL;
598 usleep(50);
599 retval = __usbstorage_clearerrors(dev, lun);
600 if (retval<0)
602 USBStorage_Reset(dev);
603 retval = __usbstorage_clearerrors(dev, lun);
606 retval = USBStorage_Inquiry(dev, lun);
608 retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], &n_sectors);
609 if(retval >= 0 && (dev->sector_size[lun]<512 || n_sectors==0))
610 return INVALID_LUN;
612 return retval;
615 s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun)
617 int n;
618 s32 retval;
619 u8 cmd[] = {SCSI_INQUIRY, lun << 5,0,0,36,0};
620 u8 response[36];
622 for(n=0;n<2;n++)
624 memset(response,0,36);
626 retval = __cycle(dev, lun, response, 36, cmd, 6, 0, NULL, NULL);
627 if(retval>=0) break;
630 if(retval>=0) retval=*response & 31;
632 if(retval>=0)
634 switch(retval)
636 // info from http://en.wikipedia.org/wiki/SCSI_Peripheral_Device_Type
637 case 5: // CDROM
638 case 7: // optical memory device (e.g., some optical disks)
639 __dvd_mounted = 1;
640 break;
641 default:
642 __dvd_mounted = 0;
643 break;
647 return retval;
650 s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors)
652 s32 retval;
653 u8 cmd[10] = {SCSI_READ_CAPACITY, lun<<5};
654 u8 response[8];
656 retval = __cycle(dev, lun, response, sizeof(response), cmd, sizeof(cmd), 0, NULL, NULL);
657 if(retval >= 0)
659 if(n_sectors != NULL)
660 memcpy(n_sectors, response, 4);
661 if(sector_size != NULL)
662 memcpy(sector_size, response + 4, 4);
663 retval = USBSTORAGE_OK;
666 return retval;
669 s32 USBStorage_IsDVD()
671 u32 sectorsize, numSectors;
673 if(!__mounted || __usbfd.sector_size[__lun] != 2048)
674 return 0;
676 if(USBStorage_ReadCapacity(&__usbfd, __lun, &sectorsize, &numSectors) < 0)
677 return 0;
679 if(sectorsize == 2048)
680 return 1;
681 return 0;
684 /* lo_ej = load/eject, controls the tray
685 * start = start(1) or stop(0) the motor (or eject(0), load(1))
686 * imm = return before the command has completed
687 * it might be a good idea to call this before STM_ShutdownToStandby() so the USB HDD doesn't stay on
689 s32 USBStorage_StartStop(usbstorage_handle *dev, u8 lun, u8 lo_ej, u8 start, u8 imm)
691 u8 status = 0;
692 s32 retval = USBSTORAGE_OK;
693 u8 cmd[] = {
694 SCSI_START_STOP,
695 (lun << 5) | (imm&1),
698 ((lo_ej&1)<<1) | (start&1),
702 if(lun >= dev->max_lun)
703 return IPC_EINVAL;
705 LWP_MutexLock(dev->lock);
707 retval = __send_cbw(dev, lun, 0, CBW_IN, cmd, sizeof(cmd));
709 // if imm==0, wait up to 10secs for spinup to finish
710 if (retval >= 0)
711 retval = __read_csw(dev, &status, NULL, (imm ? USBSTORAGE_TIMEOUT : 10));
713 LWP_MutexUnlock(dev->lock);
715 if(retval >=0 && status != 0)
716 retval = USBSTORAGE_ESTATUS;
718 return retval;
721 s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer)
723 u8 status = 0;
724 s32 retval;
725 u8 cmd[] = {
726 SCSI_READ_10,
727 lun << 5,
728 sector >> 24,
729 sector >> 16,
730 sector >> 8,
731 sector,
733 n_sectors >> 8,
734 n_sectors,
738 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
739 return IPC_EINVAL;
741 // more than 60s since last use - make sure drive is awake
742 if(ticks_to_secs(gettime() - usb_last_used) > 60)
744 usbtimeout = 10;
745 USBStorage_MountLUN(dev, lun);
748 retval = __cycle(dev, lun, buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 0, &status, NULL);
749 if(retval > 0 && status != 0)
750 retval = USBSTORAGE_ESTATUS;
752 usb_last_used = gettime();
753 usbtimeout = USBSTORAGE_TIMEOUT;
755 return retval;
758 s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer)
760 u8 status = 0;
761 s32 retval;
762 u8 cmd[] = {
763 SCSI_WRITE_10,
764 lun << 5,
765 sector >> 24,
766 sector >> 16,
767 sector >> 8,
768 sector,
770 n_sectors >> 8,
771 n_sectors,
775 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
776 return IPC_EINVAL;
778 // more than 60s since last use - make sure drive is awake
779 if(ticks_to_secs(gettime() - usb_last_used) > 60)
781 usbtimeout = 10;
782 USBStorage_MountLUN(dev, lun);
785 retval = __cycle(dev, lun, (u8 *)buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 1, &status, NULL);
786 if(retval > 0 && status != 0)
787 retval = USBSTORAGE_ESTATUS;
789 usb_last_used = gettime();
790 usbtimeout = USBSTORAGE_TIMEOUT;
792 return retval;
795 s32 USBStorage_Suspend(usbstorage_handle *dev)
797 if(dev->suspended == 1)
798 return USBSTORAGE_OK;
800 USB_SuspendDevice(dev->usb_fd);
801 dev->suspended = 1;
803 return USBSTORAGE_OK;
807 The following is for implementing a DISC_INTERFACE
808 as used by libfat
811 static bool __usbstorage_Startup(void)
813 if(USB_Initialize() < 0 || USBStorage_Initialize() < 0)
814 return false;
816 return true;
819 static bool __usbstorage_IsInserted(void)
821 usb_device_entry *buffer;
822 u8 device_count;
823 u8 i, j;
824 u16 vid, pid;
825 s32 maxLun;
826 s32 retval;
828 if(__mounted)
829 return true;
831 if(!__inited)
832 return false;
834 buffer = (usb_device_entry*)__lwp_heap_allocate(&__heap, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
835 if (!buffer)
836 return false;
838 memset(buffer, 0, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
840 if (USB_GetDeviceList(buffer, DEVLIST_MAXSIZE, USB_CLASS_MASS_STORAGE, &device_count) < 0)
842 if (__vid != 0 || __pid != 0)
843 USBStorage_Close(&__usbfd);
845 __lwp_heap_free(&__heap, buffer);
846 return false;
849 usleep(100);
851 if (__vid != 0 || __pid != 0) {
852 for(i = 0; i < device_count; i++) {
853 vid = buffer[i].vid;
854 pid = buffer[i].pid;
855 if(vid != 0 || pid != 0) {
856 if((vid == __vid) && (pid == __pid)) {
857 __mounted = true;
858 __lwp_heap_free(&__heap,buffer);
859 usleep(50); // I don't know why I have to wait but it's needed
860 return true;
864 USBStorage_Close(&__usbfd); // device changed or unplugged, return false the first time to notify to the client that he must unmount devices
865 __lwp_heap_free(&__heap,buffer);
866 return false;
868 for (i = 0; i < device_count; i++) {
869 vid = buffer[i].vid;
870 pid = buffer[i].pid;
871 if (vid == 0 || pid == 0)
872 continue;
874 if (USBStorage_Open(&__usbfd, buffer[i].device_id, vid, pid) < 0)
875 continue;
877 maxLun = USBStorage_GetMaxLUN(&__usbfd);
878 for (j = 0; j < maxLun; j++) {
879 retval = USBStorage_MountLUN(&__usbfd, j);
881 if (retval == INVALID_LUN)
882 continue;
884 if (retval < 0)
886 __usbstorage_reset(&__usbfd);
887 continue;
890 __mounted = true;
891 __lun = j;
892 __vid = vid;
893 __pid = pid;
894 usb_last_used = gettime()-secs_to_ticks(100);
895 usleep(10000);
896 break;
899 if (__mounted)
900 break;
902 USBStorage_Close(&__usbfd);
904 __lwp_heap_free(&__heap, buffer);
906 return __mounted;
909 static bool __usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer)
911 s32 retval;
913 if (!__mounted)
914 return false;
916 retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer);
918 return retval >= 0;
921 static bool __usbstorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer)
923 s32 retval;
925 if (!__mounted)
926 return false;
928 retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer);
930 return retval >= 0;
933 static bool __usbstorage_ClearStatus(void)
935 return true;
938 static bool __usbstorage_Shutdown(void)
940 if (__vid != 0 || __pid != 0)
941 USBStorage_Close(&__usbfd);
943 return true;
946 void USBStorage_Deinitialize()
948 __usbstorage_Shutdown();
949 LWP_CloseQueue(__usbstorage_waitq);
950 __inited = false;
953 DISC_INTERFACE __io_usbstorage = {
954 DEVICE_TYPE_WII_USB,
955 FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB,
956 (FN_MEDIUM_STARTUP)&__usbstorage_Startup,
957 (FN_MEDIUM_ISINSERTED)&__usbstorage_IsInserted,
958 (FN_MEDIUM_READSECTORS)&__usbstorage_ReadSectors,
959 (FN_MEDIUM_WRITESECTORS)&__usbstorage_WriteSectors,
960 (FN_MEDIUM_CLEARSTATUS)&__usbstorage_ClearStatus,
961 (FN_MEDIUM_SHUTDOWN)&__usbstorage_Shutdown
964 #endif /* HW_RVL */