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.
11 // *** OS dependent CDROM stuffola ***
15 // *** End OS dependent ***
22 static uint8 maxHostAdapters = 0, numCDDrives = 0, driveNum;
23 static uint8 haID[8], tID[8];
24 static uint8 driveName[8][26];
25 static uint8 tracks[100][3]; // One-based index
26 static uint32 numTracks = 0;
27 static uint8 sessions[40][5]; // Zero-based index
28 static uint32 numSessions = 0;
29 static bool readTOC = false;
31 static HINSTANCE hASPILib = NULL; // Handle to ASPI for Win32 (WNASPI.DLL)
32 static uint32 (* ASPI_GetASPI32SupportInfo)(void); // WNASPI.DLL function pointers
33 static uint32 (* ASPI_SendASPI32Command)(LPSRB);
35 // Private function prototypes
37 static bool InitASPI(void);
38 static bool SendAsyncASPICmd(uint8, uint8, uint8 *, uint8, uint8 *, uint32, uint8);
39 /*static*/ bool GetRawTOC(void);
43 // Initialize the Win32 ASPI layer
45 static bool InitASPI(void)
47 hASPILib = LoadLibrary("WNASPI32");
51 WriteLog("CDINTF: Could not load WNASPI32.DLL!\n");
55 ASPI_GetASPI32SupportInfo = (uint32 (*)(void))GetProcAddress(hASPILib, "GetASPI32SupportInfo");
56 ASPI_SendASPI32Command = (uint32 (*)(LPSRB))GetProcAddress(hASPILib, "SendASPI32Command");
58 if (!ASPI_GetASPI32SupportInfo || !ASPI_SendASPI32Command)
60 WriteLog("CDINTF: Could not import functions from WNASPI32.DLL!\n");
64 uint32 supportInfo = ASPI_GetASPI32SupportInfo(); // Initialize ASPI layer
65 uint8 retCode = (supportInfo >> 8) & 0xFF;
66 maxHostAdapters = supportInfo & 0xFF;
68 if (retCode != SS_COMP && retCode != SS_NO_ADAPTERS)
70 WriteLog("CDINTF: Could not initialise using GetASPI32SupportInfo function!\n");
74 if (retCode == SS_NO_ADAPTERS)
76 WriteLog("CDINTF: ASPI initialized, but no host adapters were found!\n");
80 /* // Set timeouts for ALL devices to 15 seconds. Nothing we deal with should
81 // take that long to do ANYTHING. We are just doing inquiries to most
82 // devices, and then simple reads to CDs, disks, etc. so 10 seconds (even
83 // if they have to spin up) should be plenty.
85 SRB_GetSetTimeouts srbTimeouts;
86 //This doesn't seem to do anything, and isn't even mentioned in Adaptec's ASPI paper...
87 //(It *is* mentioned elsewhere, in other Adaptec documentation, and it does nothing because it
89 //It *does* return code $81 (SS_INVALID_HA) which means it doesn't like $FF for the HAID...
90 //OK, it works with Adaptec's driver, but not the default MS driver...
91 //Looks like we really don't need it anyway.
92 //If we really wanted to, we could do it in CDIntfInit()...!
93 memset(&srbTimeouts, 0, sizeof(SRB_GetSetTimeouts));
94 srbTimeouts.SRB_Cmd = SC_GETSET_TIMEOUTS;
95 srbTimeouts.SRB_Flags = SRB_DIR_OUT;
96 srbTimeouts.SRB_HaId = 0xFF;
97 srbTimeouts.SRB_Target = 0xFF;
98 srbTimeouts.SRB_Lun = 0xFF;
99 srbTimeouts.SRB_Timeout = 15 * 2;
100 ASPI_SendASPI32Command(&srbTimeouts);
101 WriteLog("CDINTF: Set Timeout command returned %02X...\n", srbTimeouts.SRB_Status);//*/
103 WriteLog("CDINTF: Successfully initialized.\n");
108 // Sends the passed in Command Description Block to the APSI layer for processing.
109 // Since this uses the asynchronous EXEC_SCSI_CMD, we also wait for completion by
110 // using a semaphore.
112 static bool SendAsyncASPICmd(uint8 hostID, uint8 targID, uint8 * cdb, uint8 CDBLen,
113 uint8 * buffer, uint32 bufferLen, uint8 SRBFlags)
115 SRB_ExecSCSICmd SRB; // The SRB variable with CDB included
117 memset(&SRB, 0, sizeof(SRB));
118 memcpy(SRB.CDBByte, cdb, CDBLen); // Copy CDB into SRB's CDB
120 SRB.SRB_Cmd = SC_EXEC_SCSI_CMD;
121 SRB.SRB_Flags = SRBFlags | SRB_EVENT_NOTIFY;
122 SRB.SRB_HaId = hostID;
123 SRB.SRB_Target = targID;
124 SRB.SRB_BufPointer = buffer;
125 SRB.SRB_BufLen = bufferLen;
126 SRB.SRB_CDBLen = CDBLen;
127 SRB.SRB_SenseLen = SENSE_LEN;
129 HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
133 WriteLog("CDINTF: Couldn't create event!\n");
137 SRB.SRB_PostProc = (void *)hEvent;
138 ASPI_SendASPI32Command(&SRB);
140 if (SRB.SRB_Status == SS_PENDING)
141 WaitForSingleObject(hEvent, INFINITE);
145 if (SRB.SRB_Status != SS_COMP)
147 WriteLog("CDINTF: SCSI command %02X failed [Error: %02X].\n", SRB.CDBByte[0], SRB.SRB_Status);
155 // OS specific implementation of OS agnostic functions
158 bool CDIntfInit(void)
162 WriteLog("CDINTF: Failed to init Win32 ASPI layer!\n");
166 SRB_HAInquiry srbHAInquiry;
167 SRB_GDEVBlock srbGDEVBlock;
168 uint8 inquiryCDB[6], inquiryBuf[36];
170 for(uint8 HAID=0; HAID<maxHostAdapters; HAID++)
172 memset(&srbHAInquiry, 0, sizeof(srbHAInquiry));
173 srbHAInquiry.SRB_Cmd = SC_HA_INQUIRY;
174 srbHAInquiry.SRB_HaId = HAID;
176 ASPI_SendASPI32Command(&srbHAInquiry);
178 if (srbHAInquiry.SRB_Status != SS_COMP)
181 // Do a host adapter inquiry to get max target count. If the
182 // target count isn't 8 or 16 then go with a default of 8.
184 uint8 maxTargets = srbHAInquiry.HA_Unique[3];
185 if (maxTargets != 8 && maxTargets != 16)
188 // Loop over all the targets on this host adapter.
190 for(uint8 target=0; target<maxTargets; target++)
192 // Issue get device type call to see if there is a device we're
193 // interested in at this address. We're interested in CDROMs.
195 memset(&srbGDEVBlock, 0, sizeof(srbGDEVBlock));
196 srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
197 srbGDEVBlock.SRB_HaId = HAID;
198 srbGDEVBlock.SRB_Target = target;
200 ASPI_SendASPI32Command(&srbGDEVBlock);
202 if (srbGDEVBlock.SRB_Status != SS_COMP || srbGDEVBlock.SRB_DeviceType != DTYPE_CDROM)
205 memset(inquiryCDB, 0, 6); // Issue an INQUIRY.
206 inquiryCDB[0] = SCSI_INQUIRY;
207 inquiryCDB[4] = 36; // Size in bytes of inquiry buffer.
209 bool successful = SendAsyncASPICmd(HAID, target, inquiryCDB, 6, inquiryBuf, 36, SRB_DIR_IN);
211 // Make sure the inquiry worked. Check if it failed, or if the inquiry data
212 // returns a different device type than we got before (guards against certain
213 // device drivers and against vendor unique devices).
215 if (!successful || inquiryBuf[0] != DTYPE_CDROM)
218 haID[numCDDrives] = HAID, tID[numCDDrives] = target;
220 // Here we do a 'stringTrimRight' on the vendor and product strings...
222 uint32 vendorSize = 0, productSize = 0;
224 for(int i=7; i>=0; i--)
226 if (inquiryBuf[8+i] != ' ')
233 for(int i=15; i>=0; i--)
235 if (inquiryBuf[16+i] != ' ')
242 memcpy(driveName[numCDDrives], inquiryBuf + 8, vendorSize);
243 driveName[numCDDrives][vendorSize] = ' ';
244 memcpy(driveName[numCDDrives] + vendorSize + 1, inquiryBuf + 16, productSize);
245 driveName[numCDDrives][vendorSize + productSize + 1] = 0;
247 WriteLog("CDINTF: Found CD-ROM device [%s]. HAID:%u, TID:%u LUN:0\n", driveName[numCDDrives], haID[numCDDrives], tID[numCDDrives]);
253 if (numCDDrives == 0)
255 WriteLog("CDINTF: No CDROM type drives found.\n");
259 //Most likely, will need to read a default out of the config file. But for now... !!! FIX !!!
260 driveNum = 0; // For now, default to first drive found
265 void CDIntfDone(void)
268 FreeLibrary(hASPILib); // Unload ASPI library if it was loaded.
271 bool CDIntfReadBlock(uint32 sector, uint8 * buffer)
275 memset(cdb, 0, sizeof(cdb));
276 // 0: command, 2-5: block # (hi->lo) 6-8: number of blocks to read, 9: read type,
277 // 10: subchannel select
278 cdb[0] = 0xBE; // Code for ReadCD CDB12 command
279 cdb[2] = (sector >> 24) & 0xFF;
280 cdb[3] = (sector >> 16) & 0xFF;
281 cdb[4] = (sector >> 8) & 0xFF;
282 cdb[5] = sector & 0xFF;
283 cdb[8] = 1; // No. of sectors to read from CD (LSB)
284 cdb[9] = 0xF8; // Raw read, 2352 bytes per sector
285 cdb[10] = 1; // Selects read RAW 96 bytes/sector sub-channel data (Raw P-W)
287 return SendAsyncASPICmd(haID[driveNum], tID[driveNum], cdb, 12, buffer, 2352+96, SRB_DIR_IN);
290 uint32 CDIntfGetNumSessions(void)
292 // WriteLog("CDINTF: GetNumSessions unimplemented!\n");
293 // Still need relevant code here... !!! FIX !!! [DONE]
297 return numSessions - 1;
300 void CDIntfSelectDrive(uint32 driveNum)
302 if (driveNum < numCDDrives)
306 uint32 CDIntfGetCurrentDrive(void)
311 const uint8 * CDIntfGetDriveName(uint32 driveNum)
313 if (driveNum > numCDDrives)
316 return driveName[driveNum];
319 //This stuff could probably be OK in the unified cdintf.cpp file...
320 uint8 CDIntfGetSessionInfo(uint32 session, uint32 offset)
322 // Need better error handling than this... !!! FIX !!!
327 if (session >= numSessions || offset > 4)
328 return 0xFF; // Bad index passed in...
330 return sessions[session][offset];
333 uint8 CDIntfGetTrackInfo(uint32 track, uint32 offset)
335 // Need better error handling than this... !!! FIX !!!
340 if (track > numTracks || offset > 2)
341 return 0xFF; // Bad index passed in...
343 return tracks[track][offset];
346 //OK, now the rest is OK, but this is still locking up like a MF!
347 // Testing, testing...
348 //Still don't know why this is locking up! Especially as the following function works! Aarrrgggghhhhh!
349 //It was the dataLen. For some reason, it needs at *least* 11 more bytes (could be less!)
350 /*static*/ bool GetRawTOC(void)
355 // Read disk TOC length
357 cmd[0] = SCSI_READ_TOC;
358 cmd[2] = 2; // Get session info also
359 cmd[6] = 1; // Session # to start reading
360 cmd[8] = 4; // Buffer length
362 if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, reqData, 4, SRB_DIR_IN))
364 WriteLog("TOC: Cannot read disk TOC length.\n");
368 // Header is total TOC space needed + header (0-1), min session (2), max session (3)
369 // uint32 dataLen = ((reqData[0] << 8) | reqData[1]);
370 uint32 dataLen = ((reqData[0] << 8) | reqData[1]) + 11; // Why the extra???
372 WriteLog("TOC: Raw TOC data len: %d\n", dataLen);
374 uint8 * data = new uint8[dataLen];
377 cmd[7] = dataLen >> 8;
380 if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, data, dataLen, SRB_DIR_IN))
383 WriteLog("TOC: Cannot read disk TOC.\n");
387 int numEntries = (((data[0] << 8) | data[1]) - 2) / 11;
388 uint8 * p = data + 4;
390 numSessions = data[3], numTracks = 0;
391 // Important entries are 0, 3, 8, 9, 10 (session #, track #, M, S, F)
392 // WriteLog("TOC: [Sess] [adrCtl] [?] [point] [?] [?] [?] [?] [pmin] [psec] [pframe]\n");
393 uint32 firstTrackOffset = 0;
394 for(int i=0; i<numEntries; i++, p+=11)
396 /* WriteLog("TOC: %d %02x %02d %2x %02d:%02d:%02d %02d %02d:%02d:%02d",
397 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10]);
399 if (p[3] > 0 && p[3] < 99)
400 WriteLog(" <-- Track #%u", p[3]);
404 // We do session # - 1 to make it zero-based, since this is what the Jaguar
405 // CD BIOS expects. We leave the tracks one-based.
407 uint32 s = p[0] - 1, t = p[3];
412 firstTrackOffset = (((p[8] * 60) + p[9]) * 75) + p[10];
414 tracks[t][0] = p[8], tracks[t][1] = p[9], tracks[t][2] = p[10];
416 // For some reason, the TOC returned from the "session TOC" command
417 // causes all tracks to have a 150 block (from what I've seen) offset
418 // from what's reported. Apparently it's not possible to read those
419 // first 150 blocks using the current incarnation of CDIntf_ReadBlock.
420 // So we subtract the offset out here...
422 uint32 curTrack = (((tracks[t][0] * 60) + tracks[t][1]) * 75) + tracks[t][2];
423 curTrack -= firstTrackOffset;
424 tracks[t][2] = curTrack % 75;
426 tracks[t][1] = curTrack % 60;
427 tracks[t][0] = curTrack / 60;
433 sessions[s][0] = p[8];
435 sessions[s][1] = p[8];
437 sessions[s][2] = p[8], sessions[s][3] = p[9], sessions[s][4] = p[10];
440 WriteLog("CDINTF: Disc summary\n # of sessions: %u, # of tracks: %u\n", numSessions, numTracks);
441 WriteLog(" Session info:\n");
442 for(uint32 i=0; i<numSessions; i++)
443 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]);
444 WriteLog(" Track info:\n");
445 for(uint32 i=1; i<=numTracks; i++)
446 WriteLog(" %2u: start=%2d:%02d:%02d\n", i, tracks[i][0], tracks[i][1], tracks[i][2]);