bump version for next release
[libogc.git] / libdi / di.c
blob8d99fd262b324f31216e1a3db2bbfaed0e18eef3
1 /*-------------------------------------------------------------
3 di.c -- Drive Interface library
5 Team Twiizers
6 Copyright (C) 2008
8 Erant
9 marcan
11 rodries
12 emukidid
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
31 distribution.
33 -------------------------------------------------------------*/
35 #include <errno.h>
36 #include <string.h>
37 #include <malloc.h>
38 #include <unistd.h>
40 #include <di/di.h>
41 #include <ogc/cache.h>
42 #include <ogc/es.h>
43 #include <ogc/ipc.h>
44 #include <ogc/ios.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
51 int di_fd = -1;
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){
71 int ret;
73 if(!buf)
74 return -EINVAL;
76 if((uint32_t)buf & 0x1F) // This only works with 32 byte aligned addresses!
77 return -EFAULT;
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);
85 if(ret == 2)
86 ret = EIO;
88 return (ret == 1)? 0 : -ret;
91 static int _DI_ReadDVD_D0_Async(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb){
92 int ret;
94 if(!buf)
95 return -EINVAL;
97 if((uint32_t)buf & 0x1F)
98 return -EFAULT;
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.
103 dic[3] = len;
104 dic[4] = lba;
106 ret = IOS_IoctlAsync(di_fd, DVD_READ, dic, 0x20, buf, len << 11,ipc_cb, buf);
108 if(ret == 2)
109 ret = EIO;
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?
116 _dvdReg[0] = 0x2E;
117 _dvdReg[1] = 0;
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)
128 return 1;
129 return 0;
132 static int _DI_ReadDVD_A8(void* buf, uint32_t len, uint32_t lba){
133 int ret, retry_count = LIBDI_MAX_RETRIES;
135 if(!buf)
136 return -EINVAL;
138 if((uint32_t)buf & 0x1F) // This only works with 32 byte aligned addresses!
139 return -EFAULT;
141 if(have_ahbprot)
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.
148 do{
149 ret = IOS_Ioctl(di_fd, DVD_READ_UNENCRYPTED, dic, 0x20, buf, len << 11);
150 retry_count--;
151 }while(ret != 1 && retry_count > 0);
153 if(ret == 2)
154 ret = EIO;
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;
162 if(!buf)
163 return -EINVAL;
165 if((uint32_t)buf & 0x1F)
166 return -EFAULT;
168 if(have_ahbprot)
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.
174 dic[3] = len;
175 dic[4] = lba;
177 do{
178 ret = IOS_Ioctl(di_fd, DVD_READ, dic, 0x20, buf, len << 11);
179 retry_count--;
180 }while(ret != 1 && retry_count > 0);
182 if(ret == 2)
183 ret = EIO;
185 return (ret == 1)? 0 : -ret;
188 ///// Cache
189 #define CACHE_FREE 0xFFFFFFFF
190 #define BLOCK_SIZE 0x800
191 #define CACHEBLOCKS 26
192 typedef struct
194 uint32_t block;
195 void *ptr;
196 } cache_page;
197 static cache_page *cache_read = NULL;
199 static void CreateDVDCache()
201 if (cache_read != NULL)
202 return;
203 cache_read = (cache_page *) malloc(sizeof(cache_page));
204 if (cache_read == NULL)
205 return;
207 cache_read->block = CACHE_FREE;
208 cache_read->ptr = memalign(32, BLOCK_SIZE * CACHEBLOCKS);
209 if (cache_read->ptr == NULL)
211 free(cache_read);
212 cache_read = NULL;
213 return;
215 memset(cache_read->ptr, 0, BLOCK_SIZE);
218 static int ReadBlockFromCache(void *buf, uint32_t len, uint32_t block)
220 int retval;
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);
228 return 0;
231 if (len > CACHEBLOCKS)
232 return DI_ReadDVDptr(buf, len, block);
234 retval = DI_ReadDVDptr(cache_read->ptr, CACHEBLOCKS, block);
235 if (retval)
237 cache_read->block = CACHE_FREE;
238 return retval;
241 cache_read->block = block;
242 memcpy(buf, cache_read->ptr, len * BLOCK_SIZE);
244 return 0;
248 Initialize the DI interface, should always be called first!
251 u32 __di_check_ahbprot(void) {
252 return ((*(vu32*)0xcd800064 == 0xFFFFFFFF) ? 1 : 0);
255 int DI_Init() {
256 if(di_fd >= 0)
257 return 1;
259 state = DVD_INIT | DVD_NO_DISC;
260 have_ahbprot = __di_check_ahbprot();
262 if(have_ahbprot == 0)
263 return 0;
265 if (di_fd < 0)
266 di_fd = IOS_Open(di_path, 2);
268 if (di_fd < 0)
269 return di_fd;
271 if (!bufferMutex)
272 LWP_MutexInit(&bufferMutex, false);
274 if(use_dvd_cache)
275 CreateDVDCache();
277 return 0;
280 void DI_LoadDVDX(bool load) {
281 load_dvdx = load;
284 void DI_UseCache(bool use) {
285 use_dvd_cache = use;
288 void DI_Mount() {
289 if(di_fd < 0)
290 return;
292 uint32_t status;
294 if (DI_GetCoverRegister(&status) != 0) {
295 state = DVD_NO_DISC;
296 return;
299 if ((status & DVD_COVER_DISC_INSERTED) == 0) {
300 state = DVD_NO_DISC;
301 return;
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
311 void DI_Close(){
312 if(di_fd < 0)
313 return;
315 if (di_fd > 0)
316 IOS_Close(di_fd);
318 di_fd = -1;
320 DI_ReadDVDptr = NULL;
321 DI_ReadDVDAsyncptr = NULL;
322 state = DVD_INIT | DVD_NO_DISC;
324 if (bufferMutex) {
325 LWP_MutexDestroy(bufferMutex);
326 bufferMutex = 0;
330 static int _DI_ReadDVD_Check(void* buf, uint32_t len, uint32_t lba)
332 int ret;
334 ret = _DI_ReadDVD_D0(buf, len, lba);
335 if (ret == 0)
337 state = state | DVD_D0;
338 DI_ReadDVDptr = _DI_ReadDVD_D0;
339 DI_ReadDVDAsyncptr = _DI_ReadDVD_D0_Async;
340 return ret;
342 ret = _DI_ReadDVD_A8(buf, len, lba);
343 if (ret == 0)
345 state = state | DVD_A8;
346 DI_ReadDVDptr = _DI_ReadDVD_A8;
347 DI_ReadDVDAsyncptr = _DI_ReadDVD_A8_Async;
348 return ret;
350 return ret;
353 static int _DI_ReadDVD_Check_Async(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb)
355 int ret;
357 ret = _DI_ReadDVD_D0_Async(buf, len, lba, ipc_cb);
358 if (ret == 0)
360 state = state | DVD_D0;
361 DI_ReadDVDptr = _DI_ReadDVD_D0;
362 DI_ReadDVDAsyncptr = _DI_ReadDVD_D0_Async;
363 return ret;
365 ret = _DI_ReadDVD_A8_Async(buf, len, lba, ipc_cb);
366 if (ret == 0)
368 state = state | DVD_A8;
369 DI_ReadDVDptr = _DI_ReadDVD_A8;
370 DI_ReadDVDAsyncptr = _DI_ReadDVD_A8_Async;
371 return ret;
373 return ret;
376 static void _DI_SetCallback(int ioctl_nr, ipccallback ipc_cb){
377 if ((di_fd < 0) || !ipc_cb)
378 return;
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[] = {
397 DVD_GETCOVER,
398 DVD_WAITFORCOVERCLOSE,
399 DVD_RESET,
400 DVD_IDENTIFY, // This one will complete when the drive is ready.
401 DVD_READ_DISCID,
403 const int return_table[] = {1,1,4,1,1};
405 if(cur_state > 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.
411 cur_state += 2;
412 else
413 cur_state++; // If the previous callback succeeded, moving on to the next
415 retry_count = LIBDI_MAX_RETRIES;
417 else
419 retry_count--;
420 if(retry_count < 0){ // Drive init failed for unknown reasons.
421 retry_count = LIBDI_MAX_RETRIES;
422 cur_state = 0;
423 state = DVD_UNKNOWN;
424 return 0;
427 _DI_SetCallback(callback_table[cur_state - 1], _cover_callback);
430 else // Callback chain has completed OK. The drive is ready.
432 state = DVD_READY;
433 DI_ReadDVDptr = _DI_ReadDVD_Check;
434 DI_ReadDVDAsyncptr = _DI_ReadDVD_Check_Async;
437 if(di_cb)
438 di_cb(state,0);
440 retry_count = LIBDI_MAX_RETRIES;
441 cur_state = 0;
443 return 0;
446 /* Get current status, will return the API status */
447 int DI_GetStatus(){
448 return state;
451 void DI_SetInitCallback(di_callback cb){
452 di_cb = cb;
456 Request an identification from the drive, returned in a DI_DriveID struct
458 int DI_Identify(DI_DriveID* id){
459 if(di_fd < 0)
460 return -ENXIO;
462 if(!id)
463 return -EINVAL;
465 LWP_MutexLock(bufferMutex);
467 dic[0] = DVD_IDENTIFY << 24;
469 int ret = IOS_Ioctl(di_fd, DVD_IDENTIFY, dic, 0x20, outbuf, 0x20);
471 if(ret == 2)
472 ret = EIO;
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){
485 if(di_fd < 0)
486 return -ENXIO;
488 if(!error)
489 return -EINVAL;
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);
497 if(ret == 2)
498 ret = EIO;
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;
507 Reset the drive.
509 int DI_Reset(){
510 if(di_fd < 0)
511 return -ENXIO;
513 LWP_MutexLock(bufferMutex);
515 dic[0] = DVD_RESET << 24;
516 dic[1] = 1;
518 int ret = IOS_Ioctl(di_fd, DVD_RESET, dic, 0x20, outbuf, 0x20);
520 if(ret == 2)
521 ret = EIO;
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){
532 if(di_fd < 0)
533 return -ENXIO;
535 int ret;
536 if(DI_ReadDVDptr){
537 LWP_MutexLock(bufferMutex);
538 ret = ReadBlockFromCache(buf,len,lba);
539 LWP_MutexUnlock(bufferMutex);
540 return ret;
542 return -1;
545 int DI_ReadDVDAsync(void* buf, uint32_t len, uint32_t lba,ipccallback ipc_cb){
546 if(di_fd < 0)
547 return -ENXIO;
549 int ret;
550 if(DI_ReadDVDAsyncptr){
551 LWP_MutexLock(bufferMutex);
552 ret = DI_ReadDVDAsyncptr(buf,len,lba,ipc_cb);
553 LWP_MutexUnlock(bufferMutex);
554 return ret;
556 return -1;
560 Unknown what this does as of now...
562 int DI_ReadDVDConfig(uint32_t* val, uint32_t flag){
563 if(di_fd < 0)
564 return -ENXIO;
566 if(!val)
567 return -EINVAL;
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.
574 dic[3] = *val;
576 int ret = IOS_Ioctl(di_fd, DVD_READ_CONFIG, dic, 0x20, outbuf, 0x20);
578 if(ret == 2)
579 ret = EIO;
581 *val = outbuf[0];
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){
590 if(di_fd < 0)
591 return -ENXIO;
593 if(!copyright)
594 return -EINVAL;
596 LWP_MutexLock(bufferMutex);
598 dic[0] = DVD_READ_COPYRIGHT << 24;
599 dic[1] = 0;
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.
604 if(ret == 2)
605 ret = EIO;
607 LWP_MutexUnlock(bufferMutex);
608 return (ret == 1)? 0 : -ret;
611 int DI_Read_BCA(void *outbuf)
613 if(di_fd < 0)
614 return -ENXIO;
616 if(!outbuf)
617 return -EINVAL;
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);
624 if(ret == 2)
625 ret = EIO;
627 LWP_MutexUnlock(bufferMutex);
628 return (ret == 1)? 0 : -ret;
632 Returns 0x800 bytes worth of Disc key
634 int DI_ReadDVDDiscKey(void* buf){
635 int ret;
636 int retry_count = LIBDI_MAX_RETRIES;
638 if(di_fd < 0)
639 return -ENXIO;
641 if(!buf)
642 return -EINVAL;
644 if((uint32_t)buf & 0x1F)
645 return -EFAULT;
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);
653 retry_count--;
654 }while(ret != 1 && retry_count > 0);
656 if(ret == 2)
657 ret = EIO;
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){
667 int ret;
668 int retry_count = LIBDI_MAX_RETRIES;
670 if(di_fd < 0)
671 return -ENXIO;
673 if(!buf)
674 return -EINVAL;
676 if((uint32_t)buf & 0x1F)
677 return -EFAULT;
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);
686 retry_count--;
687 }while(ret != 1 && retry_count > 0);
689 if(ret == 2)
690 ret = EIO;
692 LWP_MutexUnlock(bufferMutex);
693 return (ret == 1)? 0 : -ret;
696 int DI_ReportKey(int keytype, uint32_t lba, void* buf){
697 if(di_fd < 0)
698 return -ENXIO;
700 if(!buf)
701 return -EINVAL;
703 if((uint32_t)buf & 0x1F)
704 return -EFAULT;
706 LWP_MutexLock(bufferMutex);
708 dic[0] = DVD_REPORTKEY << 24;
709 dic[1] = keytype & 0xFF;
710 dic[2] = lba;
712 int ret = IOS_Ioctl(di_fd, DVD_REPORTKEY, dic, 0x20, buf, 0x20);
714 if(ret == 2)
715 ret = EIO;
717 LWP_MutexUnlock(bufferMutex);
718 return (ret == 1)? 0 : -ret;
721 int DI_GetCoverRegister(uint32_t* status){
722 if(di_fd < 0)
723 return -ENXIO;
725 LWP_MutexLock(bufferMutex);
726 memset(dic, 0x00, 0x20);
728 int ret = IOS_Ioctl(di_fd, DVD_GETCOVER, dic, 0x20, outbuf, 0x20);
729 if(ret == 2)
730 ret = EIO;
732 *status = outbuf[0];
734 LWP_MutexUnlock(bufferMutex);
735 return (ret == 1)? 0 : -ret;
738 /* Internal function for controlling motor operations */
739 static int _DI_SetMotor(int flag){
740 if(di_fd < 0)
741 return -ENXIO;
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);
751 if(ret == 2)
752 ret = EIO;
754 LWP_MutexUnlock(bufferMutex);
755 return(ret == 1)? 0 : -ret;
758 /* Stop the drives motor */
759 int DI_StopMotor(){
760 return _DI_SetMotor(0);
763 /* Stop the motor, and eject the disc. Also needs a reset afterwards for normal operation */
764 int DI_Eject(){
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.
774 int DI_KillDrive(){
775 return _DI_SetMotor(2);
778 int DI_ClosePartition() {
779 if(di_fd < 0)
780 return -ENXIO;
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);
788 if(ret == 2)
789 ret = EIO;
791 LWP_MutexUnlock(bufferMutex);
792 return(ret == 1)? 0 : -ret;
795 int DI_OpenPartition(uint32_t offset)
797 if(di_fd < 0)
798 return -ENXIO;
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;
805 dic[1] = offset;
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;
812 vectors[2].len = 0;
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);
821 if(ret == 2)
822 ret = EIO;
824 LWP_MutexUnlock(bufferMutex);
825 return(ret == 1)? 0 : -ret;
829 int DI_Read(void *buf, uint32_t size, uint32_t offset)
831 if(di_fd < 0)
832 return -ENXIO;
834 if(!buf)
835 return -EINVAL;
837 if((uint32_t)buf & 0x1F)
838 return -EFAULT;
840 LWP_MutexLock(bufferMutex);
842 dic[0] = DVD_LOW_READ << 24;
843 dic[1] = size;
844 dic[2] = offset;
846 int ret = IOS_Ioctl(di_fd, DVD_LOW_READ, dic, 0x20, buf, size);
848 if(ret == 2)
849 ret = EIO;
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;
859 if(di_fd < 0)
860 return -ENXIO;
862 if(!buf)
863 return -EINVAL;
865 if((uint32_t)buf & 0x1F) // This only works with 32 byte aligned addresses!
866 return -EFAULT;
868 LWP_MutexLock(bufferMutex);
870 dic[0] = DVD_READ_UNENCRYPTED << 24;
871 dic[1] = size;
872 dic[2] = offset;
874 do{
875 ret = IOS_Ioctl(di_fd, DVD_READ_UNENCRYPTED, dic, 0x20, buf, size);
876 retry_count--;
877 }while(ret != 1 && retry_count > 0);
879 if(ret == 2)
880 ret = EIO;
882 LWP_MutexUnlock(bufferMutex);
884 return (ret == 1)? 0 : -ret;
887 int DI_ReadDiscID(uint64_t *id)
889 if(di_fd < 0)
890 return -ENXIO;
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);
898 if(ret == 2)
899 ret = EIO;
901 memcpy(id, outbuf, sizeof(*id));
903 LWP_MutexUnlock(bufferMutex);
904 return(ret == 1)? 0 : -ret;
907 static bool diio_Startup()
909 u64 t1,t2;
911 if(di_fd < 0)
912 return false;
914 DI_Mount();
916 t1=ticks_to_millisecs(gettime());
918 while(state & DVD_INIT)
920 usleep(500);
921 t2=ticks_to_millisecs(gettime());
922 if( (t2 - t1) > MOUNT_TIMEOUT)
923 return false; // timeout
926 if(state & DVD_READY)
927 return true;
928 return false;
931 static bool diio_IsInserted()
933 u32 val;
935 if(di_fd < 0)
936 return false;
938 DI_GetCoverRegister(&val);
939 if(val & 0x2)
940 return true;
942 return false;
945 static bool diio_ReadSectors(sec_t sector,sec_t numSectors,void *buffer)
947 if(DI_ReadDVD(buffer, numSectors, sector) == 0)
948 return true;
949 return false;
952 static bool diio_WriteSectors(sec_t sector,sec_t numSectors,const void *buffer)
954 return true;
957 static bool diio_ClearStatus()
959 return true;
962 static bool diio_Shutdown()
964 DI_StopMotor();
965 return true;
968 const DISC_INTERFACE __io_wiidvd = {
969 DEVICE_TYPE_WII_DVD,
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