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