1 /*-------------------------------------------------------------
3 di.c -- Drive Interface library
14 This software is provided 'as-is', without any express or implied
15 warranty. In no event will the authors be held liable for any
16 damages arising from the use of this software.
18 Permission is granted to anyone to use this software for any
19 purpose, including commercial applications, and to alter it and
20 redistribute it freely, subject to the following restrictions:
22 1. The origin of this software must not be misrepresented; you
23 must not claim that you wrote the original software. If you use
24 this software in a product, an acknowledgment in the product
25 documentation would be appreciated but is not required.
27 2. Altered source versions must be plainly marked as such, and
28 must not be misrepresented as being the original software.
30 3. This notice may not be removed or altered from any source
33 -------------------------------------------------------------*/
41 #include <ogc/cache.h>
44 #include <ogc/mutex.h>
45 #include <ogc/lwp_watchdog.h>
47 #define MOUNT_TIMEOUT 15000 // 15 seconds
50 static bool load_dvdx
= false;
51 static int have_ahbprot
= 0;
52 static int state
= DVD_INIT
| DVD_NO_DISC
;
54 static int _cover_callback(int ret
, void* usrdata
);
56 static unsigned int bufferMutex
= 0;
57 static uint32_t outbuf
[8] __attribute__((aligned(32)));
58 static uint32_t dic
[8] __attribute__((aligned(32)));
59 static const char di_path
[] ATTRIBUTE_ALIGN(32) = "/dev/di";
61 static read_func DI_ReadDVDptr
= NULL
;
62 static read_func_async DI_ReadDVDAsyncptr
= NULL
;
63 static di_callback di_cb
= NULL
;
65 static int _DI_ReadDVD_A8_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
){
71 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
74 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
75 dic
[1] = len
<< 11; // 1 LB is 2048 bytes
76 dic
[2] = lba
<< 9; // Nintendo's read function uses byteOffset >> 2, so we only shift 9 left, not 11.
78 ret
= IOS_IoctlAsync(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, len
<< 11,ipc_cb
, buf
);
83 return (ret
== 1)? 0 : -ret
;
86 static int _DI_ReadDVD_D0_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
){
92 if((uint32_t)buf
& 0x1F)
95 dic
[0] = DVD_READ
<< 24;
96 dic
[1] = 0; // Unknown what this does as of now. (Sets some value to 0x10 in the drive if set).
97 dic
[2] = 0; // USE_DEFAULT_CONFIG flag. Drive will use default config if this bit is set.
101 ret
= IOS_IoctlAsync(di_fd
, DVD_READ
, dic
, 0x20, buf
, len
<< 11,ipc_cb
, buf
);
106 return (ret
== 1)? 0 : -ret
;
109 volatile unsigned long* dvd
= (volatile unsigned long*)0xCD806000;
111 static int _DI_ReadDVD(void* buf
, uint32_t len
, uint32_t lba
, uint32_t read_cmd
){
112 if ((((int) buf
) & 0xC0000000) == 0x80000000) // cached?
116 dvd
[3] = read_cmd
== 0xD0000000 ? lba
: lba
<< 9;
117 dvd
[4] = read_cmd
== 0xD0000000 ? len
: len
<< 11;
118 dvd
[5] = (unsigned long) buf
;
120 dvd
[7] = 3; // enable reading!
121 DCInvalidateRange(buf
, len
<< 11);
129 static int _DI_ReadDVD_A8(void* buf
, uint32_t len
, uint32_t lba
){
130 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
135 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
139 return _DI_ReadDVD(buf
, len
, lba
, 0xA8000000);
141 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
142 dic
[1] = len
<< 11; // 1 LB is 2048 bytes
143 dic
[2] = lba
<< 9; // Nintendo's read function uses byteOffset >> 2, so we only shift 9 left, not 11.
146 ret
= IOS_Ioctl(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, len
<< 11);
148 }while(ret
!= 1 && retry_count
> 0);
153 return (ret
== 1)? 0 : -ret
;
156 static int _DI_ReadDVD_D0(void* buf
, uint32_t len
, uint32_t lba
){
157 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
162 if((uint32_t)buf
& 0x1F)
166 return _DI_ReadDVD(buf
, len
, lba
, 0xD0000000);
168 dic
[0] = DVD_READ
<< 24;
169 dic
[1] = 0; // Unknown what this does as of now. (Sets some value to 0x10 in the drive if set).
170 dic
[2] = 0; // USE_DEFAULT_CONFIG flag. Drive will use default config if this bit is set.
175 ret
= IOS_Ioctl(di_fd
, DVD_READ
, dic
, 0x20, buf
, len
<< 11);
177 }while(ret
!= 1 && retry_count
> 0);
182 return (ret
== 1)? 0 : -ret
;
186 #define CACHE_FREE 0xFFFFFFFF
187 #define BLOCK_SIZE 0x800
188 #define CACHEBLOCKS 26
194 static cache_page
*cache_read
= NULL
;
196 static void CreateDVDCache()
198 if (cache_read
!= NULL
)
200 cache_read
= (cache_page
*) malloc(sizeof(cache_page
));
201 if (cache_read
== NULL
)
204 cache_read
->block
= CACHE_FREE
;
205 cache_read
->ptr
= memalign(32, BLOCK_SIZE
* CACHEBLOCKS
);
206 if (cache_read
->ptr
== NULL
)
212 memset(cache_read
->ptr
, 0, BLOCK_SIZE
);
215 static int ReadBlockFromCache(void *buf
, uint32_t len
, uint32_t block
)
219 if (cache_read
== NULL
)
220 return DI_ReadDVDptr(buf
, len
, block
);
222 if ((block
>= cache_read
->block
) && (block
+ len
< (cache_read
->block
+ CACHEBLOCKS
)))
224 memcpy(buf
, cache_read
->ptr
+ ((block
- cache_read
->block
) * BLOCK_SIZE
), BLOCK_SIZE
* len
);
228 if (len
> CACHEBLOCKS
)
229 return DI_ReadDVDptr(buf
, len
, block
);
231 retval
= DI_ReadDVDptr(cache_read
->ptr
, CACHEBLOCKS
, block
);
234 cache_read
->block
= CACHE_FREE
;
238 cache_read
->block
= block
;
239 memcpy(buf
, cache_read
->ptr
, len
* BLOCK_SIZE
);
245 Initialize the DI interface, should always be called first!
248 s32
__DI_StubLaunch(void);
249 u32
__di_check_ahbprot(void);
255 state
= DVD_INIT
| DVD_NO_DISC
;
256 have_ahbprot
= __di_check_ahbprot();
258 if(have_ahbprot
!= 1 && load_dvdx
) {
259 int res
= __DI_StubLaunch(); // Marcan's 1337 magics happen here!
266 di_fd
= IOS_Open(di_path
, 2);
272 LWP_MutexInit(&bufferMutex
, false);
278 void DI_LoadDVDX(bool load
){
288 if (DI_GetCoverRegister(&status
) != 0) {
293 if ((status
& DVD_COVER_DISC_INSERTED
) == 0) {
298 state
= DVD_INIT
| DVD_NO_DISC
;
299 _cover_callback(1, NULL
); // Initialize the callback chain.
301 if (cache_read
!= NULL
)
302 cache_read
->block
= CACHE_FREE
; // reset cache
314 DI_ReadDVDptr
= NULL
;
315 DI_ReadDVDAsyncptr
= NULL
;
316 state
= DVD_INIT
| DVD_NO_DISC
;
319 LWP_MutexDestroy(bufferMutex
);
324 static int _DI_ReadDVD_Check(void* buf
, uint32_t len
, uint32_t lba
)
328 ret
= _DI_ReadDVD_D0(buf
, len
, lba
);
331 state
= state
| DVD_D0
;
332 DI_ReadDVDptr
= _DI_ReadDVD_D0
;
333 DI_ReadDVDAsyncptr
= _DI_ReadDVD_D0_Async
;
336 ret
= _DI_ReadDVD_A8(buf
, len
, lba
);
339 state
= state
| DVD_A8
;
340 DI_ReadDVDptr
= _DI_ReadDVD_A8
;
341 DI_ReadDVDAsyncptr
= _DI_ReadDVD_A8_Async
;
347 static int _DI_ReadDVD_Check_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
)
351 ret
= _DI_ReadDVD_D0_Async(buf
, len
, lba
, ipc_cb
);
354 state
= state
| DVD_D0
;
355 DI_ReadDVDptr
= _DI_ReadDVD_D0
;
356 DI_ReadDVDAsyncptr
= _DI_ReadDVD_D0_Async
;
359 ret
= _DI_ReadDVD_A8_Async(buf
, len
, lba
, ipc_cb
);
362 state
= state
| DVD_A8
;
363 DI_ReadDVDptr
= _DI_ReadDVD_A8
;
364 DI_ReadDVDAsyncptr
= _DI_ReadDVD_A8_Async
;
370 static void _DI_SetCallback(int ioctl_nr
, ipccallback ipc_cb
){
371 if ((di_fd
< 0) || !ipc_cb
)
374 while(LWP_MutexLock(bufferMutex
));
376 memset(dic
, 0x00, sizeof(dic
));
378 dic
[0] = ioctl_nr
<< 24;
379 dic
[1] = (ioctl_nr
== DVD_RESET
)? 1 : 0; // For reset callback. Dirty, I know...
381 IOS_IoctlAsync(di_fd
,ioctl_nr
, dic
, 0x20, outbuf
, 0x20, ipc_cb
, outbuf
);
383 LWP_MutexUnlock(bufferMutex
);
386 #define COVER_CLOSED (*((uint32_t*)usrdata) & DVD_COVER_DISC_INSERTED)
388 static int _cover_callback(int ret
, void* usrdata
){
389 static int cur_state
= 0;
390 static int retry_count
= LIBDI_MAX_RETRIES
;
391 const int callback_table
[] = {
393 DVD_WAITFORCOVERCLOSE
,
395 DVD_IDENTIFY
, // This one will complete when the drive is ready.
398 const int return_table
[] = {1,1,4,1,1};
401 state
&= ~DVD_NO_DISC
;
403 if(callback_table
[cur_state
]){
404 if(ret
== return_table
[cur_state
]){
405 if(cur_state
== 1 && COVER_CLOSED
) // Disc inside, skipping wait for cover.
408 cur_state
++; // If the previous callback succeeded, moving on to the next
410 retry_count
= LIBDI_MAX_RETRIES
;
415 if(retry_count
< 0){ // Drive init failed for unknown reasons.
416 retry_count
= LIBDI_MAX_RETRIES
;
422 _DI_SetCallback(callback_table
[cur_state
- 1], _cover_callback
);
425 else // Callback chain has completed OK. The drive is ready.
428 DI_ReadDVDptr
= _DI_ReadDVD_Check
;
429 DI_ReadDVDAsyncptr
= _DI_ReadDVD_Check_Async
;
435 retry_count
= LIBDI_MAX_RETRIES
;
441 /* Get current status, will return the API status */
446 void DI_SetInitCallback(di_callback cb
){
451 Request an identification from the drive, returned in a DI_DriveID struct
453 int DI_Identify(DI_DriveID
* id
){
460 while(LWP_MutexLock(bufferMutex
));
462 dic
[0] = DVD_IDENTIFY
<< 24;
464 int ret
= IOS_Ioctl(di_fd
, DVD_IDENTIFY
, dic
, 0x20, outbuf
, 0x20);
469 memcpy(id
,outbuf
,sizeof(DI_DriveID
));
471 LWP_MutexUnlock(bufferMutex
);
472 return (ret
== 1)? 0 : -ret
;
476 Returns the current error code on the drive.
477 yagcd has a pretty comprehensive list of possible error codes
479 int DI_GetError(uint32_t* error
){
486 while(LWP_MutexLock(bufferMutex
));
488 dic
[0] = DVD_GET_ERROR
<< 24;
490 int ret
= IOS_Ioctl(di_fd
, DVD_GET_ERROR
, dic
, 0x20, outbuf
, 0x20);
495 *error
= outbuf
[0]; // Error code is returned as an int in the first four bytes of outbuf.
497 LWP_MutexUnlock(bufferMutex
);
498 return (ret
== 1)? 0 : -ret
;
508 while(LWP_MutexLock(bufferMutex
));
510 dic
[0] = DVD_RESET
<< 24;
513 int ret
= IOS_Ioctl(di_fd
, DVD_RESET
, dic
, 0x20, outbuf
, 0x20);
518 LWP_MutexUnlock(bufferMutex
);
519 return (ret
== 1)? 0 : -ret
;
523 Main read function, basically just a wrapper to the function pointer.
524 Nicer then just exposing the pointer itself
526 int DI_ReadDVD(void* buf
, uint32_t len
, uint32_t lba
){
532 while(LWP_MutexLock(bufferMutex
));
533 ret
= ReadBlockFromCache(buf
,len
,lba
);
534 LWP_MutexUnlock(bufferMutex
);
540 int DI_ReadDVDAsync(void* buf
, uint32_t len
, uint32_t lba
,ipccallback ipc_cb
){
545 if(DI_ReadDVDAsyncptr
){
546 while(LWP_MutexLock(bufferMutex
));
547 ret
= DI_ReadDVDAsyncptr(buf
,len
,lba
,ipc_cb
);
548 LWP_MutexUnlock(bufferMutex
);
555 Unknown what this does as of now...
557 int DI_ReadDVDConfig(uint32_t* val
, uint32_t flag
){
564 while(LWP_MutexLock(bufferMutex
));
566 dic
[0] = DVD_READ_CONFIG
<< 24;
567 dic
[1] = flag
& 0x1; // Update flag, val will be written if this is 1, val won't be written if it's 0.
568 dic
[2] = 0; // Command will fail driveside if this is not zero.
571 int ret
= IOS_Ioctl(di_fd
, DVD_READ_CONFIG
, dic
, 0x20, outbuf
, 0x20);
577 LWP_MutexUnlock(bufferMutex
);
578 return (ret
== 1)? 0 : -ret
;
582 Read the copyright information on a DVDVideo
584 int DI_ReadDVDCopyright(uint32_t* copyright
){
591 while(LWP_MutexLock(bufferMutex
));
593 dic
[0] = DVD_READ_COPYRIGHT
<< 24;
596 int ret
= IOS_Ioctl(di_fd
, DVD_READ_COPYRIGHT
, dic
, 0x20, outbuf
, 0x20);
597 *copyright
= *((uint32_t*)outbuf
); // Copyright information is returned as an int in the first four bytes of outbuf.
602 LWP_MutexUnlock(bufferMutex
);
603 return (ret
== 1)? 0 : -ret
;
607 Returns 0x800 bytes worth of Disc key
609 int DI_ReadDVDDiscKey(void* buf
){
611 int retry_count
= LIBDI_MAX_RETRIES
;
619 if((uint32_t)buf
& 0x1F)
622 while(LWP_MutexLock(bufferMutex
));
624 dic
[0] = DVD_READ_DISCKEY
<< 24;
625 dic
[1] = 0; // Unknown what this flag does.
627 ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCKEY
, dic
, 0x20, buf
, 0x800);
629 }while(ret
!= 1 && retry_count
> 0);
634 LWP_MutexUnlock(bufferMutex
);
635 return (ret
== 1)? 0 : -ret
;
639 This function will read the initial sector on the DVD, which contains stuff like the booktype
641 int DI_ReadDVDPhysical(void* buf
){
643 int retry_count
= LIBDI_MAX_RETRIES
;
651 if((uint32_t)buf
& 0x1F)
654 while(LWP_MutexLock(bufferMutex
));
656 dic
[0] = DVD_READ_PHYSICAL
<< 24;
657 dic
[1] = 0; // Unknown what this flag does.
660 ret
= IOS_Ioctl(di_fd
, DVD_READ_PHYSICAL
, dic
, 0x20, buf
, 0x800);
662 }while(ret
!= 1 && retry_count
> 0);
667 LWP_MutexUnlock(bufferMutex
);
668 return (ret
== 1)? 0 : -ret
;
671 int DI_ReportKey(int keytype
, uint32_t lba
, void* buf
){
678 if((uint32_t)buf
& 0x1F)
681 while(LWP_MutexLock(bufferMutex
));
683 dic
[0] = DVD_REPORTKEY
<< 24;
684 dic
[1] = keytype
& 0xFF;
687 int ret
= IOS_Ioctl(di_fd
, DVD_REPORTKEY
, dic
, 0x20, buf
, 0x20);
692 LWP_MutexUnlock(bufferMutex
);
693 return (ret
== 1)? 0 : -ret
;
696 int DI_GetCoverRegister(uint32_t* status
){
700 while(LWP_MutexLock(bufferMutex
));
702 memset(dic
, 0x00, 0x20);
704 int ret
= IOS_Ioctl(di_fd
, DVD_GETCOVER
, dic
, 0x20, outbuf
, 0x20);
710 LWP_MutexUnlock(bufferMutex
);
711 return (ret
== 1)? 0 : -ret
;
714 /* Internal function for controlling motor operations */
715 static int _DI_SetMotor(int flag
){
719 while(LWP_MutexLock(bufferMutex
));
721 dic
[0] = DVD_SET_MOTOR
<< 24;
722 dic
[1] = flag
& 0x1; // Eject flag.
723 dic
[2] = (flag
>> 1) & 0x1; // Don't use this flag, it kills the drive untill next reset.
725 int ret
= IOS_Ioctl(di_fd
, DVD_SET_MOTOR
, dic
, 0x20, outbuf
, 0x20);
730 LWP_MutexUnlock(bufferMutex
);
731 return(ret
== 1)? 0 : -ret
;
734 /* Stop the drives motor */
736 return _DI_SetMotor(0);
739 /* Stop the motor, and eject the disc. Also needs a reset afterwards for normal operation */
741 return _DI_SetMotor(1);
744 /* Warning, this will kill your drive untill the next reset. Will not respond to DI commands,
745 will not take in or eject the disc. Your drive will be d - e - d, dead.
747 I deem this function to be harmless, as normal operation will resume after a reset.
748 However, I am not liable for anyones drive exploding as a result from using this function.
751 return _DI_SetMotor(2);
754 int DI_ClosePartition() {
758 while(LWP_MutexLock(bufferMutex
));
760 dic
[0] = DVD_CLOSE_PARTITION
<< 24;
762 int ret
= IOS_Ioctl(di_fd
, DVD_CLOSE_PARTITION
, dic
, 0x20, outbuf
, 0x20);
767 LWP_MutexUnlock(bufferMutex
);
768 return(ret
== 1)? 0 : -ret
;
771 int DI_OpenPartition(uint32_t offset
)
776 static ioctlv vectors
[5] __attribute__((aligned(32)));
777 static char certs
[0x49e4] __attribute__((aligned(32)));
778 while(LWP_MutexLock(bufferMutex
));
780 dic
[0] = DVD_OPEN_PARTITION
<< 24;
783 vectors
[0].data
= dic
;
784 vectors
[0].len
= 0x20;
785 vectors
[1].data
= NULL
;
786 vectors
[1].len
= 0x2a4;
787 vectors
[2].data
= NULL
;
790 vectors
[3].data
= certs
;
791 vectors
[3].len
= 0x49e4;
792 vectors
[4].data
= outbuf
;
793 vectors
[4].len
= 0x20;
795 int ret
= IOS_Ioctlv(di_fd
, DVD_OPEN_PARTITION
, 3, 2, vectors
);
800 LWP_MutexUnlock(bufferMutex
);
801 return(ret
== 1)? 0 : -ret
;
805 int DI_Read(void *buf
, uint32_t size
, uint32_t offset
)
813 if((uint32_t)buf
& 0x1F)
816 while(LWP_MutexLock(bufferMutex
));
818 dic
[0] = DVD_LOW_READ
<< 24;
822 int ret
= IOS_Ioctl(di_fd
, DVD_LOW_READ
, dic
, 0x20, buf
, size
);
827 LWP_MutexUnlock(bufferMutex
);
828 return(ret
== 1)? 0 : -ret
;
831 int DI_UnencryptedRead(void *buf
, uint32_t size
, uint32_t offset
)
833 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
841 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
844 while(LWP_MutexLock(bufferMutex
));
846 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
851 ret
= IOS_Ioctl(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, size
);
853 }while(ret
!= 1 && retry_count
> 0);
858 LWP_MutexUnlock(bufferMutex
);
860 return (ret
== 1)? 0 : -ret
;
863 int DI_ReadDiscID(uint64_t *id
)
868 while(LWP_MutexLock(bufferMutex
));
870 dic
[0] = DVD_READ_DISCID
<< 24;
872 int ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCID
, dic
, 0x20, outbuf
, 0x20);
877 memcpy(id
, outbuf
, sizeof(*id
));
879 LWP_MutexUnlock(bufferMutex
);
880 return(ret
== 1)? 0 : -ret
;
883 static bool diio_Startup()
892 t1
=ticks_to_millisecs(gettime());
894 while(state
& DVD_INIT
)
897 t2
=ticks_to_millisecs(gettime());
898 if( (t2
- t1
) > MOUNT_TIMEOUT
)
899 return false; // timeout
902 if(state
& DVD_READY
)
907 static bool diio_IsInserted()
914 DI_GetCoverRegister(&val
);
921 static bool diio_ReadSectors(sec_t sector
,sec_t numSectors
,void *buffer
)
923 if(DI_ReadDVD(buffer
, numSectors
, sector
) == 0)
928 static bool diio_WriteSectors(sec_t sector
,sec_t numSectors
,const void *buffer
)
933 static bool diio_ClearStatus()
938 static bool diio_Shutdown()
943 const DISC_INTERFACE __io_wiidvd
= {
945 FEATURE_MEDIUM_CANREAD
| FEATURE_WII_DVD
,
946 (FN_MEDIUM_STARTUP
)&diio_Startup
,
947 (FN_MEDIUM_ISINSERTED
)&diio_IsInserted
,
948 (FN_MEDIUM_READSECTORS
)&diio_ReadSectors
,
949 (FN_MEDIUM_WRITESECTORS
)&diio_WriteSectors
,
950 (FN_MEDIUM_CLEARSTATUS
)&diio_ClearStatus
,
951 (FN_MEDIUM_SHUTDOWN
)&diio_Shutdown