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>
42 int _DI_ReadDVD_ReadID(void* buf
, uint32_t len
, uint32_t lba
);
43 int _DI_ReadDVD_ReadID_Async(void* buf
, uint32_t len
, uint32_t lba
,ipccallback ipc_cb
);
45 int _DI_ReadDVD_A8(void* buf
, uint32_t len
, uint32_t lba
);
46 int _DI_ReadDVD_D0(void* buf
, uint32_t len
, uint32_t lba
);
48 int _DI_ReadDVD_A8_Async(void* buf
, uint32_t len
, uint32_t lba
,ipccallback ipc_cb
);
49 int _DI_ReadDVD_D0_Async(void* buf
, uint32_t len
, uint32_t lba
,ipccallback ipc_cb
);
51 void _DI_SetCallback(int di_command
, ipccallback
);
52 static int _cover_callback(int ret
, void* usrdata
);
54 int state
= DVD_INIT
| DVD_NO_DISC
;
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 char di_path
[] ATTRIBUTE_ALIGN(32) = "/dev/di";
61 read_func DI_ReadDVDptr
= NULL
;
62 read_func_async DI_ReadDVDAsyncptr
= NULL
;
63 di_callback di_cb
= NULL
;
66 Initialize the DI interface, should always be called first!
74 state
= DVD_INIT
| DVD_NO_DISC
;
77 __DI_LoadStub(); // Marcan's 1337 magics happen here!
82 di_fd
= IOS_Open(di_path
, 2);
85 LWP_MutexInit(&bufferMutex
, false);
87 return (di_fd
>= 0)? di_fd
: -1;
93 if (DI_GetCoverRegister(&status
) != 0) {
98 if ((status
& DVD_COVER_DISC_INSERTED
) == 0) {
103 state
= DVD_INIT
| DVD_NO_DISC
;
104 _cover_callback(1, NULL
); // Initialize the callback chain.
113 DI_ReadDVDptr
= NULL
;
114 DI_ReadDVDAsyncptr
= NULL
;
115 state
= DVD_INIT
| DVD_NO_DISC
;
118 LWP_MutexDestroy(bufferMutex
);
124 #define COVER_CLOSED (*((uint32_t*)usrdata) & DVD_COVER_DISC_INSERTED)
126 static int _cover_callback(int ret
, void* usrdata
){
127 static int cur_state
= 0;
128 static int retry_count
= LIBDI_MAX_RETRIES
;
129 const int callback_table
[] = {
131 DVD_WAITFORCOVERCLOSE
,
133 DVD_IDENTIFY
, // This one will complete when the drive is ready.
136 const int return_table
[] = {1,1,4,1,1};
139 state
&= ~DVD_NO_DISC
;
141 if(callback_table
[cur_state
]){
142 if(ret
== return_table
[cur_state
]){
143 if(cur_state
== 1 && COVER_CLOSED
) // Disc inside, skipping wait for cover.
146 cur_state
++; // If the previous callback succeeded, moving on to the next
148 retry_count
= LIBDI_MAX_RETRIES
;
153 if(retry_count
< 0){ // Drive init failed for unknown reasons.
154 retry_count
= LIBDI_MAX_RETRIES
;
160 _DI_SetCallback(callback_table
[cur_state
- 1], _cover_callback
);
163 else // Callback chain has completed OK. The drive is ready.
166 if(IOS_GetVersion() < 200){
168 DI_ReadDVDptr
= _DI_ReadDVD_D0
;
169 DI_ReadDVDAsyncptr
= _DI_ReadDVD_D0_Async
;
174 DI_ReadDVDptr
= _DI_ReadDVD_A8
;
175 DI_ReadDVDAsyncptr
= _DI_ReadDVD_A8_Async
;
181 retry_count
= LIBDI_MAX_RETRIES
;
187 /* Get current status, will return the API status */
192 void DI_SetInitCallback(di_callback cb
){
196 void _DI_SetCallback(int ioctl_nr
, ipccallback ipc_cb
){
197 if(di_fd
< 0 || !ipc_cb
) return;
200 while(LWP_MutexLock(bufferMutex
));
202 memset(dic
, 0x00, sizeof(dic
));
204 dic
[0] = ioctl_nr
<< 24;
205 dic
[1] = (ioctl_nr
== DVD_RESET
)? 1 : 0; // For reset callback. Dirty, I know...
207 IOS_IoctlAsync(di_fd
,ioctl_nr
, dic
, 0x20, outbuf
, 0x20, ipc_cb
, outbuf
);
209 //We're done with the buffer now.
210 LWP_MutexUnlock(bufferMutex
);
214 Request an identification from the drive, returned in a DI_DriveID struct
216 int DI_Identify(DI_DriveID
* id
){
227 while(LWP_MutexLock(bufferMutex
));
229 dic
[0] = DVD_IDENTIFY
<< 24;
231 int ret
= IOS_Ioctl(di_fd
, DVD_IDENTIFY
, dic
, 0x20, outbuf
, 0x20);
233 if(ret
== 2) errno
= EIO
;
235 memcpy(id
,outbuf
,sizeof(DI_DriveID
));
237 LWP_MutexUnlock(bufferMutex
);
238 return (ret
== 1)? 0 : -ret
;
242 Returns the current error code on the drive.
243 yagcd has a pretty comprehensive list of possible error codes
245 int DI_GetError(uint32_t* error
){
257 while(LWP_MutexLock(bufferMutex
));
259 dic
[0] = DVD_GET_ERROR
<< 24;
261 int ret
= IOS_Ioctl(di_fd
, DVD_GET_ERROR
, dic
, 0x20, outbuf
, 0x20);
263 if(ret
== 2) errno
= EIO
;
265 *error
= outbuf
[0]; // Error code is returned as an int in the first four bytes of outbuf.
267 LWP_MutexUnlock(bufferMutex
);
268 return (ret
== 1)? 0 : -ret
;
279 while(LWP_MutexLock(bufferMutex
));
281 dic
[0] = DVD_RESET
<< 24;
284 int ret
= IOS_Ioctl(di_fd
, DVD_RESET
, dic
, 0x20, outbuf
, 0x20);
286 if(ret
== 2) errno
= EIO
;
288 LWP_MutexUnlock(bufferMutex
);
289 return (ret
== 1)? 0 : -ret
;
293 Main read function, basically just a wrapper to the function pointer.
294 Nicer then just exposing the pointer itself
296 int DI_ReadDVD(void* buf
, uint32_t len
, uint32_t lba
){
299 // Wait for the lock. Doing it here saves me from doing it in all the read functions.
300 while(LWP_MutexLock(bufferMutex
));
301 ret
= DI_ReadDVDptr(buf
,len
,lba
);
302 LWP_MutexUnlock(bufferMutex
);
308 int DI_ReadDVDAsync(void* buf
, uint32_t len
, uint32_t lba
,ipccallback ipc_cb
){
310 if(DI_ReadDVDAsyncptr
){
311 while(LWP_MutexLock(bufferMutex
));
312 ret
= DI_ReadDVDAsyncptr(buf
,len
,lba
,ipc_cb
);
313 LWP_MutexUnlock(bufferMutex
);
320 Unknown what this does as of now...
322 int DI_ReadDVDConfig(uint32_t* val
, uint32_t flag
){
333 while(LWP_MutexLock(bufferMutex
));
335 dic
[0] = DVD_READ_CONFIG
<< 24;
336 dic
[1] = flag
& 0x1; // Update flag, val will be written if this is 1, val won't be written if it's 0.
337 dic
[2] = 0; // Command will fail driveside if this is not zero.
340 int ret
= IOS_Ioctl(di_fd
, DVD_READ_CONFIG
, dic
, 0x20, outbuf
, 0x20);
342 if(ret
== 2) errno
= EIO
;
345 LWP_MutexUnlock(bufferMutex
);
346 return (ret
== 1)? 0 : -ret
;
350 Read the copyright information on a DVDVideo
352 int DI_ReadDVDCopyright(uint32_t* copyright
){
364 while(LWP_MutexLock(bufferMutex
));
366 dic
[0] = DVD_READ_COPYRIGHT
<< 24;
369 int ret
= IOS_Ioctl(di_fd
, DVD_READ_COPYRIGHT
, dic
, 0x20, outbuf
, 0x20);
370 *copyright
= *((uint32_t*)outbuf
); // Copyright information is returned as an int in the first four bytes of outbuf.
372 if(ret
== 2) errno
= EIO
;
373 LWP_MutexUnlock(bufferMutex
);
374 return (ret
== 1)? 0 : -ret
;
378 Returns 0x800 bytes worth of Disc key
380 int DI_ReadDVDDiscKey(void* buf
){
382 int retry_count
= LIBDI_MAX_RETRIES
;
394 if((uint32_t)buf
& 0x1F){
400 while(LWP_MutexLock(bufferMutex
));
402 dic
[0] = DVD_READ_DISCKEY
<< 24;
403 dic
[1] = 0; // Unknown what this flag does.
405 ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCKEY
, dic
, 0x20, buf
, 0x800);
407 }while(ret
!= 1 && retry_count
> 0);
409 if(ret
== 2) errno
= EIO
;
411 LWP_MutexUnlock(bufferMutex
);
412 return (ret
== 1)? 0 : -ret
;
416 This function will read the initial sector on the DVD, which contains stuff like the booktype
418 int DI_ReadDVDPhysical(void* buf
){
420 int retry_count
= LIBDI_MAX_RETRIES
;
432 if((uint32_t)buf
& 0x1F){
438 while(LWP_MutexLock(bufferMutex
));
440 dic
[0] = DVD_READ_PHYSICAL
<< 24;
441 dic
[1] = 0; // Unknown what this flag does.
444 ret
= IOS_Ioctl(di_fd
, DVD_READ_PHYSICAL
, dic
, 0x20, buf
, 0x800);
446 }while(ret
!= 1 && retry_count
> 0);
448 if(ret
== 2) errno
= EIO
;
450 LWP_MutexUnlock(bufferMutex
);
451 return (ret
== 1)? 0 : -ret
;
454 int DI_ReportKey(int keytype
, uint32_t lba
, void* buf
){
465 if((uint32_t)buf
& 0x1F){
470 while(LWP_MutexLock(bufferMutex
));
472 dic
[0] = DVD_REPORTKEY
<< 24;
473 dic
[1] = keytype
& 0xFF;
476 int ret
= IOS_Ioctl(di_fd
, DVD_REPORTKEY
, dic
, 0x20, buf
, 0x20);
478 if(ret
== 2) errno
= EIO
;
480 LWP_MutexUnlock(bufferMutex
);
481 return (ret
== 1)? 0 : -ret
;
484 int DI_GetCoverRegister(uint32_t* status
){
490 while(LWP_MutexLock(bufferMutex
));
492 memset(dic
, 0x00, 0x20);
494 int ret
= IOS_Ioctl(di_fd
, DVD_GETCOVER
, dic
, 0x20, outbuf
, 0x20);
495 if(ret
== 2) errno
= EIO
;
499 LWP_MutexUnlock(bufferMutex
);
500 return (ret
== 1)? 0 : -ret
;
503 /* Internal function for controlling motor operations */
504 int _DI_SetMotor(int flag
){
511 while(LWP_MutexLock(bufferMutex
));
513 dic
[0] = DVD_SET_MOTOR
<< 24;
514 dic
[1] = flag
& 0x1; // Eject flag.
515 dic
[2] = (flag
>> 1) & 0x1; // Don't use this flag, it kills the drive untill next reset.
517 int ret
= IOS_Ioctl(di_fd
, DVD_SET_MOTOR
, dic
, 0x20, outbuf
, 0x20);
519 if(ret
== 2) errno
= EIO
;
521 LWP_MutexUnlock(bufferMutex
);
522 return(ret
== 1)? 0 : -ret
;
525 /* Stop the drives motor */
527 return _DI_SetMotor(0);
530 /* Stop the motor, and eject the disc. Also needs a reset afterwards for normal operation */
532 return _DI_SetMotor(1);
535 /* Warning, this will kill your drive untill the next reset. Will not respond to DI commands,
536 will not take in or eject the disc. Your drive will be d - e - d, dead.
538 I deem this function to be harmless, as normal operation will resume after a reset.
539 However, I am not liable for anyones drive exploding as a result from using this function.
542 return _DI_SetMotor(2);
545 int DI_ClosePartition() {
551 while(LWP_MutexLock(bufferMutex
));
553 dic
[0] = DVD_CLOSE_PARTITION
<< 24;
555 int ret
= IOS_Ioctl(di_fd
, DVD_CLOSE_PARTITION
, dic
, 0x20, outbuf
, 0x20);
557 if(ret
== 2) errno
= EIO
;
559 LWP_MutexUnlock(bufferMutex
);
560 return(ret
== 1)? 0 : -ret
;
563 int DI_OpenPartition(uint32_t offset
)
570 static ioctlv vectors
[5] __attribute__((aligned(32)));
571 static char certs
[0x49e4] __attribute__((aligned(32)));
572 while(LWP_MutexLock(bufferMutex
));
574 dic
[0] = DVD_OPEN_PARTITION
<< 24;
577 vectors
[0].data
= dic
;
578 vectors
[0].len
= 0x20;
579 vectors
[1].data
= NULL
;
580 vectors
[1].len
= 0x2a4;
581 vectors
[2].data
= NULL
;
584 vectors
[3].data
= certs
;
585 vectors
[3].len
= 0x49e4;
586 vectors
[4].data
= outbuf
;
587 vectors
[4].len
= 0x20;
589 int ret
= IOS_Ioctlv(di_fd
, DVD_OPEN_PARTITION
, 3, 2, vectors
);
591 if(ret
== 2) errno
= EIO
;
593 LWP_MutexUnlock(bufferMutex
);
594 return(ret
== 1)? 0 : -ret
;
598 int DI_Read(void *buf
, uint32_t size
, uint32_t offset
)
610 if((uint32_t)buf
& 0x1F){
616 while(LWP_MutexLock(bufferMutex
));
618 dic
[0] = DVD_LOW_READ
<< 24;
622 int ret
= IOS_Ioctl(di_fd
, DVD_LOW_READ
, dic
, 0x20, buf
, size
);
624 if(ret
== 2) errno
= EIO
;
626 LWP_MutexUnlock(bufferMutex
);
627 return(ret
== 1)? 0 : -ret
;
630 int DI_UnencryptedRead(void *buf
, uint32_t size
, uint32_t offset
)
632 int ret
, retry_count
= LIBDI_MAX_RETRIES
;
644 if((uint32_t)buf
& 0x1F){ // This only works with 32 byte aligned addresses!
650 while(LWP_MutexLock(bufferMutex
));
652 dic
[0] = DVD_READ_UNENCRYPTED
<< 24;
657 ret
= IOS_Ioctl(di_fd
, DVD_READ_UNENCRYPTED
, dic
, 0x20, buf
, size
);
659 }while(ret
!= 1 && retry_count
> 0);
661 if(ret
== 2) errno
= EIO
;
663 LWP_MutexUnlock(bufferMutex
);
665 return (ret
== 1)? 0 : -ret
;
668 int DI_ReadDiscID(uint64_t *id
)
676 while(LWP_MutexLock(bufferMutex
));
678 dic
[0] = DVD_READ_DISCID
<< 24;
680 int ret
= IOS_Ioctl(di_fd
, DVD_READ_DISCID
, dic
, 0x20, outbuf
, 0x20);
682 if(ret
== 2) errno
= EIO
;
684 memcpy(id
, outbuf
, sizeof(*id
));
686 LWP_MutexUnlock(bufferMutex
);
687 return(ret
== 1)? 0 : -ret
;