filename | src/drivers/osx_iokit.m |
changeset | 964:f2f3c7612d06 |
next | 1023:264e2fd90be8 |
author | nkeynes |
date | Thu Jan 15 04:15:11 2009 +0000 (15 years ago) |
permissions | -rw-r--r-- |
last change | Add support for the Intel ICC compiler (C only, icc doesn't support Obj-C) - Rename Obj-C source to .m - Separate paths.c into paths_unix.c and paths_osx.m - Add configuration detection of ICC, along with specific opt flags |
file | annotate | diff | log | raw |
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00001.2 +++ b/src/drivers/osx_iokit.m Thu Jan 15 04:15:11 2009 +00001.3 @@ -0,0 +1,356 @@1.4 +/**1.5 + * $Id$1.6 + *1.7 + * OSX support functions for handling the IOKit registry.1.8 + * Currently this manages access to CD/DVD drives + media, plus HID devices.1.9 + *1.10 + * The HID part is much simpler...1.11 + *1.12 + * Copyright (c) 2008 Nathan Keynes.1.13 + *1.14 + * This program is free software; you can redistribute it and/or modify1.15 + * it under the terms of the GNU General Public License as published by1.16 + * the Free Software Foundation; either version 2 of the License, or1.17 + * (at your option) any later version.1.18 + *1.19 + * This program is distributed in the hope that it will be useful,1.20 + * but WITHOUT ANY WARRANTY; without even the implied warranty of1.21 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1.22 + * GNU General Public License for more details.1.23 + */1.24 +1.25 +#include <glib/gmem.h>1.26 +#include <glib/gstrfuncs.h>1.27 +#include <sys/param.h>1.28 +#include <paths.h>1.29 +#include <string.h>1.30 +#include <unistd.h>1.31 +#include <stdio.h>1.32 +#include <fcntl.h>1.33 +#include <CoreFoundation/CFRunLoop.h>1.34 +#include <IOKit/IOMessage.h>1.35 +#include <IOKit/hid/IOHIDLib.h>1.36 +#include "osx_iokit.h"1.37 +1.38 +1.39 +1.40 +static IONotificationPortRef notify_port = 0;1.41 +static io_iterator_t iokit_iterators[4] = {0,0,0,0};1.42 +1.43 +struct osx_cdrom_drive {1.44 + io_string_t ioservice_path;1.45 + io_string_t vendor_name;1.46 + io_string_t product_name;1.47 + char media_path[MAXPATHLEN]; // BSD device path if media present, otherwise the empty string.1.48 + io_iterator_t media_load_iterator;1.49 + io_iterator_t media_unload_iterator;1.50 + int media_fh; // BSD device handle if open, otherwise -11.51 + media_changed_callback_t media_changed;1.52 + void *media_changed_user_data;1.53 +};1.54 +1.55 +static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen );1.56 +1.57 +/***************** IOKit Callbacks ******************/1.58 +1.59 +/**1.60 + * Called from IOKit for any IOMessages on an IOMedia. Currently the only message1.61 + * we're interested in is service termination.1.62 + */1.63 +static void osx_cdrom_media_notify( void *ref, io_service_t service, uint32_t msgType,1.64 + void *msgArgument )1.65 +{1.66 + if( msgType == kIOMessageServiceIsTerminated ) {1.67 + osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;1.68 + if( drive->media_changed != NULL ) {1.69 + drive->media_changed( drive, FALSE, drive->media_changed_user_data );1.70 + }1.71 + if( drive->media_fh != -1 ) {1.72 + close(drive->media_fh);1.73 + drive->media_fh = -1;1.74 + }1.75 + drive->media_path[0] = '\0';1.76 + IOObjectRelease( drive->media_unload_iterator );1.77 + }1.78 +}1.79 +1.80 +/**1.81 + * Called from IOKit when an IOMedia is inserted that we have be interested in.1.82 + * FIXME: Can the matcher be restricted to descendents of the drive node? currently1.83 + * we watch for all IOMedia events and compare the device path to see if it's one we1.84 + * care about.1.85 + * FIXME: We assume for now that a drive has at most one piece of media at a time.1.86 + * If this isn't the case, the system may get a little confused.1.87 + */1.88 +static void osx_cdrom_media_inserted( void *ref, io_iterator_t iterator )1.89 +{1.90 + osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;1.91 +1.92 + io_object_t object;1.93 + while( (object = IOIteratorNext(iterator)) != 0 ) {1.94 + io_string_t iopath = "";1.95 + IORegistryEntryGetPath( object, kIOServicePlane, iopath );1.96 + if( drive != NULL && g_str_has_prefix(iopath, drive->ioservice_path ) &&1.97 + get_bsdname_for_iomedia(object, drive->media_path, sizeof(drive->media_path)) ) {1.98 + // A disc was inserted within the drive of interest1.99 + if( drive->media_fh != -1 ) {1.100 + close(drive->media_fh);1.101 + drive->media_fh = -1;1.102 + }1.103 +1.104 + if( drive->media_changed != NULL ) {1.105 + drive->media_changed(drive, TRUE, drive->media_changed_user_data);1.106 + }1.107 + // Add a notification listener to get removal events.1.108 + IOServiceAddInterestNotification( notify_port, object, kIOGeneralInterest,1.109 + osx_cdrom_media_notify, drive, &drive->media_unload_iterator );1.110 +1.111 + }1.112 + IOObjectRelease( object );1.113 + }1.114 +}1.115 +1.116 +static void osx_drives_changed( void *ref, io_iterator_t iterator )1.117 +{1.118 + io_object_t object;1.119 + while( (object = IOIteratorNext(iterator)) != 0 ) {1.120 + IOObjectRelease(object);1.121 + }1.122 +1.123 +}1.124 +1.125 +/******************** Support functions *********************/1.126 +1.127 +/**1.128 + * Determine the BSD device name (ie "/dev/rdisk1") for a given IO object.1.129 + * @return TRUE if the device name was retrieved, FALSE if the request failed.1.130 + */1.131 +static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen )1.132 +{1.133 + gboolean result = FALSE;1.134 + CFTypeRef pathRef = IORegistryEntryCreateCFProperty(iomedia, CFSTR(kIOBSDNameKey),1.135 + kCFAllocatorDefault, 0 );1.136 + if( pathRef ) {1.137 + char pathlen;1.138 + strcpy( buf, _PATH_DEV "r" );1.139 + pathlen = strlen(buf);1.140 + if( CFStringGetCString( pathRef, buf + pathlen, buflen-pathlen,1.141 + kCFStringEncodingASCII ) != noErr ) {1.142 + result = TRUE;1.143 + }1.144 + CFRelease(pathRef);1.145 + }1.146 + return result;1.147 +}1.148 +1.149 +static gboolean osx_cdrom_drive_get_name( io_object_t object, char *vendor, int vendor_len,1.150 + char *product, int product_len )1.151 +{1.152 + gboolean result = FALSE;1.153 + CFMutableDictionaryRef props = 0;1.154 + if( IORegistryEntryCreateCFProperties(object, &props, kCFAllocatorDefault, kNilOptions) == KERN_SUCCESS ) {1.155 + CFDictionaryRef dict =1.156 + (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOPropertyDeviceCharacteristicsKey));1.157 + if( dict != NULL ) {1.158 + CFTypeRef value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyVendorNameKey));1.159 + if( value && CFGetTypeID(value) == CFStringGetTypeID() ) {1.160 + CFStringGetCString( (CFStringRef)value, vendor, vendor_len, kCFStringEncodingUTF8 );1.161 + } else {1.162 + vendor[0] = 0;1.163 + }1.164 +1.165 + value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyProductNameKey));1.166 + if ( value && CFGetTypeID(value) == CFStringGetTypeID() ) {1.167 + CFStringGetCString( (CFStringRef)value, product, product_len, kCFStringEncodingUTF8 );1.168 + } else {1.169 + product[0] = 0;1.170 + }1.171 + result = TRUE;1.172 + }1.173 +1.174 + CFRelease(props);1.175 + }1.176 + return result;1.177 +}1.178 +1.179 +/**1.180 + * Construct and initialize a new osx_cdrom_drive object, including registering1.181 + * it's media inserted notification.1.182 + */1.183 +static osx_cdrom_drive_t osx_cdrom_drive_new( io_object_t device )1.184 +{1.185 + osx_cdrom_drive_t drive = g_malloc0(sizeof(struct osx_cdrom_drive));1.186 +1.187 + IORegistryEntryGetPath( device, kIOServicePlane, drive->ioservice_path );1.188 + osx_cdrom_drive_get_name( device, drive->vendor_name, sizeof(drive->vendor_name),1.189 + drive->product_name, sizeof(drive->product_name) );1.190 + drive->media_path[0] = '\0';1.191 + drive->media_changed = NULL;1.192 + drive->media_changed_user_data = NULL;1.193 + drive->media_fh = -1;1.194 +1.195 + IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,1.196 + IOServiceMatching("IOMedia"),1.197 + osx_cdrom_media_inserted, drive,1.198 + &drive->media_load_iterator );1.199 + osx_cdrom_media_inserted( drive, drive->media_load_iterator );1.200 + return drive;1.201 +}1.202 +1.203 +/************************ Exported functions *************************/1.204 +1.205 +osx_cdrom_drive_t osx_cdrom_open_drive( const char *devname )1.206 +{1.207 + io_object_t object = IORegistryEntryFromPath( kIOMasterPortDefault, devname );1.208 + if( object == MACH_PORT_NULL ) {1.209 + return NULL;1.210 + }1.211 +1.212 + osx_cdrom_drive_t drive = osx_cdrom_drive_new( object );1.213 + IOObjectRelease( object );1.214 + return drive;1.215 +}1.216 +1.217 +void osx_cdrom_set_media_changed_callback( osx_cdrom_drive_t drive,1.218 + media_changed_callback_t callback,1.219 + void *user_data )1.220 +{1.221 + drive->media_changed = callback;1.222 + drive->media_changed_user_data = user_data;1.223 +}1.224 +1.225 +void osx_cdrom_close_drive( osx_cdrom_drive_t drive )1.226 +{1.227 + IOObjectRelease( drive->media_load_iterator );1.228 + IOObjectRelease( drive->media_unload_iterator );1.229 + if( drive->media_fh != -1 ) {1.230 + close(drive->media_fh);1.231 + drive->media_fh = -1;1.232 + }1.233 + g_free( drive );1.234 +}1.235 +1.236 +int osx_cdrom_get_media_handle( osx_cdrom_drive_t drive )1.237 +{1.238 + if( drive->media_fh == -1 ) {1.239 + if( drive->media_path[0] != '\0' ) {1.240 + drive->media_fh = open( drive->media_path, O_RDONLY|O_NONBLOCK );1.241 + }1.242 + }1.243 + return drive->media_fh;1.244 +}1.245 +1.246 +void osx_cdrom_release_media_handle( osx_cdrom_drive_t drive )1.247 +{1.248 + if( drive->media_fh != -1 ) {1.249 + close( drive->media_fh );1.250 + drive->media_fh = -1;1.251 + }1.252 +}1.253 +1.254 +static io_object_t iterator_find_cdrom( io_object_t iterator, find_drive_callback_t callback, void *user_data )1.255 +{1.256 + io_object_t object;1.257 + while( (object = IOIteratorNext(iterator)) != 0 ) {1.258 + io_string_t iopath = "";1.259 + char product[256], vendor[256];1.260 + IORegistryEntryGetPath( object, kIOServicePlane, iopath );1.261 + osx_cdrom_drive_get_name( object, vendor, sizeof(vendor), product, sizeof(product) );1.262 + if( callback( object, vendor, product, iopath, user_data ) ) {1.263 + IOObjectRelease(iterator);1.264 + return object;1.265 + }1.266 + IOObjectRelease(object);1.267 + }1.268 + IOObjectRelease(iterator);1.269 + return 0;1.270 +}1.271 +1.272 +1.273 +/**1.274 + * Search for a CD or DVD drive (instance of IODVDServices or IOCompactDiscServices).1.275 + * The callback will be called repeatedly until either it returns TRUE, or all drives1.276 + * have been iterated over.1.277 + *1.278 + * @return an IO registry entry for the matched drive, or 0 if no drives matched.1.279 + *1.280 + * Note: Use of IOCompactDiscServices is somewhat tentative since I don't have a Mac1.281 + * with a CD-Rom drive.1.282 + */1.283 +io_object_t find_cdrom_drive( find_drive_callback_t callback, void *user_data )1.284 +{1.285 + mach_port_t master_port;1.286 + CFMutableDictionaryRef match;1.287 + io_iterator_t services;1.288 + io_object_t result;1.289 +1.290 + if( IOMasterPort( MACH_PORT_NULL, &master_port ) != KERN_SUCCESS ) {1.291 + return 0; // Failed to get the master port?1.292 + }1.293 +1.294 + match = IOServiceMatching("IODVDServices");1.295 + if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {1.296 + return 0;1.297 + }1.298 +1.299 + result = iterator_find_cdrom( services, callback, user_data );1.300 + if( result != 0 ) {1.301 + return result;1.302 + }1.303 +1.304 + match = IOServiceMatching("IOCompactDiscServices");1.305 + if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {1.306 + return 0;1.307 + }1.308 + return iterator_find_cdrom( services, callback, user_data );1.309 +}1.310 +1.311 +1.312 +// *********************** Notification management ************************/1.313 +1.314 +static void osx_hid_inserted( void *ref, io_iterator_t iterator )1.315 +{1.316 + io_object_t object;1.317 + while( (object = IOIteratorNext(iterator)) != 0 ) {1.318 + io_string_t iopath = "";1.319 + IORegistryEntryGetPath( object, kIOServicePlane, iopath );1.320 + IOObjectRelease( object );1.321 + }1.322 +}1.323 +1.324 +gboolean osx_register_iokit_notifications()1.325 +{1.326 + notify_port = IONotificationPortCreate( kIOMasterPortDefault );1.327 + CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );1.328 + CFRunLoopAddSource( CFRunLoopGetCurrent(), runloop_source, kCFRunLoopCommonModes );1.329 +1.330 + // Drive notifications1.331 + if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,1.332 + IOServiceMatching("IOCompactDiscServies"),1.333 + osx_drives_changed, NULL, &iokit_iterators[0] ) != kIOReturnSuccess ) {1.334 + ERROR( "IOServiceAddMatchingNotification failed" );1.335 + }1.336 + osx_drives_changed(NULL, iokit_iterators[0]);1.337 + if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,1.338 + IOServiceMatching("IODVDServies"),1.339 + osx_drives_changed, NULL, &iokit_iterators[1] ) != kIOReturnSuccess ) {1.340 + ERROR( "IOServiceAddMatchingNotification failed" );1.341 + }1.342 + osx_drives_changed(NULL, iokit_iterators[1]);1.343 +1.344 + if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,1.345 + IOServiceMatching(kIOHIDDeviceKey),1.346 + osx_hid_inserted, NULL, &iokit_iterators[2] ) != kIOReturnSuccess ) {1.347 + ERROR( "IOServiceAddMatchingNotification failed" );1.348 + }1.349 + osx_hid_inserted(NULL, iokit_iterators[2]);1.350 + return TRUE;1.351 +}1.352 +1.353 +void osx_unregister_iokit_notifications()1.354 +{1.355 + CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );1.356 + CFRunLoopRemoveSource( CFRunLoopGetCurrent(), runloop_source, kCFRunLoopCommonModes );1.357 + IONotificationPortDestroy( notify_port );1.358 + notify_port = 0;1.359 +}
.