]> Shamusworld >> Repos - virtualjaguar/blob - src/cdintf_win32.cpp
New OS dependent CDROM code
[virtualjaguar] / src / cdintf_win32.cpp
1 //
2 // OS specific CDROM interface (Win32)
3 //
4 // by James L. Hammons
5 //
6
7 // OS dependent CDROM stuffola
8 #include <windows.h>
9 #include <stdio.h>
10 #include "wnaspi32.h"
11 #include "scsidefs.h"
12 // End OS dependent
13
14 #include "log.h"
15
16 using namespace std;
17
18 // Local variables
19
20 DWORD remain = 0, sector = 0;
21 BYTE cdBuf[2532 * 10];
22
23 // Private function prototypes
24
25
26 HINSTANCE WNASPI32_handle = NULL;  //Handle to ASPI for Win32 (WNASPI.DLL)
27 //WNASPI.DLL functions
28 DWORD (*ASPI_GetASPI32SupportInfo)(VOID);
29 DWORD (*ASPI_SendASPI32Command)(LPSRB);
30 //BOOL  (*ASPI_GetASPI32Buffer)(PASPI32BUFF);
31 //BOOL  (*ASPI_FreeASPI32Buffer)(PASPI32BUFF);
32 //BOOL  (*ASPI_TranslateASPI32Address)(PDWORD, PDWORD);
33
34 byte DataBuf[2352];  //Buffer for holding data to/from drive
35 //************************************************************
36 //End of globals
37
38 //
39 // Load the WNASPI32.DLL and import the required functions, then initialise
40 // Win ASPI 32.
41 //
42 BOOL InitASPI()
43 {
44         DWORD dwSupportInfo;
45         SRB_GetSetTimeouts srbTimeouts;
46
47         WNASPI32_handle = LoadLibrary("WNASPI32"); //WNASPI32.DLL
48
49         if (!WNASPI32_handle)
50         {
51                 WriteLog("CDINTF: Could not load WNASPI32.DLL\n");
52                 return FALSE;
53         };
54
55     /*
56     ** Get the ASPI entry points.  Note that only two functions are mandatory:
57     ** GetASPI32SupportInfo and SendASPI32Command.  The code will run if the
58     ** others are not present.
59     */
60     ASPI_GetASPI32SupportInfo = (DWORD (*)(void))GetProcAddress(WNASPI32_handle, "GetASPI32SupportInfo");
61     ASPI_SendASPI32Command = (DWORD (*)(LPSRB))GetProcAddress(WNASPI32_handle, "SendASPI32Command");
62     //ASPI_GetASPI32Buffer = (BOOL (*)(PASPI32BUFF))GetProcAddress(WNASPI32_handle, "GetASPI32Buffer");
63     //ASPI_FreeASPI32Buffer = (BOOL (*)(PASPI32BUFF))GetProcAddress(WNASPI32_handle, "FreeASPI32Buffer");
64     //ASPI_TranslateASPI32Address = (BOOL (*)(PDWORD, PDWORD))GetProcAddress(WNASPI32_handle, "TranslateASPI32Address");
65
66     //Check if the 2 functions were imported.
67     if(!ASPI_GetASPI32SupportInfo || !ASPI_SendASPI32Command)
68     {
69         WriteLog("Could not import GetASPI32SupportInfo & SendASPI32Command functions from WNASPI32.DLL\n");
70         return FALSE;
71     };
72
73     //Initialise Win ASPI 32 by calling ASPI_GetASPI32SupportInfo().
74     dwSupportInfo = ASPI_GetASPI32SupportInfo();
75     if (HIBYTE(LOWORD(dwSupportInfo)) != SS_COMP && HIBYTE(LOWORD(dwSupportInfo)) != SS_NO_ADAPTERS)
76     {
77         WriteLog("Could not initialise using GetASPI32SupportInfo function or no adapters\n");
78         return FALSE;
79     };
80
81     /*
82     ** Set timeouts for ALL devices to 15 seconds.  Nothing we deal with should
83     ** take that long to do ANYTHING.  We are just doing inquiries to most
84     ** devices, and then simple reads to CDs, disks, etc. so 10 seconds (even
85     ** if they have to spin up) should be plenty. 
86     */
87     memset(&srbTimeouts, 0, sizeof(SRB_GetSetTimeouts));
88     srbTimeouts.SRB_Cmd = SC_GETSET_TIMEOUTS;
89     srbTimeouts.SRB_HaId = 0xFF;
90     srbTimeouts.SRB_Flags = SRB_DIR_OUT;
91     srbTimeouts.SRB_Target = 0xFF;
92     srbTimeouts.SRB_Lun = 0xFF;
93     srbTimeouts.SRB_Timeout = 15 * 2;
94     ASPI_SendASPI32Command(&srbTimeouts);
95
96     return TRUE;
97 };
98
99 /*
100     Sends a SRB (SCSI Request Block) to Win ASPI32 driver for processing and
101     waits for completion.
102
103     The SRB contains the CDB (Command Descriptor Block) which has the
104     raw SCSI command/info you want to send to the drive.  So effectively
105     this is actually sending the SCSI command to the drive.
106
107     Note that this uses a Windows event to wait for the drive to reply back
108     which is efficient.  If an event is not available (which should never
109     happen really) a loop is used to poll for reply back.
110
111     Both methods will wait infinitely - so the drive must reply back otherwise
112     this software will appear to hang.  There seems to be no mechanism for
113     cancelling the SRB process - the Win ASPI 32 'SC_ABORT_SRB' command doesn't
114     seem to work.
115 */
116 BOOL SendASPICMD_and_wait(BYTE HA_ID, BYTE Target_ID, BYTE SRB_flags, DWORD Buffer_len,
117         PBYTE Buffer, BYTE CDB_len, PBYTE CDB)
118 {
119     DWORD           dwASPIStatus;
120     HANDLE          hevent_SRB;  //A handle for a new Windows event
121     SRB_ExecSCSICmd SRB;  //The SRB variable with CDB included
122     BOOL b_retry = TRUE;
123
124     //Clear & setup the SRB for this command..
125     memset(&SRB, 0, sizeof(SRB_ExecSCSICmd));  //Set it to zeroes
126     memcpy(SRB.CDBByte, CDB, CDB_len);  //Copy CDB into SRB
127
128     SRB.SRB_Cmd = SC_EXEC_SCSI_CMD;
129     SRB.SRB_HaId = HA_ID;
130     SRB.SRB_Target = Target_ID;
131     //SRB.SRB_Lun = 0;  //It's already zero
132     SRB.SRB_Flags = SRB_flags;
133     SRB.SRB_BufLen = Buffer_len;
134     SRB.SRB_BufPointer = Buffer;
135     SRB.SRB_SenseLen = SENSE_LEN;
136     SRB.SRB_CDBLen = CDB_len;
137
138     do
139     {
140         /*
141         ** Create an event (if possible) and issue the command.  After sending
142         ** the command, wait for completion.
143         */
144         hevent_SRB = CreateEvent(NULL, TRUE, FALSE, NULL);
145         if (hevent_SRB)
146         {
147             //Windows event method for waiting - efficient.
148
149             SRB.SRB_Flags |= SRB_EVENT_NOTIFY;
150             SRB.SRB_PostProc = (LPVOID)hevent_SRB;
151         
152             //Send the SRB for processing.
153             dwASPIStatus = ASPI_SendASPI32Command((LPSRB)&SRB);
154             if (dwASPIStatus == SS_PENDING)
155             {
156                 //Wait for reply back.
157                 WaitForSingleObject(hevent_SRB, INFINITE);
158             }
159             CloseHandle(hevent_SRB);
160         }
161         else
162         {
163             //Polling method for waiting - not very efficient.
164
165             //Send the SRB for processing.
166             ASPI_SendASPI32Command((LPSRB)&SRB);
167             //Wait for reply back.
168             while(SRB.SRB_Status == SS_PENDING);
169         };
170
171         /*
172         ** Check for errors.  We'll retry on unit attention condition.  Anything
173         ** else will generate an error msg.
174         */
175         if (SRB.SRB_Status != SS_COMP)
176         {
177                         if (b_retry && (SRB.SRB_TargStat != STATUS_CHKCOND
178                                 || (SRB.SenseArea[2] & 0x0F) != KEY_UNITATT))
179                 b_retry = FALSE;
180             else
181             {
182                 WriteLog("SCSI command failed.\n");
183                 return FALSE;
184             }
185         }
186     }
187     while (b_retry == FALSE);
188
189     return TRUE;
190 };
191
192 /*
193     Lists all available CDROM type drives.  This includes:
194         - CDROM drives
195         - CD rewriters/Combo
196         - DVD drives
197         - DVD rewriters
198         etc
199
200     It can only list the SCSI IDs not drive letters.  Win ASPI 32 does
201     not provide a mechanism for this.  You will have to use windows API
202     for this.
203 */
204 VOID ListDevices()
205 {
206     BYTE          HaId;
207     BYTE          Target;
208     BYTE          MaxHaId;
209     BYTE          MaxTarget;
210     BYTE          InquiryBuf[36];
211     BYTE          InquiryCDB[6];
212     CHAR          szVendor[9];
213     CHAR          szProduct[17];
214     CHAR          szRev[5];
215     BOOL          bSRB_exec;
216     DWORD         dwASPIStatus;
217     DWORD         dwMaxTransferBytes;    
218     SRB_HAInquiry srbHAInquiry;
219     SRB_GDEVBlock srbGDEVBlock;
220     DWORD         n_CDROM_drives=0;  //No of CDROM type drives found.
221
222     //Use support info for host adapter count and loop over all of them.
223     dwASPIStatus = ASPI_GetASPI32SupportInfo();
224     if(HIBYTE(LOWORD(dwASPIStatus)) == SS_COMP)
225     {
226         MaxHaId = LOBYTE(LOWORD(dwASPIStatus));
227         for(HaId = 0; HaId < MaxHaId; HaId++)
228         {
229             /*
230             ** Do a host adapter inquiry to get max target count.  If the 
231             ** target count isn't 8 or 16 then go with a default of 8.
232             */
233             memset(&srbHAInquiry, 0, sizeof(SRB_HAInquiry));
234             srbHAInquiry.SRB_Cmd = SC_HA_INQUIRY;
235             srbHAInquiry.SRB_HaId = HaId;
236
237             ASPI_SendASPI32Command((LPSRB)&srbHAInquiry);
238             if(srbHAInquiry.SRB_Status != SS_COMP)
239             {
240                 continue;
241             };
242
243             MaxTarget = srbHAInquiry.HA_Unique[3];
244             if(MaxTarget != 8 && MaxTarget != 16)
245             {
246                 MaxTarget = 8;
247             };
248
249             /*
250             ** Loop over all the targets on this host adapter.
251             */
252             for(Target = 0; Target < MaxTarget; Target++ )
253             {
254                 /*
255                 ** Issue get device type call to see if there is a device we're
256                 ** interested in at this address.  We're interested in CDROMs.
257                 */
258                 memset(&srbGDEVBlock, 0, sizeof(SRB_GDEVBlock));
259                 srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
260                 srbGDEVBlock.SRB_HaId = HaId;
261                 srbGDEVBlock.SRB_Target = Target;
262
263                 ASPI_SendASPI32Command((LPSRB)&srbGDEVBlock);
264                 if(srbGDEVBlock.SRB_Status != SS_COMP ||
265                   (srbGDEVBlock.SRB_DeviceType != DTYPE_CDROM))
266                 {                    
267                     continue;
268                 };
269
270                 /*
271                 ** Determine the max transfer count of this target.  It will
272                 ** be the min of the host adapters min count and the size
273                 ** of our global transfer buffer.
274                 */
275                 dwMaxTransferBytes = (DWORD)&srbHAInquiry.HA_Unique[4];
276
277                 /*
278                 ** Issue an INQUIRY.
279                 */
280                 memset(InquiryCDB, 0, 6);
281                 InquiryCDB[0] = SCSI_INQUIRY;
282                 InquiryCDB[4] = 36;  //Size in bytes of inquiry buffer.
283
284                 //Send SCSI device inquiry command and wait for completion.
285                 bSRB_exec = SendASPICMD_and_wait
286                 (
287                     HaId,
288                     Target,
289                     SRB_DIR_IN,
290                     36,
291                     InquiryBuf,
292                     6,
293                     InquiryCDB
294                 );
295
296                 /*
297                 ** Make sure the inquiry worked.  If it failed, or if the
298                 ** inquiry data returns a different device type than we got
299                 ** before (guards against certain device drivers and against
300                 ** vendor unique devices).
301                 */
302                 if(!bSRB_exec || (InquiryBuf[0] != DTYPE_CDROM))
303                     continue;
304
305                 /*
306                 ** Add this target to the screen.
307                 */
308                 WriteLog("Host adapter ID: %ld\n", HaId);
309                 WriteLog("Target ID      : %ld\n", Target);
310                 WriteLog("LUN ID         : 0\n");
311                 WriteLog("Max buffer size: %ld bytes\n", dwMaxTransferBytes);
312
313                 memcpy(szVendor, InquiryBuf + 8, 8);                
314                 szVendor[8] = '\0';  //Terminate the string
315
316                 memcpy(szProduct, InquiryBuf + 16, 16);
317                 szProduct[16] = '\0';  //Terminate the string
318
319                 memcpy(szRev, InquiryBuf + 32, 4);
320                 szRev[4] = '\0';  //Terminate the string
321
322                 WriteLog("Vendor         : %s\n", szVendor);
323                 WriteLog("Product        : %s\n", szProduct);
324                 WriteLog("Revision       : %s\n\n", szRev);
325
326                 n_CDROM_drives++;
327             }
328         }
329
330         if (n_CDROM_drives == 0)
331             WriteLog("No CDROM type drives found.\n");
332     }
333 }
334
335 //
336 // 1. Sets up the CDB for MMC readcd (CDB12) command.
337 // 2. Send the request to the drive.
338 // 3. If success displays the sector data as hex on the screen.
339 //
340 BOOL ReadCD(BYTE HA_ID, BYTE Target_ID, long int MMC_LBA_sector)
341 {
342     BYTE read_CDB12[12];
343     long int MMC_LBA_sector2;    
344
345     //CDB with values for ReadCD CDB12 command.  The values were taken from MMC1 draft paper.
346     read_CDB12[0] = 0xBE;  //Code for ReadCD CDB12 command
347     read_CDB12[1] = 0;        
348
349     read_CDB12[5] = byte(MMC_LBA_sector);   //Least sig byte of LBA sector no. to read from CD
350     MMC_LBA_sector2 = MMC_LBA_sector >> 8;
351     read_CDB12[4] = byte(MMC_LBA_sector2);  //2nd byte of:
352     MMC_LBA_sector2 = MMC_LBA_sector2 >> 8;
353     read_CDB12[3] = byte(MMC_LBA_sector2);  //3rd byte of:
354     MMC_LBA_sector2 = MMC_LBA_sector2 >> 8;
355     read_CDB12[2] = byte(MMC_LBA_sector2);  //Most significant byte        
356
357     read_CDB12[6] = 0;  //No. of sectors to read from CD byte 2 (MSB)
358     read_CDB12[7] = 0;  //No. of sectors to read from CD byte 1
359     read_CDB12[8] = 1;  //No. of sectors to read from CD byte 0 (LSB)
360     read_CDB12[9] = 0xF8;  //Raw read, 2352 bytes per sector
361     read_CDB12[10] = 0;  //Sub-channel selection bits.
362     read_CDB12[11] = 0;
363
364         return SendASPICMD_and_wait(HA_ID, Target_ID, SRB_DIR_IN, 2352, DataBuf, 12, read_CDB12);
365 }
366
367 //
368 // Initialize the SDL sound system
369 //
370 void __CDInit(void)
371 {
372         if (!InitASPI())
373         {
374                 WriteLog("Sound: Failed to init ASPI layer!\n");
375                 exit(1);//bad!
376         }
377 }
378
379 //
380 // Close down the SDL sound subsystem
381 //
382 void __CDDone(void)
383 {
384     //Unload ASPI if it has been loaded.
385         if (WNASPI32_handle)
386                 FreeLibrary(WNASPI32_handle);
387 }
388
389 /*//
390 // Sound card callback handler
391 //
392 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
393 {
394         if (remain > 0)
395         {
396                 memcpy(buffer, DataBuf + 2352 - remain, remain);
397                 length -= remain;
398                 buffer += remain;
399                 remain = 0;
400         }
401
402         while (length > 0)
403         {
404                 ReadCD(0, 1, sector++);
405                 memcpy(buffer, DataBuf, (length >= 2352 ? 2352 : length));
406                 length -= 2352;
407                 buffer += 2352;
408         }
409
410         if (length < 0)
411                 remain = -length;
412 }*/