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 bool use_dvd_cache
= true;
52 static int have_ahbprot
= 0;
53 static int state
= DVD_INIT
| DVD_NO_DISC
;
55 static int _cover_callback(int ret
, void* usrdata
);
57 static unsigned int bufferMutex
= 0;
58 static uint32_t outbuf
[8] __attribute__((aligned(32)));
59 static uint32_t dic
[8] __attribute__((aligned(32)));
60 static const char di_path
[] ATTRIBUTE_ALIGN(32) = "/dev/di";
62 static read_func DI_ReadDVDptr
= NULL
;
63 static read_func_async DI_ReadDVDAsyncptr
= NULL
;
64 static di_callback di_cb
= NULL
;
66 static vu32
* const _dvdReg
= (u32
*)0xCD806000;
68 static int _DI_ReadDVD_A8_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
){
74 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
77 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
78 dic
[1] = len
<< 11; // 1 LB is 2048 bytes
79 dic
[2] = lba
<< 9; // Nintendo's read function uses byteOffset >> 2, so we only shift 9 left, not 11.
81 ret
= IOS_IoctlAsync(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, len
<< 11,ipc_cb
, buf
);
86 return (ret
== 1)? 0 : -ret
;
89 static int _DI_ReadDVD_D0_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
){
95 if((uint32_t)buf
& 0x1F)
98 dic
[0] = DVD_READ
<< 24;
99 dic
[1] = 0; // Unknown what this does as of now. (Sets some value to 0x10 in the drive if set).
100 dic
[2] = 0; // USE_DEFAULT_CONFIG flag. Drive will use default config if this bit is set.
104 ret
= IOS_IoctlAsync(di_fd
, DVD_READ
, dic
, 0x20, buf
, len
<< 11,ipc_cb
, buf
);
109 return (ret
== 1)? 0 : -ret
;
112 static int _DI_ReadDVD(void* buf
, uint32_t len
, uint32_t lba
, uint32_t read_cmd
){
113 if ((((int) buf
) & 0xC0000000) == 0x80000000) // cached?
116 _dvdReg
[2] = read_cmd
;
117 _dvdReg
[3] = read_cmd
== 0xD0000000 ? lba
: lba
<< 9;
118 _dvdReg
[4] = read_cmd
== 0xD0000000 ? len
: len
<< 11;
119 _dvdReg
[5] = (unsigned long) buf
;
120 _dvdReg
[6] = len
<< 11;
121 _dvdReg
[7] = 3; // enable reading!
122 DCInvalidateRange(buf
, len
<< 11);
123 while (_dvdReg
[7] & 1);
125 if (_dvdReg
[0] & 0x4)
130 static int _DI_ReadDVD_A8(void* buf
, uint32_t len
, uint32_t lba
){
131 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
136 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
140 return _DI_ReadDVD(buf
, len
, lba
, 0xA8000000);
142 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
143 dic
[1] = len
<< 11; // 1 LB is 2048 bytes
144 dic
[2] = lba
<< 9; // Nintendo's read function uses byteOffset >> 2, so we only shift 9 left, not 11.
147 ret
= IOS_Ioctl(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, len
<< 11);
149 }while(ret
!= 1 && retry_count
> 0);
154 return (ret
== 1)? 0 : -ret
;
157 static int _DI_ReadDVD_D0(void* buf
, uint32_t len
, uint32_t lba
){
158 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
163 if((uint32_t)buf
& 0x1F)
167 return _DI_ReadDVD(buf
, len
, lba
, 0xD0000000);
169 dic
[0] = DVD_READ
<< 24;
170 dic
[1] = 0; // Unknown what this does as of now. (Sets some value to 0x10 in the drive if set).
171 dic
[2] = 0; // USE_DEFAULT_CONFIG flag. Drive will use default config if this bit is set.
176 ret
= IOS_Ioctl(di_fd
, DVD_READ
, dic
, 0x20, buf
, len
<< 11);
178 }while(ret
!= 1 && retry_count
> 0);
183 return (ret
== 1)? 0 : -ret
;
187 #define CACHE_FREE 0xFFFFFFFF
188 #define BLOCK_SIZE 0x800
189 #define CACHEBLOCKS 26
195 static cache_page
*cache_read
= NULL
;
197 static void CreateDVDCache()
199 if (cache_read
!= NULL
)
201 cache_read
= (cache_page
*) malloc(sizeof(cache_page
));
202 if (cache_read
== NULL
)
205 cache_read
->block
= CACHE_FREE
;
206 cache_read
->ptr
= memalign(32, BLOCK_SIZE
* CACHEBLOCKS
);
207 if (cache_read
->ptr
== NULL
)
213 memset(cache_read
->ptr
, 0, BLOCK_SIZE
);
216 static int ReadBlockFromCache(void *buf
, uint32_t len
, uint32_t block
)
220 if (cache_read
== NULL
)
221 return DI_ReadDVDptr(buf
, len
, block
);
223 if ((block
>= cache_read
->block
) && (block
+ len
< (cache_read
->block
+ CACHEBLOCKS
)))
225 memcpy(buf
, cache_read
->ptr
+ ((block
- cache_read
->block
) * BLOCK_SIZE
), BLOCK_SIZE
* len
);
229 if (len
> CACHEBLOCKS
)
230 return DI_ReadDVDptr(buf
, len
, block
);
232 retval
= DI_ReadDVDptr(cache_read
->ptr
, CACHEBLOCKS
, block
);
235 cache_read
->block
= CACHE_FREE
;
239 cache_read
->block
= block
;
240 memcpy(buf
, cache_read
->ptr
, len
* BLOCK_SIZE
);
246 Initialize the DI interface, should always be called first!
249 s32
__DI_StubLaunch(void);
250 u32
__di_check_ahbprot(void);
256 state
= DVD_INIT
| DVD_NO_DISC
;
257 have_ahbprot
= __di_check_ahbprot();
259 if(have_ahbprot
!= 1 && load_dvdx
) {
260 int res
= __DI_StubLaunch(); // Marcan's 1337 magics happen here!
267 di_fd
= IOS_Open(di_path
, 2);
273 LWP_MutexInit(&bufferMutex
, false);
281 void DI_LoadDVDX(bool load
) {
285 void DI_UseCache(bool use
) {
295 if (DI_GetCoverRegister(&status
) != 0) {
300 if ((status
& DVD_COVER_DISC_INSERTED
) == 0) {
305 state
= DVD_INIT
| DVD_NO_DISC
;
306 _cover_callback(1, NULL
); // Initialize the callback chain.
308 if (cache_read
!= NULL
)
309 cache_read
->block
= CACHE_FREE
; // reset cache
321 DI_ReadDVDptr
= NULL
;
322 DI_ReadDVDAsyncptr
= NULL
;
323 state
= DVD_INIT
| DVD_NO_DISC
;
326 LWP_MutexDestroy(bufferMutex
);
331 static int _DI_ReadDVD_Check(void* buf
, uint32_t len
, uint32_t lba
)
335 ret
= _DI_ReadDVD_D0(buf
, len
, lba
);
338 state
= state
| DVD_D0
;
339 DI_ReadDVDptr
= _DI_ReadDVD_D0
;
340 DI_ReadDVDAsyncptr
= _DI_ReadDVD_D0_Async
;
343 ret
= _DI_ReadDVD_A8(buf
, len
, lba
);
346 state
= state
| DVD_A8
;
347 DI_ReadDVDptr
= _DI_ReadDVD_A8
;
348 DI_ReadDVDAsyncptr
= _DI_ReadDVD_A8_Async
;
354 static int _DI_ReadDVD_Check_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
)
358 ret
= _DI_ReadDVD_D0_Async(buf
, len
, lba
, ipc_cb
);
361 state
= state
| DVD_D0
;
362 DI_ReadDVDptr
= _DI_ReadDVD_D0
;
363 DI_ReadDVDAsyncptr
= _DI_ReadDVD_D0_Async
;
366 ret
= _DI_ReadDVD_A8_Async(buf
, len
, lba
, ipc_cb
);
369 state
= state
| DVD_A8
;
370 DI_ReadDVDptr
= _DI_ReadDVD_A8
;
371 DI_ReadDVDAsyncptr
= _DI_ReadDVD_A8_Async
;
377 static void _DI_SetCallback(int ioctl_nr
, ipccallback ipc_cb
){
378 if ((di_fd
< 0) || !ipc_cb
)
381 LWP_MutexLock(bufferMutex
);
383 memset(dic
, 0x00, sizeof(dic
));
385 dic
[0] = ioctl_nr
<< 24;
386 dic
[1] = (ioctl_nr
== DVD_RESET
)? 1 : 0; // For reset callback. Dirty, I know...
388 IOS_IoctlAsync(di_fd
,ioctl_nr
, dic
, 0x20, outbuf
, 0x20, ipc_cb
, outbuf
);
389 LWP_MutexUnlock(bufferMutex
);
392 #define COVER_CLOSED (*((uint32_t*)usrdata) & DVD_COVER_DISC_INSERTED)
394 static int _cover_callback(int ret
, void* usrdata
){
395 static int cur_state
= 0;
396 static int retry_count
= LIBDI_MAX_RETRIES
;
397 const int callback_table
[] = {
399 DVD_WAITFORCOVERCLOSE
,
401 DVD_IDENTIFY
, // This one will complete when the drive is ready.
404 const int return_table
[] = {1,1,4,1,1};
407 state
&= ~DVD_NO_DISC
;
409 if(callback_table
[cur_state
]){
410 if(ret
== return_table
[cur_state
]){
411 if(cur_state
== 1 && COVER_CLOSED
) // Disc inside, skipping wait for cover.
414 cur_state
++; // If the previous callback succeeded, moving on to the next
416 retry_count
= LIBDI_MAX_RETRIES
;
421 if(retry_count
< 0){ // Drive init failed for unknown reasons.
422 retry_count
= LIBDI_MAX_RETRIES
;
428 _DI_SetCallback(callback_table
[cur_state
- 1], _cover_callback
);
431 else // Callback chain has completed OK. The drive is ready.
434 DI_ReadDVDptr
= _DI_ReadDVD_Check
;
435 DI_ReadDVDAsyncptr
= _DI_ReadDVD_Check_Async
;
441 retry_count
= LIBDI_MAX_RETRIES
;
447 /* Get current status, will return the API status */
452 void DI_SetInitCallback(di_callback cb
){
457 Request an identification from the drive, returned in a DI_DriveID struct
459 int DI_Identify(DI_DriveID
* id
){
466 LWP_MutexLock(bufferMutex
);
468 dic
[0] = DVD_IDENTIFY
<< 24;
470 int ret
= IOS_Ioctl(di_fd
, DVD_IDENTIFY
, dic
, 0x20, outbuf
, 0x20);
475 memcpy(id
,outbuf
,sizeof(DI_DriveID
));
477 LWP_MutexUnlock(bufferMutex
);
478 return (ret
== 1)? 0 : -ret
;
482 Returns the current error code on the drive.
483 yagcd has a pretty comprehensive list of possible error codes
485 int DI_GetError(uint32_t* error
){
492 LWP_MutexLock(bufferMutex
);
494 dic
[0] = DVD_GET_ERROR
<< 24;
496 int ret
= IOS_Ioctl(di_fd
, DVD_GET_ERROR
, dic
, 0x20, outbuf
, 0x20);
501 *error
= outbuf
[0]; // Error code is returned as an int in the first four bytes of outbuf.
503 LWP_MutexUnlock(bufferMutex
);
504 return (ret
== 1)? 0 : -ret
;
514 LWP_MutexLock(bufferMutex
);
516 dic
[0] = DVD_RESET
<< 24;
519 int ret
= IOS_Ioctl(di_fd
, DVD_RESET
, dic
, 0x20, outbuf
, 0x20);
524 LWP_MutexUnlock(bufferMutex
);
525 return (ret
== 1)? 0 : -ret
;
529 Main read function, basically just a wrapper to the function pointer.
530 Nicer then just exposing the pointer itself
532 int DI_ReadDVD(void* buf
, uint32_t len
, uint32_t lba
){
538 LWP_MutexLock(bufferMutex
);
539 ret
= ReadBlockFromCache(buf
,len
,lba
);
540 LWP_MutexUnlock(bufferMutex
);
546 int DI_ReadDVDAsync(void* buf
, uint32_t len
, uint32_t lba
,ipccallback ipc_cb
){
551 if(DI_ReadDVDAsyncptr
){
552 LWP_MutexLock(bufferMutex
);
553 ret
= DI_ReadDVDAsyncptr(buf
,len
,lba
,ipc_cb
);
554 LWP_MutexUnlock(bufferMutex
);
561 Unknown what this does as of now...
563 int DI_ReadDVDConfig(uint32_t* val
, uint32_t flag
){
570 LWP_MutexLock(bufferMutex
);
572 dic
[0] = DVD_READ_CONFIG
<< 24;
573 dic
[1] = flag
& 0x1; // Update flag, val will be written if this is 1, val won't be written if it's 0.
574 dic
[2] = 0; // Command will fail driveside if this is not zero.
577 int ret
= IOS_Ioctl(di_fd
, DVD_READ_CONFIG
, dic
, 0x20, outbuf
, 0x20);
583 LWP_MutexUnlock(bufferMutex
);
584 return (ret
== 1)? 0 : -ret
;
588 Read the copyright information on a DVDVideo
590 int DI_ReadDVDCopyright(uint32_t* copyright
){
597 LWP_MutexLock(bufferMutex
);
599 dic
[0] = DVD_READ_COPYRIGHT
<< 24;
602 int ret
= IOS_Ioctl(di_fd
, DVD_READ_COPYRIGHT
, dic
, 0x20, outbuf
, 0x20);
603 *copyright
= *((uint32_t*)outbuf
); // Copyright information is returned as an int in the first four bytes of outbuf.
608 LWP_MutexUnlock(bufferMutex
);
609 return (ret
== 1)? 0 : -ret
;
612 int DI_Read_BCA(void *outbuf
)
620 memset(dic
, 0, sizeof(dic
));
621 dic
[0] = DVD_READ_BCA
<< 24;
623 int ret
= IOS_Ioctl(di_fd
, DVD_READ_BCA
, dic
, 0x20, outbuf
, 64);
628 LWP_MutexUnlock(bufferMutex
);
629 return (ret
== 1)? 0 : -ret
;
633 Returns 0x800 bytes worth of Disc key
635 int DI_ReadDVDDiscKey(void* buf
){
637 int retry_count
= LIBDI_MAX_RETRIES
;
645 if((uint32_t)buf
& 0x1F)
648 LWP_MutexLock(bufferMutex
);
650 dic
[0] = DVD_READ_DISCKEY
<< 24;
651 dic
[1] = 0; // Unknown what this flag does.
653 ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCKEY
, dic
, 0x20, buf
, 0x800);
655 }while(ret
!= 1 && retry_count
> 0);
660 LWP_MutexUnlock(bufferMutex
);
661 return (ret
== 1)? 0 : -ret
;
665 This function will read the initial sector on the DVD, which contains stuff like the booktype
667 int DI_ReadDVDPhysical(void* buf
){
669 int retry_count
= LIBDI_MAX_RETRIES
;
677 if((uint32_t)buf
& 0x1F)
680 LWP_MutexLock(bufferMutex
);
682 dic
[0] = DVD_READ_PHYSICAL
<< 24;
683 dic
[1] = 0; // Unknown what this flag does.
686 ret
= IOS_Ioctl(di_fd
, DVD_READ_PHYSICAL
, dic
, 0x20, buf
, 0x800);
688 }while(ret
!= 1 && retry_count
> 0);
693 LWP_MutexUnlock(bufferMutex
);
694 return (ret
== 1)? 0 : -ret
;
697 int DI_ReportKey(int keytype
, uint32_t lba
, void* buf
){
704 if((uint32_t)buf
& 0x1F)
707 LWP_MutexLock(bufferMutex
);
709 dic
[0] = DVD_REPORTKEY
<< 24;
710 dic
[1] = keytype
& 0xFF;
713 int ret
= IOS_Ioctl(di_fd
, DVD_REPORTKEY
, dic
, 0x20, buf
, 0x20);
718 LWP_MutexUnlock(bufferMutex
);
719 return (ret
== 1)? 0 : -ret
;
722 int DI_GetCoverRegister(uint32_t* status
){
726 LWP_MutexLock(bufferMutex
);
727 memset(dic
, 0x00, 0x20);
729 int ret
= IOS_Ioctl(di_fd
, DVD_GETCOVER
, dic
, 0x20, outbuf
, 0x20);
735 LWP_MutexUnlock(bufferMutex
);
736 return (ret
== 1)? 0 : -ret
;
739 /* Internal function for controlling motor operations */
740 static int _DI_SetMotor(int flag
){
744 LWP_MutexLock(bufferMutex
);
746 dic
[0] = DVD_SET_MOTOR
<< 24;
747 dic
[1] = flag
& 0x1; // Eject flag.
748 dic
[2] = (flag
>> 1) & 0x1; // Don't use this flag, it kills the drive until next reset.
750 int ret
= IOS_Ioctl(di_fd
, DVD_SET_MOTOR
, dic
, 0x20, outbuf
, 0x20);
755 LWP_MutexUnlock(bufferMutex
);
756 return(ret
== 1)? 0 : -ret
;
759 /* Stop the drives motor */
761 return _DI_SetMotor(0);
764 /* Stop the motor, and eject the disc. Also needs a reset afterwards for normal operation */
766 return _DI_SetMotor(1);
769 /* Warning, this will kill your drive untill the next reset. Will not respond to DI commands,
770 will not take in or eject the disc. Your drive will be d - e - d, dead.
772 I deem this function to be harmless, as normal operation will resume after a reset.
773 However, I am not liable for anyones drive exploding as a result from using this function.
776 return _DI_SetMotor(2);
779 int DI_ClosePartition() {
783 LWP_MutexLock(bufferMutex
);
785 dic
[0] = DVD_CLOSE_PARTITION
<< 24;
787 int ret
= IOS_Ioctl(di_fd
, DVD_CLOSE_PARTITION
, dic
, 0x20, outbuf
, 0x20);
792 LWP_MutexUnlock(bufferMutex
);
793 return(ret
== 1)? 0 : -ret
;
796 int DI_OpenPartition(uint32_t offset
)
801 static ioctlv vectors
[5] __attribute__((aligned(32)));
802 static char certs
[0x49e4] __attribute__((aligned(32)));
803 LWP_MutexLock(bufferMutex
);
805 dic
[0] = DVD_OPEN_PARTITION
<< 24;
808 vectors
[0].data
= dic
;
809 vectors
[0].len
= 0x20;
810 vectors
[1].data
= NULL
;
811 vectors
[1].len
= 0x2a4;
812 vectors
[2].data
= NULL
;
815 vectors
[3].data
= certs
;
816 vectors
[3].len
= 0x49e4;
817 vectors
[4].data
= outbuf
;
818 vectors
[4].len
= 0x20;
820 int ret
= IOS_Ioctlv(di_fd
, DVD_OPEN_PARTITION
, 3, 2, vectors
);
825 LWP_MutexUnlock(bufferMutex
);
826 return(ret
== 1)? 0 : -ret
;
830 int DI_Read(void *buf
, uint32_t size
, uint32_t offset
)
838 if((uint32_t)buf
& 0x1F)
841 LWP_MutexLock(bufferMutex
);
843 dic
[0] = DVD_LOW_READ
<< 24;
847 int ret
= IOS_Ioctl(di_fd
, DVD_LOW_READ
, dic
, 0x20, buf
, size
);
852 LWP_MutexUnlock(bufferMutex
);
853 return(ret
== 1)? 0 : -ret
;
856 int DI_UnencryptedRead(void *buf
, uint32_t size
, uint32_t offset
)
858 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
866 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
869 LWP_MutexLock(bufferMutex
);
871 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
876 ret
= IOS_Ioctl(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, size
);
878 }while(ret
!= 1 && retry_count
> 0);
883 LWP_MutexUnlock(bufferMutex
);
885 return (ret
== 1)? 0 : -ret
;
888 int DI_ReadDiscID(uint64_t *id
)
893 LWP_MutexLock(bufferMutex
);
895 dic
[0] = DVD_READ_DISCID
<< 24;
897 int ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCID
, dic
, 0x20, outbuf
, 0x20);
902 memcpy(id
, outbuf
, sizeof(*id
));
904 LWP_MutexUnlock(bufferMutex
);
905 return(ret
== 1)? 0 : -ret
;
908 static bool diio_Startup()
917 t1
=ticks_to_millisecs(gettime());
919 while(state
& DVD_INIT
)
922 t2
=ticks_to_millisecs(gettime());
923 if( (t2
- t1
) > MOUNT_TIMEOUT
)
924 return false; // timeout
927 if(state
& DVD_READY
)
932 static bool diio_IsInserted()
939 DI_GetCoverRegister(&val
);
946 static bool diio_ReadSectors(sec_t sector
,sec_t numSectors
,void *buffer
)
948 if(DI_ReadDVD(buffer
, numSectors
, sector
) == 0)
953 static bool diio_WriteSectors(sec_t sector
,sec_t numSectors
,const void *buffer
)
958 static bool diio_ClearStatus()
963 static bool diio_Shutdown()
969 const DISC_INTERFACE __io_wiidvd
= {
971 FEATURE_MEDIUM_CANREAD
| FEATURE_WII_DVD
,
972 (FN_MEDIUM_STARTUP
)&diio_Startup
,
973 (FN_MEDIUM_ISINSERTED
)&diio_IsInserted
,
974 (FN_MEDIUM_READSECTORS
)&diio_ReadSectors
,
975 (FN_MEDIUM_WRITESECTORS
)&diio_WriteSectors
,
976 (FN_MEDIUM_CLEARSTATUS
)&diio_ClearStatus
,
977 (FN_MEDIUM_SHUTDOWN
)&diio_Shutdown