1 /*-------------------------------------------------------------
3 di.c -- Drive Interface library
11 This software is provided 'as-is', without any express or implied
12 warranty. In no event will the authors be held liable for any
13 damages arising from the use of this software.
15 Permission is granted to anyone to use this software for any
16 purpose, including commercial applications, and to alter it and
17 redistribute it freely, subject to the following restrictions:
19 1. The origin of this software must not be misrepresented; you
20 must not claim that you wrote the original software. If you use
21 this software in a product, an acknowledgment in the product
22 documentation would be appreciated but is not required.
24 2. Altered source versions must be plainly marked as such, and
25 must not be misrepresented as being the original software.
27 3. This notice may not be removed or altered from any source
30 -------------------------------------------------------------*/
38 #include <ogc/mutex.h>
39 #include <ogc/lwp_watchdog.h>
42 #define MOUNT_TIMEOUT 15000 // 15 seconds
45 static bool load_dvdx
= true;
46 static int state
= DVD_INIT
| DVD_NO_DISC
;
48 static int _cover_callback(int ret
, void* usrdata
);
50 static unsigned int bufferMutex
= 0;
51 static uint32_t outbuf
[8] __attribute__((aligned(32)));
52 static uint32_t dic
[8] __attribute__((aligned(32)));
53 static const char di_path
[] ATTRIBUTE_ALIGN(32) = "/dev/di";
55 static read_func DI_ReadDVDptr
= NULL
;
56 static read_func_async DI_ReadDVDAsyncptr
= NULL
;
57 static di_callback di_cb
= NULL
;
59 static int _DI_ReadDVD_A8_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
){
65 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
68 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
69 dic
[1] = len
<< 11; // 1 LB is 2048 bytes
70 dic
[2] = lba
<< 9; // Nintendo's read function uses byteOffset >> 2, so we only shift 9 left, not 11.
72 ret
= IOS_IoctlAsync(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, len
<< 11,ipc_cb
, buf
);
77 return (ret
== 1)? 0 : -ret
;
80 static int _DI_ReadDVD_D0_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
){
86 if((uint32_t)buf
& 0x1F)
89 dic
[0] = DVD_READ
<< 24;
90 dic
[1] = 0; // Unknown what this does as of now. (Sets some value to 0x10 in the drive if set).
91 dic
[2] = 0; // USE_DEFAULT_CONFIG flag. Drive will use default config if this bit is set.
95 ret
= IOS_IoctlAsync(di_fd
, DVD_READ
, dic
, 0x20, buf
, len
<< 11,ipc_cb
, buf
);
100 return (ret
== 1)? 0 : -ret
;
103 static int _DI_ReadDVD_A8(void* buf
, uint32_t len
, uint32_t lba
){
104 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
109 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
112 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
113 dic
[1] = len
<< 11; // 1 LB is 2048 bytes
114 dic
[2] = lba
<< 9; // Nintendo's read function uses byteOffset >> 2, so we only shift 9 left, not 11.
117 ret
= IOS_Ioctl(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, len
<< 11);
119 }while(ret
!= 1 && retry_count
> 0);
124 return (ret
== 1)? 0 : -ret
;
127 static int _DI_ReadDVD_D0(void* buf
, uint32_t len
, uint32_t lba
){
128 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
133 if((uint32_t)buf
& 0x1F)
136 dic
[0] = DVD_READ
<< 24;
137 dic
[1] = 0; // Unknown what this does as of now. (Sets some value to 0x10 in the drive if set).
138 dic
[2] = 0; // USE_DEFAULT_CONFIG flag. Drive will use default config if this bit is set.
143 ret
= IOS_Ioctl(di_fd
, DVD_READ
, dic
, 0x20, buf
, len
<< 11);
145 }while(ret
!= 1 && retry_count
> 0);
150 return (ret
== 1)? 0 : -ret
;
154 Initialize the DI interface, should always be called first!
157 s32
__DI_StubLaunch(void);
163 state
= DVD_INIT
| DVD_NO_DISC
;
166 int res
= __DI_StubLaunch(); // Marcan's 1337 magics happen here!
173 di_fd
= IOS_Open(di_path
, 2);
179 LWP_MutexInit(&bufferMutex
, false);
184 void DI_LoadDVDX(bool load
){
194 if (DI_GetCoverRegister(&status
) != 0) {
199 if ((status
& DVD_COVER_DISC_INSERTED
) == 0) {
204 state
= DVD_INIT
| DVD_NO_DISC
;
205 _cover_callback(1, NULL
); // Initialize the callback chain.
217 DI_ReadDVDptr
= NULL
;
218 DI_ReadDVDAsyncptr
= NULL
;
219 state
= DVD_INIT
| DVD_NO_DISC
;
222 LWP_MutexDestroy(bufferMutex
);
227 static int _DI_ReadDVD_Check(void* buf
, uint32_t len
, uint32_t lba
)
231 ret
= _DI_ReadDVD_D0(buf
, len
, lba
);
234 state
= state
| DVD_D0
;
235 DI_ReadDVDptr
= _DI_ReadDVD_D0
;
236 DI_ReadDVDAsyncptr
= _DI_ReadDVD_D0_Async
;
239 ret
= _DI_ReadDVD_A8(buf
, len
, lba
);
242 state
= state
| DVD_A8
;
243 DI_ReadDVDptr
= _DI_ReadDVD_A8
;
244 DI_ReadDVDAsyncptr
= _DI_ReadDVD_A8_Async
;
250 static int _DI_ReadDVD_Check_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
)
254 ret
= _DI_ReadDVD_D0_Async(buf
, len
, lba
, ipc_cb
);
257 state
= state
| DVD_D0
;
258 DI_ReadDVDptr
= _DI_ReadDVD_D0
;
259 DI_ReadDVDAsyncptr
= _DI_ReadDVD_D0_Async
;
262 ret
= _DI_ReadDVD_A8_Async(buf
, len
, lba
, ipc_cb
);
265 state
= state
| DVD_A8
;
266 DI_ReadDVDptr
= _DI_ReadDVD_A8
;
267 DI_ReadDVDAsyncptr
= _DI_ReadDVD_A8_Async
;
273 static void _DI_SetCallback(int ioctl_nr
, ipccallback ipc_cb
){
274 if ((di_fd
< 0) || !ipc_cb
)
277 while(LWP_MutexLock(bufferMutex
));
279 memset(dic
, 0x00, sizeof(dic
));
281 dic
[0] = ioctl_nr
<< 24;
282 dic
[1] = (ioctl_nr
== DVD_RESET
)? 1 : 0; // For reset callback. Dirty, I know...
284 IOS_IoctlAsync(di_fd
,ioctl_nr
, dic
, 0x20, outbuf
, 0x20, ipc_cb
, outbuf
);
286 LWP_MutexUnlock(bufferMutex
);
289 #define COVER_CLOSED (*((uint32_t*)usrdata) & DVD_COVER_DISC_INSERTED)
291 static int _cover_callback(int ret
, void* usrdata
){
292 static int cur_state
= 0;
293 static int retry_count
= LIBDI_MAX_RETRIES
;
294 const int callback_table
[] = {
296 DVD_WAITFORCOVERCLOSE
,
298 DVD_IDENTIFY
, // This one will complete when the drive is ready.
301 const int return_table
[] = {1,1,4,1,1};
304 state
&= ~DVD_NO_DISC
;
306 if(callback_table
[cur_state
]){
307 if(ret
== return_table
[cur_state
]){
308 if(cur_state
== 1 && COVER_CLOSED
) // Disc inside, skipping wait for cover.
311 cur_state
++; // If the previous callback succeeded, moving on to the next
313 retry_count
= LIBDI_MAX_RETRIES
;
318 if(retry_count
< 0){ // Drive init failed for unknown reasons.
319 retry_count
= LIBDI_MAX_RETRIES
;
325 _DI_SetCallback(callback_table
[cur_state
- 1], _cover_callback
);
328 else // Callback chain has completed OK. The drive is ready.
331 DI_ReadDVDptr
= _DI_ReadDVD_Check
;
332 DI_ReadDVDAsyncptr
= _DI_ReadDVD_Check_Async
;
338 retry_count
= LIBDI_MAX_RETRIES
;
344 /* Get current status, will return the API status */
349 void DI_SetInitCallback(di_callback cb
){
354 Request an identification from the drive, returned in a DI_DriveID struct
356 int DI_Identify(DI_DriveID
* id
){
363 while(LWP_MutexLock(bufferMutex
));
365 dic
[0] = DVD_IDENTIFY
<< 24;
367 int ret
= IOS_Ioctl(di_fd
, DVD_IDENTIFY
, dic
, 0x20, outbuf
, 0x20);
372 memcpy(id
,outbuf
,sizeof(DI_DriveID
));
374 LWP_MutexUnlock(bufferMutex
);
375 return (ret
== 1)? 0 : -ret
;
379 Returns the current error code on the drive.
380 yagcd has a pretty comprehensive list of possible error codes
382 int DI_GetError(uint32_t* error
){
389 while(LWP_MutexLock(bufferMutex
));
391 dic
[0] = DVD_GET_ERROR
<< 24;
393 int ret
= IOS_Ioctl(di_fd
, DVD_GET_ERROR
, dic
, 0x20, outbuf
, 0x20);
398 *error
= outbuf
[0]; // Error code is returned as an int in the first four bytes of outbuf.
400 LWP_MutexUnlock(bufferMutex
);
401 return (ret
== 1)? 0 : -ret
;
411 while(LWP_MutexLock(bufferMutex
));
413 dic
[0] = DVD_RESET
<< 24;
416 int ret
= IOS_Ioctl(di_fd
, DVD_RESET
, dic
, 0x20, outbuf
, 0x20);
421 LWP_MutexUnlock(bufferMutex
);
422 return (ret
== 1)? 0 : -ret
;
426 Main read function, basically just a wrapper to the function pointer.
427 Nicer then just exposing the pointer itself
429 int DI_ReadDVD(void* buf
, uint32_t len
, uint32_t lba
){
435 while(LWP_MutexLock(bufferMutex
));
436 ret
= DI_ReadDVDptr(buf
,len
,lba
);
437 LWP_MutexUnlock(bufferMutex
);
443 int DI_ReadDVDAsync(void* buf
, uint32_t len
, uint32_t lba
,ipccallback ipc_cb
){
448 if(DI_ReadDVDAsyncptr
){
449 while(LWP_MutexLock(bufferMutex
));
450 ret
= DI_ReadDVDAsyncptr(buf
,len
,lba
,ipc_cb
);
451 LWP_MutexUnlock(bufferMutex
);
458 Unknown what this does as of now...
460 int DI_ReadDVDConfig(uint32_t* val
, uint32_t flag
){
467 while(LWP_MutexLock(bufferMutex
));
469 dic
[0] = DVD_READ_CONFIG
<< 24;
470 dic
[1] = flag
& 0x1; // Update flag, val will be written if this is 1, val won't be written if it's 0.
471 dic
[2] = 0; // Command will fail driveside if this is not zero.
474 int ret
= IOS_Ioctl(di_fd
, DVD_READ_CONFIG
, dic
, 0x20, outbuf
, 0x20);
480 LWP_MutexUnlock(bufferMutex
);
481 return (ret
== 1)? 0 : -ret
;
485 Read the copyright information on a DVDVideo
487 int DI_ReadDVDCopyright(uint32_t* copyright
){
494 while(LWP_MutexLock(bufferMutex
));
496 dic
[0] = DVD_READ_COPYRIGHT
<< 24;
499 int ret
= IOS_Ioctl(di_fd
, DVD_READ_COPYRIGHT
, dic
, 0x20, outbuf
, 0x20);
500 *copyright
= *((uint32_t*)outbuf
); // Copyright information is returned as an int in the first four bytes of outbuf.
505 LWP_MutexUnlock(bufferMutex
);
506 return (ret
== 1)? 0 : -ret
;
510 Returns 0x800 bytes worth of Disc key
512 int DI_ReadDVDDiscKey(void* buf
){
514 int retry_count
= LIBDI_MAX_RETRIES
;
522 if((uint32_t)buf
& 0x1F)
525 while(LWP_MutexLock(bufferMutex
));
527 dic
[0] = DVD_READ_DISCKEY
<< 24;
528 dic
[1] = 0; // Unknown what this flag does.
530 ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCKEY
, dic
, 0x20, buf
, 0x800);
532 }while(ret
!= 1 && retry_count
> 0);
537 LWP_MutexUnlock(bufferMutex
);
538 return (ret
== 1)? 0 : -ret
;
542 This function will read the initial sector on the DVD, which contains stuff like the booktype
544 int DI_ReadDVDPhysical(void* buf
){
546 int retry_count
= LIBDI_MAX_RETRIES
;
554 if((uint32_t)buf
& 0x1F)
557 while(LWP_MutexLock(bufferMutex
));
559 dic
[0] = DVD_READ_PHYSICAL
<< 24;
560 dic
[1] = 0; // Unknown what this flag does.
563 ret
= IOS_Ioctl(di_fd
, DVD_READ_PHYSICAL
, dic
, 0x20, buf
, 0x800);
565 }while(ret
!= 1 && retry_count
> 0);
570 LWP_MutexUnlock(bufferMutex
);
571 return (ret
== 1)? 0 : -ret
;
574 int DI_ReportKey(int keytype
, uint32_t lba
, void* buf
){
581 if((uint32_t)buf
& 0x1F)
584 while(LWP_MutexLock(bufferMutex
));
586 dic
[0] = DVD_REPORTKEY
<< 24;
587 dic
[1] = keytype
& 0xFF;
590 int ret
= IOS_Ioctl(di_fd
, DVD_REPORTKEY
, dic
, 0x20, buf
, 0x20);
595 LWP_MutexUnlock(bufferMutex
);
596 return (ret
== 1)? 0 : -ret
;
599 int DI_GetCoverRegister(uint32_t* status
){
603 while(LWP_MutexLock(bufferMutex
));
605 memset(dic
, 0x00, 0x20);
607 int ret
= IOS_Ioctl(di_fd
, DVD_GETCOVER
, dic
, 0x20, outbuf
, 0x20);
613 LWP_MutexUnlock(bufferMutex
);
614 return (ret
== 1)? 0 : -ret
;
617 /* Internal function for controlling motor operations */
618 static int _DI_SetMotor(int flag
){
622 while(LWP_MutexLock(bufferMutex
));
624 dic
[0] = DVD_SET_MOTOR
<< 24;
625 dic
[1] = flag
& 0x1; // Eject flag.
626 dic
[2] = (flag
>> 1) & 0x1; // Don't use this flag, it kills the drive untill next reset.
628 int ret
= IOS_Ioctl(di_fd
, DVD_SET_MOTOR
, dic
, 0x20, outbuf
, 0x20);
633 LWP_MutexUnlock(bufferMutex
);
634 return(ret
== 1)? 0 : -ret
;
637 /* Stop the drives motor */
639 return _DI_SetMotor(0);
642 /* Stop the motor, and eject the disc. Also needs a reset afterwards for normal operation */
644 return _DI_SetMotor(1);
647 /* Warning, this will kill your drive untill the next reset. Will not respond to DI commands,
648 will not take in or eject the disc. Your drive will be d - e - d, dead.
650 I deem this function to be harmless, as normal operation will resume after a reset.
651 However, I am not liable for anyones drive exploding as a result from using this function.
654 return _DI_SetMotor(2);
657 int DI_ClosePartition() {
661 while(LWP_MutexLock(bufferMutex
));
663 dic
[0] = DVD_CLOSE_PARTITION
<< 24;
665 int ret
= IOS_Ioctl(di_fd
, DVD_CLOSE_PARTITION
, dic
, 0x20, outbuf
, 0x20);
670 LWP_MutexUnlock(bufferMutex
);
671 return(ret
== 1)? 0 : -ret
;
674 int DI_OpenPartition(uint32_t offset
)
679 static ioctlv vectors
[5] __attribute__((aligned(32)));
680 static char certs
[0x49e4] __attribute__((aligned(32)));
681 while(LWP_MutexLock(bufferMutex
));
683 dic
[0] = DVD_OPEN_PARTITION
<< 24;
686 vectors
[0].data
= dic
;
687 vectors
[0].len
= 0x20;
688 vectors
[1].data
= NULL
;
689 vectors
[1].len
= 0x2a4;
690 vectors
[2].data
= NULL
;
693 vectors
[3].data
= certs
;
694 vectors
[3].len
= 0x49e4;
695 vectors
[4].data
= outbuf
;
696 vectors
[4].len
= 0x20;
698 int ret
= IOS_Ioctlv(di_fd
, DVD_OPEN_PARTITION
, 3, 2, vectors
);
703 LWP_MutexUnlock(bufferMutex
);
704 return(ret
== 1)? 0 : -ret
;
708 int DI_Read(void *buf
, uint32_t size
, uint32_t offset
)
716 if((uint32_t)buf
& 0x1F)
719 while(LWP_MutexLock(bufferMutex
));
721 dic
[0] = DVD_LOW_READ
<< 24;
725 int ret
= IOS_Ioctl(di_fd
, DVD_LOW_READ
, dic
, 0x20, buf
, size
);
730 LWP_MutexUnlock(bufferMutex
);
731 return(ret
== 1)? 0 : -ret
;
734 int DI_UnencryptedRead(void *buf
, uint32_t size
, uint32_t offset
)
736 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
744 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
747 while(LWP_MutexLock(bufferMutex
));
749 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
754 ret
= IOS_Ioctl(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, size
);
756 }while(ret
!= 1 && retry_count
> 0);
761 LWP_MutexUnlock(bufferMutex
);
763 return (ret
== 1)? 0 : -ret
;
766 int DI_ReadDiscID(uint64_t *id
)
771 while(LWP_MutexLock(bufferMutex
));
773 dic
[0] = DVD_READ_DISCID
<< 24;
775 int ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCID
, dic
, 0x20, outbuf
, 0x20);
780 memcpy(id
, outbuf
, sizeof(*id
));
782 LWP_MutexUnlock(bufferMutex
);
783 return(ret
== 1)? 0 : -ret
;
786 static bool diio_Startup()
795 t1
=ticks_to_millisecs(gettime());
797 while(state
& DVD_INIT
)
800 t2
=ticks_to_millisecs(gettime());
801 if( (t2
- t1
) > MOUNT_TIMEOUT
)
802 return false; // timeout
805 if(state
& DVD_READY
)
810 static bool diio_IsInserted()
817 DI_GetCoverRegister(&val
);
824 static bool diio_ReadSectors(sec_t sector
,sec_t numSectors
,void *buffer
)
826 if(DI_ReadDVD(buffer
, numSectors
, sector
) == 0)
831 static bool diio_WriteSectors(sec_t sector
,sec_t numSectors
,const void *buffer
)
836 static bool diio_ClearStatus()
841 static bool diio_Shutdown()
846 const DISC_INTERFACE __io_wiidvd
= {
848 FEATURE_MEDIUM_CANREAD
| FEATURE_WII_DVD
,
849 (FN_MEDIUM_STARTUP
)&diio_Startup
,
850 (FN_MEDIUM_ISINSERTED
)&diio_IsInserted
,
851 (FN_MEDIUM_READSECTORS
)&diio_ReadSectors
,
852 (FN_MEDIUM_WRITESECTORS
)&diio_WriteSectors
,
853 (FN_MEDIUM_CLEARSTATUS
)&diio_ClearStatus
,
854 (FN_MEDIUM_SHUTDOWN
)&diio_Shutdown