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