Fixed smbClose() for older dkppc versions
[libogc.git] / libdi / di.c
blob70bc98a7e2790abd776cd9661e1829e5ed2d2484
1 /*-------------------------------------------------------------
3 di.c -- Drive Interface library
5 Team Twiizers
6 Copyright (C) 2008
8 Erant
9 marcan
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
28 distribution.
30 -------------------------------------------------------------*/
32 #include <errno.h>
33 #include <string.h>
35 #include <di/di.h>
36 #include <ogc/ipc.h>
37 #include <ogc/ios.h>
38 #include <ogc/mutex.h>
40 int di_fd = -1;
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!
69 s32 __DI_LoadStub();
71 int DI_Init(){
72 static int init = 0;
74 state = DVD_INIT | DVD_NO_DISC;
76 if(!init){
77 __DI_LoadStub(); // Marcan's 1337 magics happen here!
78 init = 1;
81 if(di_fd < 0)
82 di_fd = IOS_Open(di_path, 2);
84 if (!bufferMutex)
85 LWP_MutexInit(&bufferMutex, false);
87 return (di_fd >= 0)? di_fd : -1;
90 void DI_Mount(){
91 uint32_t status;
93 if (DI_GetCoverRegister(&status) != 0) {
94 state = DVD_NO_DISC;
95 return;
98 if ((status & DVD_COVER_DISC_INSERTED) == 0) {
99 state = DVD_NO_DISC;
100 return;
103 state = DVD_INIT | DVD_NO_DISC;
104 _cover_callback(1, NULL); // Initialize the callback chain.
107 void DI_Close(){
108 if(di_fd > 0)
109 IOS_Close(di_fd);
111 di_fd = -1;
113 DI_ReadDVDptr = NULL;
114 DI_ReadDVDAsyncptr = NULL;
115 state = DVD_INIT | DVD_NO_DISC;
117 if (bufferMutex) {
118 LWP_MutexDestroy(bufferMutex);
119 bufferMutex = 0;
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[] = {
130 DVD_GETCOVER,
131 DVD_WAITFORCOVERCLOSE,
132 DVD_RESET,
133 DVD_IDENTIFY, // This one will complete when the drive is ready.
134 DVD_READ_DISCID,
136 const int return_table[] = {1,1,4,1,1};
138 if(cur_state > 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.
144 cur_state += 2;
145 else
146 cur_state++; // If the previous callback succeeded, moving on to the next
148 retry_count = LIBDI_MAX_RETRIES;
150 else
152 retry_count--;
153 if(retry_count < 0){ // Drive init failed for unknown reasons.
154 retry_count = LIBDI_MAX_RETRIES;
155 cur_state = 0;
156 state = DVD_UNKNOWN;
157 return 0;
160 _DI_SetCallback(callback_table[cur_state - 1], _cover_callback);
163 else // Callback chain has completed OK. The drive is ready.
165 state = DVD_READY;
166 if(IOS_GetVersion() < 200){
167 state |= DVD_D0;
168 DI_ReadDVDptr = _DI_ReadDVD_D0;
169 DI_ReadDVDAsyncptr = _DI_ReadDVD_D0_Async;
171 else
173 state |= DVD_A8;
174 DI_ReadDVDptr = _DI_ReadDVD_A8;
175 DI_ReadDVDAsyncptr = _DI_ReadDVD_A8_Async;
178 if(di_cb)
179 di_cb(state,0);
181 retry_count = LIBDI_MAX_RETRIES;
182 cur_state = 0;
184 return 0;
187 /* Get current status, will return the API status */
188 int DI_GetStatus(){
189 return state;
192 void DI_SetInitCallback(di_callback cb){
193 di_cb = cb;
196 void _DI_SetCallback(int ioctl_nr, ipccallback ipc_cb){
197 if(di_fd < 0 || !ipc_cb) return;
199 // Wait for the lock
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){
217 if(di_fd < 0){
218 errno = ENXIO;
219 return -1;
222 if(!id){
223 errno = EINVAL;
224 return -1;
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){
246 if(di_fd < 0){
247 errno = ENXIO;
248 return -1;
251 if(!error){
252 errno = EINVAL;
253 return -1;
256 // Wait for the lock
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;
272 Reset the drive.
274 int DI_Reset(){
275 if(di_fd < 0)
276 return -1;
278 // Wait for the lock
279 while(LWP_MutexLock(bufferMutex));
281 dic[0] = DVD_RESET << 24;
282 dic[1] = 1;
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){
297 int ret;
298 if(DI_ReadDVDptr){
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);
303 return ret;
305 return -1;
308 int DI_ReadDVDAsync(void* buf, uint32_t len, uint32_t lba,ipccallback ipc_cb){
309 int ret;
310 if(DI_ReadDVDAsyncptr){
311 while(LWP_MutexLock(bufferMutex));
312 ret = DI_ReadDVDAsyncptr(buf,len,lba,ipc_cb);
313 LWP_MutexUnlock(bufferMutex);
314 return ret;
316 return -1;
320 Unknown what this does as of now...
322 int DI_ReadDVDConfig(uint32_t* val, uint32_t flag){
323 if(di_fd < 0){
324 errno = ENXIO;
325 return -1;
328 if(!val){
329 errno = EINVAL;
330 return -1;
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.
338 dic[3] = *val;
340 int ret = IOS_Ioctl(di_fd, DVD_READ_CONFIG, dic, 0x20, outbuf, 0x20);
342 if(ret == 2) errno = EIO;
344 *val = outbuf[0];
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){
353 if(di_fd < 0){
354 errno = ENXIO;
355 return -1;
358 if(!copyright){
359 errno = EINVAL;
360 return -1;
363 // Wait for the lock
364 while(LWP_MutexLock(bufferMutex));
366 dic[0] = DVD_READ_COPYRIGHT << 24;
367 dic[1] = 0;
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){
381 int ret;
382 int retry_count = LIBDI_MAX_RETRIES;
384 if(di_fd < 0){
385 errno = ENXIO;
386 return -1;
389 if(!buf){
390 errno = EINVAL;
391 return -1;
394 if((uint32_t)buf & 0x1F){
395 errno = EFAULT;
396 return -1;
399 // Wait for the lock
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);
406 retry_count--;
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){
419 int ret;
420 int retry_count = LIBDI_MAX_RETRIES;
422 if(di_fd < 0){
423 errno = ENXIO;
424 return -1;
427 if(!buf){
428 errno = EINVAL;
429 return -1;
432 if((uint32_t)buf & 0x1F){
433 errno = EFAULT;
434 return -1;
437 // Wait for the lock
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);
445 retry_count--;
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){
455 if(di_fd < 0){
456 errno = ENXIO;
457 return -1;
460 if(!buf){
461 errno = EINVAL;
462 return -1;
465 if((uint32_t)buf & 0x1F){
466 errno = EFAULT;
467 return -1;
470 while(LWP_MutexLock(bufferMutex));
472 dic[0] = DVD_REPORTKEY << 24;
473 dic[1] = keytype & 0xFF;
474 dic[2] = lba;
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){
485 if(di_fd < 0){
486 errno = ENXIO;
487 return -1;
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;
497 *status = outbuf[0];
499 LWP_MutexUnlock(bufferMutex);
500 return (ret == 1)? 0 : -ret;
503 /* Internal function for controlling motor operations */
504 int _DI_SetMotor(int flag){
505 if(di_fd < 0){
506 errno = ENXIO;
507 return -1;
510 // Wait for the lock
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 */
526 int DI_StopMotor(){
527 return _DI_SetMotor(0);
530 /* Stop the motor, and eject the disc. Also needs a reset afterwards for normal operation */
531 int DI_Eject(){
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.
541 int DI_KillDrive(){
542 return _DI_SetMotor(2);
545 int DI_ClosePartition() {
546 if(di_fd < 0){
547 errno = ENXIO;
548 return -1;
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)
565 if(di_fd < 0){
566 errno = ENXIO;
567 return -1;
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;
575 dic[1] = offset;
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;
582 vectors[2].len = 0;
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)
600 if(di_fd < 0){
601 errno = ENXIO;
602 return -1;
605 if(!buf){
606 errno = EINVAL;
607 return -1;
610 if((uint32_t)buf & 0x1F){
611 errno = EFAULT;
612 return -1;
615 // Wait for the lock
616 while(LWP_MutexLock(bufferMutex));
618 dic[0] = DVD_LOW_READ << 24;
619 dic[1] = size;
620 dic[2] = offset;
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;
634 if(di_fd < 0){
635 errno = ENXIO;
636 return -1;
639 if(!buf){
640 errno = EINVAL;
641 return -1;
644 if((uint32_t)buf & 0x1F){ // This only works with 32 byte aligned addresses!
645 errno = EFAULT;
646 return -1;
649 // Wait for the lock
650 while(LWP_MutexLock(bufferMutex));
652 dic[0] = DVD_READ_UNENCRYPTED << 24;
653 dic[1] = size;
654 dic[2] = offset;
656 do{
657 ret = IOS_Ioctl(di_fd, DVD_READ_UNENCRYPTED, dic, 0x20, buf, size);
658 retry_count--;
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)
670 if(di_fd < 0){
671 errno = ENXIO;
672 return -1;
675 // Wait for the lock
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;