Search
lxdream.org :: lxdream/src/drivers/osx_iokit.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/osx_iokit.c
changeset 720:b5594d1ac80a
next729:4cc913eabd3d
author nkeynes
date Sat Jul 05 11:57:36 2008 +0000 (12 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 +0000
1.2 +++ b/src/drivers/osx_iokit.c Sat Jul 05 11:57:36 2008 +0000
1.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 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 <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 -1
1.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 message
1.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? currently
1.77 + * we watch for all IOMedia events and compare the device path to see if it's one we
1.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 interest
1.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 registering
1.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 drives
1.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 Mac
1.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 notifications
1.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 +}
.