fix issue with HBC stub + 64B L2 cache (tueidj)
[libogc.git] / libogc / usbstorage.c
blob04626910c2b2cea69efa188d972e36f42f6d646f
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 <stdarg.h>
37 #include <sys/time.h>
38 #include <errno.h>
39 #include <lwp_heap.h>
40 #include <malloc.h>
42 #include "asm.h"
43 #include "processor.h"
44 #include "disc_io.h"
45 #include "lwp_watchdog.h"
47 #define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f)
49 #define HEAP_SIZE (18*1024)
50 #define TAG_START 0x0BADC0DE
52 #define CBW_SIZE 31
53 #define CBW_SIGNATURE 0x43425355
54 #define CBW_IN (1 << 7)
55 #define CBW_OUT 0
57 #define CSW_SIZE 13
58 #define CSW_SIGNATURE 0x53425355
60 #define SCSI_TEST_UNIT_READY 0x00
61 #define SCSI_REQUEST_SENSE 0x03
62 #define SCSI_INQUIRY 0x12
63 #define SCSI_START_STOP 0x1B
64 #define SCSI_READ_CAPACITY 0x25
65 #define SCSI_READ_10 0x28
66 #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 INVALID_LUN -2
92 #define MAX_TRANSFER_SIZE_V0 4096
93 #define MAX_TRANSFER_SIZE_V5 (16*1024)
95 #define DEVLIST_MAXSIZE 8
97 static heap_cntrl __heap;
98 static bool __inited = false;
99 static u64 usb_last_used = 0;
100 static lwpq_t __usbstorage_waitq = 0;
101 static u32 usbtimeout = USBSTORAGE_TIMEOUT;
104 The following is for implementing a DISC_INTERFACE
105 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);
117 s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun);
119 /* XXX: this is a *really* dirty and ugly way to send a bulkmessage with a timeout
120 * but there's currently no other known way of doing this and it's in my humble
121 * opinion still better than having a function blocking forever while waiting
122 * for the USB data/IOS reply..
125 static s32 __usb_blkmsg_cb(s32 retval, void *dummy)
127 usbstorage_handle *dev = (usbstorage_handle *)dummy;
128 dev->retval = retval;
129 SYS_CancelAlarm(dev->alarm);
130 LWP_ThreadBroadcast(__usbstorage_waitq);
131 return 0;
134 static s32 __usb_deviceremoved_cb(s32 retval,void *arg)
136 __mounted = false;
137 if(__vid != 0) USBStorage_Close(&__usbfd);
138 return 0;
141 static void __usb_timeouthandler(syswd_t alarm,void *cbarg)
143 usbstorage_handle *dev = (usbstorage_handle*)cbarg;
144 dev->retval = USBSTORAGE_ETIMEDOUT;
145 LWP_ThreadBroadcast(__usbstorage_waitq);
148 static void __usb_settimeout(usbstorage_handle *dev, u32 secs)
150 struct timespec ts;
152 ts.tv_sec = secs;
153 ts.tv_nsec = 0;
154 SYS_SetAlarm(dev->alarm,&ts,__usb_timeouthandler,dev);
157 static s32 __USB_BlkMsgTimeout(usbstorage_handle *dev, u8 bEndpoint, u16 wLength, void *rpData, u32 timeout)
159 s32 retval;
161 dev->retval = USBSTORAGE_PROCESSING;
162 retval = USB_WriteBlkMsgAsync(dev->usb_fd, bEndpoint, wLength, rpData, __usb_blkmsg_cb, (void *)dev);
163 if(retval < 0) return retval;
165 __usb_settimeout(dev, timeout);
167 do {
168 retval = dev->retval;
169 if(retval!=USBSTORAGE_PROCESSING) break;
170 else LWP_ThreadSleep(__usbstorage_waitq);
171 } while(retval==USBSTORAGE_PROCESSING);
173 if (retval<0)
174 USB_ClearHalt(dev->usb_fd, bEndpoint);
176 return retval;
179 static s32 __USB_CtrlMsgTimeout(usbstorage_handle *dev, u8 bmRequestType, u8 bmRequest, u16 wValue, u16 wIndex, u16 wLength, void *rpData)
181 s32 retval;
183 dev->retval = USBSTORAGE_PROCESSING;
184 retval = USB_WriteCtrlMsgAsync(dev->usb_fd, bmRequestType, bmRequest, wValue, wIndex, wLength, rpData, __usb_blkmsg_cb, (void *)dev);
185 if(retval < 0) return retval;
187 __usb_settimeout(dev, usbtimeout);
189 do {
190 retval = dev->retval;
191 if(retval!=USBSTORAGE_PROCESSING) break;
192 else LWP_ThreadSleep(__usbstorage_waitq);
193 } while(retval==USBSTORAGE_PROCESSING);
195 return retval;
198 static u8 *arena_ptr=NULL;
199 static u8 *cbw_buffer=NULL;
201 s32 USBStorage_Initialize()
203 u32 level;
205 if(__inited)
206 return IPC_OK;
208 _CPU_ISR_Disable(level);
209 LWP_InitQueue(&__usbstorage_waitq);
210 if(!arena_ptr) {
211 arena_ptr = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - HEAP_SIZE));
212 if((u32)arena_ptr < (u32)SYS_GetArena2Lo()) {
213 _CPU_ISR_Restore(level);
214 return IPC_ENOMEM;
216 SYS_SetArena2Hi(arena_ptr);
218 __lwp_heap_init(&__heap, arena_ptr, HEAP_SIZE, 32);
219 cbw_buffer=(u8*)__lwp_heap_allocate(&__heap, 32);
220 __inited = true;
221 _CPU_ISR_Restore(level);
222 return IPC_OK;
225 static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u8 *cb, u8 cbLen)
227 s32 retval = USBSTORAGE_OK;
229 if(cbLen == 0 || cbLen > 16)
230 return IPC_EINVAL;
232 memset(cbw_buffer, 0, CBW_SIZE);
234 __stwbrx(cbw_buffer, 0, CBW_SIGNATURE);
235 __stwbrx(cbw_buffer, 4, ++dev->tag);
236 __stwbrx(cbw_buffer, 8, len);
237 cbw_buffer[12] = flags;
238 cbw_buffer[13] = lun;
239 cbw_buffer[14] = (cbLen > 6 ? 10 : 6);
241 memcpy(cbw_buffer + 15, cb, cbLen);
243 if(dev->suspended == 1)
245 USB_ResumeDevice(dev->usb_fd);
246 dev->suspended = 0;
249 retval = __USB_BlkMsgTimeout(dev, dev->ep_out, CBW_SIZE, (void *)cbw_buffer, usbtimeout);
251 if(retval == CBW_SIZE) return USBSTORAGE_OK;
252 else if(retval > 0) return USBSTORAGE_ESHORTWRITE;
254 return retval;
257 static s32 __read_csw(usbstorage_handle *dev, u8 *status, u32 *dataResidue, u32 timeout)
259 s32 retval = USBSTORAGE_OK;
260 u32 signature, tag, _dataResidue, _status;
262 memset(cbw_buffer, 0, CSW_SIZE);
264 retval = __USB_BlkMsgTimeout(dev, dev->ep_in, CSW_SIZE, cbw_buffer, timeout);
265 if(retval > 0 && retval != CSW_SIZE) return USBSTORAGE_ESHORTREAD;
266 else if(retval < 0) return retval;
268 signature = __lwbrx(cbw_buffer, 0);
269 tag = __lwbrx(cbw_buffer, 4);
270 _dataResidue = __lwbrx(cbw_buffer, 8);
271 _status = cbw_buffer[12];
273 if(signature != CSW_SIGNATURE) return USBSTORAGE_ESIGNATURE;
275 if(dataResidue != NULL)
276 *dataResidue = _dataResidue;
277 if(status != NULL)
278 *status = _status;
280 if(tag != dev->tag) return USBSTORAGE_ETAG;
282 return USBSTORAGE_OK;
285 static s32 __cycle(usbstorage_handle *dev, u8 lun, u8 *buffer, u32 len, u8 *cb, u8 cbLen, u8 write, u8 *_status, u32 *_dataResidue)
287 s32 retval = USBSTORAGE_OK;
289 u8 status=0;
290 u32 dataResidue = 0;
291 u16 max_size;
292 u8 ep = write ? dev->ep_out : dev->ep_in;
293 s8 retries = USBSTORAGE_CYCLE_RETRIES + 1;
295 if(usb2_mode)
296 max_size=MAX_TRANSFER_SIZE_V5;
297 else
298 max_size=MAX_TRANSFER_SIZE_V0;
300 LWP_MutexLock(dev->lock);
303 u8 *_buffer = buffer;
304 u32 _len = len;
305 retries--;
307 if(retval == USBSTORAGE_ETIMEDOUT)
308 break;
310 retval = __send_cbw(dev, lun, len, (write ? CBW_OUT:CBW_IN), cb, cbLen);
312 while(_len > 0 && retval >= 0)
314 u32 thisLen = _len > max_size ? max_size : _len;
316 if ((u32)_buffer&0x1F || !((u32)_buffer&0x10000000)) {
317 if (write) memcpy(dev->buffer, _buffer, thisLen);
318 retval = __USB_BlkMsgTimeout(dev, ep, thisLen, dev->buffer, usbtimeout);
319 if (!write && retval > 0)
320 memcpy(_buffer, dev->buffer, retval);
321 } else
322 retval = __USB_BlkMsgTimeout(dev, ep, thisLen, _buffer, usbtimeout);
324 if (retval == thisLen) {
325 _len -= retval;
326 _buffer += retval;
328 else if (retval != USBSTORAGE_ETIMEDOUT)
329 retval = USBSTORAGE_EDATARESIDUE;
332 if (retval >= 0)
333 retval = __read_csw(dev, &status, &dataResidue, usbtimeout);
335 if (retval < 0) {
336 if (__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
337 retval = USBSTORAGE_ETIMEDOUT;
339 } while (retval < 0 && retries > 0);
341 LWP_MutexUnlock(dev->lock);
343 if(_status != NULL)
344 *_status = status;
345 if(_dataResidue != NULL)
346 *_dataResidue = dataResidue;
348 return retval;
351 static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun)
353 s32 retval;
354 u8 cmd[6];
355 u8 sense[SCSI_SENSE_REPLY_SIZE];
356 u8 status = 0;
358 memset(cmd, 0, sizeof(cmd));
359 cmd[0] = SCSI_TEST_UNIT_READY;
361 retval = __cycle(dev, lun, NULL, 0, cmd, 1, 0, &status, NULL);
362 if (retval < 0) return retval;
364 if (status)
366 cmd[0] = SCSI_REQUEST_SENSE;
367 cmd[1] = lun << 5;
368 cmd[4] = SCSI_SENSE_REPLY_SIZE;
369 memset(sense, 0, SCSI_SENSE_REPLY_SIZE);
370 retval = __cycle(dev, lun, sense, SCSI_SENSE_REPLY_SIZE, cmd, 6, 0, NULL, NULL);
371 if (retval>=0) {
372 switch (sense[2]&0xF) {
373 case SCSI_SENSE_NOT_READY:
374 return USBSTORAGE_EINIT;
375 case SCSI_SENSE_MEDIUM_ERROR:
376 case SCSI_SENSE_HARDWARE_ERROR:
377 return USBSTORAGE_ESENSE;
382 return retval;
385 static s32 __usbstorage_reset(usbstorage_handle *dev)
387 u32 t = usbtimeout;
388 usbtimeout = 1;
389 s32 retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_RESET, 0, dev->interface, 0, NULL);
390 usbtimeout = t;
391 usleep(60*1000);
392 USB_ClearHalt(dev->usb_fd, dev->ep_in);usleep(10000); //from http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
393 USB_ClearHalt(dev->usb_fd, dev->ep_out);usleep(10000);
394 return retval;
397 s32 USBStorage_Open(usbstorage_handle *dev, s32 device_id, u16 vid, u16 pid)
399 s32 retval = -1;
400 u8 conf = -1;
401 u8 *max_lun;
402 u32 iConf, iInterface, iEp;
403 usb_devdesc udd;
404 usb_configurationdesc *ucd;
405 usb_interfacedesc *uid;
406 usb_endpointdesc *ued;
408 max_lun = __lwp_heap_allocate(&__heap, 1);
409 if (!max_lun)
410 return IPC_ENOMEM;
412 memset(dev, 0, sizeof(*dev));
413 dev->usb_fd = -1;
415 dev->tag = TAG_START;
417 if (LWP_MutexInit(&dev->lock, false) < 0)
418 goto free_and_return;
420 if (SYS_CreateAlarm(&dev->alarm) < 0)
421 goto free_and_return;
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->bInterfaceSubClass == MASS_STORAGE_SCSI_COMMANDS
437 || uid->bInterfaceSubClass == MASS_STORAGE_RBC_COMMANDS
438 || uid->bInterfaceSubClass == MASS_STORAGE_ATA_COMMANDS
439 || uid->bInterfaceSubClass == MASS_STORAGE_QIC_COMMANDS
440 || uid->bInterfaceSubClass == MASS_STORAGE_UFI_COMMANDS
441 || uid->bInterfaceSubClass == MASS_STORAGE_SFF8070_COMMANDS) &&*/
442 uid->bInterfaceProtocol == MASS_STORAGE_BULK_ONLY)
445 if (uid->bNumEndpoints < 2)
446 continue;
448 dev->ep_in = dev->ep_out = 0;
449 for (iEp = 0; iEp < uid->bNumEndpoints; iEp++) {
450 ued = &uid->endpoints[iEp];
451 if (ued->bmAttributes != USB_ENDPOINT_BULK)
452 continue;
454 if (ued->bEndpointAddress & USB_ENDPOINT_IN) {
455 dev->ep_in = ued->bEndpointAddress;
457 else {
458 dev->ep_out = ued->bEndpointAddress;
459 if(ued->wMaxPacketSize > 64 && (dev->usb_fd>=0x20 || dev->usb_fd<-1))
460 usb2_mode=true;
461 else
462 usb2_mode=false;
466 if (dev->ep_in != 0 && dev->ep_out != 0) {
467 dev->configuration = ucd->bConfigurationValue;
468 dev->interface = uid->bInterfaceNumber;
469 dev->altInterface = uid->bAlternateSetting;
470 goto found;
476 USB_FreeDescriptors(&udd);
477 retval = USBSTORAGE_ENOINTERFACE;
478 goto free_and_return;
480 found:
481 dev->bInterfaceSubClass = uid->bInterfaceSubClass;
483 USB_FreeDescriptors(&udd);
485 retval = USBSTORAGE_EINIT;
486 // some devices return an error, ignore it
487 USB_GetConfiguration(dev->usb_fd, &conf);
489 if (conf != dev->configuration) USB_SetConfiguration(dev->usb_fd, dev->configuration);
490 if (dev->altInterface !=0) USB_SetAlternativeInterface(dev->usb_fd, dev->interface, dev->altInterface);
492 if(!usb2_mode)
493 retval = USBStorage_Reset(dev);
495 dev->suspended = 0;
497 LWP_MutexLock(dev->lock);
498 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);
499 LWP_MutexUnlock(dev->lock);
501 if (retval < 0)
502 dev->max_lun = 1;
503 else
504 dev->max_lun = *max_lun + 1;
506 if (retval == USBSTORAGE_ETIMEDOUT)
507 goto free_and_return;
509 retval = USBSTORAGE_OK;
510 dev->sector_size = (u32 *) calloc(dev->max_lun, sizeof(u32));
511 if(!dev->sector_size) {
512 retval = IPC_ENOMEM;
513 goto free_and_return;
516 /* taken from linux usbstorage module (drivers/usb/storage/transport.c)
518 * Some devices (i.e. Iomega Zip100) need this -- apparently
519 * the bulk pipes get STALLed when the GetMaxLUN request is
520 * processed. This is, in theory, harmless to all other devices
521 * (regardless of if they stall or not).
523 * 8/9/10: If anyone wants to actually use a Zip100, they can add this back.
524 * But for now, it seems to be breaking things more than it is helping.
526 //USB_ClearHalt(dev->usb_fd, dev->ep_in);
527 //USB_ClearHalt(dev->usb_fd, dev->ep_out);
529 if(!dev->buffer)
530 dev->buffer = __lwp_heap_allocate(&__heap, MAX_TRANSFER_SIZE_V5);
532 if(!dev->buffer) {
533 retval = IPC_ENOMEM;
534 } else {
535 USB_DeviceRemovalNotifyAsync(dev->usb_fd,__usb_deviceremoved_cb,dev);
536 retval = USBSTORAGE_OK;
539 free_and_return:
540 if (max_lun)
541 __lwp_heap_free(&__heap, max_lun);
543 if (retval < 0) {
544 USBStorage_Close(dev);
545 return retval;
548 return 0;
551 s32 USBStorage_Close(usbstorage_handle *dev)
553 __mounted = false;
554 __lun = 0;
555 __vid = 0;
556 __pid = 0;
558 if (dev->usb_fd != -1)
559 USB_CloseDevice(&dev->usb_fd);
561 LWP_MutexDestroy(dev->lock);
562 SYS_RemoveAlarm(dev->alarm);
564 if(dev->sector_size)
565 free(dev->sector_size);
567 if (dev->buffer)
568 __lwp_heap_free(&__heap, dev->buffer);
570 memset(dev, 0, sizeof(*dev));
571 dev->usb_fd = -1;
572 return 0;
575 s32 USBStorage_Reset(usbstorage_handle *dev)
577 s32 retval;
579 LWP_MutexLock(dev->lock);
580 retval = __usbstorage_reset(dev);
581 LWP_MutexUnlock(dev->lock);
583 return retval;
586 s32 USBStorage_GetMaxLUN(usbstorage_handle *dev)
588 return dev->max_lun;
591 s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun)
593 s32 retval;
594 u32 n_sectors;
596 if(lun >= dev->max_lun)
597 return IPC_EINVAL;
599 usleep(50);
600 retval = __usbstorage_clearerrors(dev, lun);
601 if (retval<0)
603 USBStorage_Reset(dev);
604 retval = __usbstorage_clearerrors(dev, lun);
607 retval = USBStorage_Inquiry(dev, lun);
609 retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], &n_sectors);
610 if(retval >= 0 && (dev->sector_size[lun]<512 || n_sectors==0))
611 return INVALID_LUN;
613 return retval;
616 s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun)
618 int n;
619 s32 retval;
620 u8 cmd[] = {SCSI_INQUIRY, lun << 5,0,0,36,0};
621 u8 response[36];
623 for(n=0;n<2;n++)
625 memset(response,0,36);
627 retval = __cycle(dev, lun, response, 36, cmd, 6, 0, NULL, NULL);
628 if(retval>=0) break;
631 if(retval>=0) retval=*response & 31;
633 if(retval>=0)
635 switch(retval)
637 // info from http://en.wikipedia.org/wiki/SCSI_Peripheral_Device_Type
638 case 5: // CDROM
639 case 7: // optical memory device (e.g., some optical disks)
640 __dvd_mounted = 1;
641 break;
642 default:
643 __dvd_mounted = 0;
644 break;
648 return retval;
651 s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors)
653 s32 retval;
654 u8 cmd[10] = {SCSI_READ_CAPACITY, lun<<5};
655 u8 response[8];
657 retval = __cycle(dev, lun, response, sizeof(response), cmd, sizeof(cmd), 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_IsDVD()
672 u32 sectorsize, numSectors;
674 if(!__mounted || __usbfd.sector_size[__lun] != 2048)
675 return 0;
677 if(USBStorage_ReadCapacity(&__usbfd, __lun, &sectorsize, &numSectors) < 0)
678 return 0;
680 if(sectorsize == 2048)
681 return 1;
682 return 0;
685 /* lo_ej = load/eject, controls the tray
686 * start = start(1) or stop(0) the motor (or eject(0), load(1))
687 * imm = return before the command has completed
688 * it might be a good idea to call this before STM_ShutdownToStandby() so the USB HDD doesn't stay on
690 s32 USBStorage_StartStop(usbstorage_handle *dev, u8 lun, u8 lo_ej, u8 start, u8 imm)
692 u8 status = 0;
693 s32 retval = USBSTORAGE_OK;
694 u8 cmd[] = {
695 SCSI_START_STOP,
696 (lun << 5) | (imm&1),
699 ((lo_ej&1)<<1) | (start&1),
703 if(lun >= dev->max_lun)
704 return IPC_EINVAL;
706 LWP_MutexLock(dev->lock);
708 retval = __send_cbw(dev, lun, 0, CBW_IN, cmd, sizeof(cmd));
710 // if imm==0, wait up to 10secs for spinup to finish
711 if (retval >= 0)
712 retval = __read_csw(dev, &status, NULL, (imm ? USBSTORAGE_TIMEOUT : 10));
714 LWP_MutexUnlock(dev->lock);
716 if(retval >=0 && status != 0)
717 retval = USBSTORAGE_ESTATUS;
719 return retval;
722 s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer)
724 u8 status = 0;
725 s32 retval;
726 u8 cmd[] = {
727 SCSI_READ_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 USBStorage_MountLUN(dev, lun);
749 retval = __cycle(dev, lun, buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 0, &status, NULL);
750 if(retval > 0 && status != 0)
751 retval = USBSTORAGE_ESTATUS;
753 usb_last_used = gettime();
754 usbtimeout = USBSTORAGE_TIMEOUT;
756 return retval;
759 s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer)
761 u8 status = 0;
762 s32 retval;
763 u8 cmd[] = {
764 SCSI_WRITE_10,
765 lun << 5,
766 sector >> 24,
767 sector >> 16,
768 sector >> 8,
769 sector,
771 n_sectors >> 8,
772 n_sectors,
776 if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
777 return IPC_EINVAL;
779 // more than 60s since last use - make sure drive is awake
780 if(ticks_to_secs(gettime() - usb_last_used) > 60)
782 usbtimeout = 10;
783 USBStorage_MountLUN(dev, lun);
786 retval = __cycle(dev, lun, (u8 *)buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 1, &status, NULL);
787 if(retval > 0 && status != 0)
788 retval = USBSTORAGE_ESTATUS;
790 usb_last_used = gettime();
791 usbtimeout = USBSTORAGE_TIMEOUT;
793 return retval;
796 s32 USBStorage_Suspend(usbstorage_handle *dev)
798 if(dev->suspended == 1)
799 return USBSTORAGE_OK;
801 USB_SuspendDevice(dev->usb_fd);
802 dev->suspended = 1;
804 return USBSTORAGE_OK;
808 The following is for implementing a DISC_INTERFACE
809 as used by libfat
812 static bool __usbstorage_Startup(void)
814 if(USB_Initialize() < 0 || USBStorage_Initialize() < 0)
815 return false;
817 return true;
820 static bool __usbstorage_IsInserted(void)
822 usb_device_entry *buffer;
823 u8 device_count;
824 u8 i, j;
825 u16 vid, pid;
826 s32 maxLun;
827 s32 retval;
828 u32 sectorsize, numSectors;
830 if(__mounted)
832 // device is not a USB DVD drive - always return true
833 if (__usbfd.sector_size[__lun] != 2048)
834 return true;
836 // check if DVD is inserted
837 if (USBStorage_ReadCapacity(&__usbfd, __lun, &sectorsize, &numSectors) < 0)
838 return false;
839 else
840 return true;
843 if(!__inited)
844 return false;
846 buffer = (usb_device_entry*)__lwp_heap_allocate(&__heap, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
847 if (!buffer)
848 return false;
850 memset(buffer, 0, DEVLIST_MAXSIZE * sizeof(usb_device_entry));
852 if (USB_GetDeviceList(buffer, DEVLIST_MAXSIZE, USB_CLASS_MASS_STORAGE, &device_count) < 0)
854 if (__vid != 0 || __pid != 0)
855 USBStorage_Close(&__usbfd);
857 __lwp_heap_free(&__heap, buffer);
858 return false;
861 usleep(100);
863 if (__vid != 0 || __pid != 0) {
864 for(i = 0; i < device_count; i++) {
865 vid = buffer[i].vid;
866 pid = buffer[i].pid;
867 if(vid != 0 || pid != 0) {
868 if((vid == __vid) && (pid == __pid)) {
869 __mounted = true;
870 __lwp_heap_free(&__heap,buffer);
871 usleep(50); // I don't know why I have to wait but it's needed
872 return true;
876 USBStorage_Close(&__usbfd); // device changed or unplugged, return false the first time to notify to the client that he must unmount devices
877 __lwp_heap_free(&__heap,buffer);
878 return false;
880 for (i = 0; i < device_count; i++) {
881 vid = buffer[i].vid;
882 pid = buffer[i].pid;
883 if (vid == 0 || pid == 0)
884 continue;
886 if (vid == 0x0b95 && pid == 0x7720) // USB LAN
887 continue;
889 if (USBStorage_Open(&__usbfd, buffer[i].device_id, vid, pid) < 0)
890 continue;
892 maxLun = USBStorage_GetMaxLUN(&__usbfd);
893 for (j = 0; j < maxLun; j++) {
894 retval = USBStorage_MountLUN(&__usbfd, j);
896 if (retval == INVALID_LUN)
897 continue;
899 if (retval < 0)
901 __usbstorage_reset(&__usbfd);
902 continue;
905 __mounted = true;
906 __lun = j;
907 __vid = vid;
908 __pid = pid;
909 usb_last_used = gettime()-secs_to_ticks(100);
910 usleep(10000);
911 break;
914 if (__mounted)
915 break;
917 USBStorage_Close(&__usbfd);
919 __lwp_heap_free(&__heap, buffer);
921 return __mounted;
924 static bool __usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer)
926 s32 retval;
928 if (!__mounted)
929 return false;
931 retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer);
933 return retval >= 0;
936 static bool __usbstorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer)
938 s32 retval;
940 if (!__mounted)
941 return false;
943 retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer);
945 return retval >= 0;
948 static bool __usbstorage_ClearStatus(void)
950 return true;
953 static bool __usbstorage_Shutdown(void)
955 if (__vid != 0 || __pid != 0)
956 USBStorage_Close(&__usbfd);
958 return true;
961 void USBStorage_Deinitialize()
963 __usbstorage_Shutdown();
964 LWP_CloseQueue(__usbstorage_waitq);
965 __inited = false;
968 int USBStorage_ioctl(int request, ...)
970 int retval = 0;
971 va_list ap;
972 va_start(ap, request);
974 switch (request)
976 case B_RAW_DEVICE_COMMAND:
978 u8 write;
979 raw_device_command *rdc = va_arg(ap, raw_device_command *);
980 write = (rdc->flags == B_RAW_DEVICE_DATA_IN) ? 0 : 1;
981 retval = __cycle(&__usbfd, __lun, rdc->data, rdc->data_length, rdc->command, rdc->command_length, write, &rdc->scsi_status, NULL);
982 break;
985 default:
986 retval = -1;
987 break;
990 va_end(ap);
991 return retval;
994 DISC_INTERFACE __io_usbstorage = {
995 DEVICE_TYPE_WII_USB,
996 FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB,
997 (FN_MEDIUM_STARTUP)&__usbstorage_Startup,
998 (FN_MEDIUM_ISINSERTED)&__usbstorage_IsInserted,
999 (FN_MEDIUM_READSECTORS)&__usbstorage_ReadSectors,
1000 (FN_MEDIUM_WRITESECTORS)&__usbstorage_WriteSectors,
1001 (FN_MEDIUM_CLEARSTATUS)&__usbstorage_ClearStatus,
1002 (FN_MEDIUM_SHUTDOWN)&__usbstorage_Shutdown
1005 #endif /* HW_RVL */