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>
45 #include <ogc/mutex.h>
46 #include <ogc/lwp_watchdog.h>
47 #include <ogc/machine/processor.h>
49 #define MOUNT_TIMEOUT 15000 // 15 seconds
52 static bool load_dvdx
= false;
53 static bool use_dvd_cache
= true;
54 static int have_ahbprot
= 0;
55 static int state
= DVD_INIT
| DVD_NO_DISC
;
57 static int _cover_callback(int ret
, void* usrdata
);
59 static unsigned int bufferMutex
= 0;
60 static uint32_t outbuf
[8] __attribute__((aligned(32)));
61 static uint32_t dic
[8] __attribute__((aligned(32)));
62 static const char di_path
[] ATTRIBUTE_ALIGN(32) = "/dev/di";
64 static read_func DI_ReadDVDptr
= NULL
;
65 static read_func_async DI_ReadDVDAsyncptr
= NULL
;
66 static di_callback di_cb
= NULL
;
68 static vu32
* const _dvdReg
= (u32
*)0xCD806000;
70 static int _DI_ReadDVD_A8_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
){
76 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
79 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
80 dic
[1] = len
<< 11; // 1 LB is 2048 bytes
81 dic
[2] = lba
<< 9; // Nintendo's read function uses byteOffset >> 2, so we only shift 9 left, not 11.
83 ret
= IOS_IoctlAsync(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, len
<< 11,ipc_cb
, buf
);
88 return (ret
== 1)? 0 : -ret
;
91 static int _DI_ReadDVD_D0_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
){
97 if((uint32_t)buf
& 0x1F)
100 dic
[0] = DVD_READ
<< 24;
101 dic
[1] = 0; // Unknown what this does as of now. (Sets some value to 0x10 in the drive if set).
102 dic
[2] = 0; // USE_DEFAULT_CONFIG flag. Drive will use default config if this bit is set.
106 ret
= IOS_IoctlAsync(di_fd
, DVD_READ
, dic
, 0x20, buf
, len
<< 11,ipc_cb
, buf
);
111 return (ret
== 1)? 0 : -ret
;
114 static int _DI_ReadDVD(void* buf
, uint32_t len
, uint32_t lba
, uint32_t read_cmd
){
115 if ((((int) buf
) & 0xC0000000) == 0x80000000) // cached?
118 _dvdReg
[2] = read_cmd
;
119 _dvdReg
[3] = read_cmd
== 0xD0000000 ? lba
: lba
<< 9;
120 _dvdReg
[4] = read_cmd
== 0xD0000000 ? len
: len
<< 11;
121 _dvdReg
[5] = (unsigned long) buf
;
122 _dvdReg
[6] = len
<< 11;
123 _dvdReg
[7] = 3; // enable reading!
124 DCInvalidateRange(buf
, len
<< 11);
125 while (_dvdReg
[7] & 1);
127 if (_dvdReg
[0] & 0x4)
132 static int _DI_ReadDVD_A8(void* buf
, uint32_t len
, uint32_t lba
){
133 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
138 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
142 return _DI_ReadDVD(buf
, len
, lba
, 0xA8000000);
144 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
145 dic
[1] = len
<< 11; // 1 LB is 2048 bytes
146 dic
[2] = lba
<< 9; // Nintendo's read function uses byteOffset >> 2, so we only shift 9 left, not 11.
149 ret
= IOS_Ioctl(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, len
<< 11);
151 }while(ret
!= 1 && retry_count
> 0);
156 return (ret
== 1)? 0 : -ret
;
159 static int _DI_ReadDVD_D0(void* buf
, uint32_t len
, uint32_t lba
){
160 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
165 if((uint32_t)buf
& 0x1F)
169 return _DI_ReadDVD(buf
, len
, lba
, 0xD0000000);
171 dic
[0] = DVD_READ
<< 24;
172 dic
[1] = 0; // Unknown what this does as of now. (Sets some value to 0x10 in the drive if set).
173 dic
[2] = 0; // USE_DEFAULT_CONFIG flag. Drive will use default config if this bit is set.
178 ret
= IOS_Ioctl(di_fd
, DVD_READ
, dic
, 0x20, buf
, len
<< 11);
180 }while(ret
!= 1 && retry_count
> 0);
185 return (ret
== 1)? 0 : -ret
;
189 #define CACHE_FREE 0xFFFFFFFF
190 #define BLOCK_SIZE 0x800
191 #define CACHEBLOCKS 26
197 static cache_page
*cache_read
= NULL
;
199 static void CreateDVDCache()
201 if (cache_read
!= NULL
)
203 cache_read
= (cache_page
*) malloc(sizeof(cache_page
));
204 if (cache_read
== NULL
)
207 cache_read
->block
= CACHE_FREE
;
208 cache_read
->ptr
= memalign(32, BLOCK_SIZE
* CACHEBLOCKS
);
209 if (cache_read
->ptr
== NULL
)
215 memset(cache_read
->ptr
, 0, BLOCK_SIZE
);
218 static int ReadBlockFromCache(void *buf
, uint32_t len
, uint32_t block
)
222 if (cache_read
== NULL
)
223 return DI_ReadDVDptr(buf
, len
, block
);
225 if ((block
>= cache_read
->block
) && (block
+ len
< (cache_read
->block
+ CACHEBLOCKS
)))
227 memcpy(buf
, cache_read
->ptr
+ ((block
- cache_read
->block
) * BLOCK_SIZE
), BLOCK_SIZE
* len
);
231 if (len
> CACHEBLOCKS
)
232 return DI_ReadDVDptr(buf
, len
, block
);
234 retval
= DI_ReadDVDptr(cache_read
->ptr
, CACHEBLOCKS
, block
);
237 cache_read
->block
= CACHE_FREE
;
241 cache_read
->block
= block
;
242 memcpy(buf
, cache_read
->ptr
, len
* BLOCK_SIZE
);
248 Initialize the DI interface, should always be called first!
251 u32
__di_check_ahbprot(void) {
252 return ((*(vu32
*)0xcd800064 == 0xFFFFFFFF) ? 1 : 0);
259 state
= DVD_INIT
| DVD_NO_DISC
;
260 have_ahbprot
= __di_check_ahbprot();
262 if(have_ahbprot
== 0)
266 di_fd
= IOS_Open(di_path
, 2);
272 LWP_MutexInit(&bufferMutex
, false);
280 void DI_LoadDVDX(bool load
) {
284 void DI_UseCache(bool use
) {
294 if (DI_GetCoverRegister(&status
) != 0) {
299 if ((status
& DVD_COVER_DISC_INSERTED
) == 0) {
304 state
= DVD_INIT
| DVD_NO_DISC
;
305 _cover_callback(1, NULL
); // Initialize the callback chain.
307 if (cache_read
!= NULL
)
308 cache_read
->block
= CACHE_FREE
; // reset cache
320 DI_ReadDVDptr
= NULL
;
321 DI_ReadDVDAsyncptr
= NULL
;
322 state
= DVD_INIT
| DVD_NO_DISC
;
325 LWP_MutexDestroy(bufferMutex
);
330 static int _DI_ReadDVD_Check(void* buf
, uint32_t len
, uint32_t lba
)
334 ret
= _DI_ReadDVD_D0(buf
, len
, lba
);
337 state
= state
| DVD_D0
;
338 DI_ReadDVDptr
= _DI_ReadDVD_D0
;
339 DI_ReadDVDAsyncptr
= _DI_ReadDVD_D0_Async
;
342 ret
= _DI_ReadDVD_A8(buf
, len
, lba
);
345 state
= state
| DVD_A8
;
346 DI_ReadDVDptr
= _DI_ReadDVD_A8
;
347 DI_ReadDVDAsyncptr
= _DI_ReadDVD_A8_Async
;
353 static int _DI_ReadDVD_Check_Async(void* buf
, uint32_t len
, uint32_t lba
, ipccallback ipc_cb
)
357 ret
= _DI_ReadDVD_D0_Async(buf
, len
, lba
, ipc_cb
);
360 state
= state
| DVD_D0
;
361 DI_ReadDVDptr
= _DI_ReadDVD_D0
;
362 DI_ReadDVDAsyncptr
= _DI_ReadDVD_D0_Async
;
365 ret
= _DI_ReadDVD_A8_Async(buf
, len
, lba
, ipc_cb
);
368 state
= state
| DVD_A8
;
369 DI_ReadDVDptr
= _DI_ReadDVD_A8
;
370 DI_ReadDVDAsyncptr
= _DI_ReadDVD_A8_Async
;
376 static void _DI_SetCallback(int ioctl_nr
, ipccallback ipc_cb
){
377 if ((di_fd
< 0) || !ipc_cb
)
380 LWP_MutexLock(bufferMutex
);
382 memset(dic
, 0x00, sizeof(dic
));
384 dic
[0] = ioctl_nr
<< 24;
385 dic
[1] = (ioctl_nr
== DVD_RESET
)? 1 : 0; // For reset callback. Dirty, I know...
387 IOS_IoctlAsync(di_fd
,ioctl_nr
, dic
, 0x20, outbuf
, 0x20, ipc_cb
, outbuf
);
388 LWP_MutexUnlock(bufferMutex
);
391 #define COVER_CLOSED (*((uint32_t*)usrdata) & DVD_COVER_DISC_INSERTED)
393 static int _cover_callback(int ret
, void* usrdata
){
394 static int cur_state
= 0;
395 static int retry_count
= LIBDI_MAX_RETRIES
;
396 const int callback_table
[] = {
398 DVD_WAITFORCOVERCLOSE
,
400 DVD_IDENTIFY
, // This one will complete when the drive is ready.
403 const int return_table
[] = {1,1,4,1,1};
406 state
&= ~DVD_NO_DISC
;
408 if(callback_table
[cur_state
]){
409 if(ret
== return_table
[cur_state
]){
410 if(cur_state
== 1 && COVER_CLOSED
) // Disc inside, skipping wait for cover.
413 cur_state
++; // If the previous callback succeeded, moving on to the next
415 retry_count
= LIBDI_MAX_RETRIES
;
420 if(retry_count
< 0){ // Drive init failed for unknown reasons.
421 retry_count
= LIBDI_MAX_RETRIES
;
427 _DI_SetCallback(callback_table
[cur_state
- 1], _cover_callback
);
430 else // Callback chain has completed OK. The drive is ready.
433 DI_ReadDVDptr
= _DI_ReadDVD_Check
;
434 DI_ReadDVDAsyncptr
= _DI_ReadDVD_Check_Async
;
440 retry_count
= LIBDI_MAX_RETRIES
;
446 /* Get current status, will return the API status */
451 void DI_SetInitCallback(di_callback cb
){
456 Request an identification from the drive, returned in a DI_DriveID struct
458 int DI_Identify(DI_DriveID
* id
){
465 LWP_MutexLock(bufferMutex
);
467 dic
[0] = DVD_IDENTIFY
<< 24;
469 int ret
= IOS_Ioctl(di_fd
, DVD_IDENTIFY
, dic
, 0x20, outbuf
, 0x20);
474 memcpy(id
,outbuf
,sizeof(DI_DriveID
));
476 LWP_MutexUnlock(bufferMutex
);
477 return (ret
== 1)? 0 : -ret
;
481 Returns the current error code on the drive.
482 yagcd has a pretty comprehensive list of possible error codes
484 int DI_GetError(uint32_t* error
){
491 LWP_MutexLock(bufferMutex
);
493 dic
[0] = DVD_GET_ERROR
<< 24;
495 int ret
= IOS_Ioctl(di_fd
, DVD_GET_ERROR
, dic
, 0x20, outbuf
, 0x20);
500 *error
= outbuf
[0]; // Error code is returned as an int in the first four bytes of outbuf.
502 LWP_MutexUnlock(bufferMutex
);
503 return (ret
== 1)? 0 : -ret
;
513 LWP_MutexLock(bufferMutex
);
515 dic
[0] = DVD_RESET
<< 24;
518 int ret
= IOS_Ioctl(di_fd
, DVD_RESET
, dic
, 0x20, outbuf
, 0x20);
523 LWP_MutexUnlock(bufferMutex
);
524 return (ret
== 1)? 0 : -ret
;
528 Main read function, basically just a wrapper to the function pointer.
529 Nicer then just exposing the pointer itself
531 int DI_ReadDVD(void* buf
, uint32_t len
, uint32_t lba
){
537 LWP_MutexLock(bufferMutex
);
538 ret
= ReadBlockFromCache(buf
,len
,lba
);
539 LWP_MutexUnlock(bufferMutex
);
545 int DI_ReadDVDAsync(void* buf
, uint32_t len
, uint32_t lba
,ipccallback ipc_cb
){
550 if(DI_ReadDVDAsyncptr
){
551 LWP_MutexLock(bufferMutex
);
552 ret
= DI_ReadDVDAsyncptr(buf
,len
,lba
,ipc_cb
);
553 LWP_MutexUnlock(bufferMutex
);
560 Unknown what this does as of now...
562 int DI_ReadDVDConfig(uint32_t* val
, uint32_t flag
){
569 LWP_MutexLock(bufferMutex
);
571 dic
[0] = DVD_READ_CONFIG
<< 24;
572 dic
[1] = flag
& 0x1; // Update flag, val will be written if this is 1, val won't be written if it's 0.
573 dic
[2] = 0; // Command will fail driveside if this is not zero.
576 int ret
= IOS_Ioctl(di_fd
, DVD_READ_CONFIG
, dic
, 0x20, outbuf
, 0x20);
582 LWP_MutexUnlock(bufferMutex
);
583 return (ret
== 1)? 0 : -ret
;
587 Read the copyright information on a DVDVideo
589 int DI_ReadDVDCopyright(uint32_t* copyright
){
596 LWP_MutexLock(bufferMutex
);
598 dic
[0] = DVD_READ_COPYRIGHT
<< 24;
601 int ret
= IOS_Ioctl(di_fd
, DVD_READ_COPYRIGHT
, dic
, 0x20, outbuf
, 0x20);
602 *copyright
= *((uint32_t*)outbuf
); // Copyright information is returned as an int in the first four bytes of outbuf.
607 LWP_MutexUnlock(bufferMutex
);
608 return (ret
== 1)? 0 : -ret
;
611 int DI_Read_BCA(void *outbuf
)
619 memset(dic
, 0, sizeof(dic
));
620 dic
[0] = DVD_READ_BCA
<< 24;
622 int ret
= IOS_Ioctl(di_fd
, DVD_READ_BCA
, dic
, 0x20, outbuf
, 64);
627 LWP_MutexUnlock(bufferMutex
);
628 return (ret
== 1)? 0 : -ret
;
632 Returns 0x800 bytes worth of Disc key
634 int DI_ReadDVDDiscKey(void* buf
){
636 int retry_count
= LIBDI_MAX_RETRIES
;
644 if((uint32_t)buf
& 0x1F)
647 LWP_MutexLock(bufferMutex
);
649 dic
[0] = DVD_READ_DISCKEY
<< 24;
650 dic
[1] = 0; // Unknown what this flag does.
652 ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCKEY
, dic
, 0x20, buf
, 0x800);
654 }while(ret
!= 1 && retry_count
> 0);
659 LWP_MutexUnlock(bufferMutex
);
660 return (ret
== 1)? 0 : -ret
;
664 This function will read the initial sector on the DVD, which contains stuff like the booktype
666 int DI_ReadDVDPhysical(void* buf
){
668 int retry_count
= LIBDI_MAX_RETRIES
;
676 if((uint32_t)buf
& 0x1F)
679 LWP_MutexLock(bufferMutex
);
681 dic
[0] = DVD_READ_PHYSICAL
<< 24;
682 dic
[1] = 0; // Unknown what this flag does.
685 ret
= IOS_Ioctl(di_fd
, DVD_READ_PHYSICAL
, dic
, 0x20, buf
, 0x800);
687 }while(ret
!= 1 && retry_count
> 0);
692 LWP_MutexUnlock(bufferMutex
);
693 return (ret
== 1)? 0 : -ret
;
696 int DI_ReportKey(int keytype
, uint32_t lba
, void* buf
){
703 if((uint32_t)buf
& 0x1F)
706 LWP_MutexLock(bufferMutex
);
708 dic
[0] = DVD_REPORTKEY
<< 24;
709 dic
[1] = keytype
& 0xFF;
712 int ret
= IOS_Ioctl(di_fd
, DVD_REPORTKEY
, dic
, 0x20, buf
, 0x20);
717 LWP_MutexUnlock(bufferMutex
);
718 return (ret
== 1)? 0 : -ret
;
721 int DI_GetCoverRegister(uint32_t* status
){
725 LWP_MutexLock(bufferMutex
);
726 memset(dic
, 0x00, 0x20);
728 int ret
= IOS_Ioctl(di_fd
, DVD_GETCOVER
, dic
, 0x20, outbuf
, 0x20);
734 LWP_MutexUnlock(bufferMutex
);
735 return (ret
== 1)? 0 : -ret
;
738 /* Internal function for controlling motor operations */
739 static int _DI_SetMotor(int flag
){
743 LWP_MutexLock(bufferMutex
);
745 dic
[0] = DVD_SET_MOTOR
<< 24;
746 dic
[1] = flag
& 0x1; // Eject flag.
747 dic
[2] = (flag
>> 1) & 0x1; // Don't use this flag, it kills the drive until next reset.
749 int ret
= IOS_Ioctl(di_fd
, DVD_SET_MOTOR
, dic
, 0x20, outbuf
, 0x20);
754 LWP_MutexUnlock(bufferMutex
);
755 return(ret
== 1)? 0 : -ret
;
758 /* Stop the drives motor */
760 return _DI_SetMotor(0);
763 /* Stop the motor, and eject the disc. Also needs a reset afterwards for normal operation */
765 return _DI_SetMotor(1);
768 /* Warning, this will kill your drive untill the next reset. Will not respond to DI commands,
769 will not take in or eject the disc. Your drive will be d - e - d, dead.
771 I deem this function to be harmless, as normal operation will resume after a reset.
772 However, I am not liable for anyones drive exploding as a result from using this function.
775 return _DI_SetMotor(2);
778 int DI_ClosePartition() {
782 LWP_MutexLock(bufferMutex
);
784 dic
[0] = DVD_CLOSE_PARTITION
<< 24;
786 int ret
= IOS_Ioctl(di_fd
, DVD_CLOSE_PARTITION
, dic
, 0x20, outbuf
, 0x20);
791 LWP_MutexUnlock(bufferMutex
);
792 return(ret
== 1)? 0 : -ret
;
795 int DI_OpenPartition(uint32_t offset
)
800 static ioctlv vectors
[5] __attribute__((aligned(32)));
801 static char certs
[0x49e4] __attribute__((aligned(32)));
802 LWP_MutexLock(bufferMutex
);
804 dic
[0] = DVD_OPEN_PARTITION
<< 24;
807 vectors
[0].data
= dic
;
808 vectors
[0].len
= 0x20;
809 vectors
[1].data
= NULL
;
810 vectors
[1].len
= 0x2a4;
811 vectors
[2].data
= NULL
;
814 vectors
[3].data
= certs
;
815 vectors
[3].len
= 0x49e4;
816 vectors
[4].data
= outbuf
;
817 vectors
[4].len
= 0x20;
819 int ret
= IOS_Ioctlv(di_fd
, DVD_OPEN_PARTITION
, 3, 2, vectors
);
824 LWP_MutexUnlock(bufferMutex
);
825 return(ret
== 1)? 0 : -ret
;
829 int DI_Read(void *buf
, uint32_t size
, uint32_t offset
)
837 if((uint32_t)buf
& 0x1F)
840 LWP_MutexLock(bufferMutex
);
842 dic
[0] = DVD_LOW_READ
<< 24;
846 int ret
= IOS_Ioctl(di_fd
, DVD_LOW_READ
, dic
, 0x20, buf
, size
);
851 LWP_MutexUnlock(bufferMutex
);
852 return(ret
== 1)? 0 : -ret
;
855 int DI_UnencryptedRead(void *buf
, uint32_t size
, uint32_t offset
)
857 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
865 if((uint32_t)buf
& 0x1F) // This only works with 32 byte aligned addresses!
868 LWP_MutexLock(bufferMutex
);
870 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
875 ret
= IOS_Ioctl(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, size
);
877 }while(ret
!= 1 && retry_count
> 0);
882 LWP_MutexUnlock(bufferMutex
);
884 return (ret
== 1)? 0 : -ret
;
887 int DI_ReadDiscID(uint64_t *id
)
892 LWP_MutexLock(bufferMutex
);
894 dic
[0] = DVD_READ_DISCID
<< 24;
896 int ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCID
, dic
, 0x20, outbuf
, 0x20);
901 memcpy(id
, outbuf
, sizeof(*id
));
903 LWP_MutexUnlock(bufferMutex
);
904 return(ret
== 1)? 0 : -ret
;
907 static bool diio_Startup()
916 t1
=ticks_to_millisecs(gettime());
918 while(state
& DVD_INIT
)
921 t2
=ticks_to_millisecs(gettime());
922 if( (t2
- t1
) > MOUNT_TIMEOUT
)
923 return false; // timeout
926 if(state
& DVD_READY
)
931 static bool diio_IsInserted()
938 DI_GetCoverRegister(&val
);
945 static bool diio_ReadSectors(sec_t sector
,sec_t numSectors
,void *buffer
)
947 if(DI_ReadDVD(buffer
, numSectors
, sector
) == 0)
952 static bool diio_WriteSectors(sec_t sector
,sec_t numSectors
,const void *buffer
)
957 static bool diio_ClearStatus()
962 static bool diio_Shutdown()
968 const DISC_INTERFACE __io_wiidvd
= {
970 FEATURE_MEDIUM_CANREAD
| FEATURE_WII_DVD
,
971 (FN_MEDIUM_STARTUP
)&diio_Startup
,
972 (FN_MEDIUM_ISINSERTED
)&diio_IsInserted
,
973 (FN_MEDIUM_READSECTORS
)&diio_ReadSectors
,
974 (FN_MEDIUM_WRITESECTORS
)&diio_WriteSectors
,
975 (FN_MEDIUM_CLEARSTATUS
)&diio_ClearStatus
,
976 (FN_MEDIUM_SHUTDOWN
)&diio_Shutdown