Search
lxdream.org :: lxdream/src/drivers/osx_iokit.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/osx_iokit.c
changeset 758:99ae000d4e09
prev736:a02d1475ccfd
next759:f16975739abc
author nkeynes
date Mon Jul 21 00:37:13 2008 +0000 (13 years ago)
permissions -rw-r--r--
last change Fix compiler warnings
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 <sys/param.h>
    24 #include <paths.h>
    25 #include <string.h>
    26 #include <stdio.h>
    27 #include <fcntl.h>
    28 #include <CoreFoundation/CFRunLoop.h>
    29 #include <IOKit/IOMessage.h>
    30 #include <IOKit/hid/IOHIDLib.h>
    31 #include "osx_iokit.h"
    35 static IONotificationPortRef notify_port = 0;
    36 static io_iterator_t iokit_iterators[4] = {0,0,0,0};
    38 struct osx_cdrom_drive {
    39     io_string_t ioservice_path;
    40     io_string_t vendor_name;
    41     io_string_t product_name;
    42     char media_path[MAXPATHLEN]; // BSD device path if media present, otherwise the empty string.
    43     io_iterator_t media_load_iterator;
    44     io_iterator_t media_unload_iterator;
    45     int media_fh; // BSD device handle if open, otherwise -1
    46     media_changed_callback_t media_changed;
    47     void *media_changed_user_data;
    48 };
    50 static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen );
    52 /***************** IOKit Callbacks ******************/
    54 /**
    55  * Called from IOKit for any IOMessages on an IOMedia. Currently the only message
    56  * we're interested in is service termination.
    57  */
    58 static void osx_cdrom_media_notify( void *ref, io_service_t service, uint32_t msgType,
    59                                     void *msgArgument )
    60 {
    61     if( msgType == kIOMessageServiceIsTerminated ) {
    62         osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;
    63         if( drive->media_changed != NULL ) {
    64             drive->media_changed( drive, FALSE, drive->media_changed_user_data );
    65         }
    66         if( drive->media_fh != -1 ) {
    67             close(drive->media_fh);
    68             drive->media_fh = -1;
    69         }
    70         drive->media_path[0] = '\0';
    71         IOObjectRelease( drive->media_unload_iterator );
    72     }
    73 }
    75 /**
    76  * Called from IOKit when an IOMedia is inserted that we have be interested in.
    77  * FIXME: Can the matcher be restricted to descendents of the drive node? currently
    78  * we watch for all IOMedia events and compare the device path to see if it's one we
    79  * care about.
    80  * FIXME: We assume for now that a drive has at most one piece of media at a time. 
    81  * If this isn't the case, the system may get a little confused.
    82  */
    83 static void osx_cdrom_media_inserted( void *ref, io_iterator_t iterator )
    84 {
    85     osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;
    87     io_object_t object;
    88     while( (object = IOIteratorNext(iterator)) != 0 ) {
    89         io_string_t iopath = "";
    90         IORegistryEntryGetPath( object, kIOServicePlane, iopath );
    91         if( drive != NULL && g_str_has_prefix(iopath, drive->ioservice_path ) &&
    92                 get_bsdname_for_iomedia(object, drive->media_path, sizeof(drive->media_path)) ) {
    93             // A disc was inserted within the drive of interest
    94             if( drive->media_fh != -1 ) {
    95                 close(drive->media_fh);
    96                 drive->media_fh = -1;
    97             }
    99             if( drive->media_changed != NULL ) {
   100                 drive->media_changed(drive, TRUE, drive->media_changed_user_data);
   101             }
   102             // Add a notification listener to get removal events.
   103             IOServiceAddInterestNotification( notify_port, object, kIOGeneralInterest, 
   104                                               osx_cdrom_media_notify, drive, &drive->media_unload_iterator ); 
   106         }
   107         IOObjectRelease( object );
   108     }
   109 }
   111 static void osx_drives_changed( void *ref, io_iterator_t iterator )
   112 {
   113     io_object_t object;
   114     while( (object = IOIteratorNext(iterator)) != 0 ) {
   115         IOObjectRelease(object);
   116     }
   118 }
   120 /******************** Support functions *********************/
   122 /**
   123  * Determine the BSD device name (ie "/dev/rdisk1") for a given IO object.
   124  * @return TRUE if the device name was retrieved, FALSE if the request failed.
   125  */
   126 static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen )
   127 {
   128     gboolean result = FALSE;
   129     CFTypeRef pathRef = IORegistryEntryCreateCFProperty(iomedia, CFSTR(kIOBSDNameKey),
   130             kCFAllocatorDefault, 0 );
   131     if( pathRef ) {
   132         char pathlen;
   133         strcpy( buf, _PATH_DEV "r" );
   134         pathlen = strlen(buf);
   135         if( CFStringGetCString( pathRef, buf + pathlen, buflen-pathlen,
   136                 kCFStringEncodingASCII ) != noErr ) {
   137             result = TRUE;
   138         }
   139         CFRelease(pathRef);
   140     }
   141     return result;
   142 }
   144 static gboolean osx_cdrom_drive_get_name( io_object_t object, char *vendor, int vendor_len,
   145                                           char *product, int product_len )
   146 {
   147     gboolean result = FALSE;
   148     CFMutableDictionaryRef props = 0;
   149     if( IORegistryEntryCreateCFProperties(object, &props, kCFAllocatorDefault, kNilOptions) == KERN_SUCCESS ) {
   150         CFDictionaryRef dict = 
   151             (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOPropertyDeviceCharacteristicsKey));
   152         if( dict != NULL ) {
   153             CFTypeRef value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyVendorNameKey));
   154             if( value && CFGetTypeID(value) == CFStringGetTypeID() ) {
   155                 CFStringGetCString( (CFStringRef)value, vendor, vendor_len, kCFStringEncodingUTF8 );
   156             } else {
   157                 vendor[0] = 0;
   158             }
   160             value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyProductNameKey));
   161             if ( value && CFGetTypeID(value) == CFStringGetTypeID() ) {
   162                 CFStringGetCString( (CFStringRef)value, product, product_len, kCFStringEncodingUTF8 );
   163             } else {
   164                 product[0] = 0;
   165             }
   166             result = TRUE;
   167         }
   169         CFRelease(props);
   170     }
   171     return result;
   172 }
   174 /**
   175  * Construct and initialize a new osx_cdrom_drive object, including registering
   176  * it's media inserted notification.
   177  */
   178 static osx_cdrom_drive_t osx_cdrom_drive_new( io_object_t device )  
   179 {
   180     osx_cdrom_drive_t drive = g_malloc0(sizeof(struct osx_cdrom_drive));
   182     IORegistryEntryGetPath( device, kIOServicePlane, drive->ioservice_path );
   183     osx_cdrom_drive_get_name( device, drive->vendor_name, sizeof(drive->vendor_name),
   184                               drive->product_name, sizeof(drive->product_name) );
   185     drive->media_path[0] = '\0';
   186     drive->media_changed = NULL;
   187     drive->media_changed_user_data = NULL;
   188     drive->media_fh = -1;
   190     IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification, 
   191                                       IOServiceMatching("IOMedia"),
   192                                       osx_cdrom_media_inserted, drive, 
   193                                       &drive->media_load_iterator );
   194     osx_cdrom_media_inserted( drive, drive->media_load_iterator );
   195     return drive;
   196 }
   198 /************************ Exported functions *************************/ 
   200 osx_cdrom_drive_t osx_cdrom_open_drive( const char *devname )
   201 {
   202     io_object_t object = IORegistryEntryFromPath( kIOMasterPortDefault, devname );
   203     if( object == MACH_PORT_NULL ) {
   204         return NULL;
   205     }
   207     osx_cdrom_drive_t drive = osx_cdrom_drive_new( object );
   208     IOObjectRelease( object );
   209     return drive;
   210 }
   212 void osx_cdrom_set_media_changed_callback( osx_cdrom_drive_t drive, 
   213                                            media_changed_callback_t callback,
   214                                            void *user_data )
   215 {
   216     drive->media_changed = callback;
   217     drive->media_changed_user_data = user_data;
   218 }
   220 void osx_cdrom_close_drive( osx_cdrom_drive_t drive )
   221 {
   222     IOObjectRelease( drive->media_load_iterator );
   223     IOObjectRelease( drive->media_unload_iterator );
   224     if( drive->media_fh != -1 ) {
   225         close(drive->media_fh);
   226         drive->media_fh = -1;
   227     }
   228     g_free( drive );
   229 }
   231 int osx_cdrom_get_media_handle( osx_cdrom_drive_t drive )
   232 {
   233     if( drive->media_fh == -1 ) {
   234         if( drive->media_path[0] != '\0' ) {
   235             drive->media_fh = open( drive->media_path, O_RDONLY|O_NONBLOCK );
   236         }
   237     }
   238     return drive->media_fh;
   239 }
   241 void osx_cdrom_release_media_handle( osx_cdrom_drive_t drive )
   242 {
   243     if( drive->media_fh != -1 ) {
   244         close( drive->media_fh );
   245         drive->media_fh = -1; 
   246     }
   247 }
   249 static io_object_t iterator_find_cdrom( io_object_t iterator, find_drive_callback_t callback, void *user_data )
   250 {
   251     io_object_t object;
   252     while( (object = IOIteratorNext(iterator)) != 0 ) {
   253         io_string_t iopath = "";
   254         char product[256], vendor[256];
   255         IORegistryEntryGetPath( object, kIOServicePlane, iopath );
   256         osx_cdrom_drive_get_name( object, vendor, sizeof(vendor), product, sizeof(product) );
   257         if( callback( object, vendor, product, iopath, user_data ) ) {
   258             IOObjectRelease(iterator);
   259             return object;
   260         }
   261         IOObjectRelease(object);
   262     }
   263     IOObjectRelease(iterator);
   264 }
   267 /**
   268  * Search for a CD or DVD drive (instance of IODVDServices or IOCompactDiscServices).
   269  * The callback will be called repeatedly until either it returns TRUE, or all drives
   270  * have been iterated over.
   271  * 
   272  * @return an IO registry entry for the matched drive, or 0 if no drives matched.
   273  * 
   274  * Note: Use of IOCompactDiscServices is somewhat tentative since I don't have a Mac
   275  * with a CD-Rom drive.
   276  */ 
   277 io_object_t find_cdrom_drive( find_drive_callback_t callback, void *user_data )
   278 {
   279     mach_port_t master_port;
   280     CFMutableDictionaryRef match;
   281     io_iterator_t services;
   282     io_object_t result;
   284     if( IOMasterPort( MACH_PORT_NULL, &master_port ) != KERN_SUCCESS ) {
   285         return 0; // Failed to get the master port?
   286     }
   288     match = IOServiceMatching("IODVDServices");
   289     if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {
   290         return 0;
   291     }
   293     result = iterator_find_cdrom( services, callback, user_data );
   294     if( result != 0 ) {
   295         return result;
   296     }
   298     match = IOServiceMatching("IOCompactDiscServices");
   299     if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {
   300         return 0;
   301     }
   302     return iterator_find_cdrom( services, callback, user_data );
   303 }
   306 // *********************** Notification management ************************/
   308 static void osx_hid_inserted( void *ref, io_iterator_t iterator )
   309 {
   310     io_object_t object;
   311     while( (object = IOIteratorNext(iterator)) != 0 ) {
   312         io_string_t iopath = "";
   313         IORegistryEntryGetPath( object, kIOServicePlane, iopath );
   314         IOObjectRelease( object );
   315     }
   316 }
   318 gboolean osx_register_iokit_notifications()
   319 {
   320     notify_port = IONotificationPortCreate( kIOMasterPortDefault );
   321     CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );
   322     CFRunLoopAddSource( CFRunLoopGetCurrent(), runloop_source, kCFRunLoopCommonModes );
   324     // Drive notifications
   325     if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
   326             IOServiceMatching("IOCompactDiscServies"),
   327             osx_drives_changed, NULL, &iokit_iterators[0] ) != kIOReturnSuccess ) {
   328         ERROR( "IOServiceAddMatchingNotification failed" );
   329     }
   330     osx_drives_changed(NULL, iokit_iterators[0]);
   331     if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
   332             IOServiceMatching("IODVDServies"),
   333             osx_drives_changed, NULL, &iokit_iterators[1] ) != kIOReturnSuccess ) {
   334         ERROR( "IOServiceAddMatchingNotification failed" );
   335     }
   336     osx_drives_changed(NULL, iokit_iterators[1]);
   338     if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification, 
   339             IOServiceMatching(kIOHIDDeviceKey),
   340             osx_hid_inserted, NULL, &iokit_iterators[4] ) != kIOReturnSuccess ) {
   341         ERROR( "IOServiceAddMatchingNotification failed" );
   342     }
   343     osx_hid_inserted(NULL, iokit_iterators[2]);
   344 }
   346 void osx_unregister_iokit_notifications()
   347 {
   348     CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );
   349     CFRunLoopRemoveSource( CFRunLoopGetCurrent(), runloop_source, kCFRunLoopCommonModes );
   350     IONotificationPortDestroy( notify_port );
   351     notify_port = 0;
   352 }
.