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