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. |
file | annotate | diff | log | raw |
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00001.2 +++ b/src/drivers/osx_iokit.c Sat Jul 05 11:57:36 2008 +00001.3 @@ -0,0 +1,348 @@1.4 +/**1.5 + * $Id: cd_osx.c 727 2008-06-25 10:40:45Z nkeynes $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 <sys/param.h>1.26 +#include <paths.h>1.27 +#include <string.h>1.28 +#include <stdio.h>1.29 +#include <fcntl.h>1.30 +#include <IOKit/IOMessage.h>1.31 +#include <IOKit/hid/IOHIDLib.h>1.32 +#include "osx_iokit.h"1.33 +1.34 +static IONotificationPortRef notify_port = 0;1.35 +static io_iterator_t iokit_iterators[4] = {0,0,0,0};1.36 +1.37 +struct osx_cdrom_drive {1.38 + io_string_t ioservice_path;1.39 + io_string_t vendor_name;1.40 + io_string_t product_name;1.41 + char media_path[MAXPATHLEN]; // BSD device path if media present, otherwise the empty string.1.42 + io_iterator_t media_load_iterator;1.43 + io_iterator_t media_unload_iterator;1.44 + int media_fh; // BSD device handle if open, otherwise -11.45 + media_changed_callback_t media_changed;1.46 + void *media_changed_user_data;1.47 +};1.48 +1.49 +static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen );1.50 +1.51 +/***************** IOKit Callbacks ******************/1.52 +1.53 +/**1.54 + * Called from IOKit for any IOMessages on an IOMedia. Currently the only message1.55 + * we're interested in is service termination.1.56 + */1.57 +static void osx_cdrom_media_notify( void *ref, io_service_t service, uint32_t msgType,1.58 + void *msgArgument )1.59 +{1.60 + if( msgType == kIOMessageServiceIsTerminated ) {1.61 + osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;1.62 + if( drive->media_changed != NULL ) {1.63 + drive->media_changed( drive, FALSE, drive->media_changed_user_data );1.64 + }1.65 + if( drive->media_fh != -1 ) {1.66 + close(drive->media_fh);1.67 + drive->media_fh = -1;1.68 + }1.69 + drive->media_path[0] = '\0';1.70 + IOObjectRelease( drive->media_unload_iterator );1.71 + }1.72 +}1.73 +1.74 +/**1.75 + * Called from IOKit when an IOMedia is inserted that we have be interested in.1.76 + * FIXME: Can the matcher be restricted to descendents of the drive node? currently1.77 + * we watch for all IOMedia events and compare the device path to see if it's one we1.78 + * care about.1.79 + * FIXME: We assume for now that a drive has at most one piece of media at a time.1.80 + * If this isn't the case, the system may get a little confused.1.81 + */1.82 +static void osx_cdrom_media_inserted( void *ref, io_iterator_t iterator )1.83 +{1.84 + osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;1.85 +1.86 + io_object_t object;1.87 + while( (object = IOIteratorNext(iterator)) != 0 ) {1.88 + io_string_t iopath = "";1.89 + IORegistryEntryGetPath( object, kIOServicePlane, iopath );1.90 + if( drive != NULL && g_str_has_prefix(iopath, drive->ioservice_path ) &&1.91 + get_bsdname_for_iomedia(object, drive->media_path, sizeof(drive->media_path)) ) {1.92 + // A disc was inserted within the drive of interest1.93 + if( drive->media_fh != -1 ) {1.94 + close(drive->media_fh);1.95 + drive->media_fh = -1;1.96 + }1.97 +1.98 + if( drive->media_changed != NULL ) {1.99 + drive->media_changed(drive, TRUE, drive->media_changed_user_data);1.100 + }1.101 + // Add a notification listener to get removal events.1.102 + IOServiceAddInterestNotification( notify_port, object, kIOGeneralInterest,1.103 + osx_cdrom_media_notify, drive, &drive->media_unload_iterator );1.104 +1.105 + }1.106 + IOObjectRelease( object );1.107 + }1.108 +}1.109 +1.110 +static void osx_drives_changed( void *ref, io_iterator_t iterator )1.111 +{1.112 + io_object_t object;1.113 + while( (object = IOIteratorNext(iterator)) != 0 ) {1.114 + IOObjectRelease(object);1.115 + }1.116 +1.117 +}1.118 +1.119 +/******************** Support functions *********************/1.120 +1.121 +/**1.122 + * Determine the BSD device name (ie "/dev/rdisk1") for a given IO object.1.123 + * @return TRUE if the device name was retrieved, FALSE if the request failed.1.124 + */1.125 +static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen )1.126 +{1.127 + gboolean result = FALSE;1.128 + CFTypeRef pathRef = IORegistryEntryCreateCFProperty(iomedia, CFSTR(kIOBSDNameKey),1.129 + kCFAllocatorDefault, 0 );1.130 + if( pathRef ) {1.131 + char pathlen;1.132 + strcpy( buf, _PATH_DEV "r" );1.133 + pathlen = strlen(buf);1.134 + if( CFStringGetCString( pathRef, buf + pathlen, buflen-pathlen,1.135 + kCFStringEncodingASCII ) != noErr ) {1.136 + result = TRUE;1.137 + }1.138 + CFRelease(pathRef);1.139 + }1.140 + return result;1.141 +}1.142 +1.143 +static gboolean osx_cdrom_drive_get_name( io_object_t object, char *vendor, int vendor_len,1.144 + char *product, int product_len )1.145 +{1.146 + gboolean result = FALSE;1.147 + CFMutableDictionaryRef props = 0;1.148 + if( IORegistryEntryCreateCFProperties(object, &props, kCFAllocatorDefault, kNilOptions) == KERN_SUCCESS ) {1.149 + CFDictionaryRef dict =1.150 + (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOPropertyDeviceCharacteristicsKey));1.151 + if( dict != NULL ) {1.152 + CFTypeRef value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyVendorNameKey));1.153 + if( value && CFGetTypeID(value) == CFStringGetTypeID() ) {1.154 + CFStringGetCString( (CFStringRef)value, vendor, vendor_len, kCFStringEncodingUTF8 );1.155 + } else {1.156 + vendor[0] = 0;1.157 + }1.158 +1.159 + value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyProductNameKey));1.160 + if ( value && CFGetTypeID(value) == CFStringGetTypeID() ) {1.161 + CFStringGetCString( (CFStringRef)value, product, product_len, kCFStringEncodingUTF8 );1.162 + } else {1.163 + product[0] = 0;1.164 + }1.165 + result = TRUE;1.166 + }1.167 +1.168 + CFRelease(props);1.169 + }1.170 + return result;1.171 +}1.172 +1.173 +/**1.174 + * Construct and initialize a new osx_cdrom_drive object, including registering1.175 + * it's media inserted notification.1.176 + */1.177 +static osx_cdrom_drive_t osx_cdrom_drive_new( io_object_t device )1.178 +{1.179 + osx_cdrom_drive_t drive = g_malloc0(sizeof(struct osx_cdrom_drive));1.180 +1.181 + IORegistryEntryGetPath( device, kIOServicePlane, drive->ioservice_path );1.182 + osx_cdrom_drive_get_name( device, drive->vendor_name, sizeof(drive->vendor_name),1.183 + drive->product_name, sizeof(drive->product_name) );1.184 + drive->media_path[0] = '\0';1.185 + drive->media_changed = NULL;1.186 + drive->media_changed_user_data = NULL;1.187 + drive->media_fh = -1;1.188 +1.189 + IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,1.190 + IOServiceMatching("IOMedia"),1.191 + osx_cdrom_media_inserted, drive,1.192 + &drive->media_load_iterator );1.193 + osx_cdrom_media_inserted( drive, drive->media_load_iterator );1.194 + return drive;1.195 +}1.196 +1.197 +/************************ Exported functions *************************/1.198 +1.199 +osx_cdrom_drive_t osx_cdrom_open_drive( const char *devname )1.200 +{1.201 + io_object_t object = IORegistryEntryFromPath( kIOMasterPortDefault, devname );1.202 + if( object == MACH_PORT_NULL ) {1.203 + return NULL;1.204 + }1.205 +1.206 + osx_cdrom_drive_t drive = osx_cdrom_drive_new( object );1.207 + IOObjectRelease( object );1.208 + return drive;1.209 +}1.210 +1.211 +void osx_cdrom_set_media_changed_callback( osx_cdrom_drive_t drive,1.212 + media_changed_callback_t callback,1.213 + void *user_data )1.214 +{1.215 + drive->media_changed = callback;1.216 + drive->media_changed_user_data = user_data;1.217 +}1.218 +1.219 +void osx_cdrom_close_drive( osx_cdrom_drive_t drive )1.220 +{1.221 + IOObjectRelease( drive->media_load_iterator );1.222 + IOObjectRelease( drive->media_unload_iterator );1.223 + if( drive->media_fh != -1 ) {1.224 + close(drive->media_fh);1.225 + drive->media_fh = -1;1.226 + }1.227 + g_free( drive );1.228 +}1.229 +1.230 +int osx_cdrom_get_media_handle( osx_cdrom_drive_t drive )1.231 +{1.232 + if( drive->media_fh == -1 ) {1.233 + if( drive->media_path[0] != '\0' ) {1.234 + drive->media_fh = open( drive->media_path, O_RDONLY|O_NONBLOCK );1.235 + }1.236 + }1.237 + return drive->media_fh;1.238 +}1.239 +1.240 +void osx_cdrom_release_media_handle( osx_cdrom_drive_t drive )1.241 +{1.242 + if( drive->media_fh != -1 ) {1.243 + close( drive->media_fh );1.244 + drive->media_fh = -1;1.245 + }1.246 +}1.247 +1.248 +static io_object_t iterator_find_cdrom( io_object_t iterator, find_drive_callback_t callback, void *user_data )1.249 +{1.250 + io_object_t object;1.251 + while( (object = IOIteratorNext(iterator)) != 0 ) {1.252 + io_string_t iopath = "";1.253 + char product[256], vendor[256];1.254 + IORegistryEntryGetPath( object, kIOServicePlane, iopath );1.255 + osx_cdrom_drive_get_name( object, vendor, sizeof(vendor), product, sizeof(product) );1.256 + if( callback( object, vendor, product, iopath, user_data ) ) {1.257 + IOObjectRelease(iterator);1.258 + return object;1.259 + }1.260 + IOObjectRelease(object);1.261 + }1.262 + IOObjectRelease(iterator);1.263 +}1.264 +1.265 +1.266 +/**1.267 + * Search for a CD or DVD drive (instance of IODVDServices or IOCompactDiscServices).1.268 + * The callback will be called repeatedly until either it returns TRUE, or all drives1.269 + * have been iterated over.1.270 + *1.271 + * @return an IO registry entry for the matched drive, or 0 if no drives matched.1.272 + *1.273 + * Note: Use of IOCompactDiscServices is somewhat tentative since I don't have a Mac1.274 + * with a CD-Rom drive.1.275 + */1.276 +io_object_t find_cdrom_drive( find_drive_callback_t callback, void *user_data )1.277 +{1.278 + mach_port_t master_port;1.279 + CFMutableDictionaryRef match;1.280 + io_iterator_t services;1.281 + io_object_t result;1.282 +1.283 + if( IOMasterPort( MACH_PORT_NULL, &master_port ) != KERN_SUCCESS ) {1.284 + return 0; // Failed to get the master port?1.285 + }1.286 +1.287 + match = IOServiceMatching("IODVDServices");1.288 + if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {1.289 + return 0;1.290 + }1.291 +1.292 + result = iterator_find_cdrom( services, callback, user_data );1.293 + if( result != 0 ) {1.294 + return result;1.295 + }1.296 +1.297 + match = IOServiceMatching("IOCompactDiscServices");1.298 + if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {1.299 + return 0;1.300 + }1.301 + return iterator_find_cdrom( services, callback, user_data );1.302 +}1.303 +1.304 +1.305 +// *********************** Notification management ************************/1.306 +1.307 +static void osx_hid_inserted( void *ref, io_iterator_t iterator )1.308 +{1.309 + io_object_t object;1.310 + while( (object = IOIteratorNext(iterator)) != 0 ) {1.311 + io_string_t iopath = "";1.312 + IORegistryEntryGetPath( object, kIOServicePlane, iopath );1.313 + IOObjectRelease( object );1.314 + }1.315 +}1.316 +1.317 +gboolean osx_register_iokit_notifications()1.318 +{1.319 + notify_port = IONotificationPortCreate( kIOMasterPortDefault );1.320 + CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );1.321 + CFRunLoopAddSource( CFRunLoopGetMain(), runloop_source, kCFRunLoopCommonModes );1.322 +1.323 + // Drive notifications1.324 + if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,1.325 + IOServiceMatching("IOCompactDiscServies"),1.326 + osx_drives_changed, NULL, &iokit_iterators[0] ) != kIOReturnSuccess ) {1.327 + ERROR( "IOServiceAddMatchingNotification failed" );1.328 + }1.329 + osx_drives_changed(NULL, iokit_iterators[0]);1.330 + if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,1.331 + IOServiceMatching("IODVDServies"),1.332 + osx_drives_changed, NULL, &iokit_iterators[1] ) != kIOReturnSuccess ) {1.333 + ERROR( "IOServiceAddMatchingNotification failed" );1.334 + }1.335 + osx_drives_changed(NULL, iokit_iterators[1]);1.336 +1.337 + if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,1.338 + IOServiceMatching(kIOHIDDeviceKey),1.339 + osx_hid_inserted, NULL, &iokit_iterators[4] ) != kIOReturnSuccess ) {1.340 + ERROR( "IOServiceAddMatchingNotification failed" );1.341 + }1.342 + osx_hid_inserted(NULL, iokit_iterators[2]);1.343 +}1.344 +1.345 +void osx_unregister_iokit_notifications()1.346 +{1.347 + CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );1.348 + CFRunLoopRemoveSource( CFRunLoopGetMain(), runloop_source, kCFRunLoopCommonModes );1.349 + IONotificationPortDestroy( notify_port );1.350 + notify_port = 0;1.351 +}
.