1 /*
2   Copyright (C) 2004-2005, 2008, 2010-2014, 2017
3   Rocky Bernstein <rocky@gnu.org>
4 
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 /* This file contains Win32-specific code using the DeviceIoControl
20    access method.
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26 
27 #ifdef HAVE_WIN32_CDROM
28 
29 #if defined (_XBOX)
30 # include "inttypes.h"
31 # include "NtScsi.h"
32 # include "undocumented.h"
33 #else
34 # if defined (__MINGW64_VERSION_MAJOR)
35 #  define _NTSRB_ /* Bad things happen if srb.h gets included */
36 # endif
37 # include <windows.h>
38 # ifdef HAVE_DDK_SCSI_H
39 #  include <ddk/scsi.h>
40 # endif
41 # ifdef HAVE_NTDDCDRM_H
42 #  include <ntddcdrm.h>
43 # endif
44 # ifdef HAVE_DDK_NTDDCDRM_H
45 #  include <ddk/ntddcdrm.h>
46 # endif
47 # ifdef HAVE_NTDDSCSI_H
48 #  include <ntddscsi.h>
49 # endif
50 # ifdef HAVE_DDK_NTDDSCSI_H
51 #  include <ddk/ntddscsi.h>
52 # endif
53 #endif
54 
55 #if defined (_WIN32)
56 #include <windows.h>
57 #endif
58 
59 #include <stddef.h>  /* offsetof() macro */
60 #include <sys/stat.h>
61 #include <errno.h>
62 #include <sys/types.h>
63 
64 #include <cdio/cdio.h>
65 #include <cdio/sector.h>
66 #include <cdio/util.h>
67 #include "cdio_assert.h"
68 #include <cdio/mmc.h>
69 #include "cdio/logging.h"
70 
71 #if defined (_XBOX)
72 #define windows_error(loglevel,i_err) {                    \
73    cdio_log(loglevel, "Error: file %s: line %d (%s) %ld\n", \
74                   __FILE__, __LINE__, __PRETTY_FUNCTION__, i_err); \
75 }
76 #else
77 #define windows_error(loglevel,i_err) {                                 \
78   char error_msg[80];                                                   \
79   long int count;                                                       \
80   count = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,                      \
81                         NULL, i_err, MAKELANGID(LANG_NEUTRAL,            \
82                                                 SUBLANG_DEFAULT),        \
83                         error_msg, sizeof(error_msg), NULL);             \
84   (count != 0) ?                                                         \
85     cdio_log(loglevel, "Error: file %s: line %d (%s)\n\t%s\n",           \
86              __FILE__, __LINE__, __PRETTY_FUNCTION__, error_msg)         \
87     :                                                                    \
88     cdio_log(loglevel, "Error: file %s: line %d (%s) %ld\n",             \
89              __FILE__, __LINE__, __PRETTY_FUNCTION__, (long int) i_err); \
90 }
91 #endif
92 
93 #define MAX_ERROR_BUFFER    256
94 #define MAX_DATA_BUFFER     2048
95 
96 typedef struct _TRACK_DATA_FULL {
97     UCHAR SessionNumber;
98     UCHAR Control : 4;
99     UCHAR Adr : 4;
100     UCHAR TNO;
101     UCHAR POINT;  /* Tracknumber (of session?) or lead-out/in (0xA0, 0xA1, 0xA2)  */
102     UCHAR Min;  /* Only valid if disctype is CDDA ? */
103     UCHAR Sec;  /* Only valid if disctype is CDDA ? */
104     UCHAR Frame;  /* Only valid if disctype is CDDA ? */
105     UCHAR Zero;  /* Always zero */
106     UCHAR PMIN;  /* start min, if POINT is a track; if lead-out/in 0xA0: First Track */
107     UCHAR PSEC;
108     UCHAR PFRAME;
109 } TRACK_DATA_FULL, *PTRACK_DATA_FULL;
110 
111 typedef struct _CDROM_TOC_FULL {
112     UCHAR Length[2];
113     UCHAR FirstSession;
114     UCHAR LastSession;
115     TRACK_DATA_FULL TrackData[CDIO_CD_MAX_TRACKS+3];
116 } CDROM_TOC_FULL, *PCDROM_TOC_FULL;
117 
118 #define SPT_CDB_LENGTH 32
119 #define SPT_SENSE_LENGTH 32
120 #define SPTWB_DATA_LENGTH 512
121 
122 #ifndef EMPTY_ARRAY_SIZE
123 #define EMPTY_ARRAY_SIZE 0
124 #endif
125 
126 
127 typedef struct {
128    SCSI_PASS_THROUGH_DIRECT sptd;
129    ULONG Filler; /* Realign buffer to double-word boundary */
130    UCHAR ucSenseBuf[SPT_SENSE_LENGTH];
131 } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
132 
133 #include <stdio.h>
134 
135 typedef struct _SCSI_PASS_THROUGH_WITH_BUFFERS {
136     SCSI_PASS_THROUGH    Spt;
137     ULONG                Filler;   /* realign buffer to double-word boundary */
138     UCHAR                ucSenseBuf[SPT_SENSE_LENGTH];
139     UCHAR ucDataBuf[EMPTY_ARRAY_SIZE];
140 } SCSI_PASS_THROUGH_WITH_BUFFERS;
141 
142 #ifdef HAVE_STDBOOL_H
143 # include <stdbool.h>
144 #endif
145 
146 #include "win32.h"
147 
148 #define OP_TIMEOUT_MS 60
149 
150 /*!
151   Pause playing CD through analog output
152 
153   @param p_cdio the CD object to be acted upon.
154 */
155 driver_return_code_t
audio_pause_win32ioctl(void * p_user_data)156 audio_pause_win32ioctl (void *p_user_data)
157 {
158   const _img_private_t *p_env = p_user_data;
159   DWORD dw_bytes_returned;
160 
161   bool b_success =
162     DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_PAUSE_AUDIO,
163                     NULL, (DWORD) 0, NULL, 0, &dw_bytes_returned, NULL);
164 
165   if ( ! b_success ) {
166     windows_error(CDIO_LOG_INFO, GetLastError());
167     return DRIVER_OP_ERROR;
168   }
169   return DRIVER_OP_SUCCESS;
170 }
171 
172 /*!
173   Playing starting at given MSF through analog output
174 
175   @param p_cdio the CD object to be acted upon.
176 */
177 driver_return_code_t
audio_play_msf_win32ioctl(void * p_user_data,msf_t * p_start_msf,msf_t * p_end_msf)178 audio_play_msf_win32ioctl (void *p_user_data, msf_t *p_start_msf,
179                            msf_t *p_end_msf)
180 {
181   const _img_private_t *p_env = p_user_data;
182   CDROM_PLAY_AUDIO_MSF play;
183   DWORD dw_bytes_returned;
184   bool b_success;
185 
186   play.StartingM = cdio_from_bcd8(p_start_msf->m);
187   play.StartingS = cdio_from_bcd8(p_start_msf->s);
188   play.StartingF = cdio_from_bcd8(p_start_msf->f);
189 
190   play.EndingM   = cdio_from_bcd8(p_end_msf->m);
191   play.EndingS   = cdio_from_bcd8(p_end_msf->s);
192   play.EndingF   = cdio_from_bcd8(p_end_msf->f);
193 
194   b_success =
195     DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_PLAY_AUDIO_MSF,
196                     &play, sizeof(play), NULL, 0, &dw_bytes_returned, NULL);
197 
198   if ( ! b_success ) {
199     windows_error(CDIO_LOG_INFO, GetLastError());
200     return DRIVER_OP_ERROR;
201   }
202   return DRIVER_OP_SUCCESS;
203 
204 }
205 
206 /*!
207   Read Audio Subchannel information
208 
209   @param p_cdio the CD object to be acted upon.
210 
211 */
212 driver_return_code_t
audio_read_subchannel_win32ioctl(void * p_user_data,cdio_subchannel_t * p_subchannel)213 audio_read_subchannel_win32ioctl (void *p_user_data,
214                                   cdio_subchannel_t *p_subchannel)
215 {
216   const _img_private_t *p_env = p_user_data;
217   DWORD dw_bytes_returned;
218   CDROM_SUB_Q_DATA_FORMAT q_data_format;
219   SUB_Q_CHANNEL_DATA q_subchannel_data;
220 
221   q_data_format.Format = CDIO_SUBCHANNEL_CURRENT_POSITION;
222   q_data_format.Track=0; /* Not sure if this has to be set or if so what
223                             it should be. */
224 
225   if( ! DeviceIoControl( p_env->h_device_handle,
226                        IOCTL_CDROM_READ_Q_CHANNEL,
227                        &q_data_format, sizeof(q_data_format),
228                        &q_subchannel_data, sizeof(q_subchannel_data),
229                        &dw_bytes_returned, NULL ) ) {
230     windows_error(CDIO_LOG_INFO, GetLastError());
231     return DRIVER_OP_ERROR;
232   }
233   p_subchannel->audio_status =
234     q_subchannel_data.CurrentPosition.Header.AudioStatus;
235   p_subchannel->track =
236     q_subchannel_data.CurrentPosition.TrackNumber;
237   p_subchannel->index =
238     q_subchannel_data.CurrentPosition.IndexNumber;
239   p_subchannel->index =
240     q_subchannel_data.CurrentPosition.IndexNumber;
241   p_subchannel->address = q_subchannel_data.CurrentPosition.ADR;
242   p_subchannel->control = q_subchannel_data.CurrentPosition.Control;
243 
244   {
245     const UCHAR *abs_addr =
246       q_subchannel_data.CurrentPosition.AbsoluteAddress;
247     const UCHAR *rel_addr =
248       q_subchannel_data.CurrentPosition.TrackRelativeAddress;
249 
250     p_subchannel->abs_addr.m = cdio_to_bcd8(abs_addr[1]);
251     p_subchannel->abs_addr.s = cdio_to_bcd8(abs_addr[2]);
252     p_subchannel->abs_addr.f = cdio_to_bcd8(abs_addr[3]);
253     p_subchannel->rel_addr.m = cdio_to_bcd8(rel_addr[1]);
254     p_subchannel->rel_addr.s = cdio_to_bcd8(rel_addr[2]);
255     p_subchannel->rel_addr.f = cdio_to_bcd8(rel_addr[3]);
256   }
257 
258   return DRIVER_OP_SUCCESS;
259 }
260 
261 /**
262   Resume playing an audio CD.
263 
264   @param p_user_data the CD object to be acted upon.
265 
266 */
267 driver_return_code_t
audio_resume_win32ioctl(void * p_user_data)268 audio_resume_win32ioctl (void *p_user_data)
269 {
270   const _img_private_t *p_env = p_user_data;
271   DWORD dw_bytes_returned;
272 
273   bool b_success =
274     DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_RESUME_AUDIO,
275                     NULL, (DWORD) 0, NULL, 0, &dw_bytes_returned, NULL);
276 
277   if ( ! b_success ) {
278     windows_error(CDIO_LOG_INFO, GetLastError());
279     return DRIVER_OP_ERROR;
280   }
281   return DRIVER_OP_SUCCESS;
282 }
283 
284 /**
285   Set the volume of an audio CD.
286 
287   @param p_user_data pointer to the CD object to be acted upon.
288   @param p_volume pointer to the volume levels
289 
290 */
291 driver_return_code_t
audio_set_volume_win32ioctl(void * p_user_data,cdio_audio_volume_t * p_volume)292 audio_set_volume_win32ioctl (void *p_user_data,
293                              /*in*/ cdio_audio_volume_t *p_volume)
294 {
295   const _img_private_t *p_env = p_user_data;
296   DWORD dw_bytes_returned;
297 
298   bool b_success =
299     DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_SET_VOLUME,
300                     p_volume, (DWORD) sizeof(cdio_audio_volume_t),
301                     NULL, 0, &dw_bytes_returned, NULL);
302 
303   if ( ! b_success ) {
304     windows_error(CDIO_LOG_INFO, GetLastError());
305     return DRIVER_OP_ERROR;
306   }
307   return DRIVER_OP_SUCCESS;
308 }
309 
310 driver_return_code_t
audio_get_volume_win32ioctl(void * p_user_data,cdio_audio_volume_t * p_volume)311 audio_get_volume_win32ioctl (void *p_user_data,
312                              /*out*/ cdio_audio_volume_t *p_volume)
313 {
314   const _img_private_t *p_env = p_user_data;
315   DWORD dw_bytes_returned;
316 
317   bool b_success =
318     DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_GET_VOLUME,
319                     NULL, 0,
320                     p_volume, (DWORD) sizeof(cdio_audio_volume_t),
321                     &dw_bytes_returned, NULL);
322 
323   if ( ! b_success ) {
324     windows_error(CDIO_LOG_INFO, GetLastError());
325     return DRIVER_OP_ERROR;
326   }
327   return DRIVER_OP_SUCCESS;
328 }
329 
330 /**
331   Stop playing an audio CD.
332 
333   @param p_user_data the CD object to be acted upon.
334 
335 */
336 driver_return_code_t
audio_stop_win32ioctl(void * p_user_data)337 audio_stop_win32ioctl (void *p_user_data)
338 {
339   const _img_private_t *p_env = p_user_data;
340   DWORD dw_bytes_returned;
341 
342   bool b_success =
343     DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_STOP_AUDIO,
344                     NULL, (DWORD) 0, NULL, 0, &dw_bytes_returned, NULL);
345 
346   if ( ! b_success ) {
347     windows_error(CDIO_LOG_INFO, GetLastError());
348     return DRIVER_OP_ERROR;
349   }
350   return DRIVER_OP_SUCCESS;
351 }
352 
353 /**
354   Close the tray of a CD-ROM
355 
356   @param p_user_data the CD object to be acted upon.
357 
358 */
359 driver_return_code_t
close_tray_win32ioctl(const char * psz_win32_drive)360 close_tray_win32ioctl (const char *psz_win32_drive)
361 {
362 #ifdef WIN32
363   DWORD dw_bytes_returned;
364   DWORD dw_access_flags;
365 
366   OSVERSIONINFO ov;
367   HANDLE h_device_handle;
368   BOOL   b_success;
369 
370   memset(&ov,0,sizeof(OSVERSIONINFO));
371   ov.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
372   GetVersionEx(&ov);
373 
374   if((ov.dwPlatformId==VER_PLATFORM_WIN32_NT) &&
375      (ov.dwMajorVersion>4))
376     dw_access_flags = GENERIC_READ|GENERIC_WRITE;  /* add gen write on W2k/XP */
377   else dw_access_flags = GENERIC_READ;
378 
379   h_device_handle = CreateFile( psz_win32_drive,
380                                 dw_access_flags,
381                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
382                                 NULL,
383                                 OPEN_EXISTING,
384                                 0,
385                                 NULL );
386 
387   if( h_device_handle == INVALID_HANDLE_VALUE ) {
388     return DRIVER_OP_ERROR;
389   }
390 
391   b_success =
392     DeviceIoControl(h_device_handle, IOCTL_STORAGE_LOAD_MEDIA2,
393                     NULL, (DWORD) 0, NULL, 0, &dw_bytes_returned, NULL);
394 
395 
396   CloseHandle(h_device_handle);
397 
398   if ( b_success != 0 ) {
399     windows_error(CDIO_LOG_INFO, GetLastError());
400     return DRIVER_OP_ERROR;
401   }
402   return DRIVER_OP_SUCCESS;
403 #else
404   return DRIVER_OP_UNSUPPORTED;
405 #endif
406 }
407 
408 /**
409   Produce a text composed from the system SCSI address tuple
410   "Port,Path,Target,Lun" and store
411   it in generic_img_private_t.scsi_tuple.
412   To be accessed via cdio_get_arg("scsi-tuple-linux") or ("scsi-tuple").
413   Drivers which implement this code have to return 5 valid decimal numbers
414   separated by comma, or empty text if no such numbers are available.
415   @return   1=success , 0=failure
416 */
417 static int
set_scsi_tuple_win32ioctl(_img_private_t * env)418 set_scsi_tuple_win32ioctl(_img_private_t *env)
419 #ifdef WIN32
420 {
421   char tuple[160];
422   char dataBuffer[MAX_DATA_BUFFER];
423   PSCSI_ADDRESS scsiAddress = (PSCSI_ADDRESS) dataBuffer;
424   ULONG bytesReturned;
425 
426   memset(dataBuffer, 0, sizeof(dataBuffer));
427   if (DeviceIoControl(env->h_device_handle,
428                       IOCTL_SCSI_GET_ADDRESS,
429                       NULL,
430                       0,
431                       dataBuffer,
432                       sizeof(dataBuffer),
433                       &bytesReturned,
434                       FALSE
435                       )) {
436     snprintf(tuple, sizeof(tuple), "%d,%d,%d,%d",
437             scsiAddress->PortNumber,
438             scsiAddress->PathId,
439             scsiAddress->TargetId,
440             scsiAddress->Lun);
441     env->gen.scsi_tuple = strdup(tuple);
442     return 1;
443   } else {
444     /* No tuple. */
445     env->gen.scsi_tuple = strdup("");
446     return 0;
447   }
448 }
449 #else
450 {
451   env->gen.scsi_tuple = strdup("");
452   return 0;
453 }
454 #endif
455 
456 /**
457   Run a SCSI MMC command.
458 
459   p_user_data   private CD structure
460   u_timeout_ms  time in milliseconds we will wait for the command
461                 to complete. If this value is -1, use the default
462                 time-out value.
463   u_cdb         CDB length
464   p_cdb         CDB bytes. All values that are needed should be set on
465                 input.
466   e_direction   direction the transfer is to go.
467   p_buf         Buffer for data, both sending and receiving
468   u_buf         Size of buffer
469 
470   Return DRIVER_OP_SUCCESS if command completed successfully.
471  */
472 #ifdef USE_PASSTHROUGH_DIRECT
473 int
run_mmc_cmd_win32ioctl(void * p_user_data,unsigned int u_timeout_ms,unsigned int u_cdb,const mmc_cdb_t * p_cdb,cdio_mmc_direction_t e_direction,unsigned int u_buf,void * p_buf)474 run_mmc_cmd_win32ioctl( void *p_user_data,
475                         unsigned int u_timeout_ms,
476                         unsigned int u_cdb, const mmc_cdb_t * p_cdb,
477                         cdio_mmc_direction_t e_direction,
478                         unsigned int u_buf, /*in/out*/ void *p_buf )
479 {
480   _img_private_t *p_env = p_user_data;
481   SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
482 
483   BOOL b_success;
484   DWORD dw_bytes_returned;
485   char dummy_buf[2]; /* Used if we can't use p_buf. See below. */
486   int rc = DRIVER_OP_SUCCESS;
487   unsigned int u_swb_len =
488     sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
489 
490   p_env->gen.scsi_mmc_sense_valid = 0;
491   memset(&swb, 0, u_swb_len);
492 
493   swb.sptd.Length  = sizeof(SCSI_PASS_THROUGH_DIRECT);
494   swb.sptd.PathId  = 0;      /* SCSI card ID will be filled in
495                                 automatically */
496   swb.sptd.TargetId= 0;      /* SCSI target ID will also be filled in */
497   swb.sptd.Lun     = 0;      /* SCSI lun ID will also be filled in */
498   swb.sptd.CdbLength         = u_cdb;
499   swb.sptd.SenseInfoLength   = sizeof(swb.ucSenseBuf);
500   swb.sptd.DataIn            =
501     (SCSI_MMC_DATA_READ  == e_direction) ? SCSI_IOCTL_DATA_IN :
502     (SCSI_MMC_DATA_WRITE == e_direction) ? SCSI_IOCTL_DATA_OUT :
503     SCSI_IOCTL_DATA_UNSPECIFIED;
504 
505   /* MS Windows seems to flip out of the size of the buffer is 0 or
506      1. For the 1 byte case see: BUG: SCSI Pass Through Fails with
507      Invalid User Buffer Error http://support.microsoft.com/kb/259573
508      So in those cases we will provide our own.
509   */
510   if (u_buf <= 1) {
511     swb.sptd.DataBuffer         = &dummy_buf;
512     swb.sptd.DataTransferLength = 2;
513   } else {
514     swb.sptd.DataBuffer         = p_buf;
515     swb.sptd.DataTransferLength = u_buf;
516   }
517 
518   swb.sptd.TimeOutValue      = msecs2secs(u_timeout_ms);
519   swb.sptd.SenseInfoOffset   =
520     offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
521 
522   p_env->gen.scsi_mmc_sense_valid = 0;
523   memcpy(swb.sptd.Cdb, p_cdb, u_cdb);
524 
525   /* Send the command to drive */
526   b_success = DeviceIoControl(p_env->h_device_handle,
527                               IOCTL_SCSI_PASS_THROUGH_DIRECT,
528                               (void *)&swb,
529                               u_swb_len,
530                               &swb,
531                               u_swb_len,
532                               &dw_bytes_returned,
533                               NULL);
534 
535   if (u_buf == 1) memcpy(p_buf, &dummy_buf[0], 1);
536 
537   if ( 0 == b_success ) {
538     long int last_error = GetLastError();
539     windows_error(CDIO_LOG_INFO, last_error);
540     switch (last_error) {
541     case 87:
542       rc = DRIVER_OP_BAD_PARAMETER;
543       break;
544     default:
545       rc = DRIVER_OP_ERROR;
546     }
547   }
548 
549 #ifdef FIXED_ADDITIONAL_SENSE_BUF
550   /* Record SCSI sense reply for API call mmc_last_cmd_sense().
551    */
552   if (p_swb->ucSenseBuf.additional_sense_len) {
553     /* SCSI Primary Command standard
554        SPC 4.5.3, Table 26: 252 bytes legal, 263 bytes possible */
555     int i_sense_size = p_swb->ucSenseBuf.additional_sense_len + 8;
556     if (i_sense_size > sizeof(p_swb->ucSenseBuf)) {
557       cdio_warn("Sense size retuned %d is greater buffer size %d\n",
558 		i_sense_size, sizeof(p_swb->ucSenseBuf));
559       sense_size = sizeof(p_swb->ucSenseBuf);
560     }
561     memcpy((void *) p_env->gen.scsi_mmc_sense, p_swb->ucSenseBuf, i_sense_size);
562     p_env->gen.scsi_mmc_sense_valid = sense_size;
563     if (DRIVER_OP_SUCCESS == rc)
564       rc = DRIVER_OP_MMC_SENSE_DATA;
565   }
566 #endif
567   return rc;
568 }
569 #else
570 int
run_mmc_cmd_win32ioctl(void * p_user_data,unsigned int u_timeout_ms,unsigned int u_cdb,const mmc_cdb_t * p_cdb,cdio_mmc_direction_t e_direction,unsigned int u_buf,void * p_buf)571 run_mmc_cmd_win32ioctl( void *p_user_data,
572                         unsigned int u_timeout_ms,
573                         unsigned int u_cdb, const mmc_cdb_t * p_cdb,
574                         cdio_mmc_direction_t e_direction,
575                         unsigned int u_buf, /*in/out*/ void *p_buf )
576 {
577   _img_private_t *p_env = p_user_data;
578   SCSI_PASS_THROUGH_WITH_BUFFERS *p_sptwb;
579 
580   BOOL b_success;
581   DWORD dw_bytes_returned;
582   int rc = DRIVER_OP_SUCCESS;
583   unsigned int u_swb_len =
584     sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS) + u_buf;
585 
586   p_sptwb = malloc(u_swb_len);
587 
588   p_env->gen.scsi_mmc_sense_valid = 0;
589   memset(p_sptwb, 0, u_swb_len);
590 
591   p_sptwb->Spt.Length  = sizeof(SCSI_PASS_THROUGH);
592   p_sptwb->Spt.PathId  = 0;      /* SCSI card ID will be filled in
593                                    automatically */
594   p_sptwb->Spt.TargetId= 0;      /* SCSI target ID will also be filled in */
595   p_sptwb->Spt.Lun     = 0;      /* SCSI lun ID will also be filled in */
596   p_sptwb->Spt.CdbLength         = u_cdb;
597   p_sptwb->Spt.SenseInfoLength   = sizeof(p_sptwb->ucSenseBuf);
598   p_sptwb->Spt.DataIn            =
599     (SCSI_MMC_DATA_READ  == e_direction) ? SCSI_IOCTL_DATA_IN :
600     (SCSI_MMC_DATA_WRITE == e_direction) ? SCSI_IOCTL_DATA_OUT :
601     SCSI_IOCTL_DATA_UNSPECIFIED;
602 
603   if (SCSI_MMC_DATA_WRITE == e_direction) memcpy(&(p_sptwb->ucDataBuf),
604 						   p_buf, u_buf);
605 
606   p_sptwb->Spt.DataTransferLength= u_buf;
607   p_sptwb->Spt.TimeOutValue      = msecs2secs(u_timeout_ms);
608 
609   p_sptwb->Spt.DataBufferOffset =
610     offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
611   p_sptwb->Spt.SenseInfoOffset =
612     offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
613 
614   memcpy(p_sptwb->Spt.Cdb, p_cdb, u_cdb);
615 
616   /*printf("Test 3 Sizeof SCSI_PASS_THROUGH_DIRECT %d\n", sizeof(*p_sptwb));*/
617   /* Send the command to drive */
618   b_success = DeviceIoControl(p_env->h_device_handle,
619                               IOCTL_SCSI_PASS_THROUGH,
620                               (void *)p_sptwb,
621                               u_swb_len,
622                               p_sptwb,
623                               u_swb_len,
624                               &dw_bytes_returned,
625                               NULL);
626 
627   if ( 0 == b_success ) {
628     char buffer[100];
629     long int last_error = GetLastError();
630     snprintf(buffer, sizeof(buffer),
631 	     "MMC command code: 0x%x\n", p_cdb->field[0]);
632     windows_error(CDIO_LOG_INFO, last_error);
633     cdio_log(CDIO_LOG_INFO, buffer);
634     switch (last_error) {
635     case 87:
636       rc = DRIVER_OP_BAD_PARAMETER;
637       break;
638     default:
639       rc = DRIVER_OP_ERROR;
640     }
641   }
642 
643   memcpy(p_buf, &(p_sptwb->ucDataBuf), u_buf);
644 
645   /* Record SCSI sense reply for API call mmc_last_cmd_sense().
646    */
647   if (p_sptwb->Spt.ScsiStatus && p_sptwb->Spt.SenseInfoLength > 0) {
648     int i_sense_size = p_sptwb->Spt.SenseInfoLength;
649     if (i_sense_size > sizeof(p_sptwb->ucSenseBuf)) {
650       cdio_warn("sense size returned %d is greater buffer size %d\n",
651 		i_sense_size, (int)sizeof(p_sptwb->ucSenseBuf));
652       i_sense_size = sizeof(p_sptwb->ucSenseBuf);
653     }
654     memcpy((void *) p_env->gen.scsi_mmc_sense, &(p_sptwb->ucSenseBuf),
655 	   i_sense_size);
656     p_env->gen.scsi_mmc_sense_valid = p_sptwb->Spt.SenseInfoLength;
657   }
658   free(p_sptwb);
659 
660   return rc;
661 }
662 #endif
663 
664 /**
665   Get disc type associated with cd object.
666 */
667 static discmode_t
dvd_discmode_win32ioctl(_img_private_t * p_env)668 dvd_discmode_win32ioctl (_img_private_t *p_env)
669 {
670   discmode_t discmode=CDIO_DISC_MODE_NO_INFO;
671   driver_return_code_t rc;
672 
673   /* See if this is a DVD. */
674   cdio_dvd_struct_t dvd;  /* DVD READ STRUCT for layer 0. */
675 
676   dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL;
677   dvd.physical.layer_num = 0;
678 
679   rc = mmc_get_dvd_struct_physical_private (p_env, &run_mmc_cmd_win32ioctl,
680                                             &dvd);
681 
682   if (DRIVER_OP_SUCCESS == rc) {
683     switch(dvd.physical.layer[0].book_type) {
684     case CDIO_DVD_BOOK_DVD_ROM:  return CDIO_DISC_MODE_DVD_ROM;
685     case CDIO_DVD_BOOK_DVD_RAM:  return CDIO_DISC_MODE_DVD_RAM;
686     case CDIO_DVD_BOOK_DVD_R:    return CDIO_DISC_MODE_DVD_R;
687     case CDIO_DVD_BOOK_DVD_RW:   return CDIO_DISC_MODE_DVD_RW;
688     case CDIO_DVD_BOOK_DVD_PR:   return CDIO_DISC_MODE_DVD_PR;
689     case CDIO_DVD_BOOK_DVD_PRW:  return CDIO_DISC_MODE_DVD_PRW;
690     default: return CDIO_DISC_MODE_DVD_OTHER;
691     }
692   }
693   return discmode;
694 }
695 
696 
697 /**
698   Get disc type associated with cd object.
699 */
700 discmode_t
get_discmode_win32ioctl(_img_private_t * p_env)701 get_discmode_win32ioctl (_img_private_t *p_env)
702 {
703   track_t i_track;
704   discmode_t discmode;
705 
706   if (!p_env) return CDIO_DISC_MODE_ERROR;
707 
708   discmode = dvd_discmode_win32ioctl(p_env);
709 
710   if (CDIO_DISC_MODE_NO_INFO != discmode) return discmode;
711 
712   if (!p_env->gen.toc_init) read_toc_win32ioctl (p_env);
713 
714   if (!p_env->gen.toc_init) return CDIO_DISC_MODE_ERROR;
715 
716   for (i_track = p_env->gen.i_first_track;
717        i_track < p_env->gen.i_first_track + p_env->gen.i_tracks ;
718        i_track ++) {
719     track_format_t track_fmt=get_track_format_win32ioctl(p_env, i_track);
720 
721     switch(track_fmt) {
722     case TRACK_FORMAT_AUDIO:
723       switch(discmode) {
724         case CDIO_DISC_MODE_NO_INFO:
725           discmode = CDIO_DISC_MODE_CD_DA;
726           break;
727         case CDIO_DISC_MODE_CD_DA:
728         case CDIO_DISC_MODE_CD_MIXED:
729         case CDIO_DISC_MODE_ERROR:
730           /* No change*/
731           break;
732       default:
733           discmode = CDIO_DISC_MODE_CD_MIXED;
734       }
735       break;
736     case TRACK_FORMAT_XA:
737       switch(discmode) {
738         case CDIO_DISC_MODE_NO_INFO:
739           discmode = CDIO_DISC_MODE_CD_XA;
740           break;
741         case CDIO_DISC_MODE_CD_XA:
742         case CDIO_DISC_MODE_CD_MIXED:
743         case CDIO_DISC_MODE_ERROR:
744           /* No change*/
745           break;
746       default:
747         discmode = CDIO_DISC_MODE_CD_MIXED;
748       }
749       break;
750     case TRACK_FORMAT_DATA:
751       switch(discmode) {
752         case CDIO_DISC_MODE_NO_INFO:
753           discmode = CDIO_DISC_MODE_CD_DATA;
754           break;
755         case CDIO_DISC_MODE_CD_DATA:
756         case CDIO_DISC_MODE_CD_MIXED:
757         case CDIO_DISC_MODE_ERROR:
758           /* No change*/
759           break;
760       default:
761         discmode = CDIO_DISC_MODE_CD_MIXED;
762       }
763       break;
764     case TRACK_FORMAT_ERROR:
765     default:
766       discmode = CDIO_DISC_MODE_ERROR;
767     }
768   }
769   return discmode;
770 }
771 
772 /*
773   Returns a string that can be used in a CreateFile call if
774   c_drive letter is a character. If not NULL is returned.
775  */
776 
777 const char *
is_cdrom_win32ioctl(const char c_drive_letter)778 is_cdrom_win32ioctl(const char c_drive_letter)
779 {
780 #ifdef _XBOX
781   char sz_win32_drive_full[] = "\\\\.\\X:";
782   sz_win32_drive_full[4] = c_drive_letter;
783   return strdup(sz_win32_drive_full);
784 #else
785   UINT uDriveType;
786   char sz_win32_drive[4];
787 
788   sz_win32_drive[0]= c_drive_letter;
789   sz_win32_drive[1]=':';
790   sz_win32_drive[2]='\\';
791   sz_win32_drive[3]='\0';
792 
793   uDriveType = GetDriveType(sz_win32_drive);
794 
795   switch(uDriveType) {
796   case DRIVE_CDROM: {
797     char sz_win32_drive_full[] = "\\\\.\\X:";
798     sz_win32_drive_full[4] = c_drive_letter;
799     return strdup(sz_win32_drive_full);
800   }
801   default:
802     cdio_debug("Drive %c is not a CD-ROM", c_drive_letter);
803     return NULL;
804   }
805 #endif
806 }
807 
808 /**
809    Reads an audio device using the DeviceIoControl method into data
810    starting from lsn.  Returns 0 if no error.
811  */
812 driver_return_code_t
read_audio_sectors_win32ioctl(_img_private_t * p_env,void * data,lsn_t lsn,unsigned int nblocks)813 read_audio_sectors_win32ioctl (_img_private_t *p_env, void *data, lsn_t lsn,
814                                unsigned int nblocks)
815 {
816   DWORD dw_bytes_returned;
817   RAW_READ_INFO cdrom_raw;
818 
819   /* Initialize CDROM_RAW_READ structure */
820   cdrom_raw.DiskOffset.QuadPart = (long long) CDIO_CD_FRAMESIZE_RAW * lsn;
821   cdrom_raw.SectorCount = nblocks;
822   cdrom_raw.TrackMode = CDDA;
823 
824   if( DeviceIoControl( p_env->h_device_handle,
825                        IOCTL_CDROM_RAW_READ, &cdrom_raw,
826                        sizeof(RAW_READ_INFO), data,
827                        CDIO_CD_FRAMESIZE_RAW * nblocks,
828                        &dw_bytes_returned, NULL ) == 0 ) {
829     cdio_info("Error reading audio-mode lsn %lu\n)",
830                 (long unsigned int) lsn);
831     windows_error(CDIO_LOG_INFO, GetLastError());
832     return DRIVER_OP_ERROR;
833   }
834   return DRIVER_OP_SUCCESS;
835 }
836 
837 /**
838    Reads a single raw sector using the DeviceIoControl method into
839    data starting from lsn. Returns 0 if no error.
840  */
841 static int
read_raw_sector(_img_private_t * p_env,void * p_buf,lsn_t lsn)842 read_raw_sector (_img_private_t *p_env, void *p_buf, lsn_t lsn)
843 {
844   mmc_cdb_t cdb = {{0, }};
845 
846   /* ReadCD CDB12 command.  The values were taken from MMC1 draft paper. */
847   CDIO_MMC_SET_COMMAND      (cdb.field, CDIO_MMC_GPCMD_READ_CD);
848   CDIO_MMC_SET_READ_LBA     (cdb.field, lsn);
849   CDIO_MMC_SET_READ_LENGTH24(cdb.field, 1);
850 
851   cdb.field[9]=0xF8;  /* Raw read, 2352 bytes per sector */
852 
853   return run_mmc_cmd_win32ioctl(p_env, OP_TIMEOUT_MS,
854                                 mmc_get_cmd_len(cdb.field[0]),
855                                 &cdb, SCSI_MMC_DATA_READ,
856                                 CDIO_CD_FRAMESIZE_RAW, p_buf);
857 }
858 
859 /**
860    Reads a single mode2 sector using the DeviceIoControl method into
861    data starting from lsn. Returns 0 if no error.
862  */
863 int
read_mode2_sector_win32ioctl(_img_private_t * p_env,void * p_data,lsn_t lsn,bool b_form2)864 read_mode2_sector_win32ioctl (_img_private_t *p_env, void *p_data,
865                               lsn_t lsn, bool b_form2)
866 {
867   char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
868   int ret = read_raw_sector (p_env, buf, lsn);
869 
870   if ( 0 != ret) return ret;
871 
872   memcpy (p_data,
873           buf + CDIO_CD_SYNC_SIZE + CDIO_CD_XA_HEADER,
874           b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE);
875 
876   return 0;
877 
878 }
879 
880 /**
881    Reads a single mode2 sector using the DeviceIoControl method into
882    data starting from lsn. Returns 0 if no error.
883  */
884 int
read_mode1_sector_win32ioctl(_img_private_t * env,void * data,lsn_t lsn,bool b_form2)885 read_mode1_sector_win32ioctl (_img_private_t *env, void *data,
886                               lsn_t lsn, bool b_form2)
887 {
888   char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
889   int ret = read_raw_sector (env, buf, lsn);
890 
891   if ( 0 != ret) return ret;
892 
893   memcpy (data,
894           buf + CDIO_CD_SYNC_SIZE+CDIO_CD_HEADER_SIZE,
895           b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE);
896 
897   return 0;
898 
899 }
900 
901 /**
902   Initialize internal structures for CD device.
903  */
904 bool
init_win32ioctl(_img_private_t * env)905 init_win32ioctl (_img_private_t *env)
906 {
907 #ifdef WIN32
908   OSVERSIONINFO ov;
909 #endif
910 
911 #ifdef _XBOX
912   ANSI_STRING filename;
913   OBJECT_ATTRIBUTES attributes;
914   IO_STATUS_BLOCK status;
915   HANDLE hDevice;
916   NTSTATUS error;
917 #else
918   unsigned int len=strlen(env->gen.source_name);
919   char psz_win32_drive[7];
920   DWORD dw_access_flags;
921 #endif
922 
923   cdio_debug("using winNT/2K/XP ioctl layer");
924 
925 #ifdef WIN32
926   memset(&ov,0,sizeof(OSVERSIONINFO));
927   ov.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
928   GetVersionEx(&ov);
929 
930   if((ov.dwPlatformId==VER_PLATFORM_WIN32_NT) &&
931      (ov.dwMajorVersion>4))
932     dw_access_flags = GENERIC_READ|GENERIC_WRITE;  /* add gen write on W2k/XP */
933   else dw_access_flags = GENERIC_READ;
934 #endif
935 
936   if (cdio_is_device_win32(env->gen.source_name))
937   {
938 #ifdef _XBOX
939     //  Use XBOX cdrom, no matter what drive letter is given.
940     RtlInitAnsiString(&filename,"\\Device\\Cdrom0");
941     InitializeObjectAttributes(&attributes, &filename, OBJ_CASE_INSENSITIVE,
942                                NULL);
943     error = NtCreateFile( &hDevice,
944                           GENERIC_READ |SYNCHRONIZE | FILE_READ_ATTRIBUTES,
945                           &attributes,
946                           &status,
947                           NULL,
948                           0,
949                           FILE_SHARE_READ,
950                           FILE_OPEN,
951                           FILE_NON_DIRECTORY_FILE
952                           | FILE_SYNCHRONOUS_IO_NONALERT );
953 
954     if (!NT_SUCCESS(error))
955     {
956           return false;
957     }
958     env->h_device_handle = hDevice;
959 #else
960     snprintf( psz_win32_drive, sizeof(psz_win32_drive),
961                                       "\\\\.\\%c:",
962                                       env->gen.source_name[len-2] );
963 
964     env->h_device_handle = CreateFile( psz_win32_drive,
965                                        dw_access_flags,
966                                        FILE_SHARE_READ | FILE_SHARE_WRITE,
967                                        NULL,
968                                        OPEN_EXISTING,
969                                        0,
970                                        NULL );
971 
972     if( env->h_device_handle == INVALID_HANDLE_VALUE )
973     {
974           /* No good. try toggle write. */
975           dw_access_flags ^= GENERIC_WRITE;
976           env->h_device_handle = CreateFile( psz_win32_drive,
977                                              dw_access_flags,
978                                              FILE_SHARE_READ,
979                                              NULL,
980                                              OPEN_EXISTING,
981                                              0,
982                                              NULL );
983           if (env->h_device_handle == NULL)
984                 return false;
985     }
986 #endif
987     env->b_ioctl_init = true;
988     set_scsi_tuple_win32ioctl(env);
989     return true;
990   }
991   return false;
992 }
993 
994 /**
995   Read and cache the CD's Track Table of Contents and track info.
996   via a SCSI MMC READ_TOC (FULTOC).  Return true if successful or
997   false if an error.
998 */
999 static bool
read_fulltoc_win32mmc(_img_private_t * p_env)1000 read_fulltoc_win32mmc (_img_private_t *p_env)
1001 {
1002   mmc_cdb_t  cdb = {{0, }};
1003   CDROM_TOC_FULL  cdrom_toc_full;
1004   int             i_status, i, j;
1005   int             i_track_format = 0;
1006   int             i_seen_flag;
1007 
1008   /* Operation code */
1009   CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC);
1010 
1011   cdb.field[1] = 0x00;
1012 
1013   /* Format */
1014   cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC;
1015 
1016   memset(&cdrom_toc_full, 0, sizeof(cdrom_toc_full));
1017 
1018   /* Setup to read header, to get length of data */
1019   CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(cdrom_toc_full));
1020 
1021   i_status = run_mmc_cmd_win32ioctl (p_env, 1000*60*3,
1022                                      mmc_get_cmd_len(cdb.field[0]),
1023                                      &cdb, SCSI_MMC_DATA_READ,
1024                                      sizeof(cdrom_toc_full), &cdrom_toc_full);
1025 
1026   if ( 0 != i_status ) {
1027     cdio_debug ("SCSI MMC READ_TOC failed\n");
1028     return false;
1029   }
1030 
1031   i_seen_flag=0;
1032   for( i = 0 ; i <= CDIO_CD_MAX_TRACKS+3; i++ ) {
1033 
1034     if ( 0xA0 == cdrom_toc_full.TrackData[i].POINT ) {
1035       /* First track number */
1036       p_env->gen.i_first_track = cdrom_toc_full.TrackData[i].PMIN;
1037       i_track_format = cdrom_toc_full.TrackData[i].PSEC;
1038       i_seen_flag|=0x01;
1039     }
1040 
1041     if ( 0xA1 == cdrom_toc_full.TrackData[i].POINT ) {
1042       /* Last track number */
1043       p_env->gen.i_tracks =
1044         cdrom_toc_full.TrackData[i].PMIN - p_env->gen.i_first_track + 1;
1045       i_seen_flag|=0x02;
1046     }
1047 
1048     j = cdrom_toc_full.TrackData[i].POINT;
1049     if ( 0xA2 ==  j ) {
1050       /* Start position of the lead out */
1051       p_env->tocent[ p_env->gen.i_tracks ].start_lsn =
1052         cdio_lba_to_lsn(
1053                         cdio_msf3_to_lba(
1054                                          cdrom_toc_full.TrackData[i].PMIN,
1055                                          cdrom_toc_full.TrackData[i].PSEC,
1056                                          cdrom_toc_full.TrackData[i].PFRAME
1057                                          )
1058                         );
1059       p_env->tocent[ p_env->gen.i_tracks ].Control
1060         = cdrom_toc_full.TrackData[i].Control;
1061       p_env->tocent[ p_env->gen.i_tracks ].Format  = i_track_format;
1062       i_seen_flag|=0x04;
1063     }
1064 
1065     if (cdrom_toc_full.TrackData[i].POINT > 0
1066         && cdrom_toc_full.TrackData[i].POINT <= p_env->gen.i_tracks) {
1067       p_env->tocent[j-1].start_lsn =
1068         cdio_lba_to_lsn(
1069                         cdio_msf3_to_lba(
1070                                          cdrom_toc_full.TrackData[i].PMIN,
1071                                          cdrom_toc_full.TrackData[i].PSEC,
1072                                          cdrom_toc_full.TrackData[i].PFRAME
1073                                          )
1074                         );
1075       p_env->tocent[j-1].Control =
1076         cdrom_toc_full.TrackData[i].Control;
1077       p_env->tocent[j-1].Format  = i_track_format;
1078 
1079       set_track_flags(&(p_env->gen.track_flags[j]),
1080                       p_env->tocent[j-1].Control);
1081 
1082       cdio_debug("p_sectors: %i, %lu", i,
1083                  (unsigned long int) (p_env->tocent[i].start_lsn));
1084 
1085       if (cdrom_toc_full.TrackData[i].POINT == p_env->gen.i_tracks)
1086         i_seen_flag|=0x08;
1087     }
1088 
1089     if ( 0x0F == i_seen_flag ) break;
1090   }
1091   if ( 0x0F == i_seen_flag ) {
1092     p_env->gen.toc_init = true;
1093     return true;
1094   }
1095   return false;
1096 }
1097 
1098 /**
1099   Read and cache the CD's Track Table of Contents and track info.
1100   Return true if successful or false if an error.
1101 */
1102 bool
read_toc_win32ioctl(_img_private_t * p_env)1103 read_toc_win32ioctl (_img_private_t *p_env)
1104 {
1105   CDROM_TOC    cdrom_toc;
1106   DWORD        dw_bytes_returned;
1107   unsigned int i, j;
1108   bool         b_fulltoc_first;  /* Do we do fulltoc or DeviceIoControl
1109                                     first? */
1110   if ( ! p_env ) return false;
1111 
1112   /*
1113      The MMC5 spec says:
1114        For media other than CD, information may be fabricated in order
1115                                             ^^^ ^^
1116        to emulate a CD structure for the specific media.
1117 
1118      There is no requirement though that it *has* to and some DVD
1119      drives like one by Thompson for XBOX don't support a
1120      IOCTL_CDROM_READ_TOC for DVD's. So if we have a DVD we should not
1121      prefer getting the TOC via MMC.
1122 
1123      But on the other hand in GNU/Linux it is reported that using the
1124      TOC via MMC gives better information such as for CD DATA Form 2 (used
1125      in SVCDs). So if we *don't* have a DVD I think we want to try MMC
1126      first.
1127 
1128      Is this complicated enough? I could be wrong...
1129 
1130    */
1131   b_fulltoc_first = (CDIO_DISC_MODE_NO_INFO == dvd_discmode_win32ioctl(p_env));
1132 
1133   if ( b_fulltoc_first && read_fulltoc_win32mmc(p_env) ) return true;
1134 
1135   /* SCSI-MMC READ_TOC (FULTOC) read failed or we don't want to try it
1136      initiaily.  Try reading TOC via DeviceIoControl... */
1137 
1138   if( DeviceIoControl( p_env->h_device_handle,
1139                        IOCTL_CDROM_READ_TOC,
1140                        NULL, 0, &cdrom_toc, sizeof(CDROM_TOC),
1141                        &dw_bytes_returned, NULL ) == 0 ) {
1142     cdio_log_level_t loglevel = b_fulltoc_first
1143       ? CDIO_LOG_WARN : CDIO_LOG_DEBUG;
1144 
1145 
1146     cdio_log(loglevel, "could not read TOC");
1147     windows_error(loglevel, GetLastError());
1148 
1149 #ifdef RUN_MMC_CMD_WIN32IOCTL_FULLY_FIXED
1150     /* rocky: there is some brokenness in run_mmc_cmd_win32ioctl. So until that
1151        is fixed, not running the below will mitigate that somewhat. */
1152     if ( !b_fulltoc_first && read_fulltoc_win32mmc(p_env) ) return true;
1153 #endif
1154     return false;
1155   }
1156 
1157   p_env->gen.i_first_track = cdrom_toc.FirstTrack;
1158   p_env->gen.i_tracks  = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1;
1159 
1160   j = p_env->gen.i_first_track;
1161   for( i = 0 ; i <= p_env->gen.i_tracks ; i++, j++ ) {
1162     p_env->tocent[ i ].start_lsn =
1163       cdio_lba_to_lsn(
1164                       cdio_msf3_to_lba( cdrom_toc.TrackData[i].Address[1],
1165                                         cdrom_toc.TrackData[i].Address[2],
1166                                         cdrom_toc.TrackData[i].Address[3] )
1167                       );
1168     p_env->tocent[i].Control   = cdrom_toc.TrackData[i].Control;
1169     p_env->tocent[i].Format    = cdrom_toc.TrackData[i].Adr;
1170 
1171     p_env->gen.track_flags[j].preemphasis =
1172       p_env->tocent[i].Control & 0x1
1173       ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE;
1174 
1175     p_env->gen.track_flags[j].copy_permit =
1176       p_env->tocent[i].Control & 0x2
1177       ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE;
1178 
1179     p_env->gen.track_flags[j].channels =
1180       p_env->tocent[i].Control & 0x8 ? 4 : 2;
1181 
1182 
1183     cdio_debug("p_sectors: %i, %lu", i,
1184                (unsigned long int) (p_env->tocent[i].start_lsn));
1185   }
1186   p_env->gen.toc_init = true;
1187   return true;
1188 }
1189 
1190 /**
1191   Get the LSN of the first track of the last session of
1192   on the CD.
1193  */
1194 driver_return_code_t
get_last_session_win32ioctl(void * p_user_data,lsn_t * i_last_session)1195 get_last_session_win32ioctl (void *p_user_data,
1196                              /*out*/ lsn_t *i_last_session)
1197 {
1198   const _img_private_t *p_env = p_user_data;
1199   DWORD dw_bytes_returned;
1200   CDROM_TOC_SESSION_DATA session;
1201 
1202   bool b_success =
1203     DeviceIoControl(p_env->h_device_handle, IOCTL_CDROM_GET_LAST_SESSION,
1204                     NULL, (DWORD) 0, &session, sizeof(session), &dw_bytes_returned, NULL);
1205 
1206   if ( ! b_success ) {
1207     windows_error(CDIO_LOG_INFO, GetLastError());
1208     return DRIVER_OP_ERROR;
1209   }
1210 
1211   *i_last_session = (session.TrackData[0].Address[0] << 24) |
1212     (session.TrackData[0].Address[1] << 16) |
1213     (session.TrackData[0].Address[2] << 8) |
1214     (session.TrackData[0].Address[3]);
1215 
1216   return DRIVER_OP_SUCCESS;
1217 }
1218 
1219 /**
1220   Return the media catalog number MCN.
1221 
1222   Note: string is malloc'd so caller should free() then returned
1223   string when done with it.
1224 
1225  */
1226 char *
get_mcn_win32ioctl(const _img_private_t * p_env)1227 get_mcn_win32ioctl (const _img_private_t *p_env) {
1228 
1229   DWORD dw_bytes_returned;
1230   SUB_Q_MEDIA_CATALOG_NUMBER mcn;
1231   CDROM_SUB_Q_DATA_FORMAT q_data_format;
1232 
1233   memset( &mcn, 0, sizeof(mcn) );
1234 
1235   q_data_format.Format = CDIO_SUBCHANNEL_MEDIA_CATALOG;
1236 
1237   /* MSDN info on CDROM_SUB_Q_DATA_FORMAT says if Format is set to
1238      get MCN, track must be set 0.
1239    */
1240   q_data_format.Track=0;
1241 
1242   if( ! DeviceIoControl( p_env->h_device_handle,
1243                        IOCTL_CDROM_READ_Q_CHANNEL,
1244                        &q_data_format, sizeof(q_data_format),
1245                        &mcn, sizeof(mcn),
1246                        &dw_bytes_returned, NULL ) ) {
1247     cdio_warn( "could not read Q Channel at track %d", 1);
1248   } else if (mcn.Mcval)
1249     return strdup((const char *) mcn.MediaCatalog);
1250   return NULL;
1251 }
1252 
1253 /**
1254   Return the international standard recording code ISRC.
1255 
1256   Note: string is malloc'd so caller should free() then returned
1257   string when done with it.
1258 
1259  */
1260 char *
get_track_isrc_win32ioctl(const _img_private_t * p_env,track_t i_track)1261 get_track_isrc_win32ioctl (const _img_private_t *p_env, track_t i_track) {
1262 
1263   DWORD dw_bytes_returned;
1264   SUB_Q_TRACK_ISRC isrc;
1265   CDROM_SUB_Q_DATA_FORMAT q_data_format;
1266 
1267   memset( &isrc, 0, sizeof(isrc) );
1268 
1269   q_data_format.Format = CDIO_SUBCHANNEL_TRACK_ISRC;
1270   q_data_format.Track  = i_track;
1271 
1272   if( ! DeviceIoControl( p_env->h_device_handle,
1273                        IOCTL_CDROM_READ_Q_CHANNEL,
1274                        &q_data_format, sizeof(q_data_format),
1275                        &isrc, sizeof(isrc),
1276                        &dw_bytes_returned, NULL ) ) {
1277     cdio_warn( "could not read Q Channel at track %d", 1);
1278   } else if (isrc.Tcval)
1279     return strdup((const char *) isrc.TrackIsrc);
1280   return NULL;
1281 }
1282 
1283 /**
1284   Get the format (XA, DATA, AUDIO) of a track.
1285 */
1286 track_format_t
get_track_format_win32ioctl(const _img_private_t * env,track_t i_track)1287 get_track_format_win32ioctl(const _img_private_t *env, track_t i_track)
1288 {
1289   /* This is pretty much copied from the "badly broken" cdrom_count_tracks
1290      in linux/cdrom.c.
1291   */
1292 
1293   if (env->tocent[i_track - env->gen.i_first_track].Control & 0x04) {
1294     if (env->tocent[i_track - env->gen.i_first_track].Format == 0x10)
1295       return TRACK_FORMAT_CDI;
1296     else if (env->tocent[i_track - env->gen.i_first_track].Format == 0x20)
1297       return TRACK_FORMAT_XA;
1298     else
1299       return TRACK_FORMAT_DATA;
1300   } else
1301     return TRACK_FORMAT_AUDIO;
1302 }
1303 
1304 #endif /*HAVE_WIN32_CDROM*/
1305