1 /*
2 Copyright (C) 2003-2006, 2008, 2010-2012, 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 and implements low-level
20 control of the CD drive. Inspired by vlc's cdrom.h code
21 */
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #ifdef HAVE_STDBOOL_H
28 # include <stdbool.h>
29 #endif
30
31 #include <cdio/cdio.h>
32 #include <cdio/sector.h>
33 #include <cdio/util.h>
34 #include <cdio/mmc.h>
35 #include <cdio/logging.h>
36 #include "cdio_assert.h"
37 #include "cdio_private.h" /* protoype for cdio_is_device_win32 */
38
39 #include <string.h>
40
41 #ifdef HAVE_WIN32_CDROM
42
43 #include <ctype.h>
44 #include <stdio.h>
45
46 #ifdef HAVE_STDLIB_H
47 #include <stdlib.h>
48 #endif
49
50 #ifdef HAVE_ERRNO_H
51 #include <errno.h>
52 #endif
53
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57
58 #ifdef HAVE_FCNTL_H
59 #include <fcntl.h>
60 #endif
61
62 #include <windows.h>
63 #include <winioctl.h>
64 #include "win32.h"
65
66 #ifdef HAVE_SYS_STAT_H
67 #include <sys/stat.h>
68 #endif
69
70 #ifdef HAVE_SYS_TYPES_H
71 #include <sys/types.h>
72 #endif
73
74 #if defined (_MSC_VER) || defined (_XBOX)
75 #undef IN
76 #else
77 #include "aspi32.h"
78 #endif
79
80 #ifdef _XBOX
81 #include "stdint.h"
82 #include <xtl.h>
83 #define WIN_NT 1
84 #else
85 #define WIN_NT ( GetVersion() < 0x80000000 )
86 #endif
87
88 /* mingw-w64 defines this to lseek64 when LFS is enabled */
89 #ifdef lseek
90 # undef lseek
91 #endif
92
93 /*!
94 Set the volume of an audio CD.
95
96 @param p_cdio the CD object to be acted upon.
97
98 */
99 static driver_return_code_t
audio_get_volume_win32(void * p_user_data,cdio_audio_volume_t * p_volume)100 audio_get_volume_win32 ( void *p_user_data,
101 /*out*/ cdio_audio_volume_t *p_volume)
102 {
103 if ( WIN_NT ) {
104 return audio_get_volume_win32ioctl (p_user_data, p_volume);
105 } else {
106 return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */
107 }
108 }
109
110 /*!
111 Pause playing CD through analog output
112
113 @param p_cdio the CD object to be acted upon.
114 */
115 static driver_return_code_t
audio_pause_win32(void * p_user_data)116 audio_pause_win32 (void *p_user_data)
117 {
118 if ( WIN_NT ) {
119 return audio_pause_win32ioctl (p_user_data);
120 } else {
121 return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */
122 }
123 }
124
125 /*!
126 Playing CD through analog output at the given MSF.
127
128 @param p_cdio the CD object to be acted upon.
129 */
130 static driver_return_code_t
audio_play_msf_win32(void * p_user_data,msf_t * p_start_msf,msf_t * p_end_msf)131 audio_play_msf_win32 (void *p_user_data, msf_t *p_start_msf, msf_t *p_end_msf)
132 {
133 if ( WIN_NT ) {
134 return audio_play_msf_win32ioctl (p_user_data, p_start_msf, p_end_msf);
135 } else {
136 return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */
137 }
138 }
139
140 /*!
141 Read Audio Subchannel information
142
143 @param p_cdio the CD object to be acted upon.
144
145 */
146 static driver_return_code_t
audio_read_subchannel_win32(void * p_user_data,cdio_subchannel_t * p_subchannel)147 audio_read_subchannel_win32 (void *p_user_data,
148 cdio_subchannel_t *p_subchannel)
149 {
150 if ( WIN_NT ) {
151 return audio_read_subchannel_win32ioctl (p_user_data, p_subchannel);
152 } else {
153 return audio_read_subchannel_mmc(p_user_data, p_subchannel);
154 }
155 }
156
157 /*!
158 Resume playing an audio CD.
159
160 @param p_cdio the CD object to be acted upon.
161
162 */
163 static driver_return_code_t
audio_resume_win32(void * p_user_data)164 audio_resume_win32 (void *p_user_data)
165 {
166 if ( WIN_NT ) {
167 return audio_resume_win32ioctl (p_user_data);
168 } else {
169 return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */
170 }
171 }
172
173 /*!
174 Set the volume of an audio CD.
175
176 @param p_cdio the CD object to be acted upon.
177
178 */
179 static driver_return_code_t
audio_set_volume_win32(void * p_user_data,cdio_audio_volume_t * p_volume)180 audio_set_volume_win32 ( void *p_user_data, cdio_audio_volume_t *p_volume)
181 {
182 if ( WIN_NT ) {
183 return audio_set_volume_win32ioctl (p_user_data, p_volume);
184 } else {
185 return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */
186 }
187 }
188
189 static driver_return_code_t
audio_stop_win32(void * p_user_data)190 audio_stop_win32 ( void *p_user_data)
191 {
192 if ( WIN_NT ) {
193 return audio_stop_win32ioctl (p_user_data);
194 } else {
195 return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */
196 }
197 }
198
199 /* General ioctl() CD-ROM command function */
200 static bool
_cdio_mciSendCommand(int id,UINT msg,DWORD flags,void * arg)201 _cdio_mciSendCommand(int id, UINT msg, DWORD flags, void *arg)
202 {
203 #ifdef _XBOX
204 return false;
205 #else
206 MCIERROR mci_error;
207
208 mci_error = mciSendCommand(id, msg, flags, (DWORD_PTR)arg);
209 if ( mci_error ) {
210 char error[256];
211
212 mciGetErrorString(mci_error, error, 256);
213 cdio_warn("mciSendCommand() error: %s", error);
214 }
215 return(mci_error == 0);
216 #endif
217 }
218
219 static access_mode_t
str_to_access_mode_win32(const char * psz_access_mode)220 str_to_access_mode_win32(const char *psz_access_mode)
221 {
222 const access_mode_t default_access_mode =
223 WIN_NT ? _AM_IOCTL : _AM_ASPI;
224
225 if (NULL==psz_access_mode) return default_access_mode;
226
227 if (!strcmp(psz_access_mode, "ioctl"))
228 return _AM_IOCTL;
229 else if (!strcmp(psz_access_mode, "ASPI")) {
230 #ifdef _XBOX
231 cdio_warn ("XBOX doesn't support access type: %s. Default used instead.",
232 psz_access_mode);
233 return default_access_mode;
234 #else
235 return _AM_ASPI;
236 #endif
237 } else if (!strcmp(psz_access_mode, "MMC_RDWR")) {
238 return _AM_MMC_RDWR;
239 } else if (!strcmp(psz_access_mode, "MMC_RDWR_EXCL")) {
240 return _AM_MMC_RDWR_EXCL;
241 } else {
242 cdio_warn ("unknown access type: %s. Default used instead.",
243 psz_access_mode);
244 return default_access_mode;
245 }
246 }
247
248 static discmode_t
get_discmode_win32(void * p_user_data)249 get_discmode_win32(void *p_user_data)
250 {
251 _img_private_t *p_env = p_user_data;
252
253 if (p_env->hASPI) {
254 return get_discmode_aspi (p_env);
255 } else {
256 return get_discmode_win32ioctl (p_env);
257 }
258 }
259
260 static driver_return_code_t
get_last_session_win32(void * p_user_data,lsn_t * i_last_session)261 get_last_session_win32(void *p_user_data,
262 /*out*/ lsn_t *i_last_session) {
263 if ( WIN_NT ) {
264 return get_last_session_win32ioctl(p_user_data, i_last_session);
265 } else {
266 return DRIVER_OP_UNSUPPORTED;
267 }
268 }
269
270 static const char *
is_cdrom_win32(const char drive_letter)271 is_cdrom_win32(const char drive_letter) {
272 if ( WIN_NT ) {
273 return is_cdrom_win32ioctl (drive_letter);
274 } else {
275 return is_cdrom_aspi(drive_letter);
276 }
277 }
278
279 /*!
280 Run a SCSI MMC command.
281
282 env private CD structure
283 i_timeout_ms time in milliseconds we will wait for the command
284 to complete. If this value is -1, use the default
285 time-out value.
286 p_buf Buffer for data, both sending and receiving
287 i_buf Size of buffer
288 e_direction direction the transfer is to go.
289 cdb CDB bytes. All values that are needed should be set on
290 input. We'll figure out what the right CDB length should be.
291
292 Return 0 if command completed successfully.
293 */
294 static int
run_mmc_cmd_win32(void * p_user_data,unsigned int i_timeout_ms,unsigned int i_cdb,const mmc_cdb_t * p_cdb,cdio_mmc_direction_t e_direction,unsigned int i_buf,void * p_buf)295 run_mmc_cmd_win32( void *p_user_data, unsigned int i_timeout_ms,
296 unsigned int i_cdb, const mmc_cdb_t *p_cdb,
297 cdio_mmc_direction_t e_direction,
298 unsigned int i_buf, /*in/out*/ void *p_buf )
299 {
300 _img_private_t *p_env = p_user_data;
301
302 if (p_env->hASPI) {
303 return run_mmc_cmd_aspi( p_env, i_timeout_ms, i_cdb, p_cdb,
304 e_direction, i_buf, p_buf );
305 } else {
306 return run_mmc_cmd_win32ioctl( p_env, i_timeout_ms, i_cdb, p_cdb,
307 e_direction, i_buf, p_buf );
308 }
309 }
310
311 /*!
312 Initialize CD device.
313 */
314 static bool
init_win32(void * p_user_data)315 init_win32 (void *p_user_data)
316 {
317 _img_private_t *p_env = p_user_data;
318 bool b_ret;
319 if (p_env->gen.init) {
320 cdio_error ("init called more than once");
321 return false;
322 }
323
324 p_env->gen.init = true;
325 p_env->gen.toc_init = false;
326 p_env->gen.b_cdtext_error = false;
327 p_env->gen.fd = open (p_env->gen.source_name, O_RDONLY|O_BINARY, 0);
328
329 /* Initializations */
330 p_env->h_device_handle = NULL;
331 p_env->i_sid = 0;
332 p_env->hASPI = 0;
333 p_env->lpSendCommand = 0;
334 p_env->b_aspi_init = false;
335 p_env->b_ioctl_init = false;
336
337
338 switch (p_env->access_mode) {
339 case _AM_IOCTL:
340 case _AM_MMC_RDWR:
341 case _AM_MMC_RDWR_EXCL:
342 b_ret = init_win32ioctl(p_env);
343 break;
344 case _AM_ASPI:
345 b_ret = init_aspi(p_env);
346 break;
347 default:
348 return 0;
349 }
350
351 /* It looks like get_media_changed_mmc will always
352 return 1 (media changed) on the first call. So
353 we call it here to clear that flag. We may have
354 to rethink this if there's a problem doing this
355 extra work down the line. */
356 if (b_ret) get_media_changed_mmc(p_user_data);
357 return b_ret;
358 }
359
360 /*!
361 Release and free resources associated with cd.
362 */
363 static void
free_win32(void * p_user_data)364 free_win32 (void *p_user_data)
365 {
366 _img_private_t *p_env = p_user_data;
367
368 if (NULL == p_env) return;
369 if (p_env->gen.fd >= 0) close(p_env->gen.fd);
370
371 free (p_env->gen.source_name);
372
373 if( p_env->h_device_handle )
374 CloseHandle( p_env->h_device_handle );
375 if( p_env->hASPI )
376 FreeLibrary( p_env->hASPI );
377
378 free (p_env);
379 }
380
381 /*!
382 Reads an audio device into data starting from lsn.
383 Returns 0 if no error.
384 */
385 static int
read_audio_sectors(void * p_user_data,void * p_buf,lsn_t i_lsn,unsigned int i_blocks)386 read_audio_sectors (void *p_user_data, void *p_buf, lsn_t i_lsn,
387 unsigned int i_blocks)
388 {
389 _img_private_t *p_env = p_user_data;
390 if ( p_env->hASPI ) {
391 return read_audio_sectors_aspi( p_env, p_buf, i_lsn, i_blocks );
392 } else {
393 #if 0
394 return read_audio_sectors_win32ioctl( p_env, p_buf, i_lsn, i_blocks );
395 #else
396 return mmc_read_sectors( p_env->gen.cdio, p_buf, i_lsn,
397 CDIO_MMC_READ_TYPE_CDDA, i_blocks);
398 #endif
399 }
400 }
401
402 /*!
403 Reads an audio device into data starting from lsn.
404 Returns 0 if no error.
405 */
406 static driver_return_code_t
read_data_sectors_win32(void * p_user_data,void * p_buf,lsn_t i_lsn,uint16_t i_blocksize,uint32_t i_blocks)407 read_data_sectors_win32 (void *p_user_data, void *p_buf, lsn_t i_lsn,
408 uint16_t i_blocksize, uint32_t i_blocks)
409 {
410 driver_return_code_t rc =
411 read_data_sectors_mmc(p_user_data, p_buf, i_lsn, i_blocksize, i_blocks);
412 if ( DRIVER_OP_SUCCESS != rc ) {
413 /* Try using generic "cooked" mode. */
414 return read_data_sectors_generic( p_user_data, p_buf, i_lsn,
415 i_blocksize, i_blocks );
416 }
417 return DRIVER_OP_SUCCESS;
418 }
419
420 /*!
421 Reads a single mode1 sector from cd device into data starting from
422 lsn. Returns 0 if no error.
423 */
424 static int
read_mode1_sector_win32(void * p_user_data,void * p_buf,lsn_t lsn,bool b_form2)425 read_mode1_sector_win32 (void *p_user_data, void *p_buf, lsn_t lsn,
426 bool b_form2)
427 {
428 _img_private_t *p_env = p_user_data;
429
430 if (p_env->gen.ioctls_debugged == 75)
431 cdio_debug ("only displaying every 75th ioctl from now on");
432
433 if (p_env->gen.ioctls_debugged == 30 * 75)
434 cdio_debug ("only displaying every 30*75th ioctl from now on");
435
436 if (p_env->gen.ioctls_debugged < 75
437 || (p_env->gen.ioctls_debugged < (30 * 75)
438 && p_env->gen.ioctls_debugged % 75 == 0)
439 || p_env->gen.ioctls_debugged % (30 * 75) == 0)
440 cdio_debug ("reading %lu", (unsigned long int) lsn);
441
442 p_env->gen.ioctls_debugged++;
443
444 if ( p_env->hASPI ) {
445 return read_mode1_sector_aspi( p_env, p_buf, lsn, b_form2 );
446 } else {
447 return read_mode1_sector_win32ioctl( p_env, p_buf, lsn, b_form2 );
448 }
449 }
450
451 /*!
452 Reads nblocks of mode1 sectors from cd device into data starting
453 from lsn.
454 Returns 0 if no error.
455 */
456 static int
read_mode1_sectors_win32(void * p_user_data,void * p_buf,lsn_t lsn,bool b_form2,unsigned int nblocks)457 read_mode1_sectors_win32 (void *p_user_data, void *p_buf, lsn_t lsn,
458 bool b_form2, unsigned int nblocks)
459 {
460 _img_private_t *p_env = p_user_data;
461 int i;
462 int retval;
463
464 for (i = 0; i < nblocks; i++) {
465 if (b_form2) {
466 retval = read_mode1_sector_win32 (p_env, ((char *)p_buf) +
467 (M2RAW_SECTOR_SIZE * i),
468 lsn + i, true);
469 if ( retval ) return retval;
470 } else {
471 char buf[M2RAW_SECTOR_SIZE] = { 0, };
472 if ( (retval = read_mode1_sector_win32 (p_env, buf, lsn + i, false)) )
473 return retval;
474
475 memcpy (((char *)p_buf) + (CDIO_CD_FRAMESIZE * i),
476 buf, CDIO_CD_FRAMESIZE);
477 }
478 }
479 return 0;
480 }
481
482 /*!
483 Reads a single mode2 sector from cd device into data starting
484 from lsn. Returns 0 if no error.
485 */
486 static int
read_mode2_sector_win32(void * p_user_data,void * data,lsn_t lsn,bool b_form2)487 read_mode2_sector_win32 (void *p_user_data, void *data, lsn_t lsn,
488 bool b_form2)
489 {
490 char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
491 _img_private_t *p_env = p_user_data;
492
493 if (p_env->gen.ioctls_debugged == 75)
494 cdio_debug ("only displaying every 75th ioctl from now on");
495
496 if (p_env->gen.ioctls_debugged == 30 * 75)
497 cdio_debug ("only displaying every 30*75th ioctl from now on");
498
499 if (p_env->gen.ioctls_debugged < 75
500 || (p_env->gen.ioctls_debugged < (30 * 75)
501 && p_env->gen.ioctls_debugged % 75 == 0)
502 || p_env->gen.ioctls_debugged % (30 * 75) == 0)
503 cdio_debug ("reading %lu", (unsigned long int) lsn);
504
505 p_env->gen.ioctls_debugged++;
506
507 if ( p_env->hASPI ) {
508 int ret;
509 ret = read_mode2_sector_aspi(p_user_data, buf, lsn, 1);
510 if( ret != 0 ) return ret;
511 if (b_form2)
512 memcpy (data, buf, M2RAW_SECTOR_SIZE);
513 else
514 memcpy (((char *)data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE);
515 return 0;
516 } else {
517 return read_mode2_sector_win32ioctl( p_env, data, lsn, b_form2 );
518 }
519 }
520
521 /*!
522 Reads nblocks of mode2 sectors from cd device into data starting
523 from lsn.
524 Returns 0 if no error.
525 */
526 static int
read_mode2_sectors_win32(void * p_user_data,void * data,lsn_t lsn,bool b_form2,unsigned int i_blocks)527 read_mode2_sectors_win32 (void *p_user_data, void *data, lsn_t lsn,
528 bool b_form2, unsigned int i_blocks)
529 {
530 int i;
531 int retval;
532 unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
533
534 for (i = 0; i < i_blocks; i++) {
535 if ( (retval = read_mode2_sector_win32 (p_user_data,
536 ((char *)data) + (blocksize * i),
537 lsn + i, b_form2)) )
538 return retval;
539 }
540 return 0;
541 }
542
543 /*!
544 Return the size of the CD in logical block address (LBA) units.
545 */
546 static lsn_t
get_disc_last_lsn_win32(void * p_user_data)547 get_disc_last_lsn_win32 (void *p_user_data)
548 {
549 _img_private_t *p_env = p_user_data;
550
551 return p_env->tocent[p_env->gen.i_tracks].start_lsn;
552 }
553
554 /*!
555 Set the key "arg" to "value" in source device.
556 */
557 static int
set_arg_win32(void * p_user_data,const char key[],const char value[])558 set_arg_win32 (void *p_user_data, const char key[], const char value[])
559 {
560 _img_private_t *p_env = p_user_data;
561
562 if (!strcmp (key, "source"))
563 {
564 if (!value)
565 return -2;
566
567 free (p_env->gen.source_name);
568
569 p_env->gen.source_name = strdup (value);
570 }
571 else if (!strcmp (key, "access-mode"))
572 {
573 p_env->access_mode = str_to_access_mode_win32(value);
574 if (p_env->access_mode == _AM_ASPI && !p_env->b_aspi_init)
575 return init_aspi(p_env) ? 1 : -3;
576 else if (p_env->access_mode == _AM_IOCTL && !p_env->b_ioctl_init)
577 return init_win32ioctl(p_env) ? 1 : -3;
578 else
579 return -4;
580 return 0;
581 }
582 else
583 return -1;
584
585 return 0;
586 }
587
588 /*!
589 Read and cache the CD's Track Table of Contents and track info.
590 Return true if successful or false if an error.
591 */
592 static bool
read_toc_win32(void * p_user_data)593 read_toc_win32 (void *p_user_data)
594 {
595 _img_private_t *p_env = p_user_data;
596 bool ret;
597 if( p_env->hASPI ) {
598 ret = read_toc_aspi( p_env );
599 } else {
600 ret = read_toc_win32ioctl( p_env );
601 }
602 if (ret) p_env->gen.toc_init = true ;
603 return ret;
604 }
605
606 /*!
607 Close media tray.
608 */
609 static driver_return_code_t
open_close_media_win32(const char * psz_win32_drive,DWORD command_flags)610 open_close_media_win32 (const char *psz_win32_drive, DWORD command_flags)
611 {
612 #ifdef _XBOX
613 return DRIVER_OP_UNSUPPORTED;
614 #else
615 MCI_OPEN_PARMS op;
616 DWORD i_flags;
617 int ret;
618
619 memset( &op, 0, sizeof(MCI_OPEN_PARMS) );
620 op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO;
621 op.lpstrElementName = psz_win32_drive;
622
623 /* Set the flags for the device type */
624 i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID |
625 MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE;
626
627 if( _cdio_mciSendCommand( 0, MCI_OPEN, i_flags, &op ) ) {
628 /* Eject disc */
629 ret = _cdio_mciSendCommand( op.wDeviceID, MCI_SET, command_flags, 0 ) == 0;
630 /* Release access to the device */
631 _cdio_mciSendCommand( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 );
632 } else
633 ret = DRIVER_OP_ERROR;
634
635 return ret;
636 #endif
637 }
638
639 /*!
640 Eject media.
641 */
642 static driver_return_code_t
eject_media_win32(void * p_user_data)643 eject_media_win32 (void *p_user_data)
644 {
645 const _img_private_t *p_env = p_user_data;
646 char psz_drive[4];
647 unsigned int i_device = strlen(p_env->gen.source_name);
648
649 strcpy( psz_drive, "X:" );
650 if (6 == i_device) {
651 psz_drive[0] = p_env->gen.source_name[4];
652 } else if (2 == i_device) {
653 psz_drive[0] = p_env->gen.source_name[0];
654 } else {
655 cdio_info ("Can't pick out drive letter from device %s",
656 p_env->gen.source_name);
657 return DRIVER_OP_ERROR;
658 }
659
660 return open_close_media_win32(psz_drive, MCI_SET_DOOR_OPEN);
661 }
662
663 static bool
is_mmc_supported(void * user_data)664 is_mmc_supported(void *user_data)
665 {
666 _img_private_t *env = user_data;
667 switch (env->access_mode) {
668 case _AM_NONE:
669 return false;
670 case _AM_IOCTL:
671 case _AM_ASPI:
672 case _AM_MMC_RDWR:
673 case _AM_MMC_RDWR_EXCL:
674 return true;
675 }
676 /* Not reached. */
677 return false;
678 }
679
680 /*!
681 Return the value associated with the key "arg".
682 */
683 static const char *
get_arg_win32(void * p_user_data,const char key[])684 get_arg_win32 (void *p_user_data, const char key[])
685 {
686 _img_private_t *p_env = p_user_data;
687
688 if (!strcmp (key, "source")) {
689 return p_env->gen.source_name;
690 } else if (!strcmp (key, "access-mode")) {
691 switch (p_env->access_mode) {
692 case _AM_IOCTL:
693 return "ioctl";
694 case _AM_ASPI:
695 return "ASPI";
696 case _AM_MMC_RDWR:
697 return "MMC_RDWR";
698 case _AM_MMC_RDWR_EXCL:
699 return "MMC_RDWR_EXCL";
700 case _AM_NONE:
701 return "no access method";
702 }
703 } else if (!strcmp (key, "scsi-tuple")) {
704 return p_env->gen.scsi_tuple;
705 } else if (!strcmp (key, "mmc-supported?")) {
706 return is_mmc_supported(p_user_data) ? "true" : "false";
707 }
708 return NULL;
709 }
710
711 /*!
712 Return the media catalog number MCN.
713
714 Note: string is malloc'd so caller should free() then returned
715 string when done with it.
716
717 */
718 static char *
_cdio_get_mcn(const void * p_user_data)719 _cdio_get_mcn (const void *p_user_data) {
720 const _img_private_t *p_env = p_user_data;
721
722 if( p_env->hASPI ) {
723 return mmc_get_mcn( p_env->gen.cdio );
724 } else {
725 return get_mcn_win32ioctl(p_env);
726 }
727 }
728
729 /*!
730 Return the international standard recording code ISRC.
731
732 Note: string is malloc'd so caller should free() then returned
733 string when done with it.
734
735 */
736 static char *
_cdio_get_track_isrc(const void * p_user_data,track_t i_track)737 _cdio_get_track_isrc (const void *p_user_data, track_t i_track) {
738 const _img_private_t *p_env = p_user_data;
739
740 if( p_env->hASPI ) {
741 return mmc_get_track_isrc( p_env->gen.cdio, i_track );
742 } else {
743 return get_track_isrc_win32ioctl(p_env, i_track);
744 }
745 }
746
747 /*!
748 Get format of track.
749 */
750 static track_format_t
_cdio_get_track_format(void * p_obj,track_t i_track)751 _cdio_get_track_format(void *p_obj, track_t i_track)
752 {
753 _img_private_t *p_env = p_obj;
754
755 if ( !p_env ) return TRACK_FORMAT_ERROR;
756
757 if (!p_env->gen.toc_init)
758 if (!read_toc_win32 (p_env)) return TRACK_FORMAT_ERROR;
759
760 if ( i_track < p_env->gen.i_first_track
761 || i_track >= p_env->gen.i_tracks + p_env->gen.i_first_track )
762 return TRACK_FORMAT_ERROR;
763
764 if( p_env->hASPI ) {
765 return get_track_format_aspi(p_env, i_track);
766 } else {
767 return get_track_format_win32ioctl(p_env, i_track);
768 }
769 }
770
771 /*!
772 Return true if we have XA data (green, mode2 form1) or
773 XA data (green, mode2 form2). That is track begins:
774 sync - header - subheader
775 12 4 - 8
776
777 FIXME: there's gotta be a better design for this and get_track_format?
778 */
779 static bool
_cdio_get_track_green(void * p_obj,track_t i_track)780 _cdio_get_track_green(void *p_obj, track_t i_track)
781 {
782 _img_private_t *p_env = p_obj;
783
784 switch (_cdio_get_track_format(p_env, i_track)) {
785 case TRACK_FORMAT_XA:
786 return true;
787 case TRACK_FORMAT_ERROR:
788 case TRACK_FORMAT_CDI:
789 case TRACK_FORMAT_AUDIO:
790 return false;
791 case TRACK_FORMAT_DATA:
792 if (_AM_ASPI == p_env->access_mode )
793 return ((p_env->tocent[i_track-p_env->gen.i_first_track].Control & 8) != 0);
794 default:
795 break;
796 }
797
798 /* FIXME: Dunno if this is the right way, but it's what
799 I was using in cd-info for a while.
800 */
801 return ((p_env->tocent[i_track-p_env->gen.i_first_track].Control & 2) != 0);
802 }
803
804 /*!
805 Return the starting MSF (minutes/secs/frames) for track number
806 i_tracks in obj. Track numbers start at 1.
807 The "leadout" track is specified either by
808 using i_tracks LEADOUT_TRACK or the total tracks+1.
809 False is returned if there is no track entry.
810 */
811 static bool
_cdio_get_track_msf(void * p_user_data,track_t i_tracks,msf_t * p_msf)812 _cdio_get_track_msf(void *p_user_data, track_t i_tracks, msf_t *p_msf)
813 {
814 _img_private_t *p_env = p_user_data;
815
816 if (!p_msf) return false;
817
818 if (!p_env->gen.toc_init)
819 if (!read_toc_win32 (p_env)) return false;
820
821 if (i_tracks == CDIO_CDROM_LEADOUT_TRACK) i_tracks = p_env->gen.i_tracks+1;
822
823 if (i_tracks > p_env->gen.i_tracks+1 || i_tracks == 0) {
824 return false;
825 } else {
826 cdio_lsn_to_msf(p_env->tocent[i_tracks-1].start_lsn, p_msf);
827 return true;
828 }
829 }
830
831 #endif /* HAVE_WIN32_CDROM */
832
833 /*!
834 Return an array of strings giving possible CD devices.
835 */
836 char **
cdio_get_devices_win32(void)837 cdio_get_devices_win32 (void)
838 {
839 #ifndef HAVE_WIN32_CDROM
840 return NULL;
841 #else
842 char **drives = NULL;
843 unsigned int num_drives=0;
844 char drive_letter;
845
846 /* Scan the system for CD-ROM drives.
847 */
848
849 #ifdef FINISHED
850 /* Now check the currently mounted CD drives */
851 if (NULL != (ret_drive = cdio_check_mounts("/etc/mtab"))) {
852 cdio_add_device_list(&drives, drive, &num_drives);
853 }
854
855 /* Finally check possible mountable drives in /etc/fstab */
856 if (NULL != (ret_drive = cdio_check_mounts("/etc/fstab"))) {
857 cdio_add_device_list(&drives, drive, &num_drives);
858 }
859 #endif
860
861 /* Scan the system for CD-ROM drives.
862 Not always 100% reliable, so use the USE_MNTENT code above first.
863 */
864 for (drive_letter='A'; drive_letter <= 'Z'; drive_letter++) {
865 const char *drive_str=is_cdrom_win32(drive_letter);
866 if (drive_str != NULL) {
867 cdio_add_device_list(&drives, drive_str, &num_drives);
868 }
869 }
870 cdio_add_device_list(&drives, NULL, &num_drives);
871 return drives;
872 #endif /*HAVE_WIN32_CDROM*/
873 }
874
875 /*!
876 Return a string containing the default CD device if none is specified.
877 if CdIo is NULL (we haven't initialized a specific device driver),
878 then find a suitable one and return the default device for that.
879
880 NULL is returned if we couldn't get a default device.
881 */
882 char *
cdio_get_default_device_win32(void)883 cdio_get_default_device_win32(void)
884 {
885
886 #ifdef HAVE_WIN32_CDROM
887 char drive_letter;
888
889 for (drive_letter='A'; drive_letter <= 'Z'; drive_letter++) {
890 const char *drive_str=is_cdrom_win32(drive_letter);
891 if (drive_str != NULL) {
892 return strdup(drive_str);
893 }
894 }
895 #endif
896 return NULL;
897 }
898
899 /*!
900 Return true if source_name could be a device containing a CD-ROM and
901 we are on a MS Windows platform.
902 */
903 bool
cdio_is_device_win32(const char * source_name)904 cdio_is_device_win32(const char *source_name)
905 {
906 #ifdef HAVE_WIN32_CDROM
907 unsigned int len;
908
909 if (NULL == source_name) return false;
910 len = strlen(source_name);
911
912 if ((len == 2) && isalpha((unsigned char) source_name[0])
913 && (source_name[len-1] == ':'))
914 return true;
915
916 if ( ! WIN_NT ) return false;
917
918 /* Test to see if of form: \\.\x: */
919 return ( (len == 6)
920 && source_name[0] == '\\' && source_name[1] == '\\'
921 && source_name[2] == '.' && source_name[3] == '\\'
922 && isalpha((unsigned char) source_name[len-2])
923 && (source_name[len-1] == ':') );
924 #else
925 return false;
926 #endif
927 }
928
929 driver_return_code_t
close_tray_win32(const char * psz_win32_drive)930 close_tray_win32 (const char *psz_win32_drive)
931 {
932 #ifdef HAVE_WIN32_CDROM
933 #if 1
934 return open_close_media_win32(psz_win32_drive, MCI_SET_DOOR_CLOSED|MCI_WAIT);
935 #else
936 if ( WIN_NT ) {
937 return close_tray_win32ioctl (psz_win32_drive);
938 } else {
939 return DRIVER_OP_UNSUPPORTED; /* not yet, but soon I hope */
940 }
941 #endif
942 #else
943 return DRIVER_OP_UNSUPPORTED;
944 #endif /* HAVE_WIN32_CDROM*/
945 }
946
947 /*!
948 Initialization routine. This is the only thing that doesn't
949 get called via a function pointer. In fact *we* are the
950 ones to set that up.
951 */
952 CdIo_t *
cdio_open_win32(const char * psz_source_name)953 cdio_open_win32 (const char *psz_source_name)
954 {
955 #ifdef HAVE_WIN32_CDROM
956 if ( WIN_NT ) {
957 return cdio_open_am_win32(psz_source_name, "ioctl");
958 } else {
959 return cdio_open_am_win32(psz_source_name, "ASPI");
960 }
961 #else
962 return NULL;
963 #endif /* HAVE_WIN32_CDROM */
964 }
965
966 /*!
967 Initialization routine. This is the only thing that doesn't
968 get called via a function pointer. In fact *we* are the
969 ones to set that up.
970 */
971 CdIo_t *
cdio_open_am_win32(const char * psz_orig_source,const char * psz_access_mode)972 cdio_open_am_win32 (const char *psz_orig_source, const char *psz_access_mode)
973 {
974
975 #ifdef HAVE_WIN32_CDROM
976 CdIo_t *ret;
977 _img_private_t *_data;
978 char *psz_source;
979
980 cdio_funcs_t _funcs;
981
982 memset( &_funcs, 0, sizeof(_funcs) );
983
984 _funcs.audio_get_volume = audio_get_volume_win32;
985 _funcs.audio_pause = audio_pause_win32;
986 _funcs.audio_play_msf = audio_play_msf_win32;
987 #if 0
988 _funcs.audio_play_track_index = audio_play_track_index_win32;
989 #endif
990 _funcs.audio_read_subchannel = audio_read_subchannel_win32;
991 _funcs.audio_resume = audio_resume_win32;
992 _funcs.audio_set_volume = audio_set_volume_win32;
993 _funcs.audio_stop = audio_stop_win32;
994 _funcs.eject_media = eject_media_win32;
995 _funcs.free = free_win32;
996 _funcs.get_arg = get_arg_win32;
997 _funcs.get_cdtext = get_cdtext_generic;
998 _funcs.get_cdtext_raw = read_cdtext_generic;
999 _funcs.get_default_device = cdio_get_default_device_win32;
1000 _funcs.get_devices = cdio_get_devices_win32;
1001 _funcs.get_disc_last_lsn = get_disc_last_lsn_win32;
1002 _funcs.get_discmode = get_discmode_win32;
1003 _funcs.get_drive_cap = get_drive_cap_mmc;
1004 _funcs.get_first_track_num = get_first_track_num_generic;
1005 _funcs.get_hwinfo = NULL;
1006 _funcs.get_last_session = get_last_session_win32;
1007 _funcs.get_media_changed = get_media_changed_mmc;
1008 _funcs.get_mcn = _cdio_get_mcn;
1009 _funcs.get_num_tracks = get_num_tracks_generic;
1010 _funcs.get_track_channels = get_track_channels_generic,
1011 _funcs.get_track_copy_permit = get_track_copy_permit_generic,
1012 _funcs.get_track_format = _cdio_get_track_format;
1013 _funcs.get_track_green = _cdio_get_track_green;
1014 _funcs.get_track_lba = NULL; /* This could be done if need be. */
1015 _funcs.get_track_msf = _cdio_get_track_msf;
1016 _funcs.get_track_preemphasis = get_track_preemphasis_generic,
1017 _funcs.get_track_isrc = _cdio_get_track_isrc;
1018 _funcs.lseek = NULL;
1019 _funcs.read = NULL;
1020 _funcs.read_audio_sectors = read_audio_sectors;
1021 _funcs.read_data_sectors = read_data_sectors_win32;
1022 _funcs.read_mode1_sector = read_mode1_sector_win32;
1023 _funcs.read_mode1_sectors = read_mode1_sectors_win32;
1024 _funcs.read_mode2_sector = read_mode2_sector_win32;
1025 _funcs.read_mode2_sectors = read_mode2_sectors_win32;
1026 _funcs.read_toc = read_toc_win32;
1027 _funcs.run_mmc_cmd = run_mmc_cmd_win32;
1028 _funcs.set_arg = set_arg_win32;
1029 _funcs.set_blocksize = set_blocksize_mmc;
1030 _funcs.set_speed = set_drive_speed_mmc;
1031
1032 _data = calloc(1, sizeof (_img_private_t));
1033 _data->access_mode = str_to_access_mode_win32(psz_access_mode);
1034 _data->gen.init = false;
1035 _data->gen.fd = -1;
1036
1037 if (NULL == psz_orig_source) {
1038 psz_source=cdio_get_default_device_win32();
1039 if (NULL == psz_source) {
1040 goto error_exit;
1041 }
1042 set_arg_win32(_data, "source", psz_source);
1043 free(psz_source);
1044 } else {
1045 if (cdio_is_device_win32(psz_orig_source))
1046 set_arg_win32(_data, "source", psz_orig_source);
1047 else {
1048 /* The below would be okay as an info message if all device
1049 drivers worked this way. */
1050 cdio_debug ("source %s is a not a device", psz_orig_source);
1051 goto error_exit;
1052 }
1053 }
1054
1055 ret = cdio_new ((void *)_data, &_funcs);
1056 if (ret == NULL) {
1057 goto error_exit;
1058 }
1059
1060 if (init_win32(_data)) {
1061 return ret;
1062 }
1063
1064
1065 error_exit:
1066 free_win32(_data);
1067 return(NULL);
1068 #else
1069 return NULL;
1070 #endif /* HAVE_WIN32_CDROM */
1071
1072 }
1073
1074 bool
cdio_have_win32(void)1075 cdio_have_win32 (void)
1076 {
1077 #ifdef HAVE_WIN32_CDROM
1078 return true;
1079 #else
1080 return false;
1081 #endif /* HAVE_WIN32_CDROM */
1082 }
1083