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