filename | src/drivers/osx_iokit.c |
changeset | 720:b5594d1ac80a |
next | 729:4cc913eabd3d |
author | nkeynes |
date | Sat Jul 05 11:57:36 2008 +0000 (15 years ago) |
permissions | -rw-r--r-- |
last change | Get OS X cdrom driver to the 'sort of working' stage. Hide most of the IOKit fun in a separate osx_iokit module. |
view | annotate | diff | log | raw |
1 /**
2 * $Id: cd_osx.c 727 2008-06-25 10:40:45Z nkeynes $
3 *
4 * OSX support functions for handling the IOKit registry.
5 * Currently this manages access to CD/DVD drives + media, plus HID devices.
6 *
7 * The HID part is much simpler...
8 *
9 * Copyright (c) 2008 Nathan Keynes.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 */
22 #include <sys/param.h>
23 #include <paths.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <IOKit/IOMessage.h>
28 #include <IOKit/hid/IOHIDLib.h>
29 #include "osx_iokit.h"
31 static IONotificationPortRef notify_port = 0;
32 static io_iterator_t iokit_iterators[4] = {0,0,0,0};
34 struct osx_cdrom_drive {
35 io_string_t ioservice_path;
36 io_string_t vendor_name;
37 io_string_t product_name;
38 char media_path[MAXPATHLEN]; // BSD device path if media present, otherwise the empty string.
39 io_iterator_t media_load_iterator;
40 io_iterator_t media_unload_iterator;
41 int media_fh; // BSD device handle if open, otherwise -1
42 media_changed_callback_t media_changed;
43 void *media_changed_user_data;
44 };
46 static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen );
48 /***************** IOKit Callbacks ******************/
50 /**
51 * Called from IOKit for any IOMessages on an IOMedia. Currently the only message
52 * we're interested in is service termination.
53 */
54 static void osx_cdrom_media_notify( void *ref, io_service_t service, uint32_t msgType,
55 void *msgArgument )
56 {
57 if( msgType == kIOMessageServiceIsTerminated ) {
58 osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;
59 if( drive->media_changed != NULL ) {
60 drive->media_changed( drive, FALSE, drive->media_changed_user_data );
61 }
62 if( drive->media_fh != -1 ) {
63 close(drive->media_fh);
64 drive->media_fh = -1;
65 }
66 drive->media_path[0] = '\0';
67 IOObjectRelease( drive->media_unload_iterator );
68 }
69 }
71 /**
72 * Called from IOKit when an IOMedia is inserted that we have be interested in.
73 * FIXME: Can the matcher be restricted to descendents of the drive node? currently
74 * we watch for all IOMedia events and compare the device path to see if it's one we
75 * care about.
76 * FIXME: We assume for now that a drive has at most one piece of media at a time.
77 * If this isn't the case, the system may get a little confused.
78 */
79 static void osx_cdrom_media_inserted( void *ref, io_iterator_t iterator )
80 {
81 osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;
83 io_object_t object;
84 while( (object = IOIteratorNext(iterator)) != 0 ) {
85 io_string_t iopath = "";
86 IORegistryEntryGetPath( object, kIOServicePlane, iopath );
87 if( drive != NULL && g_str_has_prefix(iopath, drive->ioservice_path ) &&
88 get_bsdname_for_iomedia(object, drive->media_path, sizeof(drive->media_path)) ) {
89 // A disc was inserted within the drive of interest
90 if( drive->media_fh != -1 ) {
91 close(drive->media_fh);
92 drive->media_fh = -1;
93 }
95 if( drive->media_changed != NULL ) {
96 drive->media_changed(drive, TRUE, drive->media_changed_user_data);
97 }
98 // Add a notification listener to get removal events.
99 IOServiceAddInterestNotification( notify_port, object, kIOGeneralInterest,
100 osx_cdrom_media_notify, drive, &drive->media_unload_iterator );
102 }
103 IOObjectRelease( object );
104 }
105 }
107 static void osx_drives_changed( void *ref, io_iterator_t iterator )
108 {
109 io_object_t object;
110 while( (object = IOIteratorNext(iterator)) != 0 ) {
111 IOObjectRelease(object);
112 }
114 }
116 /******************** Support functions *********************/
118 /**
119 * Determine the BSD device name (ie "/dev/rdisk1") for a given IO object.
120 * @return TRUE if the device name was retrieved, FALSE if the request failed.
121 */
122 static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen )
123 {
124 gboolean result = FALSE;
125 CFTypeRef pathRef = IORegistryEntryCreateCFProperty(iomedia, CFSTR(kIOBSDNameKey),
126 kCFAllocatorDefault, 0 );
127 if( pathRef ) {
128 char pathlen;
129 strcpy( buf, _PATH_DEV "r" );
130 pathlen = strlen(buf);
131 if( CFStringGetCString( pathRef, buf + pathlen, buflen-pathlen,
132 kCFStringEncodingASCII ) != noErr ) {
133 result = TRUE;
134 }
135 CFRelease(pathRef);
136 }
137 return result;
138 }
140 static gboolean osx_cdrom_drive_get_name( io_object_t object, char *vendor, int vendor_len,
141 char *product, int product_len )
142 {
143 gboolean result = FALSE;
144 CFMutableDictionaryRef props = 0;
145 if( IORegistryEntryCreateCFProperties(object, &props, kCFAllocatorDefault, kNilOptions) == KERN_SUCCESS ) {
146 CFDictionaryRef dict =
147 (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOPropertyDeviceCharacteristicsKey));
148 if( dict != NULL ) {
149 CFTypeRef value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyVendorNameKey));
150 if( value && CFGetTypeID(value) == CFStringGetTypeID() ) {
151 CFStringGetCString( (CFStringRef)value, vendor, vendor_len, kCFStringEncodingUTF8 );
152 } else {
153 vendor[0] = 0;
154 }
156 value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyProductNameKey));
157 if ( value && CFGetTypeID(value) == CFStringGetTypeID() ) {
158 CFStringGetCString( (CFStringRef)value, product, product_len, kCFStringEncodingUTF8 );
159 } else {
160 product[0] = 0;
161 }
162 result = TRUE;
163 }
165 CFRelease(props);
166 }
167 return result;
168 }
170 /**
171 * Construct and initialize a new osx_cdrom_drive object, including registering
172 * it's media inserted notification.
173 */
174 static osx_cdrom_drive_t osx_cdrom_drive_new( io_object_t device )
175 {
176 osx_cdrom_drive_t drive = g_malloc0(sizeof(struct osx_cdrom_drive));
178 IORegistryEntryGetPath( device, kIOServicePlane, drive->ioservice_path );
179 osx_cdrom_drive_get_name( device, drive->vendor_name, sizeof(drive->vendor_name),
180 drive->product_name, sizeof(drive->product_name) );
181 drive->media_path[0] = '\0';
182 drive->media_changed = NULL;
183 drive->media_changed_user_data = NULL;
184 drive->media_fh = -1;
186 IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
187 IOServiceMatching("IOMedia"),
188 osx_cdrom_media_inserted, drive,
189 &drive->media_load_iterator );
190 osx_cdrom_media_inserted( drive, drive->media_load_iterator );
191 return drive;
192 }
194 /************************ Exported functions *************************/
196 osx_cdrom_drive_t osx_cdrom_open_drive( const char *devname )
197 {
198 io_object_t object = IORegistryEntryFromPath( kIOMasterPortDefault, devname );
199 if( object == MACH_PORT_NULL ) {
200 return NULL;
201 }
203 osx_cdrom_drive_t drive = osx_cdrom_drive_new( object );
204 IOObjectRelease( object );
205 return drive;
206 }
208 void osx_cdrom_set_media_changed_callback( osx_cdrom_drive_t drive,
209 media_changed_callback_t callback,
210 void *user_data )
211 {
212 drive->media_changed = callback;
213 drive->media_changed_user_data = user_data;
214 }
216 void osx_cdrom_close_drive( osx_cdrom_drive_t drive )
217 {
218 IOObjectRelease( drive->media_load_iterator );
219 IOObjectRelease( drive->media_unload_iterator );
220 if( drive->media_fh != -1 ) {
221 close(drive->media_fh);
222 drive->media_fh = -1;
223 }
224 g_free( drive );
225 }
227 int osx_cdrom_get_media_handle( osx_cdrom_drive_t drive )
228 {
229 if( drive->media_fh == -1 ) {
230 if( drive->media_path[0] != '\0' ) {
231 drive->media_fh = open( drive->media_path, O_RDONLY|O_NONBLOCK );
232 }
233 }
234 return drive->media_fh;
235 }
237 void osx_cdrom_release_media_handle( osx_cdrom_drive_t drive )
238 {
239 if( drive->media_fh != -1 ) {
240 close( drive->media_fh );
241 drive->media_fh = -1;
242 }
243 }
245 static io_object_t iterator_find_cdrom( io_object_t iterator, find_drive_callback_t callback, void *user_data )
246 {
247 io_object_t object;
248 while( (object = IOIteratorNext(iterator)) != 0 ) {
249 io_string_t iopath = "";
250 char product[256], vendor[256];
251 IORegistryEntryGetPath( object, kIOServicePlane, iopath );
252 osx_cdrom_drive_get_name( object, vendor, sizeof(vendor), product, sizeof(product) );
253 if( callback( object, vendor, product, iopath, user_data ) ) {
254 IOObjectRelease(iterator);
255 return object;
256 }
257 IOObjectRelease(object);
258 }
259 IOObjectRelease(iterator);
260 }
263 /**
264 * Search for a CD or DVD drive (instance of IODVDServices or IOCompactDiscServices).
265 * The callback will be called repeatedly until either it returns TRUE, or all drives
266 * have been iterated over.
267 *
268 * @return an IO registry entry for the matched drive, or 0 if no drives matched.
269 *
270 * Note: Use of IOCompactDiscServices is somewhat tentative since I don't have a Mac
271 * with a CD-Rom drive.
272 */
273 io_object_t find_cdrom_drive( find_drive_callback_t callback, void *user_data )
274 {
275 mach_port_t master_port;
276 CFMutableDictionaryRef match;
277 io_iterator_t services;
278 io_object_t result;
280 if( IOMasterPort( MACH_PORT_NULL, &master_port ) != KERN_SUCCESS ) {
281 return 0; // Failed to get the master port?
282 }
284 match = IOServiceMatching("IODVDServices");
285 if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {
286 return 0;
287 }
289 result = iterator_find_cdrom( services, callback, user_data );
290 if( result != 0 ) {
291 return result;
292 }
294 match = IOServiceMatching("IOCompactDiscServices");
295 if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {
296 return 0;
297 }
298 return iterator_find_cdrom( services, callback, user_data );
299 }
302 // *********************** Notification management ************************/
304 static void osx_hid_inserted( void *ref, io_iterator_t iterator )
305 {
306 io_object_t object;
307 while( (object = IOIteratorNext(iterator)) != 0 ) {
308 io_string_t iopath = "";
309 IORegistryEntryGetPath( object, kIOServicePlane, iopath );
310 IOObjectRelease( object );
311 }
312 }
314 gboolean osx_register_iokit_notifications()
315 {
316 notify_port = IONotificationPortCreate( kIOMasterPortDefault );
317 CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );
318 CFRunLoopAddSource( CFRunLoopGetMain(), runloop_source, kCFRunLoopCommonModes );
320 // Drive notifications
321 if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
322 IOServiceMatching("IOCompactDiscServies"),
323 osx_drives_changed, NULL, &iokit_iterators[0] ) != kIOReturnSuccess ) {
324 ERROR( "IOServiceAddMatchingNotification failed" );
325 }
326 osx_drives_changed(NULL, iokit_iterators[0]);
327 if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
328 IOServiceMatching("IODVDServies"),
329 osx_drives_changed, NULL, &iokit_iterators[1] ) != kIOReturnSuccess ) {
330 ERROR( "IOServiceAddMatchingNotification failed" );
331 }
332 osx_drives_changed(NULL, iokit_iterators[1]);
334 if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
335 IOServiceMatching(kIOHIDDeviceKey),
336 osx_hid_inserted, NULL, &iokit_iterators[4] ) != kIOReturnSuccess ) {
337 ERROR( "IOServiceAddMatchingNotification failed" );
338 }
339 osx_hid_inserted(NULL, iokit_iterators[2]);
340 }
342 void osx_unregister_iokit_notifications()
343 {
344 CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );
345 CFRunLoopRemoveSource( CFRunLoopGetMain(), runloop_source, kCFRunLoopCommonModes );
346 IONotificationPortDestroy( notify_port );
347 notify_port = 0;
348 }
.