1 /*
2   Copyright (C) 2003-2006, 2008, 2010-2012, 2014-2015, 2017
3    Rocky Bernstein <rocky@gnu.org>
4   from vcdimager code:
5   Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org>
6   and VideoLAN code Copyright (C) 1998-2001 VideoLAN
7       Authors: Johan Bilien <jobi@via.ecp.fr>
8                Gildas Bazin <gbazin@netcourrier.com>
9                Jon Lech Johansen <jon-vl@nanocrew.net>
10                Derk-Jan Hartman <hartman at videolan.org>
11                Justin F. Hallett <thesin@southofheaven.org>
12 
13   This program is free software: you can redistribute it and/or modify
14   it under the terms of the GNU General Public License as published by
15   the Free Software Foundation, either version 3 of the License, or
16   (at your option) any later version.
17 
18   This program is distributed in the hope that it will be useful,
19   but WITHOUT ANY WARRANTY; without even the implied warranty of
20   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21   GNU General Public License for more details.
22 
23   You should have received a copy of the GNU General Public License
24   along with this program.  If not, see <http://www.gnu.org/licenses/>.
25 */
26 
27 /* This file contains OSX-specific code and implements low-level
28    control of the CD drive.
29 */
30 
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34 
35 #ifdef HAVE_STDBOOL_H
36 # include <stdbool.h>
37 #endif
38 
39 #include <cdio/logging.h>
40 #include <cdio/sector.h>
41 #include <cdio/util.h>
42 
43 /* For SCSI TR_* enumerations */
44 typedef enum {
45   TR_OK =            0,
46   TR_EWRITE =        1  /**< Error writing packet command (transport) */,
47   TR_EREAD =         2  /**< Error reading packet data (transport) */,
48   TR_UNDERRUN =      3  /**< Read underrun */,
49   TR_OVERRUN =       4  /**< Read overrun */,
50   TR_ILLEGAL =       5  /**< Illegal/rejected request */,
51   TR_MEDIUM =        6  /**< Medium error */,
52   TR_BUSY =          7  /**< Device busy */,
53   TR_NOTREADY =      8  /**< Device not ready */,
54   TR_FAULT =         9  /**< Device failure */,
55   TR_UNKNOWN =      10  /**< Unspecified error */,
56   TR_STREAMING =    11  /**< loss of streaming */,
57 } transport_error_t;
58 
59 #include "cdio_assert.h"
60 #include "cdio_private.h"
61 
62 #include <string.h>
63 
64 #ifdef HAVE_DARWIN_CDROM
65 #undef VERSION
66 
67 #include <CoreFoundation/CoreFoundation.h>
68 #include <IOKit/IOKitLib.h>
69 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
70 
71 #include <mach/mach.h>
72 #include <Carbon/Carbon.h>
73 #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1030
74 # include <IOKit/scsi/SCSITaskLib.h>
75 #else
76 # include <IOKit/scsi-commands/SCSITaskLib.h>
77 #endif
78 #include <IOKit/IOCFPlugIn.h>
79 #include <mach/mach_error.h>
80 
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <errno.h>
84 #include <unistd.h>
85 #include <fcntl.h>
86 
87 #include <sys/stat.h>
88 #include <sys/types.h>
89 #include <sys/ioctl.h>
90 
91 #include <paths.h>
92 #include <CoreFoundation/CoreFoundation.h>
93 #include <IOKit/IOKitLib.h>
94 #include <IOKit/IOBSD.h>
95 #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1030
96 # include <IOKit/scsi/IOSCSIMultimediaCommandsDevice.h>
97 #else
98 # include <IOKit/scsi-commands/IOSCSIMultimediaCommandsDevice.h>
99 #endif
100 #include <IOKit/storage/IOCDTypes.h>
101 #include <IOKit/storage/IODVDTypes.h>
102 #include <IOKit/storage/IOMedia.h>
103 #include <IOKit/storage/IOCDMedia.h>
104 #include <IOKit/storage/IODVDMedia.h>
105 #include <IOKit/storage/IOCDMediaBSDClient.h>
106 #include <IOKit/storage/IODVDMediaBSDClient.h>
107 #include <IOKit/storage/IOBlockStorageDevice.h>
108 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
109 
110 #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
111 #include <IOKit/storage/IOBDTypes.h>
112 #include <IOKit/storage/IOBDMedia.h>
113 #include <IOKit/storage/IOBDMediaBSDClient.h>
114 #else
115 #define kIOBDMediaClass "IOBDMedia" // It does not hurt, simplyfies rest of code
116 #endif
117 
118 #ifdef HAVE_DISKARBITRATION
119 #include <DiskArbitration/DiskArbitration.h>
120 #endif
121 
122 /* FIXME */
123 #define MAX_BIG_BUFF_SIZE  65535
124 
125 #define kIOCDBlockStorageDeviceClassString              "IOCDBlockStorageDevice"
126 
127 /* Note leadout is normally defined 0xAA, But on OSX 0xA0 is "lead in" while
128    0xA2 is "lead out". I don't understand the distinction, and therefore
129    something could be wrong. */
130 #define OSX_CDROM_LEADOUT_TRACK 0xA2
131 
132 #define TOTAL_TRACKS    (p_env->i_last_track - p_env->gen.i_first_track + 1)
133 
134 #define CDROM_CDI_TRACK 0x1
135 #define CDROM_XA_TRACK  0x2
136 
137 typedef enum {
138   _AM_NONE,
139   _AM_OSX,
140 } access_mode_t;
141 
142 #define MAX_SERVICE_NAME 1000
143 typedef struct {
144   /* Things common to all drivers like this.
145      This must be first. */
146   generic_img_private_t gen;
147 
148   access_mode_t access_mode;
149 
150   /* Track information */
151   CDTOC *pTOC;
152   int i_descriptors;
153   track_t i_last_track;      /* highest track number */
154   track_t i_last_session;    /* highest session number */
155   track_t i_first_session;   /* first session number */
156   lsn_t   *pp_lba;
157   io_service_t MediaClass_service;
158   char    psz_MediaClass_service[MAX_SERVICE_NAME];
159   SCSITaskDeviceInterface **pp_scsiTaskDeviceInterface;
160 
161   // io_service_t obj;
162   // SCSITaskDeviceInterface **scsi;
163   SCSITaskInterface **scsi_task;
164   MMCDeviceInterface **mmc;
165   IOCFPlugInInterface **plugin;
166 
167   SCSI_Sense_Data sense;
168   SCSITaskStatus status;
169   UInt64 realized_len;
170 
171 
172 } _img_private_t;
173 
174 static bool read_toc_osx (void *p_user_data);
175 static track_format_t get_track_format_osx(void *p_user_data,
176                                            track_t i_track);
177 
178 /**
179  * GetRegistryEntryProperties - Gets the registry entry properties for
180  *  an io_service_t.
181  */
182 
183 static CFMutableDictionaryRef
GetRegistryEntryProperties(io_service_t service)184 GetRegistryEntryProperties ( io_service_t service )
185 {
186   IOReturn                      err     = kIOReturnSuccess;
187   CFMutableDictionaryRef        dict    = 0;
188 
189   err = IORegistryEntryCreateCFProperties (service, &dict,
190                                            kCFAllocatorDefault, 0);
191   if ( err != kIOReturnSuccess )
192     cdio_warn( "IORegistryEntryCreateCFProperties: 0x%08x", err );
193 
194   return dict;
195 }
196 
197 /**
198  * ProbeStorageDevices - Probe devices to detect changes.
199  */
200 static bool
ProbeStorageDevices()201 ProbeStorageDevices()
202 {
203   io_service_t  next_service;
204   mach_port_t   master_port;
205   kern_return_t kern_result;
206   io_iterator_t service_iterator;
207   CFMutableDictionaryRef classes_to_match;
208 
209   kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
210   if( kern_result != KERN_SUCCESS )
211     {
212       return false;
213     }
214 
215   classes_to_match = IOServiceMatching( kIOBlockStorageDeviceClass );
216   if( classes_to_match == NULL )
217     {
218       return false;
219     }
220 
221   kern_result = IOServiceGetMatchingServices( master_port,
222                                               classes_to_match,
223                                               &service_iterator );
224   if( kern_result != KERN_SUCCESS )
225     {
226       return false;
227     }
228 
229   next_service = IOIteratorNext( service_iterator );
230   if( next_service != 0 )
231     {
232       do
233         {
234           IOServiceRequestProbe( next_service, 0 );
235 
236           IOObjectRelease( next_service );
237 
238         } while( ( next_service = IOIteratorNext( service_iterator ) ) != 0 );
239     }
240   IOObjectRelease( service_iterator );
241   return true;
242 }
243 
244 #ifdef GET_SCSI_FIXED
245 static bool
get_scsi(_img_private_t * p_env)246 get_scsi(_img_private_t *p_env)
247 {
248   SInt32 score;
249   kern_return_t err;
250   HRESULT herr;
251 
252   err = IOCreatePlugInInterfaceForService(p_env->MediaClass_service,
253                                           kIOMMCDeviceUserClientTypeID,
254                                           kIOCFPlugInInterfaceID,
255                                           &p_env->plugin,
256                                           &score);
257 
258   if (err != noErr) {
259     fprintf(stderr, "Error %x accessing MMC plugin.\n", err);
260     return false;
261     }
262 
263   herr = (*p_env->plugin) ->
264     QueryInterface(p_env->plugin, CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
265                    (void *)&p_env->mmc);
266 
267   if (herr != S_OK) {
268     fprintf(stderr, "Error %x accessing MMC interface.\n", (int) herr);
269     IODestroyPlugInInterface(p_env->plugin);
270     return false;
271   }
272 
273   p_env->pp_scsiTaskDeviceInterface =
274     (*p_env->mmc)->GetSCSITaskDeviceInterface(p_env->mmc);
275 
276   if (!p_env->pp_scsiTaskDeviceInterface) {
277     fprintf(stderr,
278             "Could not get SCSITaskkDevice interface from MMC interface.\n");
279     (*p_env->mmc)->Release(p_env->mmc);
280     IODestroyPlugInInterface(p_env->plugin);
281     return false;
282   }
283 
284   err = (*p_env->pp_scsiTaskDeviceInterface)->
285     ObtainExclusiveAccess(p_env->pp_scsiTaskDeviceInterface);
286   if (err != kIOReturnSuccess) {
287     fprintf(stderr, "Could not obtain exclusive access to the device (%x).\n",
288             err);
289 
290     if (err == kIOReturnBusy)
291       fprintf(stderr, "The volume is already mounted.\n");
292     else if (err == kIOReturnExclusiveAccess)
293       fprintf(stderr, "Another application already has exclusive access "
294               "to this device.\n");
295     else
296       fprintf(stderr, "I don't know why.\n");
297 
298     (*p_env->pp_scsiTaskDeviceInterface)->
299       Release(p_env->pp_scsiTaskDeviceInterface);
300     (*p_env->mmc)->Release(p_env->mmc);
301     IODestroyPlugInInterface(p_env->plugin);
302     return false;
303   }
304 
305   p_env->scsi_task =
306     (*p_env->pp_scsiTaskDeviceInterface) ->
307     CreateSCSITask(p_env->pp_scsiTaskDeviceInterface);
308 
309   if (!p_env->scsi_task) {
310     fprintf(stderr, "Could not create a SCSITask interface.\n");
311     (*p_env->pp_scsiTaskDeviceInterface)->
312       ReleaseExclusiveAccess(p_env->pp_scsiTaskDeviceInterface);
313     (*p_env->pp_scsiTaskDeviceInterface)->
314       Release(p_env->pp_scsiTaskDeviceInterface);
315     (*p_env->mmc)->Release(p_env->mmc);
316     IODestroyPlugInInterface(p_env->plugin);
317     return false;
318   }
319 
320   return true;
321 }
322 #endif
323 
324 static bool
init_osx(_img_private_t * p_env)325 init_osx(_img_private_t *p_env) {
326   char *psz_devname;
327   kern_return_t ret;
328   io_iterator_t iterator;
329 
330   /* Only open if not already opened. Otherwise, too many descriptors
331      are holding the device busy.  */
332   if (-1 == p_env->gen.fd)
333     p_env->gen.fd = open( p_env->gen.source_name, O_RDONLY | O_NONBLOCK );
334 
335   if (-1 == p_env->gen.fd) {
336     cdio_warn("Failed to open %s: %s", p_env->gen.source_name,
337                strerror(errno));
338     return false;
339   }
340 
341   /* Get the device name. */
342   psz_devname = strrchr( p_env->gen.source_name, '/');
343   if( NULL != psz_devname )
344     ++psz_devname;
345   else
346     psz_devname = p_env->gen.source_name;
347 
348   /* Unraw the device name. */
349   if( *psz_devname == 'r' )
350     ++psz_devname;
351 
352   ret = IOServiceGetMatchingServices( kIOMasterPortDefault,
353                                       IOBSDNameMatching(kIOMasterPortDefault,
354                                                         0, psz_devname),
355                                       &iterator );
356 
357   /* Get service iterator for the device. */
358   if( ret != KERN_SUCCESS )
359     {
360         cdio_warn( "IOServiceGetMatchingServices: 0x%08x", ret );
361         return false;
362     }
363 
364   /* first service */
365   p_env->MediaClass_service = IOIteratorNext( iterator );
366   IOObjectRelease( iterator );
367 
368   /* search for kIOCDMediaClass or kIODVDMediaClass or kIOBDMediaClass */
369   while( p_env->MediaClass_service &&
370          (!IOObjectConformsTo(p_env->MediaClass_service, kIOCDMediaClass)) &&
371          (!IOObjectConformsTo(p_env->MediaClass_service, kIODVDMediaClass)) &&
372          (!IOObjectConformsTo(p_env->MediaClass_service, kIOBDMediaClass)) )
373     {
374 
375       ret = IORegistryEntryGetParentIterator( p_env->MediaClass_service,
376                                               kIOServicePlane,
377                                               &iterator );
378       if( ret != KERN_SUCCESS )
379         {
380           cdio_warn( "IORegistryEntryGetParentIterator: 0x%08x", ret );
381           IOObjectRelease( p_env->MediaClass_service );
382           return false;
383         }
384 
385       IOObjectRelease( p_env->MediaClass_service );
386 
387       p_env->MediaClass_service = IOIteratorNext( iterator );
388       IOObjectRelease( iterator );
389     }
390 
391   if ( 0 == p_env->MediaClass_service )     {
392     cdio_warn( "search for kIOCDMediaClass/kIODVDMediaClass/kIOBDMediaClass came up empty" );
393     return false;
394   }
395 
396   /* Save the name so we can compare against this in case we have to do
397      another scan. FIXME: this is hoaky and there's got to be a better
398      variable to test or way to do.
399    */
400   IORegistryEntryGetPath(p_env->MediaClass_service, kIOServicePlane,
401                          p_env->psz_MediaClass_service);
402 #ifdef GET_SCSI_FIXED
403   return get_scsi(p_env);
404 #else
405   return true;
406 #endif
407 }
408 
409 /**
410   Run a SCSI MMC command.
411 
412   cdio          CD structure set by cdio_open().
413   i_timeout     time in milliseconds we will wait for the command
414                 to complete. If this value is -1, use the default
415                 time-out value.
416   p_buf         Buffer for data, both sending and receiving
417   i_buf         Size of buffer
418   e_direction   direction the transfer is to go.
419   cdb           CDB bytes. All values that are needed should be set on
420                 input. We'll figure out what the right CDB length should be.
421 
422   We return true if command completed successfully and false if not.
423  */
424 #if 1
425 
426 /* process a complete scsi command. */
427 static int
run_mmc_cmd_osx(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)428 run_mmc_cmd_osx( void *p_user_data,
429                  unsigned int i_timeout_ms,
430                  unsigned int i_cdb, const mmc_cdb_t *p_cdb,
431                  cdio_mmc_direction_t e_direction,
432                  unsigned int i_buf, /*in/out*/ void *p_buf )
433 {
434   _img_private_t *p_env = p_user_data;
435   uint8_t cmdbuf[16];
436   UInt8 dir;
437   IOVirtualRange buf;
438   IOReturn ret;
439 
440   if (!p_env->scsi_task) return DRIVER_OP_UNSUPPORTED;
441 
442   p_env->gen.scsi_mmc_sense_valid = 0;
443   memcpy(cmdbuf, p_cdb, i_cdb);
444 
445   dir =
446       (SCSI_MMC_DATA_READ == e_direction)
447       ? kSCSIDataTransfer_FromTargetToInitiator :
448       (SCSI_MMC_DATA_WRITE == e_direction)
449       ? kSCSIDataTransfer_FromInitiatorToTarget
450       : kSCSIDataTransfer_NoDataTransfer;
451 
452   if (!i_buf)
453     dir = kSCSIDataTransfer_NoDataTransfer;
454 
455   if (i_buf > MAX_BIG_BUFF_SIZE) {
456     fprintf(stderr, "Excessive request size: %d bytes\n", i_buf);
457     return TR_ILLEGAL;
458   }
459 
460   buf.address = (IOVirtualAddress)p_buf;
461   buf.length = i_buf;
462 
463   ret = (*p_env->scsi_task)->SetCommandDescriptorBlock(p_env->scsi_task,
464                                                        cmdbuf, i_cdb);
465   if (ret != kIOReturnSuccess) {
466     fprintf(stderr, "SetCommandDescriptorBlock: %x\n", ret);
467     return TR_UNKNOWN;
468   }
469 
470   ret = (*p_env->scsi_task)->SetScatterGatherEntries(p_env->scsi_task, &buf, 1,
471                                                      i_buf, dir);
472   if (ret != kIOReturnSuccess) {
473     fprintf(stderr, "SetScatterGatherEntries: %x\n", ret);
474     return TR_UNKNOWN;
475   }
476 
477   ret = (*p_env->scsi_task)->ExecuteTaskSync(p_env->scsi_task, &p_env->sense,
478                                              &p_env->status,
479                                              &p_env->realized_len);
480   if (ret != kIOReturnSuccess) {
481     fprintf(stderr, "ExecuteTaskSync: %x\n", ret);
482     return TR_UNKNOWN;
483   }
484 
485   if (p_env->status != kSCSITaskStatus_GOOD) {
486     int i;
487 
488     fprintf(stderr, "SCSI status: %x\n", p_env->status);
489     fprintf(stderr, "Sense: %x %x %x\n",
490             p_env->sense.SENSE_KEY,
491             p_env->sense.ADDITIONAL_SENSE_CODE,
492             p_env->sense.ADDITIONAL_SENSE_CODE_QUALIFIER);
493 
494     for (i = 0; i < i_cdb; i++)
495       fprintf(stderr, "%02x ", cmdbuf[i]);
496 
497     fprintf(stderr, "\n");
498     memcpy((void *) p_env->gen.scsi_mmc_sense, &p_env->sense, kSenseDefaultSize);
499 
500     return TR_UNKNOWN;
501   }
502 
503   if (p_env->sense.VALID_RESPONSE_CODE) {
504     char key = p_env->sense.SENSE_KEY & 0xf;
505     char ASC = p_env->sense.ADDITIONAL_SENSE_CODE;
506     char ASCQ = p_env->sense.ADDITIONAL_SENSE_CODE_QUALIFIER;
507 
508     switch (key) {
509     case 0:
510       if (errno == 0)
511         errno = EIO;
512       return (TR_UNKNOWN);
513     case 1:
514       break;
515     case 2:
516       if (errno == 0)
517         errno = EBUSY;
518       return (TR_BUSY);
519     case 3:
520       if (ASC == 0x0C && ASCQ == 0x09) {
521         /* loss of streaming */
522         if (errno == 0)
523           errno = EIO;
524         return (TR_STREAMING);
525       } else {
526         if (errno == 0)
527           errno = EIO;
528         return (TR_MEDIUM);
529       }
530     case 4:
531       if (errno == 0)
532         errno = EIO;
533       return (TR_FAULT);
534     case 5:
535       if (errno == 0)
536         errno = EINVAL;
537       return (TR_ILLEGAL);
538     default:
539       if (errno == 0)
540         errno = EIO;
541       return (TR_UNKNOWN);
542     }
543   }
544 
545   errno = 0;
546   return (0);
547 }
548 #endif
549 
550 #if 0
551 /**
552   Run a SCSI MMC command.
553 
554   cdio          CD structure set by cdio_open().
555   i_timeout     time in milliseconds we will wait for the command
556                 to complete. If this value is -1, use the default
557                 time-out value.
558   p_buf         Buffer for data, both sending and receiving
559   i_buf         Size of buffer
560   e_direction   direction the transfer is to go.
561   cdb           CDB bytes. All values that are needed should be set on
562                 input. We'll figure out what the right CDB length should be.
563 
564   We return true if command completed successfully and false if not.
565  */
566 static int
567 run_mmc_cmd_osx( const void *p_user_data,
568                  unsigned int i_timeout_ms,
569                  unsigned int i_cdb, const mmc_cdb_t *p_cdb,
570                  cdio_mmc_direction_t e_direction,
571                  unsigned int i_buf, /*in/out*/ void *p_buf )
572 {
573 
574 #ifndef SCSI_MMC_FIXED
575   return DRIVER_OP_UNSUPPORTED;
576 #else
577   const _img_private_t *p_env = p_user_data;
578   SCSITaskDeviceInterface **sc;
579   SCSITaskInterface **cmd = NULL;
580   IOVirtualRange iov;
581   SCSI_Sense_Data senseData;
582   SCSITaskStatus status;
583   UInt64 bytesTransferred;
584   IOReturn ioReturnValue;
585   int ret = 0;
586 
587   if (NULL == p_user_data) return 2;
588 
589   /* Make sure pp_scsiTaskDeviceInterface is initialized. FIXME: The code
590      should probably be reorganized better for this. */
591   if (!p_env->gen.toc_init) read_toc_osx (p_user_data) ;
592 
593   sc = p_env->pp_scsiTaskDeviceInterface;
594 
595   if (NULL == sc) return 3;
596 
597   cmd = (*sc)->CreateSCSITask(sc);
598   if (cmd == NULL) {
599     cdio_warn("Failed to create SCSI task");
600     return -1;
601   }
602 
603   iov.address = (IOVirtualAddress) p_buf;
604   iov.length = i_buf;
605 
606   ioReturnValue = (*cmd)->SetCommandDescriptorBlock(cmd, (UInt8 *) p_cdb,
607                                                     i_cdb);
608   if (ioReturnValue != kIOReturnSuccess) {
609     cdio_warn("SetCommandDescriptorBlock failed with status %x",
610               ioReturnValue);
611     return -1;
612   }
613 
614   ioReturnValue = (*cmd)->SetScatterGatherEntries(cmd, &iov, 1, i_buf,
615                                                   (SCSI_MMC_DATA_READ == e_direction ) ?
616                                                   kSCSIDataTransfer_FromTargetToInitiator :
617                                                   kSCSIDataTransfer_FromInitiatorToTarget);
618   if (ioReturnValue != kIOReturnSuccess) {
619     cdio_warn("SetScatterGatherEntries failed with status %x", ioReturnValue);
620     return -1;
621   }
622 
623   ioReturnValue = (*cmd)->SetTimeoutDuration(cmd, i_timeout_ms );
624   if (ioReturnValue != kIOReturnSuccess) {
625     cdio_warn("SetTimeoutDuration failed with status %x", ioReturnValue);
626     return -1;
627   }
628 
629   memset(&senseData, 0, sizeof(senseData));
630 
631   ioReturnValue = (*cmd)->ExecuteTaskSync(cmd,&senseData, &status, &
632                                           bytesTransferred);
633 
634   if (ioReturnValue != kIOReturnSuccess) {
635     cdio_warn("Command execution failed with status %x", ioReturnValue);
636     return -1;
637   }
638 
639   if (cmd != NULL) {
640     (*cmd)->Release(cmd);
641   }
642 
643   return (ret);
644 #endif
645 }
646 #endif /* 0*/
647 
648 /***************************************************************************
649  * GetDeviceIterator - Gets an io_iterator_t for our class type
650  ***************************************************************************/
651 
652 static io_iterator_t
GetDeviceIterator(const char * deviceClass)653 GetDeviceIterator ( const char * deviceClass )
654 {
655 
656   IOReturn      err      = kIOReturnSuccess;
657   io_iterator_t iterator = MACH_PORT_NULL;
658 
659   err = IOServiceGetMatchingServices ( kIOMasterPortDefault,
660                                        IOServiceMatching ( deviceClass ),
661                                        &iterator );
662   cdio_assert ( err == kIOReturnSuccess );
663 
664   return iterator;
665 
666 }
667 
668 /***************************************************************************
669  * GetFeaturesFlagsForDrive -Gets the bitfield which represents the
670  * features flags.
671  ***************************************************************************/
672 
673 static bool
GetFeaturesFlagsForDrive(CFDictionaryRef dict,uint32_t * i_cdFlags,uint32_t * i_dvdFlags)674 GetFeaturesFlagsForDrive ( CFDictionaryRef dict,
675                            uint32_t *i_cdFlags,
676                            uint32_t *i_dvdFlags )
677 {
678   CFDictionaryRef propertiesDict = 0;
679   CFNumberRef     flagsNumberRef = 0;
680 
681   *i_cdFlags = 0;
682   *i_dvdFlags= 0;
683 
684   propertiesDict = ( CFDictionaryRef )
685     CFDictionaryGetValue ( dict,
686                            CFSTR ( kIOPropertyDeviceCharacteristicsKey ) );
687 
688   if ( propertiesDict == 0 ) return false;
689 
690   /* Get the CD features */
691   flagsNumberRef = ( CFNumberRef )
692     CFDictionaryGetValue ( propertiesDict,
693                            CFSTR ( kIOPropertySupportedCDFeatures ) );
694   if ( flagsNumberRef != 0 ) {
695     CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_cdFlags );
696   }
697 
698   /* Get the DVD features */
699   flagsNumberRef = ( CFNumberRef )
700     CFDictionaryGetValue ( propertiesDict,
701                            CFSTR ( kIOPropertySupportedDVDFeatures ) );
702   if ( flagsNumberRef != 0 ) {
703     CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_dvdFlags );
704   }
705 
706   return true;
707 }
708 
709 /**
710   Get disc type associated with the cd object.
711 */
712 static discmode_t
get_discmode_osx(void * p_user_data)713 get_discmode_osx (void *p_user_data)
714 {
715   _img_private_t *p_env = p_user_data;
716   char str[10];
717   int32_t i_discmode = CDIO_DISC_MODE_ERROR;
718   CFDictionaryRef propertiesDict = 0;
719   CFStringRef data;
720 
721   propertiesDict  = GetRegistryEntryProperties ( p_env->MediaClass_service );
722 
723   if ( propertiesDict == 0 ) return i_discmode;
724 
725   data = ( CFStringRef )
726     CFDictionaryGetValue ( propertiesDict, CFSTR ( kIODVDMediaTypeKey ) );
727 
728   if( CFStringGetCString( data, str, sizeof(str),
729                           kCFStringEncodingASCII ) ) {
730     if (0 == strncmp(str, "DVD+R", strlen(str)) )
731       i_discmode = CDIO_DISC_MODE_DVD_PR;
732     else if (0 == strncmp(str, "DVD+RW", strlen(str)) )
733       i_discmode = CDIO_DISC_MODE_DVD_PRW;
734     else if (0 == strncmp(str, "DVD-R", strlen(str)) )
735       i_discmode = CDIO_DISC_MODE_DVD_R;
736     else if (0 == strncmp(str, "DVD-RW", strlen(str)) )
737       i_discmode = CDIO_DISC_MODE_DVD_RW;
738     else if (0 == strncmp(str, "DVD-ROM", strlen(str)) )
739       i_discmode = CDIO_DISC_MODE_DVD_ROM;
740     else if (0 == strncmp(str, "DVD-RAM", strlen(str)) )
741       i_discmode = CDIO_DISC_MODE_DVD_RAM;
742     else if (0 == strncmp(str, "CD-ROM", strlen(str)) )
743       i_discmode = CDIO_DISC_MODE_CD_DATA;
744     else if (0 == strncmp(str, "CDR", strlen(str)) )
745       i_discmode = CDIO_DISC_MODE_CD_DATA;
746     else if (0 == strncmp(str, "CDRW", strlen(str)) )
747       i_discmode = CDIO_DISC_MODE_CD_DATA;
748     //??  Handled by below? CFRelease( data );
749   }
750   CFRelease( propertiesDict );
751   if (CDIO_DISC_MODE_CD_DATA == i_discmode) {
752     /* Need to do more classification */
753     return get_discmode_cd_generic(p_user_data);
754   }
755   return i_discmode;
756 
757 }
758 
759 static io_service_t
get_drive_service_osx(const _img_private_t * p_env)760 get_drive_service_osx(const _img_private_t *p_env)
761 {
762   io_service_t  service;
763   io_iterator_t service_iterator;
764 
765   service_iterator = GetDeviceIterator ( kIOCDBlockStorageDeviceClassString );
766 
767   if( service_iterator == MACH_PORT_NULL ) return 0;
768 
769   service = IOIteratorNext( service_iterator );
770   if( service == 0 ) return 0;
771 
772   do
773     {
774       char psz_service[MAX_SERVICE_NAME];
775       IORegistryEntryGetPath(service, kIOServicePlane, psz_service);
776       psz_service[MAX_SERVICE_NAME-1] = '\0';
777 
778       /* FIXME: This is all hoaky. Here we need info from a parent class,
779          psz_service of what we opened above. We are relying on the
780          fact that the name  will be a substring of the name we
781          openned with.
782       */
783       if (0 == strncmp(psz_service, p_env->psz_MediaClass_service,
784                        strlen(psz_service))) {
785         /* Found our device */
786         IOObjectRelease( service_iterator );
787         return service;
788       }
789 
790       IOObjectRelease( service );
791 
792     } while( ( service = IOIteratorNext( service_iterator ) ) != 0 );
793 
794   IOObjectRelease( service_iterator );
795   return service;
796 }
797 
798 static void
get_drive_cap_osx(const void * p_user_data,cdio_drive_read_cap_t * p_read_cap,cdio_drive_write_cap_t * p_write_cap,cdio_drive_misc_cap_t * p_misc_cap)799 get_drive_cap_osx(const void *p_user_data,
800                   /*out*/ cdio_drive_read_cap_t  *p_read_cap,
801                   /*out*/ cdio_drive_write_cap_t *p_write_cap,
802                   /*out*/ cdio_drive_misc_cap_t  *p_misc_cap)
803 {
804   const _img_private_t *p_env = p_user_data;
805   uint32_t i_cdFlags;
806   uint32_t i_dvdFlags;
807 
808   io_service_t  service = get_drive_service_osx(p_env);
809 
810   if( service == 0 ) goto err_exit;
811 
812   /* Found our device */
813   {
814     CFDictionaryRef  properties = GetRegistryEntryProperties ( service );
815 
816     if (! GetFeaturesFlagsForDrive ( properties, &i_cdFlags,
817                                      &i_dvdFlags ) ) {
818       IOObjectRelease( service );
819       goto err_exit;
820     }
821 
822     /* Reader */
823 
824     if ( 0 != (i_cdFlags & kCDFeaturesAnalogAudioMask) )
825       *p_read_cap  |= CDIO_DRIVE_CAP_READ_AUDIO;
826 
827     if ( 0 != (i_cdFlags & kCDFeaturesWriteOnceMask) )
828       *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_R;
829 
830     if ( 0 != (i_cdFlags & kCDFeaturesCDDAStreamAccurateMask) )
831       *p_read_cap  |= CDIO_DRIVE_CAP_READ_CD_DA;
832 
833     if ( 0 != (i_dvdFlags & kDVDFeaturesReadStructuresMask) )
834       *p_read_cap  |= CDIO_DRIVE_CAP_READ_DVD_ROM;
835 
836     if ( 0 != (i_cdFlags & kCDFeaturesReWriteableMask) )
837       *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW;
838 
839     if ( 0 != (i_dvdFlags & kDVDFeaturesWriteOnceMask) )
840       *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R;
841 
842     if ( 0 != (i_dvdFlags & kDVDFeaturesRandomWriteableMask) )
843       *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM;
844 
845     if ( 0 != (i_dvdFlags & kDVDFeaturesReWriteableMask) )
846       *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RW;
847 
848     /***
849         if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRMask) )
850         *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PR;
851 
852         if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRWMask )
853         *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PRW;
854         ***/
855 
856     /* FIXME: fill out. For now assume CD-ROM is relatively modern. */
857       *p_misc_cap = (
858                      CDIO_DRIVE_CAP_MISC_CLOSE_TRAY
859                      | CDIO_DRIVE_CAP_MISC_EJECT
860                      | CDIO_DRIVE_CAP_MISC_LOCK
861                      | CDIO_DRIVE_CAP_MISC_SELECT_SPEED
862                      | CDIO_DRIVE_CAP_MISC_MULTI_SESSION
863                      | CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED
864                      | CDIO_DRIVE_CAP_MISC_RESET
865                      | CDIO_DRIVE_CAP_READ_MCN
866                      | CDIO_DRIVE_CAP_READ_ISRC
867                      );
868 
869     IOObjectRelease( service );
870   }
871 
872   return;
873 
874  err_exit:
875   *p_misc_cap = *p_write_cap = *p_read_cap = CDIO_DRIVE_CAP_UNKNOWN;
876   return;
877 }
878 
879 #if 1
880 /****************************************************************************
881  * GetDriveDescription - Gets drive description.
882  ****************************************************************************/
883 
884 static bool
get_hwinfo_osx(const CdIo_t * p_cdio,cdio_hwinfo_t * hw_info)885 get_hwinfo_osx ( const CdIo_t *p_cdio, /*out*/ cdio_hwinfo_t *hw_info)
886 {
887   _img_private_t *p_env = (_img_private_t *) p_cdio->env;
888   io_service_t  service = get_drive_service_osx(p_env);
889 
890   if ( service == 0 ) return false;
891 
892   /* Found our device */
893   {
894     CFStringRef      vendor      = NULL;
895     CFStringRef      product     = NULL;
896     CFStringRef      revision    = NULL;
897 
898     CFDictionaryRef  properties  = GetRegistryEntryProperties ( service );
899     CFDictionaryRef  deviceDict  = ( CFDictionaryRef )
900       CFDictionaryGetValue ( properties,
901                              CFSTR ( kIOPropertyDeviceCharacteristicsKey ) );
902 
903     if ( deviceDict == 0 ) return false;
904 
905     vendor = ( CFStringRef )
906       CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyVendorNameKey ) );
907 
908     if ( CFStringGetCString( vendor,
909                              (char *) &(hw_info->psz_vendor),
910                              sizeof(hw_info->psz_vendor),
911                              kCFStringEncodingASCII ) )
912       CFRelease( vendor );
913 
914     product = ( CFStringRef )
915       CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyProductNameKey ) );
916 
917     if ( CFStringGetCString( product,
918                              (char *) &(hw_info->psz_model),
919                              sizeof(hw_info->psz_model),
920                              kCFStringEncodingASCII ) )
921       CFRelease( product );
922 
923     revision = ( CFStringRef )
924       CFDictionaryGetValue ( deviceDict,
925                              CFSTR ( kIOPropertyProductRevisionLevelKey ) );
926 
927     if ( CFStringGetCString( revision,
928                              (char *) &(hw_info->psz_revision),
929                              sizeof(hw_info->psz_revision),
930                              kCFStringEncodingASCII ) )
931       CFRelease( revision );
932   }
933   return true;
934 
935 }
936 #endif
937 
938 static void
_free_osx(void * p_user_data)939 _free_osx (void *p_user_data) {
940   _img_private_t *p_env = p_user_data;
941   if (NULL == p_env) return;
942   if (p_env->gen.fd != -1)
943     close(p_env->gen.fd);
944   if (p_env->MediaClass_service)
945     IOObjectRelease( p_env->MediaClass_service );
946   cdio_generic_free(p_env);
947   if (NULL != p_env->pp_lba)  free((void *) p_env->pp_lba);
948   if (NULL != p_env->pTOC)    free((void *) p_env->pTOC);
949 
950   if (p_env->scsi_task)
951     (*p_env->scsi_task)->Release(p_env->scsi_task);
952 
953   if (p_env->pp_scsiTaskDeviceInterface)
954     (*p_env->pp_scsiTaskDeviceInterface) ->
955       ReleaseExclusiveAccess(p_env->pp_scsiTaskDeviceInterface);
956   if (p_env->pp_scsiTaskDeviceInterface)
957     (*p_env->pp_scsiTaskDeviceInterface) ->
958       Release ( p_env->pp_scsiTaskDeviceInterface );
959 
960   if (p_env->mmc)
961     (*p_env->mmc)->Release(p_env->mmc);
962 
963   if (p_env->plugin)
964     IODestroyPlugInInterface(p_env->plugin);
965 
966 }
967 
968 /**
969    Reads i_blocks of data sectors from cd device into p_data starting
970    from i_lsn.
971    Returns DRIVER_OP_SUCCESS if no error.
972  */
973 static driver_return_code_t
read_data_sectors_osx(void * p_user_data,void * p_data,lsn_t i_lsn,uint16_t i_blocksize,uint32_t i_blocks)974 read_data_sectors_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
975                        uint16_t i_blocksize, uint32_t i_blocks)
976 {
977   _img_private_t *p_env = p_user_data;
978 
979   if (!p_user_data) return DRIVER_OP_UNINIT;
980 
981   {
982     dk_cd_read_t cd_read;
983     track_t i_track = cdio_get_track(p_env->gen.cdio, i_lsn);
984 
985     memset( &cd_read, 0, sizeof(cd_read) );
986 
987     cd_read.sectorArea  = kCDSectorAreaUser;
988     cd_read.buffer      = p_data;
989 
990     /* FIXME: Do I have to put use get_track_green_osx? */
991     switch(get_track_format_osx(p_user_data, i_track)) {
992     case TRACK_FORMAT_CDI:
993     case TRACK_FORMAT_DATA:
994       cd_read.sectorType  = kCDSectorTypeMode1;
995       cd_read.offset      = i_lsn * kCDSectorSizeMode1;
996       break;
997     case TRACK_FORMAT_XA:
998       cd_read.sectorType  = kCDSectorTypeMode2;
999       cd_read.offset      = i_lsn * kCDSectorSizeMode2;
1000       break;
1001     default:
1002       return DRIVER_OP_ERROR;
1003     }
1004 
1005     cd_read.bufferLength = i_blocksize * i_blocks;
1006 
1007     if( ioctl( p_env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
1008       {
1009         cdio_info( "could not read block %d, %s", i_lsn, strerror(errno) );
1010         return DRIVER_OP_ERROR;
1011       }
1012     return DRIVER_OP_SUCCESS;
1013   }
1014 }
1015 
1016 
1017 /**
1018    Reads i_blocks of mode2 form2 sectors from cd device into data starting
1019    from i_lsn.
1020    Returns 0 if no error.
1021  */
1022 static driver_return_code_t
read_mode1_sectors_osx(void * p_user_data,void * p_data,lsn_t i_lsn,bool b_form2,uint32_t i_blocks)1023 read_mode1_sectors_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
1024                         bool b_form2, uint32_t i_blocks)
1025 {
1026   _img_private_t *p_env = p_user_data;
1027   dk_cd_read_t cd_read;
1028 
1029   memset( &cd_read, 0, sizeof(cd_read) );
1030 
1031   cd_read.sectorArea  = kCDSectorAreaUser;
1032   cd_read.buffer      = p_data;
1033   cd_read.sectorType  = kCDSectorTypeMode1;
1034 
1035   if (b_form2) {
1036     cd_read.offset       = i_lsn * kCDSectorSizeMode2;
1037     cd_read.bufferLength = kCDSectorSizeMode2 * i_blocks;
1038   } else {
1039     cd_read.offset       = i_lsn * kCDSectorSizeMode1;
1040     cd_read.bufferLength = kCDSectorSizeMode1 * i_blocks;
1041   }
1042 
1043    if( ioctl( p_env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
1044   {
1045     cdio_info( "could not read block %d, %s", i_lsn, strerror(errno) );
1046     return DRIVER_OP_ERROR;
1047   }
1048   return DRIVER_OP_SUCCESS;
1049 }
1050 
1051 /**
1052    Reads i_blocks of mode2 form2 sectors from cd device into data starting
1053    from lsn.
1054    Returns DRIVER_OP_SUCCESS if no error.
1055  */
1056 static driver_return_code_t
read_mode2_sectors_osx(void * p_user_data,void * p_data,lsn_t i_lsn,bool b_form2,uint32_t i_blocks)1057 read_mode2_sectors_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
1058                         bool b_form2, uint32_t i_blocks)
1059 {
1060   _img_private_t *p_env = p_user_data;
1061   dk_cd_read_t cd_read;
1062 
1063   memset( &cd_read, 0, sizeof(cd_read) );
1064 
1065   cd_read.sectorArea = kCDSectorAreaUser;
1066   cd_read.buffer = p_data;
1067 
1068   if (b_form2) {
1069     cd_read.offset       = i_lsn * kCDSectorSizeMode2Form2;
1070     cd_read.sectorType   = kCDSectorTypeMode2Form2;
1071     cd_read.bufferLength = kCDSectorSizeMode2Form2 * i_blocks;
1072   } else {
1073     cd_read.offset       = i_lsn * kCDSectorSizeMode2Form1;
1074     cd_read.sectorType   = kCDSectorTypeMode2Form1;
1075     cd_read.bufferLength = kCDSectorSizeMode2Form1 * i_blocks;
1076   }
1077 
1078   if( ioctl( p_env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
1079   {
1080     cdio_info( "could not read block %d, %s", i_lsn, strerror(errno) );
1081     return DRIVER_OP_ERROR;
1082   }
1083   return DRIVER_OP_SUCCESS;
1084 }
1085 
1086 
1087 /**
1088    Reads a single audio sector from CD device into p_data starting from lsn.
1089    Returns 0 if no error.
1090  */
1091 static int
read_audio_sectors_osx(void * user_data,void * p_data,lsn_t lsn,unsigned int i_blocks)1092 read_audio_sectors_osx (void *user_data, void *p_data, lsn_t lsn,
1093                              unsigned int i_blocks)
1094 {
1095   _img_private_t *env = user_data;
1096   dk_cd_read_t cd_read;
1097 
1098   memset( &cd_read, 0, sizeof(cd_read) );
1099 
1100   cd_read.offset       = lsn * kCDSectorSizeCDDA;
1101   cd_read.sectorArea   = kCDSectorAreaUser;
1102   cd_read.sectorType   = kCDSectorTypeCDDA;
1103 
1104   cd_read.buffer       = p_data;
1105   cd_read.bufferLength = kCDSectorSizeCDDA * i_blocks;
1106 
1107   if( ioctl( env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 )
1108   {
1109     cdio_info( "could not read block %d\n%s", lsn,
1110                strerror(errno));
1111     return DRIVER_OP_ERROR;
1112   }
1113   return DRIVER_OP_SUCCESS;
1114 }
1115 
1116 /**
1117    Reads a single mode2 sector from cd device into p_data starting
1118    from lsn. Returns 0 if no error.
1119  */
1120 static driver_return_code_t
read_mode1_sector_osx(void * p_user_data,void * p_data,lsn_t i_lsn,bool b_form2)1121 read_mode1_sector_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
1122                        bool b_form2)
1123 {
1124   return read_mode1_sectors_osx(p_user_data, p_data, i_lsn, b_form2, 1);
1125 }
1126 
1127 /**
1128    Reads a single mode2 sector from cd device into p_data starting
1129    from lsn. Returns 0 if no error.
1130  */
1131 static driver_return_code_t
read_mode2_sector_osx(void * p_user_data,void * p_data,lsn_t i_lsn,bool b_form2)1132 read_mode2_sector_osx (void *p_user_data, void *p_data, lsn_t i_lsn,
1133                        bool b_form2)
1134 {
1135   return read_mode2_sectors_osx(p_user_data, p_data, i_lsn, b_form2, 1);
1136 }
1137 
1138 /**
1139   Set the key "arg" to "value" in source device.
1140 */
1141 static driver_return_code_t
_set_arg_osx(void * p_user_data,const char key[],const char value[])1142 _set_arg_osx (void *p_user_data, const char key[], const char value[])
1143 {
1144   _img_private_t *p_env = p_user_data;
1145 
1146   if (!strcmp (key, "source"))
1147     {
1148       if (!value) return DRIVER_OP_ERROR;
1149       free (p_env->gen.source_name);
1150       p_env->gen.source_name = strdup (value);
1151     }
1152   else if (!strcmp (key, "access-mode"))
1153     {
1154       if (!strcmp(value, "OSX"))
1155         p_env->access_mode = _AM_OSX;
1156       else
1157         cdio_warn ("unknown access type: %s. ignored.", value);
1158     }
1159   else return DRIVER_OP_ERROR;
1160 
1161   return DRIVER_OP_SUCCESS;
1162 }
1163 
1164 #if 0
1165 static void
1166 TestDevice(_img_private_t *p_env, io_service_t service)
1167 {
1168   SInt32                          score;
1169   HRESULT                         herr;
1170   kern_return_t                   err;
1171   IOCFPlugInInterface             **plugInInterface = NULL;
1172   MMCDeviceInterface              **mmcInterface = NULL;
1173 
1174   /* Create the IOCFPlugIn interface so we can query it. */
1175 
1176   err = IOCreatePlugInInterfaceForService ( service,
1177                                             kIOMMCDeviceUserClientTypeID,
1178                                             kIOCFPlugInInterfaceID,
1179                                             &plugInInterface,
1180                                             &score );
1181   if ( err != noErr ) {
1182     printf("IOCreatePlugInInterfaceForService returned %d\n", err);
1183     return;
1184   }
1185 
1186   /* Query the interface for the MMCDeviceInterface. */
1187 
1188   herr = ( *plugInInterface )->QueryInterface ( plugInInterface,
1189                                                 CFUUIDGetUUIDBytes ( kIOMMCDeviceInterfaceID ),
1190                                                 ( LPVOID ) &mmcInterface );
1191 
1192   if ( herr != S_OK )     {
1193     printf("QueryInterface returned %ld\n", herr);
1194     return;
1195   }
1196 
1197   p_env->pp_scsiTaskDeviceInterface =
1198     ( *mmcInterface )->GetSCSITaskDeviceInterface ( mmcInterface );
1199 
1200   if ( NULL == p_env->pp_scsiTaskDeviceInterface )  {
1201     printf("GetSCSITaskDeviceInterface returned NULL\n");
1202     return;
1203   }
1204 
1205   ( *mmcInterface )->Release ( mmcInterface );
1206   IODestroyPlugInInterface ( plugInInterface );
1207 }
1208 #endif
1209 
1210 /**
1211   Read and cache the CD's Track Table of Contents and track info.
1212   Return false if successful or true if an error.
1213 */
1214 static bool
read_toc_osx(void * p_user_data)1215 read_toc_osx (void *p_user_data)
1216 {
1217   _img_private_t *p_env = p_user_data;
1218   CFDictionaryRef propertiesDict = 0;
1219   CFDataRef data;
1220 
1221   /* create a CF dictionary containing the TOC */
1222   propertiesDict = GetRegistryEntryProperties( p_env->MediaClass_service );
1223 
1224   if ( 0 == propertiesDict )     {
1225     return false;
1226   }
1227 
1228   /* get the TOC from the dictionary */
1229   data = (CFDataRef) CFDictionaryGetValue( propertiesDict,
1230                                            CFSTR(kIOCDMediaTOCKey) );
1231   if ( data  != NULL ) {
1232     CFRange range;
1233     CFIndex buf_len;
1234 
1235     buf_len = CFDataGetLength( data ) + 1;
1236     range = CFRangeMake( 0, buf_len );
1237 
1238     if( ( p_env->pTOC = (CDTOC *)malloc( buf_len ) ) != NULL ) {
1239       CFDataGetBytes( data, range, (u_char *) p_env->pTOC );
1240     } else {
1241       cdio_warn( "Trouble allocating CDROM TOC" );
1242       CFRelease( propertiesDict );
1243       return false;
1244     }
1245   } else     {
1246     cdio_warn( "Trouble reading TOC" );
1247     CFRelease( propertiesDict );
1248     return false;
1249   }
1250 
1251   /* TestDevice(p_env, service); */
1252   CFRelease( propertiesDict );
1253 
1254   p_env->i_descriptors = CDTOCGetDescriptorCount ( p_env->pTOC );
1255 
1256   /* Read in starting sectors. There may be non-tracks mixed in with
1257      the real tracks.  So find the first and last track number by
1258      scanning. Also find the lead-out track position.
1259    */
1260   {
1261     int i, i_leadout = -1;
1262 
1263     CDTOCDescriptor *pTrackDescriptors;
1264 
1265     p_env->pp_lba = malloc( p_env->i_descriptors * sizeof(int) );
1266     if( p_env->pp_lba == NULL )
1267       {
1268         cdio_warn("Out of memory in allocating track starting LSNs" );
1269         free( p_env->pTOC );
1270         return false;
1271       }
1272 
1273     pTrackDescriptors = p_env->pTOC->descriptors;
1274 
1275     p_env->gen.i_first_track = CDIO_CD_MAX_TRACKS+1;
1276     p_env->i_last_track      = CDIO_CD_MIN_TRACK_NO;
1277     p_env->i_first_session   = CDIO_CD_MAX_TRACKS+1;
1278     p_env->i_last_session    = CDIO_CD_MIN_TRACK_NO;
1279 
1280     for( i = 0; i < p_env->i_descriptors; i++ )
1281       {
1282         track_t i_track     = pTrackDescriptors[i].point;
1283         session_t i_session = pTrackDescriptors[i].session;
1284 
1285         cdio_debug( "point: %d, tno: %d, session: %d, adr: %d, control:%d, "
1286                     "address: %d:%d:%d, p: %d:%d:%d",
1287                     i_track,
1288                     pTrackDescriptors[i].tno, i_session,
1289                     pTrackDescriptors[i].adr, pTrackDescriptors[i].control,
1290                     pTrackDescriptors[i].address.minute,
1291                     pTrackDescriptors[i].address.second,
1292                     pTrackDescriptors[i].address.frame,
1293                     pTrackDescriptors[i].p.minute,
1294                     pTrackDescriptors[i].p.second,
1295                     pTrackDescriptors[i].p.frame );
1296 
1297         /* track information has adr = 1 */
1298         if ( 0x01 != pTrackDescriptors[i].adr )
1299           continue;
1300 
1301         if( i_track == OSX_CDROM_LEADOUT_TRACK )
1302           i_leadout = i;
1303 
1304         if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO )
1305           continue;
1306 
1307         if (p_env->gen.i_first_track > i_track)
1308           p_env->gen.i_first_track = i_track;
1309 
1310         if (p_env->i_last_track < i_track)
1311           p_env->i_last_track = i_track;
1312 
1313         if (p_env->i_first_session > i_session)
1314           p_env->i_first_session = i_session;
1315 
1316         if (p_env->i_last_session < i_session)
1317           p_env->i_last_session = i_session;
1318       }
1319 
1320     /* Now that we know what the first track number is, we can make sure
1321        index positions are ordered starting at 0.
1322      */
1323     for( i = 0; i < p_env->i_descriptors; i++ )
1324       {
1325         track_t i_track = pTrackDescriptors[i].point;
1326 
1327         if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO )
1328           continue;
1329 
1330         /* Note what OSX calls a LBA we call an LSN. So below re we
1331            really have have MSF -> LSN -> LBA.
1332          */
1333         p_env->pp_lba[i_track - p_env->gen.i_first_track] =
1334           cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i].p ));
1335         set_track_flags(&(p_env->gen.track_flags[i_track]),
1336                         pTrackDescriptors[i].control);
1337       }
1338 
1339     if( i_leadout == -1 )
1340       {
1341         cdio_warn( "CD leadout not found" );
1342         free( p_env->pp_lba );
1343         free( (void *) p_env->pTOC );
1344         return false;
1345       }
1346 
1347     /* Set leadout sector.
1348        Note what OSX calls a LBA we call an LSN. So below re we
1349        really have have MSF -> LSN -> LBA.
1350     */
1351     p_env->pp_lba[TOTAL_TRACKS] =
1352       cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p ));
1353     p_env->gen.i_tracks = TOTAL_TRACKS;
1354   }
1355 
1356   p_env->gen.toc_init   = true;
1357 
1358   return( true );
1359 
1360 }
1361 
1362 /**
1363   Return the starting LSN track number
1364   i_track in obj.  Track numbers start at 1.
1365   The "leadout" track is specified either by
1366   using i_track LEADOUT_TRACK or the total tracks+1.
1367   False is returned if there is no track entry.
1368 */
1369 static lsn_t
get_track_lba_osx(void * p_user_data,track_t i_track)1370 get_track_lba_osx(void *p_user_data, track_t i_track)
1371 {
1372   _img_private_t *p_env = p_user_data;
1373 
1374   if (!p_env->gen.toc_init) read_toc_osx (p_env) ;
1375   if (!p_env->gen.toc_init) return CDIO_INVALID_LSN;
1376 
1377   if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->i_last_track+1;
1378 
1379   if (i_track > p_env->i_last_track + 1 || i_track < p_env->gen.i_first_track) {
1380     return CDIO_INVALID_LSN;
1381   } else {
1382     return p_env->pp_lba[i_track - p_env->gen.i_first_track];
1383   }
1384 }
1385 
1386 /**
1387   Eject media . Return DRIVER_OP_SUCCESS if successful.
1388 
1389   The only way to cleanly unmount the disc under MacOS X (before
1390   Tiger) is to use the 'disktool' command line utility. It uses the
1391   non-public DiskArbitration API, which can not be used by Cocoa or
1392   Carbon applications.
1393 
1394   Since Tiger (MacOS X 10.4), DiskArbitration is a public framework
1395   and we can use it as needed.
1396 
1397  */
1398 
1399 #ifndef HAVE_DISKARBITRATION
1400 static driver_return_code_t
_eject_media_osx(void * user_data)1401 _eject_media_osx (void *user_data) {
1402 
1403   _img_private_t *p_env = user_data;
1404 
1405   FILE *p_file;
1406   char *psz_drive;
1407   char sz_cmd[32];
1408 
1409   if( ( psz_drive = (char *)strstr( p_env->gen.source_name, "disk" ) ) != NULL &&
1410       strlen( psz_drive ) > 4 )
1411     {
1412 #define EJECT_CMD "/usr/sbin/hdiutil eject %s"
1413       snprintf( sz_cmd, sizeof(sz_cmd), EJECT_CMD, psz_drive );
1414 #undef EJECT_CMD
1415 
1416       if( ( p_file = popen( sz_cmd, "r" ) ) != NULL )
1417         {
1418           char psz_result[0x200];
1419           int i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_file );
1420 
1421           if( i_ret == 0 && ferror( p_file ) != 0 )
1422             {
1423               pclose( p_file );
1424               return DRIVER_OP_ERROR;
1425             }
1426 
1427           pclose( p_file );
1428 
1429           psz_result[ i_ret ] = 0;
1430 
1431           if( strstr( psz_result, "Disk Ejected" ) != NULL )
1432             {
1433               return DRIVER_OP_SUCCESS;
1434             }
1435         }
1436     }
1437 
1438   return DRIVER_OP_ERROR;
1439 }
1440 #else /* HAVE_DISKARBITRATION */
1441 typedef struct dacontext_s {
1442     int                 result;
1443     Boolean             completed;
1444     DASessionRef        session;
1445     CFRunLoopRef        runloop;
1446     CFRunLoopSourceRef  cancel;
1447 } dacontext_t;
1448 
cancel_runloop(void * info)1449 static void cancel_runloop(void *info) { /* do nothing */ }
1450 
1451 static CFRunLoopSourceContext cancelRunLoopSourceContext = {
1452     .perform = cancel_runloop
1453 };
1454 
media_eject_callback(DADiskRef disk,DADissenterRef dissenter,void * context)1455 static void media_eject_callback(DADiskRef disk, DADissenterRef dissenter, void *context)
1456 {
1457     dacontext_t *dacontext = (dacontext_t *)context;
1458 
1459     if ( dissenter )
1460       {
1461         CFStringRef status = DADissenterGetStatusString(dissenter);
1462         if (status)
1463         {
1464                 size_t cstr_size = CFStringGetLength(status);
1465                 char *cstr = malloc(cstr_size);
1466                 if ( CFStringGetCString( status,
1467                                  cstr, cstr_size,
1468                                  kCFStringEncodingASCII ) )
1469                 CFRelease( status );
1470 
1471                 cdio_warn("%s", cstr);
1472 
1473                 free(cstr);
1474         }
1475       }
1476 
1477     dacontext->result    = (dissenter ? DRIVER_OP_ERROR : DRIVER_OP_SUCCESS);
1478     dacontext->completed = TRUE;
1479     CFRunLoopSourceSignal(dacontext->cancel);
1480     CFRunLoopWakeUp(dacontext->runloop);
1481 }
1482 
media_unmount_callback(DADiskRef disk,DADissenterRef dissenter,void * context)1483 static void media_unmount_callback(DADiskRef disk, DADissenterRef dissenter, void *context)
1484 {
1485     dacontext_t *dacontext = (dacontext_t *)context;
1486 
1487     if (!dissenter) {
1488         DADiskEject(disk, kDADiskEjectOptionDefault, media_eject_callback, context);
1489         dacontext->result = dacontext->result == DRIVER_OP_UNINIT ? DRIVER_OP_SUCCESS : dacontext->result;
1490     }
1491     else {
1492         dacontext->result    = DRIVER_OP_ERROR;
1493         dacontext->completed = TRUE;
1494         CFRunLoopSourceSignal(dacontext->cancel);
1495         CFRunLoopWakeUp(dacontext->runloop);
1496     }
1497 }
1498 
1499 static driver_return_code_t
_eject_media_osx(void * user_data)1500 _eject_media_osx (void *user_data) {
1501 
1502   _img_private_t *p_env = user_data;
1503   char *psz_drive;
1504 
1505   DADiskRef       disk;
1506   dacontext_t     dacontext;
1507   CFDictionaryRef description;
1508 
1509   if( ( psz_drive = (char *)strstr( p_env->gen.source_name, "disk" ) ) == NULL ||
1510       strlen( psz_drive ) <= 4 )
1511     {
1512       return DRIVER_OP_ERROR;
1513     }
1514 
1515   if (p_env->gen.fd != -1)
1516     close(p_env->gen.fd);
1517   p_env->gen.fd = -1;
1518 
1519   dacontext.result    = DRIVER_OP_UNINIT;
1520   dacontext.completed = FALSE;
1521   dacontext.runloop   = CFRunLoopGetCurrent();
1522   dacontext.cancel    = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &cancelRunLoopSourceContext);
1523 
1524   if (!dacontext.cancel)
1525     {
1526       return DRIVER_OP_ERROR;
1527     }
1528 
1529   if (!(dacontext.session = DASessionCreate(kCFAllocatorDefault)))
1530     {
1531       CFRelease(dacontext.cancel);
1532       return DRIVER_OP_ERROR;
1533     }
1534 
1535   if ((disk = DADiskCreateFromBSDName(kCFAllocatorDefault, dacontext.session, psz_drive)) != NULL)
1536     {
1537       if ((description = DADiskCopyDescription(disk)) != NULL)
1538         {
1539           /* Does the device need to be unmounted first? */
1540           DASessionScheduleWithRunLoop(dacontext.session, dacontext.runloop, kCFRunLoopDefaultMode);
1541           CFRunLoopAddSource(dacontext.runloop, dacontext.cancel, kCFRunLoopDefaultMode);
1542 
1543           if (CFDictionaryGetValueIfPresent(description, kDADiskDescriptionVolumePathKey, NULL))
1544             {
1545               DADiskUnmount(disk, kDADiskUnmountOptionWhole, media_unmount_callback, &dacontext);
1546             }
1547           else
1548             {
1549               DADiskEject(disk, kDADiskEjectOptionDefault, media_eject_callback, &dacontext);
1550               dacontext.result = dacontext.result == DRIVER_OP_UNINIT ? DRIVER_OP_SUCCESS : dacontext.result;
1551             }
1552           while (!dacontext.completed)
1553             {
1554               if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 30.0, TRUE) == kCFRunLoopRunTimedOut) break;  /* timeout after 30 seconds */
1555             }
1556           CFRunLoopRemoveSource(dacontext.runloop, dacontext.cancel, kCFRunLoopDefaultMode);
1557           DASessionUnscheduleFromRunLoop(dacontext.session, dacontext.runloop, kCFRunLoopDefaultMode);
1558           CFRelease(description);
1559         }
1560       CFRelease(disk);
1561     }
1562 
1563   CFRunLoopSourceInvalidate(dacontext.cancel);
1564   CFRelease(dacontext.cancel);
1565   CFRelease(dacontext.session);
1566   return dacontext.result;
1567 }
1568 #endif
1569 
1570 /**
1571    Return the size of the CD in logical block address (LBA) units.
1572  */
1573 static lsn_t
get_disc_last_lsn_osx(void * user_data)1574 get_disc_last_lsn_osx (void *user_data)
1575 {
1576   return get_track_lba_osx(user_data, CDIO_CDROM_LEADOUT_TRACK);
1577 }
1578 
1579 /**
1580   Return the value associated with the key "arg".
1581 */
1582 static const char *
_get_arg_osx(void * user_data,const char key[])1583 _get_arg_osx (void *user_data, const char key[])
1584 {
1585   _img_private_t *p_env = user_data;
1586 
1587   if (!strcmp (key, "source")) {
1588     return p_env->gen.source_name;
1589   } else if (!strcmp (key, "access-mode")) {
1590     switch (p_env->access_mode) {
1591     case _AM_OSX:
1592       return "OS X";
1593     case _AM_NONE:
1594       return "no access method";
1595     }
1596   }
1597   return NULL;
1598 }
1599 
1600 /**
1601   Return the media catalog number MCN.
1602  */
1603 static char *
get_mcn_osx(const void * user_data)1604 get_mcn_osx (const void *user_data) {
1605   const _img_private_t *p_env = user_data;
1606   dk_cd_read_mcn_t cd_read;
1607 
1608   memset( &cd_read, 0, sizeof(cd_read) );
1609 
1610   if( ioctl( p_env->gen.fd, DKIOCCDREADMCN, &cd_read ) < 0 )
1611   {
1612     cdio_debug( "could not read MCN, %s", strerror(errno) );
1613     return NULL;
1614   }
1615   return strdup((char*)cd_read.mcn);
1616 }
1617 
1618 /**
1619   Return the international standard recording code ISRC.
1620  */
1621 static char *
get_track_isrc_osx(const void * user_data,track_t i_track)1622 get_track_isrc_osx (const void *user_data, track_t i_track) {
1623   const _img_private_t *p_env = user_data;
1624   dk_cd_read_isrc_t cd_read;
1625 
1626   memset( &cd_read, 0, sizeof(cd_read) );
1627 
1628   cd_read.track = i_track;
1629 
1630   if( ioctl( p_env->gen.fd, DKIOCCDREADISRC, &cd_read ) < 0 )
1631   {
1632     cdio_debug( "could not read ISRC, %s", strerror(errno) );
1633     return NULL;
1634   }
1635   return strdup((char*)cd_read.isrc);
1636 }
1637 
1638 /**
1639   Get format of track.
1640 */
1641 static track_format_t
get_track_format_osx(void * p_user_data,track_t i_track)1642 get_track_format_osx(void *p_user_data, track_t i_track)
1643 {
1644   _img_private_t *p_env = p_user_data;
1645   dk_cd_read_track_info_t cd_read;
1646   CDTrackInfo a_track;
1647 
1648   if (!p_env->gen.toc_init) read_toc_osx (p_env) ;
1649 
1650   if (i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track)
1651     return TRACK_FORMAT_ERROR;
1652 
1653   memset( &cd_read, 0, sizeof(cd_read) );
1654 
1655   cd_read.address = i_track;
1656   cd_read.addressType = kCDTrackInfoAddressTypeTrackNumber;
1657 
1658   cd_read.buffer = &a_track;
1659   cd_read.bufferLength = sizeof(CDTrackInfo);
1660 
1661   if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 )
1662   {
1663     cdio_warn( "could not read trackinfo for track %d:\n%s", i_track,
1664                strerror(errno));
1665     return TRACK_FORMAT_ERROR;
1666   }
1667 
1668   cdio_debug( "%d: trackinfo trackMode: %x dataMode: %x", i_track,
1669               a_track.trackMode, a_track.dataMode );
1670 
1671   if (a_track.trackMode == CDIO_CDROM_DATA_TRACK) {
1672     if (a_track.dataMode == CDROM_CDI_TRACK) {
1673       return TRACK_FORMAT_CDI;
1674     } else if (a_track.dataMode == CDROM_XA_TRACK) {
1675       return TRACK_FORMAT_XA;
1676     } else {
1677       return TRACK_FORMAT_DATA;
1678     }
1679   } else {
1680     return TRACK_FORMAT_AUDIO;
1681   }
1682 
1683 }
1684 
1685 /**
1686   Return true if we have XA data (green, mode2 form1) or
1687   XA data (green, mode2 form2). That is track begins:
1688   sync - header - subheader
1689   12     4      -  8
1690 
1691   FIXME: there's gotta be a better design for this and get_track_format?
1692 */
1693 static bool
get_track_green_osx(void * p_user_data,track_t i_track)1694 get_track_green_osx(void *p_user_data, track_t i_track)
1695 {
1696   _img_private_t *p_env = p_user_data;
1697   CDTrackInfo a_track;
1698 
1699   if (!p_env->gen.toc_init) read_toc_osx (p_env) ;
1700 
1701   if ( i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track )
1702     return false;
1703 
1704   else {
1705 
1706     dk_cd_read_track_info_t cd_read;
1707 
1708     memset( &cd_read, 0, sizeof(cd_read) );
1709 
1710     cd_read.address      = i_track;
1711     cd_read.addressType  = kCDTrackInfoAddressTypeTrackNumber;
1712 
1713     cd_read.buffer       = &a_track;
1714     cd_read.bufferLength = sizeof(CDTrackInfo);
1715 
1716     if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 ) {
1717       cdio_warn( "could not read trackinfo for track %d:\n%s", i_track,
1718                  strerror(errno));
1719       return false;
1720     }
1721     return ((a_track.trackMode & CDIO_CDROM_DATA_TRACK) != 0);
1722   }
1723 }
1724 
1725 /* Set CD-ROM drive speed */
1726 static int
set_speed_osx(void * p_user_data,int i_speed)1727 set_speed_osx (void *p_user_data, int i_speed)
1728 {
1729   const _img_private_t *p_env = p_user_data;
1730 
1731   if (!p_env) return -1;
1732   return ioctl(p_env->gen.fd, DKIOCCDSETSPEED, i_speed);
1733 }
1734 
1735 #endif /* HAVE_DARWIN_CDROM */
1736 
1737 /**
1738   Close tray on CD-ROM.
1739 
1740   @param psz_drive the CD-ROM drive to be closed.
1741 
1742 */
1743 
1744 /* FIXME: We don't use the device name because we don't how
1745    to.
1746  */
1747 #define CLOSE_TRAY_CMD "/usr/sbin/drutil tray close"
1748 driver_return_code_t
close_tray_osx(const char * psz_drive)1749 close_tray_osx (const char *psz_drive)
1750 {
1751 #ifdef HAVE_DARWIN_CDROM
1752   FILE *p_file;
1753   char sz_cmd[80];
1754 
1755   if ( !psz_drive) return DRIVER_OP_UNINIT;
1756 
1757   /* Right now we really aren't making use of snprintf, but
1758      possibly someday we will.
1759    */
1760   snprintf( sz_cmd, sizeof(sz_cmd), CLOSE_TRAY_CMD );
1761 
1762   if( ( p_file = popen( sz_cmd, "r" ) ) != NULL )
1763     {
1764       char psz_result[0x200];
1765       int i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_file );
1766 
1767       if( i_ret == 0 && ferror( p_file ) != 0 )
1768         {
1769           pclose( p_file );
1770           return DRIVER_OP_ERROR;
1771         }
1772 
1773       pclose( p_file );
1774 
1775       psz_result[ i_ret ] = 0;
1776 
1777       if( 0 == i_ret )
1778         {
1779           return DRIVER_OP_SUCCESS;
1780         }
1781     }
1782 
1783   return DRIVER_OP_ERROR;
1784 #else
1785   return DRIVER_OP_NO_DRIVER;
1786 #endif /*HAVE_DARWIN_CDROM*/
1787 }
1788 
1789 /**
1790   Return a string containing the default CD device if none is specified.
1791  */
1792 char **
cdio_get_devices_osx(void)1793 cdio_get_devices_osx(void)
1794 {
1795 #ifndef HAVE_DARWIN_CDROM
1796   return NULL;
1797 #else
1798   io_object_t   next_media;
1799   mach_port_t   master_port;
1800   kern_return_t kern_result;
1801   io_iterator_t media_iterator;
1802   CFMutableDictionaryRef classes_to_match;
1803   char        **drives = NULL;
1804   unsigned int  num_drives=0;
1805 
1806   /* Probe devices to get up to date information. */
1807   ProbeStorageDevices();
1808 
1809   kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
1810   if( kern_result != KERN_SUCCESS )
1811     {
1812       return( NULL );
1813     }
1814 
1815   classes_to_match = IOServiceMatching( kIOMediaClass );
1816   if( classes_to_match == NULL )
1817     {
1818       return( NULL );
1819     }
1820 
1821   CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey),
1822                         kCFBooleanTrue );
1823 
1824   CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaWholeKey),
1825                         kCFBooleanTrue );
1826 
1827   kern_result = IOServiceGetMatchingServices( master_port,
1828                                               classes_to_match,
1829                                               &media_iterator );
1830   if( kern_result != KERN_SUCCESS )
1831     {
1832       return( NULL );
1833     }
1834 
1835   next_media = IOIteratorNext( media_iterator );
1836   if( next_media != 0 )
1837     {
1838       char psz_buf[0x32];
1839       size_t dev_path_length;
1840       CFTypeRef str_bsd_path;
1841 
1842       do
1843         {
1844           str_bsd_path =
1845             IORegistryEntryCreateCFProperty( next_media,
1846                                              CFSTR( kIOBSDNameKey ),
1847                                              kCFAllocatorDefault,
1848                                              0 );
1849           if( str_bsd_path == NULL )
1850             {
1851               IOObjectRelease( next_media );
1852               continue;
1853             }
1854 
1855           /* Below, by appending 'r' to the BSD node name, we indicate
1856              a raw disk. Raw disks receive I/O requests directly and
1857              don't go through a buffer cache. */
1858           snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
1859           dev_path_length = strlen( psz_buf );
1860 
1861           if( CFStringGetCString( str_bsd_path,
1862                                   (char*)&psz_buf + dev_path_length,
1863                                   sizeof(psz_buf) - dev_path_length,
1864                                   kCFStringEncodingASCII ) )
1865             {
1866               cdio_add_device_list(&drives, strdup(psz_buf), &num_drives);
1867             }
1868           CFRelease( str_bsd_path );
1869           IOObjectRelease( next_media );
1870 
1871         } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 );
1872     }
1873   IOObjectRelease( media_iterator );
1874   cdio_add_device_list(&drives, NULL, &num_drives);
1875   return drives;
1876 #endif /* HAVE_DARWIN_CDROM */
1877 }
1878 
1879 /**
1880   Return a string containing the default CD device if none is specified.
1881  */
1882 char *
cdio_get_default_device_osx(void)1883 cdio_get_default_device_osx(void)
1884 {
1885 #ifndef HAVE_DARWIN_CDROM
1886   return NULL;
1887 #else
1888   io_object_t   next_media;
1889   kern_return_t kern_result;
1890   io_iterator_t media_iterator;
1891   CFMutableDictionaryRef classes_to_match;
1892 
1893   /* Probe devices to get up to date information. */
1894   ProbeStorageDevices();
1895 
1896   classes_to_match = IOServiceMatching( kIOMediaClass );
1897   if( classes_to_match == NULL )
1898     {
1899       return( NULL );
1900     }
1901 
1902   CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey),
1903                         kCFBooleanTrue );
1904 
1905   CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaWholeKey),
1906                         kCFBooleanTrue );
1907 
1908   kern_result = IOServiceGetMatchingServices( kIOMasterPortDefault,
1909                                               classes_to_match,
1910                                               &media_iterator );
1911   if( kern_result != KERN_SUCCESS )
1912     {
1913       return( NULL );
1914     }
1915 
1916   next_media = IOIteratorNext( media_iterator );
1917   if( next_media != 0 )
1918     {
1919       char psz_buf[0x32];
1920       size_t dev_path_length;
1921       CFTypeRef str_bsd_path;
1922 
1923       do
1924         {
1925           /* Skip other removable media, like USB flash memory keys:  */
1926           if (!IOObjectConformsTo(next_media, kIODVDMediaClass) &&
1927               !IOObjectConformsTo(next_media, kIOCDMediaClass) &&
1928               !IOObjectConformsTo(next_media, kIOBDMediaClass))
1929             continue;
1930 
1931           str_bsd_path = IORegistryEntryCreateCFProperty( next_media,
1932                                                           CFSTR( kIOBSDNameKey ),
1933                                                           kCFAllocatorDefault,
1934                                                           0 );
1935           if( str_bsd_path == NULL )
1936             {
1937               IOObjectRelease( next_media );
1938               continue;
1939             }
1940 
1941           snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
1942           dev_path_length = strlen( psz_buf );
1943 
1944           if( CFStringGetCString( str_bsd_path,
1945                                   (char*)&psz_buf + dev_path_length,
1946                                   sizeof(psz_buf) - dev_path_length,
1947                                   kCFStringEncodingASCII ) )
1948             {
1949               CFRelease( str_bsd_path );
1950               IOObjectRelease( next_media );
1951               IOObjectRelease( media_iterator );
1952               return strdup( psz_buf );
1953             }
1954 
1955           CFRelease( str_bsd_path );
1956           IOObjectRelease( next_media );
1957 
1958         } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 );
1959     }
1960   IOObjectRelease( media_iterator );
1961   cdio_warn ("cdio_get_default_device() - No CD/DVD/BD media - returning NULL");
1962   return NULL;
1963 #endif /* HAVE_DARWIN_CDROM */
1964 }
1965 
1966 /**
1967   Initialization routine. This is the only thing that doesn't
1968   get called via a function pointer. In fact *we* are the
1969   ones to set that up.
1970  */
1971 CdIo_t *
cdio_open_am_osx(const char * psz_source_name,const char * psz_access_mode)1972 cdio_open_am_osx (const char *psz_source_name, const char *psz_access_mode)
1973 {
1974 
1975   if (psz_access_mode != NULL)
1976     cdio_warn ("there is only one access mode for OS X. Arg %s ignored",
1977                psz_access_mode);
1978   return cdio_open_osx(psz_source_name);
1979 }
1980 
1981 
1982 /**
1983   Initialization routine. This is the only thing that doesn't
1984   get called via a function pointer. In fact *we* are the
1985   ones to set that up.
1986  */
1987 CdIo_t *
cdio_open_osx(const char * psz_orig_source)1988 cdio_open_osx (const char *psz_orig_source)
1989 {
1990 #ifdef HAVE_DARWIN_CDROM
1991   CdIo_t *ret;
1992   _img_private_t *_data;
1993   char *psz_source;
1994 
1995   cdio_funcs_t _funcs = {
1996     .eject_media           = _eject_media_osx,
1997     .free                  = _free_osx,
1998     .get_arg               = _get_arg_osx,
1999     .get_cdtext            = get_cdtext_generic,
2000     .get_cdtext_raw        = read_cdtext_generic,
2001     .get_default_device    = cdio_get_default_device_osx,
2002     .get_devices           = cdio_get_devices_osx,
2003     .get_disc_last_lsn     = get_disc_last_lsn_osx,
2004     .get_discmode          = get_discmode_osx,
2005     .get_drive_cap         = get_drive_cap_osx,
2006     .get_first_track_num   = get_first_track_num_generic,
2007     .get_hwinfo            = get_hwinfo_osx,
2008     .get_mcn               = get_mcn_osx,
2009     .get_num_tracks        = get_num_tracks_generic,
2010     .get_track_channels    = get_track_channels_generic,
2011     .get_track_copy_permit = get_track_copy_permit_generic,
2012     .get_track_format      = get_track_format_osx,
2013     .get_track_green       = get_track_green_osx,
2014     .get_track_lba         = get_track_lba_osx,
2015     .get_track_msf         = NULL,
2016     .get_track_preemphasis = get_track_preemphasis_generic,
2017     .get_track_isrc        = get_track_isrc_osx,
2018     .lseek                 = cdio_generic_lseek,
2019     .read                  = cdio_generic_read,
2020     .read_audio_sectors    = read_audio_sectors_osx,
2021     .read_data_sectors     = read_data_sectors_osx,
2022     .read_mode1_sector     = read_mode1_sector_osx,
2023     .read_mode1_sectors    = read_mode1_sectors_osx,
2024     .read_mode2_sector     = read_mode2_sector_osx,
2025     .read_mode2_sectors    = read_mode2_sectors_osx,
2026     .read_toc              = read_toc_osx,
2027     .run_mmc_cmd           = run_mmc_cmd_osx,
2028     .set_arg               = _set_arg_osx,
2029     .set_speed             = set_speed_osx,
2030   };
2031 
2032   _data                     = calloc (1, sizeof (_img_private_t));
2033   _data->access_mode        = _AM_OSX;
2034   _data->MediaClass_service = 0;
2035   _data->gen.init           = false;
2036   _data->gen.fd             = -1;
2037   _data->gen.toc_init       = false;
2038   _data->gen.b_cdtext_error = false;
2039 
2040   if (NULL == psz_orig_source) {
2041     psz_source=cdio_get_default_device_osx();
2042     if (NULL == psz_source) {
2043 	goto error_exit;
2044     }
2045 
2046     _set_arg_osx(_data, "source", psz_source);
2047     free(psz_source);
2048   } else {
2049     if (cdio_is_device_generic(psz_orig_source))
2050       _set_arg_osx(_data, "source", psz_orig_source);
2051     else {
2052       /* The below would be okay if all device drivers worked this way. */
2053 #if 0
2054       cdio_info ("source %s is a not a device", psz_orig_source);
2055 #endif
2056       goto error_exit;
2057     }
2058   }
2059 
2060   ret = cdio_new ((void *)_data, &_funcs);
2061   if (ret == NULL)
2062     goto error_exit;
2063 
2064   ret->driver_id = DRIVER_OSX;
2065 
2066   if (cdio_generic_init(_data, O_RDONLY | O_NONBLOCK) && init_osx(_data)) {
2067     return ret;
2068   }
2069   free(ret);
2070  error_exit:
2071   cdio_generic_free(_data);
2072   return NULL;
2073 
2074 #else
2075   return NULL;
2076 #endif /* HAVE_DARWIN_CDROM */
2077 
2078 }
2079 
2080 bool
cdio_have_osx(void)2081 cdio_have_osx (void)
2082 {
2083 #ifdef HAVE_DARWIN_CDROM
2084   return true;
2085 #else
2086   return false;
2087 #endif /* HAVE_DARWIN_CDROM */
2088 }
2089