5 * This file is part of the HandBrake source code.
6 * Homepage: <http://handbrake.fr/>.
7 * It may be used under the terms of the GNU General Public License.
10 #include <IOKit/IOKitLib.h>
11 #include <IOKit/storage/IOMedia.h>
12 #include <IOKit/storage/IODVDMedia.h>
14 #import "HBDVDDetector.h"
17 @interface HBDVDDetector (Private)
19 - (NSString *)bsdNameForPath;
20 - (BOOL)pathHasVideoTS;
22 - (io_service_t)getIOKitServiceForBSDName;
23 - (BOOL)isDVDService: (io_service_t)service;
24 - (BOOL)isWholeMediaService: (io_service_t)service;
29 @implementation HBDVDDetector
31 + (HBDVDDetector *)detectorForPath: (NSString *)aPath
33 return [[[self alloc] initWithPath:aPath] autorelease];
37 - (HBDVDDetector *)initWithPath: (NSString *)aPath
39 NSAssert(aPath, @"nil string passed to drive detector.");
40 if( self = [super init] )
42 path = [aPath retain];
63 bsdName = [[self bsdNameForPath] retain];
65 return ( [self pathHasVideoTS] && [self deviceIsDVD] );
69 - (NSString *)devicePath
73 bsdName = [[self bsdNameForPath] retain];
75 return [NSString stringWithFormat:@"/dev/%@", bsdName];
81 @implementation HBDVDDetector (Private)
83 - (NSString *)bsdNameForPath
87 err = FSPathMakeRef( (const UInt8 *) [path fileSystemRepresentation],
94 // Get the volume reference number.
95 FSCatalogInfo catalogInfo;
96 err = FSGetCatalogInfo( &ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL,
102 FSVolumeRefNum volRefNum = catalogInfo.volume;
104 // Now let's get the device name
105 GetVolParmsInfoBuffer volumeParms;
106 err = FSGetVolumeParms ( volRefNum, &volumeParms, sizeof( volumeParms ) );
113 // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the vMDeviceID field.
114 // It is actually a char * value. This is mentioned in the header CoreServices/CarbonCore/Files.h.
115 if( volumeParms.vMVersion < 4 )
120 // vMDeviceID might be zero as is reported with experimental ZFS (zfs-119) support in Leopard.
121 if( !volumeParms.vMDeviceID )
126 return [NSString stringWithCString:(const char *)volumeParms.vMDeviceID];
130 - (BOOL)pathHasVideoTS
132 // Check one level under the path
133 if( [[NSFileManager defaultManager] fileExistsAtPath:
134 [path stringByAppendingPathComponent:@"VIDEO_TS"]] )
139 // Now check above the path
140 return [[path pathComponents] containsObject:@"VIDEO_TS"];
146 io_service_t service = [self getIOKitServiceForBSDName];
147 if( service == IO_OBJECT_NULL )
151 BOOL result = [self isDVDService:service];
152 IOObjectRelease(service);
157 - (io_service_t)getIOKitServiceForBSDName
159 CFMutableDictionaryRef matchingDict;
160 matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, [bsdName UTF8String] );
161 if( matchingDict == NULL )
163 return IO_OBJECT_NULL;
166 // Fetch the object with the matching BSD node name. There should only be
167 // one match, so IOServiceGetMatchingService is used instead of
168 // IOServiceGetMatchingServices to simplify the code.
169 return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict );
173 - (BOOL)isDVDService: (io_service_t)service
175 // Find the IOMedia object that represents the entire (whole) media that the
178 // If the volume is on partitioned media, the whole media object will be a
179 // parent of the volume's media object. If the media is not partitioned, the
180 // volume's media object will be the whole media object.
182 // The whole media object is indicated in the IORegistry by the presence of
183 // a property with the key "Whole" and value "Yes".
185 // Create an iterator across all parents of the service object passed in.
186 kern_return_t kernResult;
188 kernResult = IORegistryEntryCreateIterator( service,
190 kIORegistryIterateRecursively | kIORegistryIterateParents,
192 if( kernResult != KERN_SUCCESS )
196 if( iter == IO_OBJECT_NULL )
202 // A reference on the initial service object is released in the do-while loop below,
203 // so add a reference to balance.
204 IOObjectRetain( service );
209 isDVD = ( [self isWholeMediaService:service] &&
210 IOObjectConformsTo(service, kIODVDMediaClass) );
211 IOObjectRelease(service);
212 } while( !isDVD && (service = IOIteratorNext(iter)) );
213 IOObjectRelease( iter );
219 - (BOOL)isWholeMediaService: (io_service_t)service
222 // Determine if the object passed in represents an IOMedia (or subclass) object.
223 // If it does, test the "Whole" property.
226 Boolean isWholeMedia = NO;
228 if( IOObjectConformsTo(service, kIOMediaClass) )
230 CFTypeRef wholeMedia;
231 wholeMedia = IORegistryEntryCreateCFProperty( service,
232 CFSTR(kIOMediaWholeKey),
239 isWholeMedia = CFBooleanGetValue( (CFBooleanRef)wholeMedia );
240 CFRelease(wholeMedia);