]> Shamusworld >> Repos - virtualjaguar/blob - src/cdintf_win32.cpp
Forgot missing new files... :-P
[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 // STILL TO DO:
12 //
13 // - Convert this shit to platform independent libcdio
14 //
15
16 // *** OS dependent CDROM stuffola ***
17 #include <windows.h>
18 #include "wnaspi32.h"
19 #include "scsidefs.h"
20 // *** End OS dependent ***
21 #include "log.h"
22
23 using namespace std;
24
25 // Local variables
26
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;
35
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);
39
40 // Private function prototypes
41
42 static bool InitASPI(void);
43 static bool SendAsyncASPICmd(uint8, uint8, uint8 *, uint8, uint8 *, uint32, uint8);
44 /*static*/ bool GetRawTOC(void);
45
46
47 //
48 // Initialize the Win32 ASPI layer
49 //
50 static bool InitASPI(void)
51 {
52         hASPILib = LoadLibrary("WNASPI32");
53
54         if (!hASPILib)
55         {
56                 WriteLog("CDINTF: Could not load WNASPI32.DLL!\n");
57                 return false;
58         }
59
60         ASPI_GetASPI32SupportInfo = (uint32 (*)(void))GetProcAddress(hASPILib, "GetASPI32SupportInfo");
61         ASPI_SendASPI32Command = (uint32 (*)(LPSRB))GetProcAddress(hASPILib, "SendASPI32Command");
62
63         if (!ASPI_GetASPI32SupportInfo || !ASPI_SendASPI32Command)
64         {
65                 WriteLog("CDINTF: Could not import functions from WNASPI32.DLL!\n");
66                 return false;
67         }
68
69         uint32 supportInfo = ASPI_GetASPI32SupportInfo();       // Initialize ASPI layer
70         uint8 retCode = (supportInfo >> 8) & 0xFF;
71         maxHostAdapters = supportInfo & 0xFF;
72
73         if (retCode != SS_COMP && retCode != SS_NO_ADAPTERS)
74         {
75                 WriteLog("CDINTF: Could not initialise using GetASPI32SupportInfo function!\n");
76                 return false;
77         }
78
79         if (retCode == SS_NO_ADAPTERS)
80         {
81                 WriteLog("CDINTF: ASPI initialized, but no host adapters were found!\n");
82                 return false;
83         }
84
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. 
89
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
93 // errors out!)
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);//*/
107
108         WriteLog("CDINTF: Successfully initialized.\n");
109         return true;
110 }
111
112 //
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.
116 //
117 static bool SendAsyncASPICmd(uint8 hostID, uint8 targID, uint8 * cdb, uint8 CDBLen,
118         uint8 * buffer, uint32 bufferLen, uint8 SRBFlags)
119 {
120         SRB_ExecSCSICmd SRB;                                                    // The SRB variable with CDB included
121
122         memset(&SRB, 0, sizeof(SRB));
123         memcpy(SRB.CDBByte, cdb, CDBLen);                               // Copy CDB into SRB's CDB
124
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;
133
134         HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
135
136         if (!hEvent)
137         {
138                 WriteLog("CDINTF: Couldn't create event!\n");
139                 return false;
140         }
141
142         SRB.SRB_PostProc = (void *)hEvent;
143         ASPI_SendASPI32Command(&SRB);
144
145         if (SRB.SRB_Status == SS_PENDING)
146                 WaitForSingleObject(hEvent, INFINITE);
147
148         CloseHandle(hEvent);
149
150         if (SRB.SRB_Status != SS_COMP)
151         {
152                 WriteLog("CDINTF: SCSI command %02X failed [Error: %02X].\n", SRB.CDBByte[0], SRB.SRB_Status);
153                 return false;
154         }
155
156         return true;
157 }
158
159 //
160 // OS specific implementation of OS agnostic functions
161 //
162
163 bool CDIntfInit(void)
164 {
165         if (!InitASPI())
166         {
167                 WriteLog("CDINTF: Failed to init Win32 ASPI layer!\n");
168                 return false;
169         }
170
171         SRB_HAInquiry srbHAInquiry;
172         SRB_GDEVBlock srbGDEVBlock;
173         uint8 inquiryCDB[6], inquiryBuf[36];
174
175         for(uint8 HAID=0; HAID<maxHostAdapters; HAID++)
176         {
177                 memset(&srbHAInquiry, 0, sizeof(srbHAInquiry));
178                 srbHAInquiry.SRB_Cmd = SC_HA_INQUIRY;
179                 srbHAInquiry.SRB_HaId = HAID;
180
181                 ASPI_SendASPI32Command(&srbHAInquiry);
182
183                 if (srbHAInquiry.SRB_Status != SS_COMP)
184                         continue;
185
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.
188
189                 uint8 maxTargets = srbHAInquiry.HA_Unique[3];
190                 if (maxTargets != 8 && maxTargets != 16)
191                         maxTargets = 8;
192
193                 // Loop over all the targets on this host adapter.
194
195                 for(uint8 target=0; target<maxTargets; target++)
196                 {
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.
199
200                         memset(&srbGDEVBlock, 0, sizeof(srbGDEVBlock));
201                         srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
202                         srbGDEVBlock.SRB_HaId = HAID;
203                         srbGDEVBlock.SRB_Target = target;
204
205                         ASPI_SendASPI32Command(&srbGDEVBlock);
206
207                         if (srbGDEVBlock.SRB_Status != SS_COMP || srbGDEVBlock.SRB_DeviceType != DTYPE_CDROM)
208                                 continue;
209
210                         memset(inquiryCDB, 0, 6);                               // Issue an INQUIRY.
211                         inquiryCDB[0] = SCSI_INQUIRY;
212                         inquiryCDB[4] = 36;                                             // Size in bytes of inquiry buffer.
213
214                         bool successful = SendAsyncASPICmd(HAID, target, inquiryCDB, 6, inquiryBuf, 36, SRB_DIR_IN);
215
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).
219
220                         if (!successful || inquiryBuf[0] != DTYPE_CDROM)
221                                 continue;
222
223                         haID[numCDDrives] = HAID, tID[numCDDrives] = target;
224
225                         // Here we do a 'stringTrimRight' on the vendor and product strings...
226
227                         uint32 vendorSize = 0, productSize = 0;
228
229                         for(int i=7; i>=0; i--)
230                         {
231                                 if (inquiryBuf[8+i] != ' ')
232                                 {
233                                         vendorSize = i + 1;
234                                         break;
235                                 }
236                         }
237
238                         for(int i=15; i>=0; i--)
239                         {
240                                 if (inquiryBuf[16+i] != ' ')
241                                 {
242                                         productSize = i + 1;
243                                         break;
244                                 }
245                         }
246
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;
251
252                         WriteLog("CDINTF: Found CD-ROM device [%s]. HAID:%u, TID:%u LUN:0\n", driveName[numCDDrives], haID[numCDDrives], tID[numCDDrives]);
253
254                         numCDDrives++;
255                 }
256         }
257
258         if (numCDDrives == 0)
259         {
260                 WriteLog("CDINTF: No CDROM type drives found.\n");
261                 return false;
262         }
263
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
266
267         return true;
268 }
269
270 void CDIntfDone(void)
271 {
272         if (hASPILib)
273                 FreeLibrary(hASPILib);                                          // Unload ASPI library if it was loaded.
274 }
275
276 bool CDIntfReadBlock(uint32 sector, uint8 * buffer)
277 {
278         uint8 cdb[12];
279
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)
291
292         return SendAsyncASPICmd(haID[driveNum], tID[driveNum], cdb, 12, buffer, 2352+96, SRB_DIR_IN);
293 }
294
295 uint32 CDIntfGetNumSessions(void)
296 {
297 //      WriteLog("CDINTF: GetNumSessions unimplemented!\n");
298         // Still need relevant code here... !!! FIX !!! [DONE]
299         if (!readTOC)
300                 GetRawTOC();
301
302         return numSessions - 1;
303 }
304
305 void CDIntfSelectDrive(uint32 driveNum)
306 {
307         if (driveNum < numCDDrives)
308                 driveNum = driveNum;
309 }
310
311 uint32 CDIntfGetCurrentDrive(void)
312 {
313         return driveNum;
314 }
315
316 const uint8 * CDIntfGetDriveName(uint32 driveNum)
317 {
318         if (driveNum > numCDDrives)
319                 return NULL;
320
321         return driveName[driveNum];
322 }
323
324 //This stuff could probably be OK in the unified cdintf.cpp file...
325 uint8 CDIntfGetSessionInfo(uint32 session, uint32 offset)
326 {
327 // Need better error handling than this... !!! FIX !!!
328         if (!readTOC)
329                 if (!GetRawTOC())
330                         return 0xFF;
331
332         if (session >= numSessions || offset > 4)
333                 return 0xFF;                                                            // Bad index passed in...
334
335         return sessions[session][offset];
336 }
337
338 uint8 CDIntfGetTrackInfo(uint32 track, uint32 offset)
339 {
340 // Need better error handling than this... !!! FIX !!!
341         if (!readTOC)
342                 if (!GetRawTOC())
343                         return 0xFF;
344
345         if (track > numTracks || offset > 2)
346                 return 0xFF;                                                            // Bad index passed in...
347
348         return tracks[track][offset];
349 }
350
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)
356 {
357         uint8 cmd[10];
358         uint8 reqData[4];
359
360         // Read disk TOC length
361         memset(cmd, 0, 10);
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
366
367         if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, reqData, 4, SRB_DIR_IN))
368         {
369                 WriteLog("TOC: Cannot read disk TOC length.\n");
370                 return false;
371         }
372
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???
376   
377         WriteLog("TOC: Raw TOC data len: %d\n", dataLen);
378
379         uint8 * data = new uint8[dataLen];
380   
381         // Read disk TOC
382         cmd[7] = dataLen >> 8;
383         cmd[8] = dataLen;
384
385         if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, data, dataLen, SRB_DIR_IN))
386         {
387                 delete[] data;
388                 WriteLog("TOC: Cannot read disk TOC.\n");
389                 return false;
390         }
391
392         int numEntries = (((data[0] << 8) | data[1]) - 2) / 11;
393         uint8 * p = data + 4;
394
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)
400         {
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]);
403
404                 if (p[3] > 0 && p[3] < 99)
405                         WriteLog("   <-- Track #%u", p[3]);
406
407                 WriteLog("\n");//*/
408
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.
411
412                 uint32 s = p[0] - 1, t = p[3];
413
414                 if (t < 100)
415                 {
416                         if (t == 1)
417                                 firstTrackOffset = (((p[8] * 60) + p[9]) * 75) + p[10];
418
419                         tracks[t][0] = p[8], tracks[t][1] = p[9], tracks[t][2] = p[10];
420
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...
426
427                         uint32 curTrack = (((tracks[t][0] * 60) + tracks[t][1]) * 75) + tracks[t][2];
428                         curTrack -= firstTrackOffset;
429                         tracks[t][2] = curTrack % 75;
430                         curTrack /= 75;
431                         tracks[t][1] = curTrack % 60;
432                         tracks[t][0] = curTrack / 60;
433
434                         if (t > numTracks)
435                                 numTracks = t;
436                 }
437                 else if (t == 0xA0)
438                         sessions[s][0] = p[8];
439                 else if (t == 0xA1)
440                         sessions[s][1] = p[8];
441                 else if (t == 0xA2)
442                         sessions[s][2] = p[8], sessions[s][3] = p[9], sessions[s][4] = p[10];
443         }
444
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]);
452
453         delete[] data;
454         readTOC = true;
455
456         return true;
457 }