add dvd reading from ppc (emukidid) and small sector cache (rodries). necessary for...
[libogc.git] / libdi / di.c
blob82bd3203c2cf5c455f0c9319fcd76eae70b808b3
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/ipc.h>
43 #include <ogc/ios.h>
44 #include <ogc/mutex.h>
45 #include <ogc/lwp_watchdog.h>
47 #define MOUNT_TIMEOUT 15000 // 15 seconds
49 int di_fd = -1;
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){
66 int ret;
68 if(!buf)
69 return -EINVAL;
71 if((uint32_t)buf & 0x1F) // This only works with 32 byte aligned addresses!
72 return -EFAULT;
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);
80 if(ret == 2)
81 ret = EIO;
83 return (ret == 1)? 0 : -ret;
86 static int _DI_ReadDVD_D0_Async(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb){
87 int ret;
89 if(!buf)
90 return -EINVAL;
92 if((uint32_t)buf & 0x1F)
93 return -EFAULT;
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.
98 dic[3] = len;
99 dic[4] = lba;
101 ret = IOS_IoctlAsync(di_fd, DVD_READ, dic, 0x20, buf, len << 11,ipc_cb, buf);
103 if(ret == 2)
104 ret = EIO;
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?
113 dvd[0] = 0x2E;
114 dvd[1] = 0;
115 dvd[2] = read_cmd;
116 dvd[3] = read_cmd == 0xD0000000 ? lba : lba << 9;
117 dvd[4] = read_cmd == 0xD0000000 ? len : len << 11;
118 dvd[5] = (unsigned long) buf;
119 dvd[6] = len << 11;
120 dvd[7] = 3; // enable reading!
121 DCInvalidateRange(buf, len << 11);
122 while (dvd[7] & 1);
124 if (dvd[0] & 0x4)
125 return 1;
126 return 0;
129 static int _DI_ReadDVD_A8(void* buf, uint32_t len, uint32_t lba){
130 int ret, retry_count = LIBDI_MAX_RETRIES;
132 if(!buf)
133 return -EINVAL;
135 if((uint32_t)buf & 0x1F) // This only works with 32 byte aligned addresses!
136 return -EFAULT;
138 if(have_ahbprot)
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.
145 do{
146 ret = IOS_Ioctl(di_fd, DVD_READ_UNENCRYPTED, dic, 0x20, buf, len << 11);
147 retry_count--;
148 }while(ret != 1 && retry_count > 0);
150 if(ret == 2)
151 ret = EIO;
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;
159 if(!buf)
160 return -EINVAL;
162 if((uint32_t)buf & 0x1F)
163 return -EFAULT;
165 if(have_ahbprot)
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.
171 dic[3] = len;
172 dic[4] = lba;
174 do{
175 ret = IOS_Ioctl(di_fd, DVD_READ, dic, 0x20, buf, len << 11);
176 retry_count--;
177 }while(ret != 1 && retry_count > 0);
179 if(ret == 2)
180 ret = EIO;
182 return (ret == 1)? 0 : -ret;
185 ///// Cache
186 #define CACHE_FREE 0xFFFFFFFF
187 #define BLOCK_SIZE 0x800
188 #define CACHEBLOCKS 26
189 typedef struct
191 uint32_t block;
192 void *ptr;
193 } cache_page;
194 static cache_page *cache_read = NULL;
196 static void CreateDVDCache()
198 if (cache_read != NULL)
199 return;
200 cache_read = (cache_page *) malloc(sizeof(cache_page));
201 if (cache_read == NULL)
202 return;
204 cache_read->block = CACHE_FREE;
205 cache_read->ptr = memalign(32, BLOCK_SIZE * CACHEBLOCKS);
206 if (cache_read->ptr == NULL)
208 free(cache_read);
209 cache_read = NULL;
210 return;
212 memset(cache_read->ptr, 0, BLOCK_SIZE);
215 static int ReadBlockFromCache(void *buf, uint32_t len, uint32_t block)
217 int retval;
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);
225 return 0;
228 if (len > CACHEBLOCKS)
229 return DI_ReadDVDptr(buf, len, block);
231 retval = DI_ReadDVDptr(cache_read->ptr, CACHEBLOCKS, block);
232 if (retval)
234 cache_read->block = CACHE_FREE;
235 return retval;
238 cache_read->block = block;
239 memcpy(buf, cache_read->ptr, len * BLOCK_SIZE);
241 return 0;
245 Initialize the DI interface, should always be called first!
248 s32 __DI_StubLaunch(void);
249 u32 __di_check_ahbprot(void);
251 int DI_Init() {
252 if(di_fd >= 0)
253 return 1;
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!
261 if (res < 0)
262 return res;
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 CreateDVDCache();
275 return 0;
278 void DI_LoadDVDX(bool load){
279 load_dvdx = load;
282 void DI_Mount() {
283 if(di_fd < 0)
284 return;
286 uint32_t status;
288 if (DI_GetCoverRegister(&status) != 0) {
289 state = DVD_NO_DISC;
290 return;
293 if ((status & DVD_COVER_DISC_INSERTED) == 0) {
294 state = DVD_NO_DISC;
295 return;
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
305 void DI_Close(){
306 if(di_fd < 0)
307 return;
309 if (di_fd > 0)
310 IOS_Close(di_fd);
312 di_fd = -1;
314 DI_ReadDVDptr = NULL;
315 DI_ReadDVDAsyncptr = NULL;
316 state = DVD_INIT | DVD_NO_DISC;
318 if (bufferMutex) {
319 LWP_MutexDestroy(bufferMutex);
320 bufferMutex = 0;
324 static int _DI_ReadDVD_Check(void* buf, uint32_t len, uint32_t lba)
326 int ret;
328 ret = _DI_ReadDVD_D0(buf, len, lba);
329 if (ret == 0)
331 state = state | DVD_D0;
332 DI_ReadDVDptr = _DI_ReadDVD_D0;
333 DI_ReadDVDAsyncptr = _DI_ReadDVD_D0_Async;
334 return ret;
336 ret = _DI_ReadDVD_A8(buf, len, lba);
337 if (ret == 0)
339 state = state | DVD_A8;
340 DI_ReadDVDptr = _DI_ReadDVD_A8;
341 DI_ReadDVDAsyncptr = _DI_ReadDVD_A8_Async;
342 return ret;
344 return ret;
347 static int _DI_ReadDVD_Check_Async(void* buf, uint32_t len, uint32_t lba, ipccallback ipc_cb)
349 int ret;
351 ret = _DI_ReadDVD_D0_Async(buf, len, lba, ipc_cb);
352 if (ret == 0)
354 state = state | DVD_D0;
355 DI_ReadDVDptr = _DI_ReadDVD_D0;
356 DI_ReadDVDAsyncptr = _DI_ReadDVD_D0_Async;
357 return ret;
359 ret = _DI_ReadDVD_A8_Async(buf, len, lba, ipc_cb);
360 if (ret == 0)
362 state = state | DVD_A8;
363 DI_ReadDVDptr = _DI_ReadDVD_A8;
364 DI_ReadDVDAsyncptr = _DI_ReadDVD_A8_Async;
365 return ret;
367 return ret;
370 static void _DI_SetCallback(int ioctl_nr, ipccallback ipc_cb){
371 if ((di_fd < 0) || !ipc_cb)
372 return;
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[] = {
392 DVD_GETCOVER,
393 DVD_WAITFORCOVERCLOSE,
394 DVD_RESET,
395 DVD_IDENTIFY, // This one will complete when the drive is ready.
396 DVD_READ_DISCID,
398 const int return_table[] = {1,1,4,1,1};
400 if(cur_state > 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.
406 cur_state += 2;
407 else
408 cur_state++; // If the previous callback succeeded, moving on to the next
410 retry_count = LIBDI_MAX_RETRIES;
412 else
414 retry_count--;
415 if(retry_count < 0){ // Drive init failed for unknown reasons.
416 retry_count = LIBDI_MAX_RETRIES;
417 cur_state = 0;
418 state = DVD_UNKNOWN;
419 return 0;
422 _DI_SetCallback(callback_table[cur_state - 1], _cover_callback);
425 else // Callback chain has completed OK. The drive is ready.
427 state = DVD_READY;
428 DI_ReadDVDptr = _DI_ReadDVD_Check;
429 DI_ReadDVDAsyncptr = _DI_ReadDVD_Check_Async;
432 if(di_cb)
433 di_cb(state,0);
435 retry_count = LIBDI_MAX_RETRIES;
436 cur_state = 0;
438 return 0;
441 /* Get current status, will return the API status */
442 int DI_GetStatus(){
443 return state;
446 void DI_SetInitCallback(di_callback cb){
447 di_cb = cb;
451 Request an identification from the drive, returned in a DI_DriveID struct
453 int DI_Identify(DI_DriveID* id){
454 if(di_fd < 0)
455 return -ENXIO;
457 if(!id)
458 return -EINVAL;
460 while(LWP_MutexLock(bufferMutex));
462 dic[0] = DVD_IDENTIFY << 24;
464 int ret = IOS_Ioctl(di_fd, DVD_IDENTIFY, dic, 0x20, outbuf, 0x20);
466 if(ret == 2)
467 ret = EIO;
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){
480 if(di_fd < 0)
481 return -ENXIO;
483 if(!error)
484 return -EINVAL;
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);
492 if(ret == 2)
493 ret = EIO;
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;
502 Reset the drive.
504 int DI_Reset(){
505 if(di_fd < 0)
506 return -ENXIO;
508 while(LWP_MutexLock(bufferMutex));
510 dic[0] = DVD_RESET << 24;
511 dic[1] = 1;
513 int ret = IOS_Ioctl(di_fd, DVD_RESET, dic, 0x20, outbuf, 0x20);
515 if(ret == 2)
516 ret = EIO;
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){
527 if(di_fd < 0)
528 return -ENXIO;
530 int ret;
531 if(DI_ReadDVDptr){
532 while(LWP_MutexLock(bufferMutex));
533 ret = ReadBlockFromCache(buf,len,lba);
534 LWP_MutexUnlock(bufferMutex);
535 return ret;
537 return -1;
540 int DI_ReadDVDAsync(void* buf, uint32_t len, uint32_t lba,ipccallback ipc_cb){
541 if(di_fd < 0)
542 return -ENXIO;
544 int ret;
545 if(DI_ReadDVDAsyncptr){
546 while(LWP_MutexLock(bufferMutex));
547 ret = DI_ReadDVDAsyncptr(buf,len,lba,ipc_cb);
548 LWP_MutexUnlock(bufferMutex);
549 return ret;
551 return -1;
555 Unknown what this does as of now...
557 int DI_ReadDVDConfig(uint32_t* val, uint32_t flag){
558 if(di_fd < 0)
559 return -ENXIO;
561 if(!val)
562 return -EINVAL;
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.
569 dic[3] = *val;
571 int ret = IOS_Ioctl(di_fd, DVD_READ_CONFIG, dic, 0x20, outbuf, 0x20);
573 if(ret == 2)
574 ret = EIO;
576 *val = outbuf[0];
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){
585 if(di_fd < 0)
586 return -ENXIO;
588 if(!copyright)
589 return -EINVAL;
591 while(LWP_MutexLock(bufferMutex));
593 dic[0] = DVD_READ_COPYRIGHT << 24;
594 dic[1] = 0;
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.
599 if(ret == 2)
600 ret = EIO;
602 LWP_MutexUnlock(bufferMutex);
603 return (ret == 1)? 0 : -ret;
607 Returns 0x800 bytes worth of Disc key
609 int DI_ReadDVDDiscKey(void* buf){
610 int ret;
611 int retry_count = LIBDI_MAX_RETRIES;
613 if(di_fd < 0)
614 return -ENXIO;
616 if(!buf)
617 return -EINVAL;
619 if((uint32_t)buf & 0x1F)
620 return -EFAULT;
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);
628 retry_count--;
629 }while(ret != 1 && retry_count > 0);
631 if(ret == 2)
632 ret = EIO;
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){
642 int ret;
643 int retry_count = LIBDI_MAX_RETRIES;
645 if(di_fd < 0)
646 return -ENXIO;
648 if(!buf)
649 return -EINVAL;
651 if((uint32_t)buf & 0x1F)
652 return -EFAULT;
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);
661 retry_count--;
662 }while(ret != 1 && retry_count > 0);
664 if(ret == 2)
665 ret = EIO;
667 LWP_MutexUnlock(bufferMutex);
668 return (ret == 1)? 0 : -ret;
671 int DI_ReportKey(int keytype, uint32_t lba, void* buf){
672 if(di_fd < 0)
673 return -ENXIO;
675 if(!buf)
676 return -EINVAL;
678 if((uint32_t)buf & 0x1F)
679 return -EFAULT;
681 while(LWP_MutexLock(bufferMutex));
683 dic[0] = DVD_REPORTKEY << 24;
684 dic[1] = keytype & 0xFF;
685 dic[2] = lba;
687 int ret = IOS_Ioctl(di_fd, DVD_REPORTKEY, dic, 0x20, buf, 0x20);
689 if(ret == 2)
690 ret = EIO;
692 LWP_MutexUnlock(bufferMutex);
693 return (ret == 1)? 0 : -ret;
696 int DI_GetCoverRegister(uint32_t* status){
697 if(di_fd < 0)
698 return -ENXIO;
700 while(LWP_MutexLock(bufferMutex));
702 memset(dic, 0x00, 0x20);
704 int ret = IOS_Ioctl(di_fd, DVD_GETCOVER, dic, 0x20, outbuf, 0x20);
705 if(ret == 2)
706 ret = EIO;
708 *status = outbuf[0];
710 LWP_MutexUnlock(bufferMutex);
711 return (ret == 1)? 0 : -ret;
714 /* Internal function for controlling motor operations */
715 static int _DI_SetMotor(int flag){
716 if(di_fd < 0)
717 return -ENXIO;
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);
727 if(ret == 2)
728 ret = EIO;
730 LWP_MutexUnlock(bufferMutex);
731 return(ret == 1)? 0 : -ret;
734 /* Stop the drives motor */
735 int DI_StopMotor(){
736 return _DI_SetMotor(0);
739 /* Stop the motor, and eject the disc. Also needs a reset afterwards for normal operation */
740 int DI_Eject(){
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.
750 int DI_KillDrive(){
751 return _DI_SetMotor(2);
754 int DI_ClosePartition() {
755 if(di_fd < 0)
756 return -ENXIO;
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);
764 if(ret == 2)
765 ret = EIO;
767 LWP_MutexUnlock(bufferMutex);
768 return(ret == 1)? 0 : -ret;
771 int DI_OpenPartition(uint32_t offset)
773 if(di_fd < 0)
774 return -ENXIO;
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;
781 dic[1] = offset;
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;
788 vectors[2].len = 0;
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);
797 if(ret == 2)
798 ret = EIO;
800 LWP_MutexUnlock(bufferMutex);
801 return(ret == 1)? 0 : -ret;
805 int DI_Read(void *buf, uint32_t size, uint32_t offset)
807 if(di_fd < 0)
808 return -ENXIO;
810 if(!buf)
811 return -EINVAL;
813 if((uint32_t)buf & 0x1F)
814 return -EFAULT;
816 while(LWP_MutexLock(bufferMutex));
818 dic[0] = DVD_LOW_READ << 24;
819 dic[1] = size;
820 dic[2] = offset;
822 int ret = IOS_Ioctl(di_fd, DVD_LOW_READ, dic, 0x20, buf, size);
824 if(ret == 2)
825 ret = EIO;
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;
835 if(di_fd < 0)
836 return -ENXIO;
838 if(!buf)
839 return -EINVAL;
841 if((uint32_t)buf & 0x1F) // This only works with 32 byte aligned addresses!
842 return -EFAULT;
844 while(LWP_MutexLock(bufferMutex));
846 dic[0] = DVD_READ_UNENCRYPTED << 24;
847 dic[1] = size;
848 dic[2] = offset;
850 do{
851 ret = IOS_Ioctl(di_fd, DVD_READ_UNENCRYPTED, dic, 0x20, buf, size);
852 retry_count--;
853 }while(ret != 1 && retry_count > 0);
855 if(ret == 2)
856 ret = EIO;
858 LWP_MutexUnlock(bufferMutex);
860 return (ret == 1)? 0 : -ret;
863 int DI_ReadDiscID(uint64_t *id)
865 if(di_fd < 0)
866 return -ENXIO;
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);
874 if(ret == 2)
875 ret = EIO;
877 memcpy(id, outbuf, sizeof(*id));
879 LWP_MutexUnlock(bufferMutex);
880 return(ret == 1)? 0 : -ret;
883 static bool diio_Startup()
885 u64 t1,t2;
887 if(di_fd < 0)
888 return false;
890 DI_Mount();
892 t1=ticks_to_millisecs(gettime());
894 while(state & DVD_INIT)
896 usleep(500);
897 t2=ticks_to_millisecs(gettime());
898 if( (t2 - t1) > MOUNT_TIMEOUT)
899 return false; // timeout
902 if(state & DVD_READY)
903 return true;
904 return false;
907 static bool diio_IsInserted()
909 u32 val;
911 if(di_fd < 0)
912 return false;
914 DI_GetCoverRegister(&val);
915 if(val & 0x2)
916 return true;
918 return false;
921 static bool diio_ReadSectors(sec_t sector,sec_t numSectors,void *buffer)
923 if(DI_ReadDVD(buffer, numSectors, sector) == 0)
924 return true;
925 return false;
928 static bool diio_WriteSectors(sec_t sector,sec_t numSectors,const void *buffer)
930 return true;
933 static bool diio_ClearStatus()
935 return true;
938 static bool diio_Shutdown()
940 return true;
943 const DISC_INTERFACE __io_wiidvd = {
944 DEVICE_TYPE_WII_DVD,
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