Search
lxdream.org :: lxdream/src/drivers/osx_iokit.m
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/osx_iokit.m
changeset 1023:264e2fd90be8
prev964:f2f3c7612d06
next1297:7e98a164b2d9
author nkeynes
date Sun Mar 04 21:10:12 2012 +1000 (10 years ago)
permissions -rw-r--r--
last change Move glsl loading into common gl code, and set a display capability flag
view annotate diff log raw
     1 /**
     2  * $Id$
     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 <glib/gmem.h>
    23 #include <glib/gstrfuncs.h>
    24 #include <sys/param.h>
    25 #include <paths.h>
    26 #include <string.h>
    27 #include <unistd.h>
    28 #include <stdio.h>
    29 #include <fcntl.h>
    30 #include <CoreFoundation/CFRunLoop.h>
    31 #include <IOKit/IOMessage.h>
    32 #include <IOKit/hid/IOHIDLib.h>
    33 #include "osx_iokit.h"
    37 static IONotificationPortRef notify_port = 0;
    38 static io_iterator_t iokit_iterators[4] = {0,0,0,0};
    40 struct osx_cdrom_drive {
    41     io_string_t ioservice_path;
    42     io_string_t vendor_name;
    43     io_string_t product_name;
    44     char media_path[MAXPATHLEN]; // BSD device path if media present, otherwise the empty string.
    45     io_iterator_t media_load_iterator;
    46     io_iterator_t media_unload_iterator;
    47     int media_fh; // BSD device handle if open, otherwise -1
    48     media_changed_callback_t media_changed;
    49     void *media_changed_user_data;
    50 };
    52 static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen );
    53 static gboolean get_boolean_property( io_object_t io, CFStringRef key, gboolean def );
    55 /***************** IOKit Callbacks ******************/
    57 /**
    58  * Called from IOKit for any IOMessages on an IOMedia. Currently the only message
    59  * we're interested in is service termination.
    60  */
    61 static void osx_cdrom_media_notify( void *ref, io_service_t service, uint32_t msgType,
    62                                     void *msgArgument )
    63 {
    64     if( msgType == kIOMessageServiceIsTerminated ) {
    65         osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;
    66         if( drive->media_changed != NULL ) {
    67             drive->media_changed( drive, FALSE, drive->media_changed_user_data );
    68         }
    69         if( drive->media_fh != -1 ) {
    70             close(drive->media_fh);
    71             drive->media_fh = -1;
    72         }
    73         drive->media_path[0] = '\0';
    74         IOObjectRelease( drive->media_unload_iterator );
    75     }
    76 }
    78 /**
    79  * Called from IOKit when an IOMedia is inserted that we have be interested in.
    80  * FIXME: Can the matcher be restricted to descendents of the drive node? currently
    81  * we watch for all IOMedia events and compare the device path to see if it's one we
    82  * care about.
    83  * FIXME: We assume for now that a drive has at most one piece of media at a time. 
    84  * If this isn't the case, the system may get a little confused.
    85  */
    86 static void osx_cdrom_media_inserted( void *ref, io_iterator_t iterator )
    87 {
    88     osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;
    90     io_object_t object;
    91     while( (object = IOIteratorNext(iterator)) != 0 ) {
    92         io_string_t iopath = "";
    93         IORegistryEntryGetPath( object, kIOServicePlane, iopath );
    94         if( drive != NULL && g_str_has_prefix(iopath, drive->ioservice_path ) &&
    95             get_boolean_property(object, CFSTR("Whole"), TRUE) &&
    96             get_bsdname_for_iomedia(object, drive->media_path, sizeof(drive->media_path)) ) {
    97             // A disc was inserted within the drive of interest
    98             if( drive->media_fh != -1 ) {
    99                 close(drive->media_fh);
   100                 drive->media_fh = -1;
   101             }
   103             if( drive->media_changed != NULL ) {
   104                 drive->media_changed(drive, TRUE, drive->media_changed_user_data);
   105             }
   106             // Add a notification listener to get removal events.
   107             IOServiceAddInterestNotification( notify_port, object, kIOGeneralInterest, 
   108                                               osx_cdrom_media_notify, drive, &drive->media_unload_iterator ); 
   110         }
   111         IOObjectRelease( object );
   112     }
   113 }
   115 static void osx_drives_changed( void *ref, io_iterator_t iterator )
   116 {
   117     io_object_t object;
   118     while( (object = IOIteratorNext(iterator)) != 0 ) {
   119         IOObjectRelease(object);
   120     }
   122 }
   124 /******************** Support functions *********************/
   126 /**
   127  * Determine the BSD device name (ie "/dev/rdisk1") for a given IO object.
   128  * @return TRUE if the device name was retrieved, FALSE if the request failed.
   129  */
   130 static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen )
   131 {
   132     gboolean result = FALSE;
   133     CFTypeRef pathRef = IORegistryEntryCreateCFProperty(iomedia, CFSTR(kIOBSDNameKey),
   134             kCFAllocatorDefault, 0 );
   135     if( pathRef ) {
   136         char pathlen;
   137         strcpy( buf, _PATH_DEV "r" );
   138         pathlen = strlen(buf);
   139         if( CFStringGetCString( pathRef, buf + pathlen, buflen-pathlen,
   140                 kCFStringEncodingASCII ) != noErr ) {
   141             result = TRUE;
   142         }
   143         CFRelease(pathRef);
   144     }
   145     return result;
   146 }
   148 /**
   149  * Retrieve a boolean property from the io object, and return as a gboolean. If
   150  * the key is not present, return def instead.
   151  */
   152 static gboolean get_boolean_property( io_object_t io, CFStringRef key, gboolean def )
   153 {
   154     gboolean result = def;
   155     CFTypeRef ref = IORegistryEntryCreateCFProperty(io, key, kCFAllocatorDefault, 0 );
   156     if( ref ) {
   157     	result = CFBooleanGetValue(ref);
   158         CFRelease(ref);
   159     }
   160     return result;
   161  }
   163 static gboolean osx_cdrom_drive_get_name( io_object_t object, char *vendor, int vendor_len,
   164                                           char *product, int product_len )
   165 {
   166     gboolean result = FALSE;
   167     CFMutableDictionaryRef props = 0;
   168     if( IORegistryEntryCreateCFProperties(object, &props, kCFAllocatorDefault, kNilOptions) == KERN_SUCCESS ) {
   169         CFDictionaryRef dict = 
   170             (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOPropertyDeviceCharacteristicsKey));
   171         if( dict != NULL ) {
   172             CFTypeRef value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyVendorNameKey));
   173             if( value && CFGetTypeID(value) == CFStringGetTypeID() ) {
   174                 CFStringGetCString( (CFStringRef)value, vendor, vendor_len, kCFStringEncodingUTF8 );
   175             } else {
   176                 vendor[0] = 0;
   177             }
   179             value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyProductNameKey));
   180             if ( value && CFGetTypeID(value) == CFStringGetTypeID() ) {
   181                 CFStringGetCString( (CFStringRef)value, product, product_len, kCFStringEncodingUTF8 );
   182             } else {
   183                 product[0] = 0;
   184             }
   185             result = TRUE;
   186         }
   188         CFRelease(props);
   189     }
   190     return result;
   191 }
   193 /**
   194  * Construct and initialize a new osx_cdrom_drive object, including registering
   195  * it's media inserted notification.
   196  */
   197 static osx_cdrom_drive_t osx_cdrom_drive_new( io_object_t device )  
   198 {
   199     osx_cdrom_drive_t drive = g_malloc0(sizeof(struct osx_cdrom_drive));
   201     IORegistryEntryGetPath( device, kIOServicePlane, drive->ioservice_path );
   202     osx_cdrom_drive_get_name( device, drive->vendor_name, sizeof(drive->vendor_name),
   203                               drive->product_name, sizeof(drive->product_name) );
   204     drive->media_path[0] = '\0';
   205     drive->media_changed = NULL;
   206     drive->media_changed_user_data = NULL;
   207     drive->media_fh = -1;
   209     IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification, 
   210                                       IOServiceMatching("IOMedia"),
   211                                       osx_cdrom_media_inserted, drive, 
   212                                       &drive->media_load_iterator );
   213     osx_cdrom_media_inserted( drive, drive->media_load_iterator );
   214     return drive;
   215 }
   217 /************************ Exported functions *************************/ 
   219 osx_cdrom_drive_t osx_cdrom_open_drive( const char *devname )
   220 {
   221     io_object_t object = IORegistryEntryFromPath( kIOMasterPortDefault, devname );
   222     if( object == MACH_PORT_NULL ) {
   223         return NULL;
   224     }
   226     osx_cdrom_drive_t drive = osx_cdrom_drive_new( object );
   227     IOObjectRelease( object );
   228     return drive;
   229 }
   231 void osx_cdrom_set_media_changed_callback( osx_cdrom_drive_t drive, 
   232                                            media_changed_callback_t callback,
   233                                            void *user_data )
   234 {
   235     drive->media_changed = callback;
   236     drive->media_changed_user_data = user_data;
   237 }
   239 void osx_cdrom_close_drive( osx_cdrom_drive_t drive )
   240 {
   241     IOObjectRelease( drive->media_load_iterator );
   242     IOObjectRelease( drive->media_unload_iterator );
   243     if( drive->media_fh != -1 ) {
   244         close(drive->media_fh);
   245         drive->media_fh = -1;
   246     }
   247     g_free( drive );
   248 }
   250 int osx_cdrom_get_media_handle( osx_cdrom_drive_t drive )
   251 {
   252     if( drive->media_fh == -1 ) {
   253         if( drive->media_path[0] != '\0' ) {
   254             drive->media_fh = open( drive->media_path, O_RDONLY|O_NONBLOCK );
   255         }
   256     }
   257     return drive->media_fh;
   258 }
   260 void osx_cdrom_release_media_handle( osx_cdrom_drive_t drive )
   261 {
   262     if( drive->media_fh != -1 ) {
   263         close( drive->media_fh );
   264         drive->media_fh = -1; 
   265     }
   266 }
   268 static io_object_t iterator_find_cdrom( io_object_t iterator, find_drive_callback_t callback, void *user_data )
   269 {
   270     io_object_t object;
   271     while( (object = IOIteratorNext(iterator)) != 0 ) {
   272         io_string_t iopath = "";
   273         char product[256], vendor[256];
   274         IORegistryEntryGetPath( object, kIOServicePlane, iopath );
   275         osx_cdrom_drive_get_name( object, vendor, sizeof(vendor), product, sizeof(product) );
   276         if( callback( object, vendor, product, iopath, user_data ) ) {
   277             IOObjectRelease(iterator);
   278             return object;
   279         }
   280         IOObjectRelease(object);
   281     }
   282     IOObjectRelease(iterator);
   283     return 0;
   284 }
   287 /**
   288  * Search for a CD or DVD drive (instance of IODVDServices or IOCompactDiscServices).
   289  * The callback will be called repeatedly until either it returns TRUE, or all drives
   290  * have been iterated over.
   291  * 
   292  * @return an IO registry entry for the matched drive, or 0 if no drives matched.
   293  * 
   294  * Note: Use of IOCompactDiscServices is somewhat tentative since I don't have a Mac
   295  * with a CD-Rom drive.
   296  */ 
   297 io_object_t find_cdrom_drive( find_drive_callback_t callback, void *user_data )
   298 {
   299     mach_port_t master_port;
   300     CFMutableDictionaryRef match;
   301     io_iterator_t services;
   302     io_object_t result;
   304     if( IOMasterPort( MACH_PORT_NULL, &master_port ) != KERN_SUCCESS ) {
   305         return 0; // Failed to get the master port?
   306     }
   308     match = IOServiceMatching("IODVDServices");
   309     if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {
   310         return 0;
   311     }
   313     result = iterator_find_cdrom( services, callback, user_data );
   314     if( result != 0 ) {
   315         return result;
   316     }
   318     match = IOServiceMatching("IOCompactDiscServices");
   319     if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {
   320         return 0;
   321     }
   322     return iterator_find_cdrom( services, callback, user_data );
   323 }
   326 // *********************** Notification management ************************/
   328 static void osx_hid_inserted( void *ref, io_iterator_t iterator )
   329 {
   330     io_object_t object;
   331     while( (object = IOIteratorNext(iterator)) != 0 ) {
   332         io_string_t iopath = "";
   333         IORegistryEntryGetPath( object, kIOServicePlane, iopath );
   334         IOObjectRelease( object );
   335     }
   336 }
   338 gboolean osx_register_iokit_notifications()
   339 {
   340     notify_port = IONotificationPortCreate( kIOMasterPortDefault );
   341     CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );
   342     CFRunLoopAddSource( CFRunLoopGetCurrent(), runloop_source, kCFRunLoopCommonModes );
   344     // Drive notifications
   345     if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
   346             IOServiceMatching("IOCompactDiscServies"),
   347             osx_drives_changed, NULL, &iokit_iterators[0] ) != kIOReturnSuccess ) {
   348         ERROR( "IOServiceAddMatchingNotification failed" );
   349     }
   350     osx_drives_changed(NULL, iokit_iterators[0]);
   351     if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
   352             IOServiceMatching("IODVDServies"),
   353             osx_drives_changed, NULL, &iokit_iterators[1] ) != kIOReturnSuccess ) {
   354         ERROR( "IOServiceAddMatchingNotification failed" );
   355     }
   356     osx_drives_changed(NULL, iokit_iterators[1]);
   358     if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification, 
   359             IOServiceMatching(kIOHIDDeviceKey),
   360             osx_hid_inserted, NULL, &iokit_iterators[2] ) != kIOReturnSuccess ) {
   361         ERROR( "IOServiceAddMatchingNotification failed" );
   362     }
   363     osx_hid_inserted(NULL, iokit_iterators[2]);
   364     return TRUE;
   365 }
   367 void osx_unregister_iokit_notifications()
   368 {
   369     CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );
   370     CFRunLoopRemoveSource( CFRunLoopGetCurrent(), runloop_source, kCFRunLoopCommonModes );
   371     IONotificationPortDestroy( notify_port );
   372     notify_port = 0;
   373 }
.