/* * Copyright (C) 2008 Nuke (wiinuke@gmail.com) * * this file is part of GeckoOS for USB Gecko * http://www.usbgecko.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include "dvd_broadway.h" #define DI_CMDCTX_CNT 4 #define DVD_DISKIDSIZE 0x20 #define DVD_DRVINFSIZE 0x20 #define IOCTL_DI_INQUIRY 0x12 #define IOCTL_DI_READID 0x70 #define IOCTL_DI_READ 0x71 #define IOCTL_DI_WAITCVRCLOSE 0x79 #define IOCTL_DI_COVER 0x7A #define IOCTL_DI_RESETNOTIFY 0x7E #define IOCTL_DI_RESET 0x8A #define IOCTL_DI_OPENPART 0x8B #define IOCTL_DI_CLOSEPART 0x8C #define IOCTL_DI_UNENCREAD 0x8D #define IOCTL_DI_ENABLE_DVD 0x8E #define IOCTL_DI_SEEK 0xAB #define IOCTL_DI_READ_DVDVIDEO 0xD0 #define IOCTL_DI_STOPLASER 0xD2 #define IOCTL_DI_OFFSET 0xD9 #define IOCTL_DI_REQERROR 0xE0 #define IOCTL_DI_STOPMOTOR 0xE3 #define IOCTL_DI_SETOFFBASE 0xF0 #define IOCTL_DI_GETOFFBASE 0xF1 #define IOCTL_DI_SETCRYPTMODE 0xF2 #define IOCTL_DI_GETCRYPTMODE 0xF3 #define IOCTL_DI_SETDVDROMMODE 0xF4 #define IOCTL_DI_GETDVDROMMODE 0xF5 #define _SHIFTL(v, s, w) \ ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) #define _SHIFTR(v, s, w) \ ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) struct dicommand { u32 diReg[8]; }; struct dicontext { lwp_node node; dvdcallbacklow cb; struct dicommand *cmd; }; static s32 __dvd_fd = -1; static u32 __dvd_spinupval = 1; static lwp_queue __di_contextq; static u32 __dvd_readlength = 0; static u32 __dvd_cbinprogress = 0; static u32 __dvd_reqinprogress = 0; static u32 __dvd_lowinitcalled = 0; static struct dicommand *__di_commands = NULL; static struct dicontext __di_contexts[DI_CMDCTX_CNT]; static u32 __di_regbuffer[0x08] ATTRIBUTE_ALIGN( 32 ); static u32 __di_regvalcache[0x08] ATTRIBUTE_ALIGN( 32 ); static u32 __di_lastticketerror[0x08] ATTRIBUTE_ALIGN( 32 ); static ioctlv __di_iovector[0x08] ATTRIBUTE_ALIGN( 32 ); static char __di_fs[] ATTRIBUTE_ALIGN( 32 ) = "/dev/di"; extern u32 __IPC_ClntInit(); static __inline__ lwp_node* __lwp_queue_head(lwp_queue *queue) { return (lwp_node*) queue; } static __inline__ lwp_node* __lwp_queue_tail(lwp_queue *queue) { return (lwp_node*) &queue->perm_null; } static __inline__ void __lwp_queue_init_empty(lwp_queue *queue) { queue->first = __lwp_queue_tail(queue); queue->perm_null = NULL; queue->last = __lwp_queue_head(queue); } static struct dicontext* __dvd_getcontext(dvdcallbacklow cb) { struct dicontext *ctx; ctx = (struct dicontext*) __lwp_queue_get(&__di_contextq); if (ctx != NULL) ctx->cb = cb; return ctx; } static s32 __dvd_iostransactionCB(s32 result, void *usrdata) { struct dicontext *ctx = (struct dicontext*) usrdata; __dvd_reqinprogress = 0; if (ctx->cb != NULL) { __dvd_cbinprogress = 1; if (result != 0) __dvd_readlength = 0; ctx->cb(result); __dvd_cbinprogress = 0; } __lwp_queue_append(&__di_contextq, &ctx->node); return 0; } static s32 __dvd_ioscoverregisterCB(s32 result, void *usrdata) { struct dicontext *ctx = (struct dicontext*) usrdata; __dvd_reqinprogress = 0; __di_regvalcache[1] = __di_regbuffer[0]; if (ctx->cb != NULL) { __dvd_cbinprogress = 1; ctx->cb(result); __dvd_cbinprogress = 0; } __lwp_queue_append(&__di_contextq, &ctx->node); return 0; } static s32 __dvd_ioscovercloseCB(s32 result, void *usrdata) { struct dicontext *ctx = (struct dicontext*) usrdata; __dvd_reqinprogress = 0; if (ctx->cb != NULL) { __dvd_cbinprogress = 1; ctx->cb(result); __dvd_cbinprogress = 0; } __lwp_queue_append(&__di_contextq, &ctx->node); return 0; } s32 bwDVD_LowInit() { s32 i, ret = 0; u32 ipclo, ipchi; lwp_queue inactives; struct dicontext *ctx; if (__dvd_lowinitcalled == 0) { ret = __IPC_ClntInit(); if (ret < 0) return ret; ipclo = (((u32) IPC_GetBufferLo() + 0x1f) & ~0x1f); ipchi = (u32) IPC_GetBufferHi(); if (ipchi >= (ipclo + (sizeof(struct dicommand) * DI_CMDCTX_CNT))) { __di_commands = (struct dicommand*) ipclo; IPC_SetBufferLo((void*) (ipclo + (sizeof(struct dicommand) * DI_CMDCTX_CNT))); memset(__di_commands, 0, (sizeof(struct dicommand) * DI_CMDCTX_CNT)); i = 0; __lwp_queue_init_empty(&__di_contextq); __lwp_queue_initialize(&inactives, __di_contexts, DI_CMDCTX_CNT, sizeof(struct dicontext)); while ((ctx = (struct dicontext*) __lwp_queue_get(&inactives)) != NULL) { ctx->cmd = &__di_commands[i]; ctx->cb = NULL; __lwp_queue_append(&__di_contextq, &ctx->node); i++; } } ret = IOS_Open(__di_fs, 0); if (ret < 0) return ret; __dvd_fd = ret; // __dvd_lowinitcalled = 1; // printf("DVD_LowInit(%d)\n",ret); } return 0; } s32 bwDVD_LowInquiry(dvddrvinfo *info, dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_INQUIRY << 24); ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_INQUIRY, cmd->diReg, sizeof(struct dicommand), info, DVD_DRVINFSIZE, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_LowReadID(dvddiskid *diskID, dvdcallbacklow cb) { s32 ret = 0; struct dicontext *ctx; struct dicommand *cmd; // printf("DVD_LowReadID()\n"); __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_READID << 24); ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_READID, cmd->diReg, sizeof(struct dicommand), diskID, DVD_DISKIDSIZE, __dvd_iostransactionCB, ctx); // printf("DVD_LowReadID(%d)\n",ret); return ret; } s32 bwDVD_LowRead(void *buf, u32 len, u32 offset, dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; if (buf == NULL || ((u32) buf % 32) != 0) return -1; __dvd_reqinprogress = 1; __dvd_readlength = len; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_READ << 24); cmd->diReg[1] = len; cmd->diReg[2] = offset; ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_READ, cmd->diReg, sizeof(struct dicommand), buf, len, __dvd_iostransactionCB, ctx); return ret; } // never got this function working, probably removed from wii s32 bwDVD_LowReadVideo(void *buf, u32 len, u32 offset, dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; __dvd_readlength = len; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_READ_DVDVIDEO << 24); cmd->diReg[1] = len; cmd->diReg[2] = offset; ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_READ_DVDVIDEO, cmd->diReg, sizeof(struct dicommand), buf, len, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_LowStopLaser(dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_STOPLASER << 24); ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_STOPLASER, cmd->diReg, sizeof(struct dicommand), __di_regvalcache, 0x20, __dvd_iostransactionCB, ctx); return ret; } // never got this function working, probably removed from wii s32 bwDVD_EnableVideo(dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_ENABLE_DVD << 24); ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_ENABLE_DVD, cmd->diReg, sizeof(struct dicommand), __di_regvalcache, 0x20, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_LowSeek(u32 offset, dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_SEEK << 24); cmd->diReg[1] = offset; ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_SEEK, cmd->diReg, sizeof(struct dicommand), NULL, 0, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_LowOffset(u64 offset, dvdcallbacklow cb) { s32 ret; //u32 *off = (u32*)(void*)(&offset); union { u64 off64; u32 off32[2]; } off; off.off64 = offset; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_OFFSET << 24); cmd->diReg[1] = 0; if (off.off32[0]) cmd->diReg[1] = 1; cmd->diReg[2] = off.off32[1]; ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_OFFSET, cmd->diReg, sizeof(struct dicommand), __di_regvalcache, 0x20, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_LowPrepareCoverRegister(dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_COVER << 24); ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_COVER, cmd->diReg, sizeof(struct dicommand), __di_regbuffer, 0x20, __dvd_ioscoverregisterCB, ctx); return ret; } s32 bwDVD_LowOpenPartition(u32 offset, void *eticket, u32 certin_len, void *certificate_in, void *certificate_out, dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; if (eticket != NULL && ((u32) eticket % 32) != 0) return -1; if (certificate_in != NULL && ((u32) certificate_in % 32) != 0) return -1; if (certificate_out != NULL && ((u32) certificate_out % 32) != 0) return -1; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_OPENPART << 24); cmd->diReg[1] = offset; __di_iovector[0].data = cmd; __di_iovector[0].len = sizeof(struct dicommand); __di_iovector[1].data = eticket; if (eticket == NULL) __di_iovector[1].len = 0; else __di_iovector[1].len = 676; __di_iovector[2].data = certificate_in; if (certificate_in == NULL) __di_iovector[2].len = 0; else __di_iovector[2].len = certin_len; __di_iovector[3].data = certificate_out; __di_iovector[3].len = 18916; __di_iovector[4].data = __di_lastticketerror; __di_iovector[4].len = 0x20; ret = IOS_IoctlvAsync(__dvd_fd, IOCTL_DI_OPENPART, 3, 2, __di_iovector, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_LowClosePartition(dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_CLOSEPART << 24); ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_CLOSEPART, cmd->diReg, sizeof(struct dicommand), NULL, 0, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_LowUnencryptedRead(void *buf, u32 len, u32 offset, dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; __dvd_readlength = len; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_UNENCREAD << 24); cmd->diReg[1] = len; cmd->diReg[2] = offset; ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_UNENCREAD, cmd->diReg, sizeof(struct dicommand), buf, len, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_LowWaitCoverClose(dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_WAITCVRCLOSE << 24); ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_WAITCVRCLOSE, cmd->diReg, sizeof(struct dicommand), NULL, 0, __dvd_ioscovercloseCB, ctx); return ret; } s32 bwDVD_LowResetNotify() { s32 ret; struct dicontext *ctx; struct dicommand *cmd; if (__dvd_cbinprogress == 1) return -1; ctx = __dvd_getcontext(NULL); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_RESETNOTIFY << 24); ret = IOS_Ioctl(__dvd_fd, IOCTL_DI_RESETNOTIFY, cmd->diReg, sizeof(struct dicommand), NULL, 0); return ret; } s32 bwDVD_LowReset(dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; // printf("DVD_LowReset()\n"); __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_RESET << 24); cmd->diReg[1] = __dvd_spinupval; ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_RESET, cmd->diReg, sizeof(struct dicommand), NULL, 0, __dvd_iostransactionCB, ctx); // printf("DVD_LowReset(%d)\n",ret); return ret; } s32 bwDVD_LowStopMotor(u8 stop1, u8 stop2, dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_STOPMOTOR << 24); cmd->diReg[1] = (stop1 << 24); cmd->diReg[2] = (stop2 << 24); ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_STOPMOTOR, cmd->diReg, sizeof(struct dicommand), __di_regvalcache, 0x20, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_LowRequestError(dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_REQERROR << 24); ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_REQERROR, cmd->diReg, sizeof(struct dicommand), __di_regvalcache, 0x20, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_SetDecryption(s32 mode, dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_SETCRYPTMODE << 24); cmd->diReg[1] = mode; ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_SETCRYPTMODE, cmd->diReg, sizeof(struct dicommand), __di_regvalcache, 0x20, __dvd_iostransactionCB, ctx); return ret; } s32 bwDVD_SetOffset(u32 offset, dvdcallbacklow cb) { s32 ret; struct dicontext *ctx; struct dicommand *cmd; __dvd_reqinprogress = 1; ctx = __dvd_getcontext(cb); if (ctx == NULL) return IPC_ENOMEM; cmd = ctx->cmd; cmd->diReg[0] = (IOCTL_DI_SETOFFBASE << 24); cmd->diReg[1] = offset; ret = IOS_IoctlAsync(__dvd_fd, IOCTL_DI_SETOFFBASE, cmd->diReg, sizeof(struct dicommand), __di_regvalcache, 0x20, __dvd_iostransactionCB, ctx); return ret; }