2 // OS specific CDROM interface (Win32)
6 // Currently, we use the ASPI layer for Win32, but this may or may not
7 // work on NT based Windows. If necessary, we'll put in the required
8 // NT based code, but for now, it's ASPI or nothing.
13 // - Convert this shit to platform independent libcdio
16 // *** OS dependent CDROM stuffola ***
20 // *** End OS dependent ***
27 static uint8 maxHostAdapters = 0, numCDDrives = 0, driveNum;
28 static uint8 haID[8], tID[8];
29 static uint8 driveName[8][26];
30 static uint8 tracks[100][3]; // One-based index
31 static uint32 numTracks = 0;
32 static uint8 sessions[40][5]; // Zero-based index
33 static uint32 numSessions = 0;
34 static bool readTOC = false;
36 static HINSTANCE hASPILib = NULL; // Handle to ASPI for Win32 (WNASPI.DLL)
37 static uint32 (* ASPI_GetASPI32SupportInfo)(void); // WNASPI.DLL function pointers
38 static uint32 (* ASPI_SendASPI32Command)(LPSRB);
40 // Private function prototypes
42 static bool InitASPI(void);
43 static bool SendAsyncASPICmd(uint8, uint8, uint8 *, uint8, uint8 *, uint32, uint8);
44 /*static*/ bool GetRawTOC(void);
48 // Initialize the Win32 ASPI layer
50 static bool InitASPI(void)
52 hASPILib = LoadLibrary("WNASPI32");
56 WriteLog("CDINTF: Could not load WNASPI32.DLL!\n");
60 ASPI_GetASPI32SupportInfo = (uint32 (*)(void))GetProcAddress(hASPILib, "GetASPI32SupportInfo");
61 ASPI_SendASPI32Command = (uint32 (*)(LPSRB))GetProcAddress(hASPILib, "SendASPI32Command");
63 if (!ASPI_GetASPI32SupportInfo || !ASPI_SendASPI32Command)
65 WriteLog("CDINTF: Could not import functions from WNASPI32.DLL!\n");
69 uint32 supportInfo = ASPI_GetASPI32SupportInfo(); // Initialize ASPI layer
70 uint8 retCode = (supportInfo >> 8) & 0xFF;
71 maxHostAdapters = supportInfo & 0xFF;
73 if (retCode != SS_COMP && retCode != SS_NO_ADAPTERS)
75 WriteLog("CDINTF: Could not initialise using GetASPI32SupportInfo function!\n");
79 if (retCode == SS_NO_ADAPTERS)
81 WriteLog("CDINTF: ASPI initialized, but no host adapters were found!\n");
85 /* // Set timeouts for ALL devices to 15 seconds. Nothing we deal with should
86 // take that long to do ANYTHING. We are just doing inquiries to most
87 // devices, and then simple reads to CDs, disks, etc. so 10 seconds (even
88 // if they have to spin up) should be plenty.
90 SRB_GetSetTimeouts srbTimeouts;
91 //This doesn't seem to do anything, and isn't even mentioned in Adaptec's ASPI paper...
92 //(It *is* mentioned elsewhere, in other Adaptec documentation, and it does nothing because it
94 //It *does* return code $81 (SS_INVALID_HA) which means it doesn't like $FF for the HAID...
95 //OK, it works with Adaptec's driver, but not the default MS driver...
96 //Looks like we really don't need it anyway.
97 //If we really wanted to, we could do it in CDIntfInit()...!
98 memset(&srbTimeouts, 0, sizeof(SRB_GetSetTimeouts));
99 srbTimeouts.SRB_Cmd = SC_GETSET_TIMEOUTS;
100 srbTimeouts.SRB_Flags = SRB_DIR_OUT;
101 srbTimeouts.SRB_HaId = 0xFF;
102 srbTimeouts.SRB_Target = 0xFF;
103 srbTimeouts.SRB_Lun = 0xFF;
104 srbTimeouts.SRB_Timeout = 15 * 2;
105 ASPI_SendASPI32Command(&srbTimeouts);
106 WriteLog("CDINTF: Set Timeout command returned %02X...\n", srbTimeouts.SRB_Status);//*/
108 WriteLog("CDINTF: Successfully initialized.\n");
113 // Sends the passed in Command Description Block to the APSI layer for processing.
114 // Since this uses the asynchronous EXEC_SCSI_CMD, we also wait for completion by
115 // using a semaphore.
117 static bool SendAsyncASPICmd(uint8 hostID, uint8 targID, uint8 * cdb, uint8 CDBLen,
118 uint8 * buffer, uint32 bufferLen, uint8 SRBFlags)
120 SRB_ExecSCSICmd SRB; // The SRB variable with CDB included
122 memset(&SRB, 0, sizeof(SRB));
123 memcpy(SRB.CDBByte, cdb, CDBLen); // Copy CDB into SRB's CDB
125 SRB.SRB_Cmd = SC_EXEC_SCSI_CMD;
126 SRB.SRB_Flags = SRBFlags | SRB_EVENT_NOTIFY;
127 SRB.SRB_HaId = hostID;
128 SRB.SRB_Target = targID;
129 SRB.SRB_BufPointer = buffer;
130 SRB.SRB_BufLen = bufferLen;
131 SRB.SRB_CDBLen = CDBLen;
132 SRB.SRB_SenseLen = SENSE_LEN;
134 HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
138 WriteLog("CDINTF: Couldn't create event!\n");
142 SRB.SRB_PostProc = (void *)hEvent;
143 ASPI_SendASPI32Command(&SRB);
145 if (SRB.SRB_Status == SS_PENDING)
146 WaitForSingleObject(hEvent, INFINITE);
150 if (SRB.SRB_Status != SS_COMP)
152 WriteLog("CDINTF: SCSI command %02X failed [Error: %02X].\n", SRB.CDBByte[0], SRB.SRB_Status);
160 // OS specific implementation of OS agnostic functions
163 bool CDIntfInit(void)
167 WriteLog("CDINTF: Failed to init Win32 ASPI layer!\n");
171 SRB_HAInquiry srbHAInquiry;
172 SRB_GDEVBlock srbGDEVBlock;
173 uint8 inquiryCDB[6], inquiryBuf[36];
175 for(uint8 HAID=0; HAID<maxHostAdapters; HAID++)
177 memset(&srbHAInquiry, 0, sizeof(srbHAInquiry));
178 srbHAInquiry.SRB_Cmd = SC_HA_INQUIRY;
179 srbHAInquiry.SRB_HaId = HAID;
181 ASPI_SendASPI32Command(&srbHAInquiry);
183 if (srbHAInquiry.SRB_Status != SS_COMP)
186 // Do a host adapter inquiry to get max target count. If the
187 // target count isn't 8 or 16 then go with a default of 8.
189 uint8 maxTargets = srbHAInquiry.HA_Unique[3];
190 if (maxTargets != 8 && maxTargets != 16)
193 // Loop over all the targets on this host adapter.
195 for(uint8 target=0; target<maxTargets; target++)
197 // Issue get device type call to see if there is a device we're
198 // interested in at this address. We're interested in CDROMs.
200 memset(&srbGDEVBlock, 0, sizeof(srbGDEVBlock));
201 srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
202 srbGDEVBlock.SRB_HaId = HAID;
203 srbGDEVBlock.SRB_Target = target;
205 ASPI_SendASPI32Command(&srbGDEVBlock);
207 if (srbGDEVBlock.SRB_Status != SS_COMP || srbGDEVBlock.SRB_DeviceType != DTYPE_CDROM)
210 memset(inquiryCDB, 0, 6); // Issue an INQUIRY.
211 inquiryCDB[0] = SCSI_INQUIRY;
212 inquiryCDB[4] = 36; // Size in bytes of inquiry buffer.
214 bool successful = SendAsyncASPICmd(HAID, target, inquiryCDB, 6, inquiryBuf, 36, SRB_DIR_IN);
216 // Make sure the inquiry worked. Check if it failed, or if the inquiry data
217 // returns a different device type than we got before (guards against certain
218 // device drivers and against vendor unique devices).
220 if (!successful || inquiryBuf[0] != DTYPE_CDROM)
223 haID[numCDDrives] = HAID, tID[numCDDrives] = target;
225 // Here we do a 'stringTrimRight' on the vendor and product strings...
227 uint32 vendorSize = 0, productSize = 0;
229 for(int i=7; i>=0; i--)
231 if (inquiryBuf[8+i] != ' ')
238 for(int i=15; i>=0; i--)
240 if (inquiryBuf[16+i] != ' ')
247 memcpy(driveName[numCDDrives], inquiryBuf + 8, vendorSize);
248 driveName[numCDDrives][vendorSize] = ' ';
249 memcpy(driveName[numCDDrives] + vendorSize + 1, inquiryBuf + 16, productSize);
250 driveName[numCDDrives][vendorSize + productSize + 1] = 0;
252 WriteLog("CDINTF: Found CD-ROM device [%s]. HAID:%u, TID:%u LUN:0\n", driveName[numCDDrives], haID[numCDDrives], tID[numCDDrives]);
258 if (numCDDrives == 0)
260 WriteLog("CDINTF: No CDROM type drives found.\n");
264 //Most likely, will need to read a default out of the config file. But for now... !!! FIX !!!
265 driveNum = 0; // For now, default to first drive found
270 void CDIntfDone(void)
273 FreeLibrary(hASPILib); // Unload ASPI library if it was loaded.
276 bool CDIntfReadBlock(uint32 sector, uint8 * buffer)
280 memset(cdb, 0, sizeof(cdb));
281 // 0: command, 2-5: block # (hi->lo) 6-8: number of blocks to read, 9: read type,
282 // 10: subchannel select
283 cdb[0] = 0xBE; // Code for ReadCD CDB12 command
284 cdb[2] = (sector >> 24) & 0xFF;
285 cdb[3] = (sector >> 16) & 0xFF;
286 cdb[4] = (sector >> 8) & 0xFF;
287 cdb[5] = sector & 0xFF;
288 cdb[8] = 1; // No. of sectors to read from CD (LSB)
289 cdb[9] = 0xF8; // Raw read, 2352 bytes per sector
290 cdb[10] = 1; // Selects read RAW 96 bytes/sector sub-channel data (Raw P-W)
292 return SendAsyncASPICmd(haID[driveNum], tID[driveNum], cdb, 12, buffer, 2352+96, SRB_DIR_IN);
295 uint32 CDIntfGetNumSessions(void)
297 // WriteLog("CDINTF: GetNumSessions unimplemented!\n");
298 // Still need relevant code here... !!! FIX !!! [DONE]
302 return numSessions - 1;
305 void CDIntfSelectDrive(uint32 driveNum)
307 if (driveNum < numCDDrives)
311 uint32 CDIntfGetCurrentDrive(void)
316 const uint8 * CDIntfGetDriveName(uint32 driveNum)
318 if (driveNum > numCDDrives)
321 return driveName[driveNum];
324 //This stuff could probably be OK in the unified cdintf.cpp file...
325 uint8 CDIntfGetSessionInfo(uint32 session, uint32 offset)
327 // Need better error handling than this... !!! FIX !!!
332 if (session >= numSessions || offset > 4)
333 return 0xFF; // Bad index passed in...
335 return sessions[session][offset];
338 uint8 CDIntfGetTrackInfo(uint32 track, uint32 offset)
340 // Need better error handling than this... !!! FIX !!!
345 if (track > numTracks || offset > 2)
346 return 0xFF; // Bad index passed in...
348 return tracks[track][offset];
351 //OK, now the rest is OK, but this is still locking up like a MF!
352 // Testing, testing...
353 //Still don't know why this is locking up! Especially as the following function works! Aarrrgggghhhhh!
354 //It was the dataLen. For some reason, it needs at *least* 11 more bytes (could be less!)
355 /*static*/ bool GetRawTOC(void)
360 // Read disk TOC length
362 cmd[0] = SCSI_READ_TOC;
363 cmd[2] = 2; // Get session info also
364 cmd[6] = 1; // Session # to start reading
365 cmd[8] = 4; // Buffer length
367 if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, reqData, 4, SRB_DIR_IN))
369 WriteLog("TOC: Cannot read disk TOC length.\n");
373 // Header is total TOC space needed + header (0-1), min session (2), max session (3)
374 // uint32 dataLen = ((reqData[0] << 8) | reqData[1]);
375 uint32 dataLen = ((reqData[0] << 8) | reqData[1]) + 11; // Why the extra???
377 WriteLog("TOC: Raw TOC data len: %d\n", dataLen);
379 uint8 * data = new uint8[dataLen];
382 cmd[7] = dataLen >> 8;
385 if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, data, dataLen, SRB_DIR_IN))
388 WriteLog("TOC: Cannot read disk TOC.\n");
392 int numEntries = (((data[0] << 8) | data[1]) - 2) / 11;
393 uint8 * p = data + 4;
395 numSessions = data[3], numTracks = 0;
396 // Important entries are 0, 3, 8, 9, 10 (session #, track #, M, S, F)
397 // WriteLog("TOC: [Sess] [adrCtl] [?] [point] [?] [?] [?] [?] [pmin] [psec] [pframe]\n");
398 uint32 firstTrackOffset = 0;
399 for(int i=0; i<numEntries; i++, p+=11)
401 /* WriteLog("TOC: %d %02x %02d %2x %02d:%02d:%02d %02d %02d:%02d:%02d",
402 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10]);
404 if (p[3] > 0 && p[3] < 99)
405 WriteLog(" <-- Track #%u", p[3]);
409 // We do session # - 1 to make it zero-based, since this is what the Jaguar
410 // CD BIOS expects. We leave the tracks one-based.
412 uint32 s = p[0] - 1, t = p[3];
417 firstTrackOffset = (((p[8] * 60) + p[9]) * 75) + p[10];
419 tracks[t][0] = p[8], tracks[t][1] = p[9], tracks[t][2] = p[10];
421 // For some reason, the TOC returned from the "session TOC" command
422 // causes all tracks to have a 150 block (from what I've seen) offset
423 // from what's reported. Apparently it's not possible to read those
424 // first 150 blocks using the current incarnation of CDIntf_ReadBlock.
425 // So we subtract the offset out here...
427 uint32 curTrack = (((tracks[t][0] * 60) + tracks[t][1]) * 75) + tracks[t][2];
428 curTrack -= firstTrackOffset;
429 tracks[t][2] = curTrack % 75;
431 tracks[t][1] = curTrack % 60;
432 tracks[t][0] = curTrack / 60;
438 sessions[s][0] = p[8];
440 sessions[s][1] = p[8];
442 sessions[s][2] = p[8], sessions[s][3] = p[9], sessions[s][4] = p[10];
445 WriteLog("CDINTF: Disc summary\n # of sessions: %u, # of tracks: %u\n", numSessions, numTracks);
446 WriteLog(" Session info:\n");
447 for(uint32 i=0; i<numSessions; i++)
448 WriteLog(" %u: min track=%2u, max track=%2u, lead out=%2d:%02d:%02d\n", i+1, sessions[i][0], sessions[i][1], sessions[i][2], sessions[i][3], sessions[i][4]);
449 WriteLog(" Track info:\n");
450 for(uint32 i=1; i<=numTracks; i++)
451 WriteLog(" %2u: start=%2d:%02d:%02d\n", i, tracks[i][0], tracks[i][1], tracks[i][2]);