diff --git a/src/dos/cdrom_aspi_win32.cpp b/src/dos/cdrom_aspi_win32.cpp new file mode 100644 index 00000000..cd63999b --- /dev/null +++ b/src/dos/cdrom_aspi_win32.cpp @@ -0,0 +1,733 @@ +/* + * Copyright (C) 2002-2003 The DOSBox Team + * + * 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 Library 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. + */ + +#if defined (WIN32) + +#include "cdrom.h" +#include "scsidefs.h" // Aspi stuff +#include "dosbox.h" + +// ***************************************************************** +// Windows ASPI functions (should work for all WIN with ASPI layer) +// ***************************************************************** + +CDROM_Interface_Aspi::CDROM_Interface_Aspi(void) +{ + hASPI = NULL; + hEvent = NULL; + pGetASPI32SupportInfo = NULL; + pSendASPI32Command = NULL; + memset(&oldLeadOut,0,sizeof(oldLeadOut)); +}; + +CDROM_Interface_Aspi::~CDROM_Interface_Aspi(void) +{ + // Stop Audio + StopAudio(); + + pGetASPI32SupportInfo = NULL; // clear funcs + pSendASPI32Command = NULL; + + if (hASPI) { // free aspi + FreeLibrary(hASPI); + hASPI=NULL; + } +}; + +bool GetRegistryValue(HKEY& hKey,char* valueName, char* buffer, ULONG bufferSize) +// hKey has to be open +{ + // Read subkey + ULONG valType; + ULONG result; + result = RegQueryValueEx(hKey,valueName,NULL,&valType,(unsigned char*)&buffer[0],&bufferSize); + return (result == ERROR_SUCCESS); +}; + +BYTE CDROM_Interface_Aspi::GetHostAdapter(char* hardwareID) +{ + SRB_HAInquiry sh; + SRB_GDEVBlock sd; + DWORD d = pGetASPI32SupportInfo(); + int cnt = LOBYTE(LOWORD(d)); + int i,j,k,max; + + for(i=0; i4)) { + if (GetDriveType(path)==DRIVE_CDROM) { + // WIN XP/NT/2000 + int iDA,iDT,iDL; + letter = path[0]; + HANDLE hF = OpenIOCTLFile(letter,FALSE); + GetIOCTLAdapter(hF,&iDA,&iDT,&iDL); + CloseHandle(hF); + // Set SCSI IDs + haId = iDA; + target = iDT; + lun = iDL; + return true; + } + } else { + // win 95/98/ME have to scan the registry... + // lets hope the layout is always the same... i dunno... + char key[2048]; + HKEY hKeyBase; + bool found = false; + strcpy(key,"ENUM\\SCSI"); + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,key,0,KEY_READ,&hKeyBase)==ERROR_SUCCESS) { + found = ScanRegistry(hKeyBase); + }; + RegCloseKey(hKeyBase); + return found; + } + return false; +}; + +bool CDROM_Interface_Aspi::GetAudioTracks(int& stTrack, int& endTrack, TMSF& leadOut) +{ + TOC toc; + if (GetTOC((LPTOC)&toc) == SS_COMP) { + stTrack = toc.cFirstTrack; + endTrack = toc.cLastTrack; + leadOut.min = (unsigned char)(toc.tracks[endTrack].lAddr >> 8) &0xFF; + leadOut.sec = (unsigned char)(toc.tracks[endTrack].lAddr >> 16) &0xFF; + leadOut.fr = (unsigned char)(toc.tracks[endTrack].lAddr >> 24) &0xFF; + return true; + } + return false; +}; + +bool CDROM_Interface_Aspi::GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr) +{ + TOC toc; + if (GetTOC((LPTOC)&toc) == SS_COMP) { + start.min = (unsigned char)(toc.tracks[track-1].lAddr >> 8) &0xFF; + start.sec = (unsigned char)(toc.tracks[track-1].lAddr >> 16) &0xFF; + start.fr = (unsigned char)(toc.tracks[track-1].lAddr >> 24) &0xFF; + attr = toc.tracks[track-1].cAdrCtrl; + return true; + }; + return false; +}; + +HANDLE CDROM_Interface_Aspi::OpenIOCTLFile(char cLetter,BOOL bAsync) +{ + HANDLE hF; + char szFName[16]; + OSVERSIONINFO ov; + DWORD dwFlags; + DWORD dwIOCTLAttr; +// if(bAsync) dwIOCTLAttr=FILE_FLAG_OVERLAPPED; +// else + dwIOCTLAttr=0; + + memset(&ov,0,sizeof(OSVERSIONINFO)); + ov.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); + GetVersionEx(&ov); + + if ((ov.dwPlatformId==VER_PLATFORM_WIN32_NT) && (ov.dwMajorVersion>4)) + dwFlags = GENERIC_READ|GENERIC_WRITE; // add gen write on W2k/XP + else + dwFlags = GENERIC_READ; + + wsprintf(szFName, "\\\\.\\%c:",cLetter); + + hF=CreateFile(szFName,dwFlags,FILE_SHARE_READ, // open drive + NULL,OPEN_EXISTING,dwIOCTLAttr,NULL); + + if (hF==INVALID_HANDLE_VALUE) { + dwFlags^=GENERIC_WRITE; // mmm... no success + hF=CreateFile(szFName,dwFlags,FILE_SHARE_READ, // -> open drive again + NULL,OPEN_EXISTING,dwIOCTLAttr,NULL); + if (hF==INVALID_HANDLE_VALUE) return NULL; + } + return hF; +} + +void CDROM_Interface_Aspi::GetIOCTLAdapter(HANDLE hF,int * iDA,int * iDT,int * iDL) +{ + char szBuf[1024]; + PSCSI_ADDRESS pSA; + DWORD dwRet; + + *iDA=*iDT=*iDL=-1; + if(hF==NULL) return; + + memset(szBuf,0,1024); + + pSA=(PSCSI_ADDRESS)szBuf; + pSA->Length=sizeof(SCSI_ADDRESS); + + if(!DeviceIoControl(hF,IOCTL_SCSI_GET_ADDRESS,NULL, + 0,pSA,sizeof(SCSI_ADDRESS), + &dwRet,NULL)) + return; + + *iDA = pSA->PortNumber; + *iDT = pSA->TargetId; + *iDL = pSA->Lun; +} + +DWORD CDROM_Interface_Aspi::GetTOC(LPTOC toc) +{ + SRB_ExecSCSICmd s;DWORD dwStatus; + + hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + memset(&s,0,sizeof(s)); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaId = haId; + s.SRB_Target = target; + s.SRB_Lun = lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = sizeof(*toc); + s.SRB_BufPointer = (BYTE FAR *)toc; + s.SRB_SenseLen = SENSE_LEN; + s.SRB_CDBLen = 0x0A; + s.SRB_PostProc = (LPVOID)hEvent; + s.CDBByte[0] = SCSI_READ_TOC; + s.CDBByte[1] = 0x02; // 0x02 for MSF + s.CDBByte[7] = 0x03; + s.CDBByte[8] = 0x24; + + ResetEvent(hEvent); + dwStatus=pSendASPI32Command((LPSRB)&s); + + if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,30000); + + CloseHandle(hEvent); + + return (s.SRB_Status==SS_COMP); +} + +bool CDROM_Interface_Aspi::PlayAudioSector(unsigned long start,unsigned long len) +{ + SRB_ExecSCSICmd s;DWORD dwStatus; + + hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + memset(&s,0,sizeof(s)); + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaId = haId; + s.SRB_Target = target; + s.SRB_Lun = lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = 0; + s.SRB_BufPointer = 0; + s.SRB_SenseLen = SENSE_LEN; + s.SRB_CDBLen = 12; + s.SRB_PostProc = (LPVOID)hEvent; + + s.CDBByte[0] = SCSI_PLAYAUD_12; + s.CDBByte[1] = lun << 5; + s.CDBByte[2] = (unsigned char)((start >> 24) & 0xFF); + s.CDBByte[3] = (unsigned char)((start >> 16) & 0xFF); + s.CDBByte[4] = (unsigned char)((start >> 8) & 0xFF); + s.CDBByte[5] = (unsigned char)((start & 0xFF)); + s.CDBByte[6] = (unsigned char)((len >> 24) & 0xFF); + s.CDBByte[7] = (unsigned char)((len >> 16) & 0xFF); + s.CDBByte[8] = (unsigned char)((len >> 8) & 0xFF); + s.CDBByte[9] = (unsigned char)(len & 0xFF); + + ResetEvent(hEvent); + + dwStatus = pSendASPI32Command((LPSRB)&s); + + if(dwStatus==SS_PENDING) WaitForSingleObject(hEvent,10000); + + CloseHandle(hEvent); + + return s.SRB_Status==SS_COMP; +} + +bool CDROM_Interface_Aspi::StopAudio(void) +{ + return PauseAudio(false); +}; + +bool CDROM_Interface_Aspi::PauseAudio(bool resume) +{ + SRB_ExecSCSICmd s;DWORD dwStatus; + + hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + memset(&s,0,sizeof(s)); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaId = haId; + s.SRB_Target = target; + s.SRB_Lun = lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_BufLen = 0x00; + s.SRB_SenseLen = SENSE_LEN; + s.SRB_CDBLen = 0x0A; + s.SRB_PostProc = (LPVOID)hEvent; + s.CDBByte[0] = 0x4B; + s.CDBByte[8] = (unsigned char)resume; // Pause + + ResetEvent(hEvent); + dwStatus=pSendASPI32Command((LPSRB)&s); + + if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,30000); + + CloseHandle(hEvent); + + return (s.SRB_Status==SS_COMP); +}; + +bool CDROM_Interface_Aspi::GetAudioSub(unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos) +{ + SUB_Q_CURRENT_POSITION pos; + SRB_ExecSCSICmd s;DWORD dwStatus; + + hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + memset(&s,0,sizeof(s)); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaId = haId; + s.SRB_Target = target; + s.SRB_Lun = lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_SenseLen = SENSE_LEN; + + s.SRB_BufLen = sizeof(pos); + s.SRB_BufPointer = (BYTE FAR *)&pos; + s.SRB_CDBLen = 10; + s.SRB_PostProc = (LPVOID)hEvent; + + s.CDBByte[0] = SCSI_SUBCHANNEL; + s.CDBByte[1] = (lun<<5)|2; // lun & msf + s.CDBByte[2] = 0x40; // subq + s.CDBByte[3] = 0x01; // curr pos info + s.CDBByte[6] = 0; // track number (only in isrc mode, ignored) + s.CDBByte[7] = 0; // alloc len + s.CDBByte[8] = sizeof(pos); + + ResetEvent(hEvent); + + dwStatus = pSendASPI32Command((LPSRB)&s); + + if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF); + + CloseHandle(hEvent); + + if (s.SRB_Status!=SS_COMP) return false; + + attr = (pos.ADR<<4) | pos.Control; + track = pos.TrackNumber; + index = pos.IndexNumber; + absPos.min = pos.AbsoluteAddress[1]; + absPos.sec = pos.AbsoluteAddress[2]; + absPos.fr = pos.AbsoluteAddress[3]; + relPos.min = pos.TrackRelativeAddress[1]; + relPos.sec = pos.TrackRelativeAddress[2]; + relPos.fr = pos.TrackRelativeAddress[3]; + + return true; +}; + +bool CDROM_Interface_Aspi::GetUPC(unsigned char& attr, char* upcdata) +{ + SUB_Q_MEDIA_CATALOG_NUMBER upc; + SRB_ExecSCSICmd s;DWORD dwStatus; + + hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + memset(&s,0,sizeof(s)); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaId = haId; + s.SRB_Target = target; + s.SRB_Lun = lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_SenseLen = SENSE_LEN; + + s.SRB_BufLen = sizeof(upc); + s.SRB_BufPointer = (BYTE FAR *)&upc; + s.SRB_CDBLen = 10; + s.SRB_PostProc = (LPVOID)hEvent; + + s.CDBByte[0] = SCSI_SUBCHANNEL; + s.CDBByte[1] = (lun<<5)|2; // lun & msf + s.CDBByte[2] = 0x40; // subq + s.CDBByte[3] = 0x02; // get upc + s.CDBByte[6] = 0; // track number (only in isrc mode, ignored) + s.CDBByte[7] = 0; // alloc len + s.CDBByte[8] = sizeof(upc); + + ResetEvent(hEvent); + + dwStatus = pSendASPI32Command((LPSRB)&s); + + if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF); + + CloseHandle(hEvent); + + if (s.SRB_Status!=SS_COMP) return false; + +// attr = (upc.ADR<<4) | upc.Control; + attr = 0; + int pos = 0; + // Convert to mscdex format +// for (int i=0; i<6; i++) upcdata[i] = (upc.MediaCatalog[pos++]<<4)+(upc.MediaCatalog[pos++]&0x0F); +// upcdata[6] = (upc.MediaCatalog[pos++]<<4); + for (int i=0; i<7; i++) upcdata[i] = upc.MediaCatalog[i]; + + return true; +}; + +bool CDROM_Interface_Aspi::GetAudioStatus(bool& playing, bool& pause) +{ + playing = pause = false; + + SUB_Q_HEADER sub; + SRB_ExecSCSICmd s;DWORD dwStatus; + + hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + memset(&s,0,sizeof(s)); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaId = haId; + s.SRB_Target = target; + s.SRB_Lun = lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_SenseLen = SENSE_LEN; + + s.SRB_BufLen = sizeof(sub); + s.SRB_BufPointer = (BYTE FAR *)⊂ + s.SRB_CDBLen = 10; + s.SRB_PostProc = (LPVOID)hEvent; + + s.CDBByte[0] = SCSI_SUBCHANNEL; + s.CDBByte[1] = (lun<<5)|2; // lun & msf + s.CDBByte[2] = 0x00; // no subq + s.CDBByte[3] = 0x00; // dont care + s.CDBByte[6] = 0; // track number (only in isrc mode, ignored) + s.CDBByte[7] = 0; // alloc len + s.CDBByte[8] = sizeof(sub); + + ResetEvent(hEvent); + + dwStatus = pSendASPI32Command((LPSRB)&s); + + if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF); + + CloseHandle(hEvent); + + if (s.SRB_Status!=SS_COMP) return false; + + playing = (sub.AudioStatus==0x11); + pause = (sub.AudioStatus==0x12); + + return true; +}; + +bool CDROM_Interface_Aspi::LoadUnloadMedia(bool unload) +{ + SRB_ExecSCSICmd s;DWORD dwStatus; + + hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + memset(&s,0,sizeof(s)); + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaId = haId; + s.SRB_Target = target; + s.SRB_Lun = lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_SenseLen = SENSE_LEN; + + s.SRB_BufLen = 0; + s.SRB_BufPointer = 0; + s.SRB_CDBLen = 14; + s.SRB_PostProc = (LPVOID)hEvent; + + s.CDBByte[0] = SCSI_LOAD_UN; + s.CDBByte[1] = (lun<<5)|1; // lun & immediate + s.CDBByte[4] = (unload ? 0x02:0x03); // unload/load media + + ResetEvent(hEvent); + + dwStatus = pSendASPI32Command((LPSRB)&s); + + if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF); + + CloseHandle(hEvent); + + if (s.SRB_Status!=SS_COMP) return false; + + return true; +}; + +bool CDROM_Interface_Aspi::GetMediaTrayStatus(bool& mediaPresent, bool& mediaChanged, bool& trayOpen) +{ + // Seems not possible to get this values using ioctl... + int track1,track2; + TMSF leadOut; + // If we can read, there's a media + mediaPresent = GetAudioTracks(track1, track2, leadOut), + trayOpen = !mediaPresent; + mediaChanged = (oldLeadOut.min!=leadOut.min) || (oldLeadOut.sec!=leadOut.sec) || (oldLeadOut.fr!=leadOut.fr); + // Save old values + oldLeadOut.min = leadOut.min; + oldLeadOut.sec = leadOut.sec; + oldLeadOut.fr = leadOut.fr; + // always success + return true; +}; + +bool CDROM_Interface_Aspi::ReadSectors(void* buffer, bool raw, unsigned long sector, unsigned long num) +{ + SRB_ExecSCSICmd s;DWORD dwStatus; + + hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + memset(&s,0,sizeof(s)); + + // FIXME : Is there a method to get cooked sectors with aspi ??? + // all combination i tried were failing. + // so we have to allocate extra mem and copy data to buffer if in cooked mode + char* inPtr = (char*)buffer; + if (!raw) inPtr = new char[num*2352]; + if (!inPtr) return false; + + s.SRB_Cmd = SC_EXEC_SCSI_CMD; + s.SRB_HaId = haId; + s.SRB_Target = target; + s.SRB_Lun = lun; + s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + s.SRB_SenseLen = SENSE_LEN; + + s.SRB_BufLen = 2352*num; //num*(raw?2352:2048); + s.SRB_BufPointer = (BYTE FAR*)inPtr; + s.SRB_CDBLen = 12; + s.SRB_PostProc = (LPVOID)hEvent; + + s.CDBByte[0] = 0xBE; + s.CDBByte[2] = (unsigned char)((sector >> 24) & 0xFF); + s.CDBByte[3] = (unsigned char)((sector >> 16) & 0xFF); + s.CDBByte[4] = (unsigned char)((sector >> 8) & 0xFF); + s.CDBByte[5] = (unsigned char)((sector & 0xFF)); + s.CDBByte[6] = (unsigned char)((num >> 16) & 0xFF); + s.CDBByte[7] = (unsigned char)((num >> 8) & 0xFF); + s.CDBByte[8] = (unsigned char) (num & 0xFF); + s.CDBByte[9] = (raw?0xF0:0x10); + + ResetEvent(hEvent); + + dwStatus = pSendASPI32Command((LPSRB)&s); + + if (dwStatus==SS_PENDING) WaitForSingleObject(hEvent,0xFFFFFFFF); + + CloseHandle(hEvent); + + if (s.SRB_Status!=SS_COMP) { + if (!raw) delete[] inPtr; + return false; + } + + if (!raw) { + // copy user data to buffer + char* source = inPtr; + source+=16; // jump 16 bytes + char* outPtr = (char*)buffer; + for (unsigned long i=0; i