Search
lxdream.org :: lxdream/src/drivers/osx_iokit.m :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/osx_iokit.m
changeset 964:f2f3c7612d06
next1023: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 +0000
1.2 +++ b/src/drivers/osx_iokit.m Thu Jan 15 04:15:11 2009 +0000
1.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 modify
1.15 + * it under the terms of the GNU General Public License as published by
1.16 + * the Free Software Foundation; either version 2 of the License, or
1.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 of
1.21 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.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 -1
1.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 message
1.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? currently
1.83 + * we watch for all IOMedia events and compare the device path to see if it's one we
1.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 interest
1.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 registering
1.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 drives
1.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 Mac
1.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 notifications
1.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 +}
.