]> Shamusworld >> Repos - virtualjaguar/blob - src/cdintf_win32.cpp
Added function implementation ;-)
[virtualjaguar] / src / cdintf_win32.cpp
1 //
2 // OS specific CDROM interface (Win32)
3 //
4 // by James L. Hammons
5 //
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.
9 //
10
11 // *** OS dependent CDROM stuffola ***
12 #include <windows.h>
13 #include "wnaspi32.h"
14 #include "scsidefs.h"
15 // *** End OS dependent ***
16 #include "log.h"
17
18 using namespace std;
19
20 // Local variables
21
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;
30
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);
34
35 // Private function prototypes
36
37 static bool InitASPI(void);
38 static bool SendAsyncASPICmd(uint8, uint8, uint8 *, uint8, uint8 *, uint32, uint8);
39 /*static*/ bool GetRawTOC(void);
40
41
42 //
43 // Initialize the Win32 ASPI layer
44 //
45 static bool InitASPI(void)
46 {
47         hASPILib = LoadLibrary("WNASPI32");
48
49         if (!hASPILib)
50         {
51                 WriteLog("CDINTF: Could not load WNASPI32.DLL!\n");
52                 return false;
53         }
54
55         ASPI_GetASPI32SupportInfo = (uint32 (*)(void))GetProcAddress(hASPILib, "GetASPI32SupportInfo");
56         ASPI_SendASPI32Command = (uint32 (*)(LPSRB))GetProcAddress(hASPILib, "SendASPI32Command");
57
58         if (!ASPI_GetASPI32SupportInfo || !ASPI_SendASPI32Command)
59         {
60                 WriteLog("CDINTF: Could not import functions from WNASPI32.DLL!\n");
61                 return false;
62         }
63
64         uint32 supportInfo = ASPI_GetASPI32SupportInfo();       // Initialize ASPI layer
65         uint8 retCode = (supportInfo >> 8) & 0xFF;
66         maxHostAdapters = supportInfo & 0xFF;
67
68         if (retCode != SS_COMP && retCode != SS_NO_ADAPTERS)
69         {
70                 WriteLog("CDINTF: Could not initialise using GetASPI32SupportInfo function!\n");
71                 return false;
72         }
73
74         if (retCode == SS_NO_ADAPTERS)
75         {
76                 WriteLog("CDINTF: ASPI initialized, but no host adapters were found!\n");
77                 return false;
78         }
79
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. 
84
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
88 // errors out!)
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);//*/
102
103         WriteLog("CDINTF: Successfully initialized.\n");
104         return true;
105 }
106
107 //
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.
111 //
112 static bool SendAsyncASPICmd(uint8 hostID, uint8 targID, uint8 * cdb, uint8 CDBLen,
113         uint8 * buffer, uint32 bufferLen, uint8 SRBFlags)
114 {
115         SRB_ExecSCSICmd SRB;                                                    // The SRB variable with CDB included
116
117         memset(&SRB, 0, sizeof(SRB));
118         memcpy(SRB.CDBByte, cdb, CDBLen);                               // Copy CDB into SRB's CDB
119
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;
128
129         HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
130
131         if (!hEvent)
132         {
133                 WriteLog("CDINTF: Couldn't create event!\n");
134                 return false;
135         }
136
137         SRB.SRB_PostProc = (void *)hEvent;
138         ASPI_SendASPI32Command(&SRB);
139
140         if (SRB.SRB_Status == SS_PENDING)
141                 WaitForSingleObject(hEvent, INFINITE);
142
143         CloseHandle(hEvent);
144
145         if (SRB.SRB_Status != SS_COMP)
146         {
147                 WriteLog("CDINTF: SCSI command %02X failed [Error: %02X].\n", SRB.CDBByte[0], SRB.SRB_Status);
148                 return false;
149         }
150
151         return true;
152 }
153
154 //
155 // OS specific implementation of OS agnostic functions
156 //
157
158 bool CDIntfInit(void)
159 {
160         if (!InitASPI())
161         {
162                 WriteLog("CDINTF: Failed to init Win32 ASPI layer!\n");
163                 return false;
164         }
165
166         SRB_HAInquiry srbHAInquiry;
167         SRB_GDEVBlock srbGDEVBlock;
168         uint8 inquiryCDB[6], inquiryBuf[36];
169
170         for(uint8 HAID=0; HAID<maxHostAdapters; HAID++)
171         {
172                 memset(&srbHAInquiry, 0, sizeof(srbHAInquiry));
173                 srbHAInquiry.SRB_Cmd = SC_HA_INQUIRY;
174                 srbHAInquiry.SRB_HaId = HAID;
175
176                 ASPI_SendASPI32Command(&srbHAInquiry);
177
178                 if (srbHAInquiry.SRB_Status != SS_COMP)
179                         continue;
180
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.
183
184                 uint8 maxTargets = srbHAInquiry.HA_Unique[3];
185                 if (maxTargets != 8 && maxTargets != 16)
186                         maxTargets = 8;
187
188                 // Loop over all the targets on this host adapter.
189
190                 for(uint8 target=0; target<maxTargets; target++)
191                 {
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.
194
195                         memset(&srbGDEVBlock, 0, sizeof(srbGDEVBlock));
196                         srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
197                         srbGDEVBlock.SRB_HaId = HAID;
198                         srbGDEVBlock.SRB_Target = target;
199
200                         ASPI_SendASPI32Command(&srbGDEVBlock);
201
202                         if (srbGDEVBlock.SRB_Status != SS_COMP || srbGDEVBlock.SRB_DeviceType != DTYPE_CDROM)
203                                 continue;
204
205                         memset(inquiryCDB, 0, 6);                               // Issue an INQUIRY.
206                         inquiryCDB[0] = SCSI_INQUIRY;
207                         inquiryCDB[4] = 36;                                             // Size in bytes of inquiry buffer.
208
209                         bool successful = SendAsyncASPICmd(HAID, target, inquiryCDB, 6, inquiryBuf, 36, SRB_DIR_IN);
210
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).
214
215                         if (!successful || inquiryBuf[0] != DTYPE_CDROM)
216                                 continue;
217
218                         haID[numCDDrives] = HAID, tID[numCDDrives] = target;
219
220                         // Here we do a 'stringTrimRight' on the vendor and product strings...
221
222                         uint32 vendorSize = 0, productSize = 0;
223
224                         for(int i=7; i>=0; i--)
225                         {
226                                 if (inquiryBuf[8+i] != ' ')
227                                 {
228                                         vendorSize = i + 1;
229                                         break;
230                                 }
231                         }
232
233                         for(int i=15; i>=0; i--)
234                         {
235                                 if (inquiryBuf[16+i] != ' ')
236                                 {
237                                         productSize = i + 1;
238                                         break;
239                                 }
240                         }
241
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;
246
247                         WriteLog("CDINTF: Found CD-ROM device [%s]. HAID:%u, TID:%u LUN:0\n", driveName[numCDDrives], haID[numCDDrives], tID[numCDDrives]);
248
249                         numCDDrives++;
250                 }
251         }
252
253         if (numCDDrives == 0)
254         {
255                 WriteLog("CDINTF: No CDROM type drives found.\n");
256                 return false;
257         }
258
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
261
262         return true;
263 }
264
265 void CDIntfDone(void)
266 {
267         if (hASPILib)
268                 FreeLibrary(hASPILib);                                          // Unload ASPI library if it was loaded.
269 }
270
271 bool CDIntfReadBlock(uint32 sector, uint8 * buffer)
272 {
273         uint8 cdb[12];
274
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)
286
287         return SendAsyncASPICmd(haID[driveNum], tID[driveNum], cdb, 12, buffer, 2352+96, SRB_DIR_IN);
288 }
289
290 uint32 CDIntfGetNumSessions(void)
291 {
292 //      WriteLog("CDINTF: GetNumSessions unimplemented!\n");
293         // Still need relevant code here... !!! FIX !!! [DONE]
294         if (!readTOC)
295                 GetRawTOC();
296
297         return numSessions - 1;
298 }
299
300 void CDIntfSelectDrive(uint32 driveNum)
301 {
302         if (driveNum < numCDDrives)
303                 driveNum = driveNum;
304 }
305
306 uint32 CDIntfGetCurrentDrive(void)
307 {
308         return driveNum;
309 }
310
311 const uint8 * CDIntfGetDriveName(uint32 driveNum)
312 {
313         if (driveNum > numCDDrives)
314                 return NULL;
315
316         return driveName[driveNum];
317 }
318
319 //This stuff could probably be OK in the unified cdintf.cpp file...
320 uint8 CDIntfGetSessionInfo(uint32 session, uint32 offset)
321 {
322 // Need better error handling than this... !!! FIX !!!
323         if (!readTOC)
324                 if (!GetRawTOC())
325                         return 0xFF;
326
327         if (session >= numSessions || offset > 4)
328                 return 0xFF;                                                            // Bad index passed in...
329
330         return sessions[session][offset];
331 }
332
333 uint8 CDIntfGetTrackInfo(uint32 track, uint32 offset)
334 {
335 // Need better error handling than this... !!! FIX !!!
336         if (!readTOC)
337                 if (!GetRawTOC())
338                         return 0xFF;
339
340         if (track > numTracks || offset > 2)
341                 return 0xFF;                                                            // Bad index passed in...
342
343         return tracks[track][offset];
344 }
345
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)
351 {
352         uint8 cmd[10];
353         uint8 reqData[4];
354
355         // Read disk TOC length
356         memset(cmd, 0, 10);
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
361
362         if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, reqData, 4, SRB_DIR_IN))
363         {
364                 WriteLog("TOC: Cannot read disk TOC length.\n");
365                 return false;
366         }
367
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???
371   
372         WriteLog("TOC: Raw TOC data len: %d\n", dataLen);
373
374         uint8 * data = new uint8[dataLen];
375   
376         // Read disk TOC
377         cmd[7] = dataLen >> 8;
378         cmd[8] = dataLen;
379
380         if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, data, dataLen, SRB_DIR_IN))
381         {
382                 delete[] data;
383                 WriteLog("TOC: Cannot read disk TOC.\n");
384                 return false;
385         }
386
387         int numEntries = (((data[0] << 8) | data[1]) - 2) / 11;
388         uint8 * p = data + 4;
389
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)
395         {
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]);
398
399                 if (p[3] > 0 && p[3] < 99)
400                         WriteLog("   <-- Track #%u", p[3]);
401
402                 WriteLog("\n");//*/
403
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.
406
407                 uint32 s = p[0] - 1, t = p[3];
408
409                 if (t < 100)
410                 {
411                         if (t == 1)
412                                 firstTrackOffset = (((p[8] * 60) + p[9]) * 75) + p[10];
413
414                         tracks[t][0] = p[8], tracks[t][1] = p[9], tracks[t][2] = p[10];
415
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...
421
422                         uint32 curTrack = (((tracks[t][0] * 60) + tracks[t][1]) * 75) + tracks[t][2];
423                         curTrack -= firstTrackOffset;
424                         tracks[t][2] = curTrack % 75;
425                         curTrack /= 75;
426                         tracks[t][1] = curTrack % 60;
427                         tracks[t][0] = curTrack / 60;
428
429                         if (t > numTracks)
430                                 numTracks = t;
431                 }
432                 else if (t == 0xA0)
433                         sessions[s][0] = p[8];
434                 else if (t == 0xA1)
435                         sessions[s][1] = p[8];
436                 else if (t == 0xA2)
437                         sessions[s][2] = p[8], sessions[s][3] = p[9], sessions[s][4] = p[10];
438         }
439
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]);
447
448         delete[] data;
449         readTOC = true;
450
451         return true;
452 }