5 * This file is part of the HandBrake source code.
6 * Homepage: <http://handbrake.m0k.org/>.
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 // Mow let's get the device name
105 GetVolParmsInfoBuffer volumeParms;
107 pb.ioParam.ioNamePtr = NULL;
108 pb.ioParam.ioVRefNum = volRefNum;
109 pb.ioParam.ioBuffer = (Ptr) &volumeParms;
110 pb.ioParam.ioReqCount = sizeof( volumeParms );
111 err = PBHGetVolParmsSync( &pb );
117 // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the vMDeviceID field.
118 // It is actually a char * value. This is mentioned in the header CoreServices/CarbonCore/Files.h.
119 return [NSString stringWithCString:(char *)volumeParms.vMDeviceID];
123 - (BOOL)pathHasVideoTS
125 // Check one level under the path
126 if( [[NSFileManager defaultManager] fileExistsAtPath:
127 [path stringByAppendingPathComponent:@"VIDEO_TS"]] )
132 // Now check above the path
133 return [[path pathComponents] containsObject:@"VIDEO_TS"];
139 io_service_t service = [self getIOKitServiceForBSDName];
140 if( service == IO_OBJECT_NULL )
144 BOOL result = [self isDVDService:service];
145 IOObjectRelease(service);
150 - (io_service_t)getIOKitServiceForBSDName
152 CFMutableDictionaryRef matchingDict;
153 matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, [bsdName cString] );
154 if( matchingDict == NULL )
156 return IO_OBJECT_NULL;
159 // Fetch the object with the matching BSD node name. There should only be
160 // one match, so IOServiceGetMatchingService is used instead of
161 // IOServiceGetMatchingServices to simplify the code.
162 return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict );
166 - (BOOL)isDVDService: (io_service_t)service
168 // Find the IOMedia object that represents the entire (whole) media that the
171 // If the volume is on partitioned media, the whole media object will be a
172 // parent of the volume's media object. If the media is not partitioned, the
173 // volume's media object will be the whole media object.
175 // The whole media object is indicated in the IORegistry by the presence of
176 // a property with the key "Whole" and value "Yes".
178 // Create an iterator across all parents of the service object passed in.
179 kern_return_t kernResult;
181 kernResult = IORegistryEntryCreateIterator( service,
183 kIORegistryIterateRecursively | kIORegistryIterateParents,
185 if( kernResult != KERN_SUCCESS )
189 if( iter == IO_OBJECT_NULL )
195 // A reference on the initial service object is released in the do-while loop below,
196 // so add a reference to balance.
197 IOObjectRetain( service );
202 isDVD = ( [self isWholeMediaService:service] &&
203 IOObjectConformsTo(service, kIODVDMediaClass) );
204 IOObjectRelease(service);
205 } while( !isDVD && (service = IOIteratorNext(iter)) );
206 IOObjectRelease( iter );
212 - (BOOL)isWholeMediaService: (io_service_t)service
215 // Determine if the object passed in represents an IOMedia (or subclass) object.
216 // If it does, test the "Whole" property.
219 Boolean isWholeMedia = NO;
221 if( IOObjectConformsTo(service, kIOMediaClass) )
223 CFTypeRef wholeMedia;
224 wholeMedia = IORegistryEntryCreateCFProperty( service,
225 CFSTR(kIOMediaWholeKey),
232 isWholeMedia = CFBooleanGetValue( (CFBooleanRef)wholeMedia );
233 CFRelease(wholeMedia);