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 799:8aeb9906d7b9
prev759:f16975739abc
author nkeynes
date Mon Sep 08 07:56:33 2008 +0000 (15 years ago)
permissions -rw-r--r--
last change Add lightgun support
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 );
    54 /***************** IOKit Callbacks ******************/
    56 /**
    57  * Called from IOKit for any IOMessages on an IOMedia. Currently the only message
    58  * we're interested in is service termination.
    59  */
    60 static void osx_cdrom_media_notify( void *ref, io_service_t service, uint32_t msgType,
    61                                     void *msgArgument )
    62 {
    63     if( msgType == kIOMessageServiceIsTerminated ) {
    64         osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;
    65         if( drive->media_changed != NULL ) {
    66             drive->media_changed( drive, FALSE, drive->media_changed_user_data );
    67         }
    68         if( drive->media_fh != -1 ) {
    69             close(drive->media_fh);
    70             drive->media_fh = -1;
    71         }
    72         drive->media_path[0] = '\0';
    73         IOObjectRelease( drive->media_unload_iterator );
    74     }
    75 }
    77 /**
    78  * Called from IOKit when an IOMedia is inserted that we have be interested in.
    79  * FIXME: Can the matcher be restricted to descendents of the drive node? currently
    80  * we watch for all IOMedia events and compare the device path to see if it's one we
    81  * care about.
    82  * FIXME: We assume for now that a drive has at most one piece of media at a time. 
    83  * If this isn't the case, the system may get a little confused.
    84  */
    85 static void osx_cdrom_media_inserted( void *ref, io_iterator_t iterator )
    86 {
    87     osx_cdrom_drive_t drive = (osx_cdrom_drive_t)ref;
    89     io_object_t object;
    90     while( (object = IOIteratorNext(iterator)) != 0 ) {
    91         io_string_t iopath = "";
    92         IORegistryEntryGetPath( object, kIOServicePlane, iopath );
    93         if( drive != NULL && g_str_has_prefix(iopath, drive->ioservice_path ) &&
    94                 get_bsdname_for_iomedia(object, drive->media_path, sizeof(drive->media_path)) ) {
    95             // A disc was inserted within the drive of interest
    96             if( drive->media_fh != -1 ) {
    97                 close(drive->media_fh);
    98                 drive->media_fh = -1;
    99             }
   101             if( drive->media_changed != NULL ) {
   102                 drive->media_changed(drive, TRUE, drive->media_changed_user_data);
   103             }
   104             // Add a notification listener to get removal events.
   105             IOServiceAddInterestNotification( notify_port, object, kIOGeneralInterest, 
   106                                               osx_cdrom_media_notify, drive, &drive->media_unload_iterator ); 
   108         }
   109         IOObjectRelease( object );
   110     }
   111 }
   113 static void osx_drives_changed( void *ref, io_iterator_t iterator )
   114 {
   115     io_object_t object;
   116     while( (object = IOIteratorNext(iterator)) != 0 ) {
   117         IOObjectRelease(object);
   118     }
   120 }
   122 /******************** Support functions *********************/
   124 /**
   125  * Determine the BSD device name (ie "/dev/rdisk1") for a given IO object.
   126  * @return TRUE if the device name was retrieved, FALSE if the request failed.
   127  */
   128 static gboolean get_bsdname_for_iomedia( io_object_t iomedia, char *buf, int buflen )
   129 {
   130     gboolean result = FALSE;
   131     CFTypeRef pathRef = IORegistryEntryCreateCFProperty(iomedia, CFSTR(kIOBSDNameKey),
   132             kCFAllocatorDefault, 0 );
   133     if( pathRef ) {
   134         char pathlen;
   135         strcpy( buf, _PATH_DEV "r" );
   136         pathlen = strlen(buf);
   137         if( CFStringGetCString( pathRef, buf + pathlen, buflen-pathlen,
   138                 kCFStringEncodingASCII ) != noErr ) {
   139             result = TRUE;
   140         }
   141         CFRelease(pathRef);
   142     }
   143     return result;
   144 }
   146 static gboolean osx_cdrom_drive_get_name( io_object_t object, char *vendor, int vendor_len,
   147                                           char *product, int product_len )
   148 {
   149     gboolean result = FALSE;
   150     CFMutableDictionaryRef props = 0;
   151     if( IORegistryEntryCreateCFProperties(object, &props, kCFAllocatorDefault, kNilOptions) == KERN_SUCCESS ) {
   152         CFDictionaryRef dict = 
   153             (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOPropertyDeviceCharacteristicsKey));
   154         if( dict != NULL ) {
   155             CFTypeRef value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyVendorNameKey));
   156             if( value && CFGetTypeID(value) == CFStringGetTypeID() ) {
   157                 CFStringGetCString( (CFStringRef)value, vendor, vendor_len, kCFStringEncodingUTF8 );
   158             } else {
   159                 vendor[0] = 0;
   160             }
   162             value = CFDictionaryGetValue(dict, CFSTR(kIOPropertyProductNameKey));
   163             if ( value && CFGetTypeID(value) == CFStringGetTypeID() ) {
   164                 CFStringGetCString( (CFStringRef)value, product, product_len, kCFStringEncodingUTF8 );
   165             } else {
   166                 product[0] = 0;
   167             }
   168             result = TRUE;
   169         }
   171         CFRelease(props);
   172     }
   173     return result;
   174 }
   176 /**
   177  * Construct and initialize a new osx_cdrom_drive object, including registering
   178  * it's media inserted notification.
   179  */
   180 static osx_cdrom_drive_t osx_cdrom_drive_new( io_object_t device )  
   181 {
   182     osx_cdrom_drive_t drive = g_malloc0(sizeof(struct osx_cdrom_drive));
   184     IORegistryEntryGetPath( device, kIOServicePlane, drive->ioservice_path );
   185     osx_cdrom_drive_get_name( device, drive->vendor_name, sizeof(drive->vendor_name),
   186                               drive->product_name, sizeof(drive->product_name) );
   187     drive->media_path[0] = '\0';
   188     drive->media_changed = NULL;
   189     drive->media_changed_user_data = NULL;
   190     drive->media_fh = -1;
   192     IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification, 
   193                                       IOServiceMatching("IOMedia"),
   194                                       osx_cdrom_media_inserted, drive, 
   195                                       &drive->media_load_iterator );
   196     osx_cdrom_media_inserted( drive, drive->media_load_iterator );
   197     return drive;
   198 }
   200 /************************ Exported functions *************************/ 
   202 osx_cdrom_drive_t osx_cdrom_open_drive( const char *devname )
   203 {
   204     io_object_t object = IORegistryEntryFromPath( kIOMasterPortDefault, devname );
   205     if( object == MACH_PORT_NULL ) {
   206         return NULL;
   207     }
   209     osx_cdrom_drive_t drive = osx_cdrom_drive_new( object );
   210     IOObjectRelease( object );
   211     return drive;
   212 }
   214 void osx_cdrom_set_media_changed_callback( osx_cdrom_drive_t drive, 
   215                                            media_changed_callback_t callback,
   216                                            void *user_data )
   217 {
   218     drive->media_changed = callback;
   219     drive->media_changed_user_data = user_data;
   220 }
   222 void osx_cdrom_close_drive( osx_cdrom_drive_t drive )
   223 {
   224     IOObjectRelease( drive->media_load_iterator );
   225     IOObjectRelease( drive->media_unload_iterator );
   226     if( drive->media_fh != -1 ) {
   227         close(drive->media_fh);
   228         drive->media_fh = -1;
   229     }
   230     g_free( drive );
   231 }
   233 int osx_cdrom_get_media_handle( osx_cdrom_drive_t drive )
   234 {
   235     if( drive->media_fh == -1 ) {
   236         if( drive->media_path[0] != '\0' ) {
   237             drive->media_fh = open( drive->media_path, O_RDONLY|O_NONBLOCK );
   238         }
   239     }
   240     return drive->media_fh;
   241 }
   243 void osx_cdrom_release_media_handle( osx_cdrom_drive_t drive )
   244 {
   245     if( drive->media_fh != -1 ) {
   246         close( drive->media_fh );
   247         drive->media_fh = -1; 
   248     }
   249 }
   251 static io_object_t iterator_find_cdrom( io_object_t iterator, find_drive_callback_t callback, void *user_data )
   252 {
   253     io_object_t object;
   254     while( (object = IOIteratorNext(iterator)) != 0 ) {
   255         io_string_t iopath = "";
   256         char product[256], vendor[256];
   257         IORegistryEntryGetPath( object, kIOServicePlane, iopath );
   258         osx_cdrom_drive_get_name( object, vendor, sizeof(vendor), product, sizeof(product) );
   259         if( callback( object, vendor, product, iopath, user_data ) ) {
   260             IOObjectRelease(iterator);
   261             return object;
   262         }
   263         IOObjectRelease(object);
   264     }
   265     IOObjectRelease(iterator);
   266     return 0;
   267 }
   270 /**
   271  * Search for a CD or DVD drive (instance of IODVDServices or IOCompactDiscServices).
   272  * The callback will be called repeatedly until either it returns TRUE, or all drives
   273  * have been iterated over.
   274  * 
   275  * @return an IO registry entry for the matched drive, or 0 if no drives matched.
   276  * 
   277  * Note: Use of IOCompactDiscServices is somewhat tentative since I don't have a Mac
   278  * with a CD-Rom drive.
   279  */ 
   280 io_object_t find_cdrom_drive( find_drive_callback_t callback, void *user_data )
   281 {
   282     mach_port_t master_port;
   283     CFMutableDictionaryRef match;
   284     io_iterator_t services;
   285     io_object_t result;
   287     if( IOMasterPort( MACH_PORT_NULL, &master_port ) != KERN_SUCCESS ) {
   288         return 0; // Failed to get the master port?
   289     }
   291     match = IOServiceMatching("IODVDServices");
   292     if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {
   293         return 0;
   294     }
   296     result = iterator_find_cdrom( services, callback, user_data );
   297     if( result != 0 ) {
   298         return result;
   299     }
   301     match = IOServiceMatching("IOCompactDiscServices");
   302     if( IOServiceGetMatchingServices(master_port, match, &services) != kIOReturnSuccess ) {
   303         return 0;
   304     }
   305     return iterator_find_cdrom( services, callback, user_data );
   306 }
   309 // *********************** Notification management ************************/
   311 static void osx_hid_inserted( void *ref, io_iterator_t iterator )
   312 {
   313     io_object_t object;
   314     while( (object = IOIteratorNext(iterator)) != 0 ) {
   315         io_string_t iopath = "";
   316         IORegistryEntryGetPath( object, kIOServicePlane, iopath );
   317         IOObjectRelease( object );
   318     }
   319 }
   321 gboolean osx_register_iokit_notifications()
   322 {
   323     notify_port = IONotificationPortCreate( kIOMasterPortDefault );
   324     CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );
   325     CFRunLoopAddSource( CFRunLoopGetCurrent(), runloop_source, kCFRunLoopCommonModes );
   327     // Drive notifications
   328     if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
   329             IOServiceMatching("IOCompactDiscServies"),
   330             osx_drives_changed, NULL, &iokit_iterators[0] ) != kIOReturnSuccess ) {
   331         ERROR( "IOServiceAddMatchingNotification failed" );
   332     }
   333     osx_drives_changed(NULL, iokit_iterators[0]);
   334     if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification,
   335             IOServiceMatching("IODVDServies"),
   336             osx_drives_changed, NULL, &iokit_iterators[1] ) != kIOReturnSuccess ) {
   337         ERROR( "IOServiceAddMatchingNotification failed" );
   338     }
   339     osx_drives_changed(NULL, iokit_iterators[1]);
   341     if( IOServiceAddMatchingNotification( notify_port, kIOFirstPublishNotification, 
   342             IOServiceMatching(kIOHIDDeviceKey),
   343             osx_hid_inserted, NULL, &iokit_iterators[2] ) != kIOReturnSuccess ) {
   344         ERROR( "IOServiceAddMatchingNotification failed" );
   345     }
   346     osx_hid_inserted(NULL, iokit_iterators[2]);
   347     return TRUE;
   348 }
   350 void osx_unregister_iokit_notifications()
   351 {
   352     CFRunLoopSourceRef runloop_source = IONotificationPortGetRunLoopSource( notify_port );
   353     CFRunLoopRemoveSource( CFRunLoopGetCurrent(), runloop_source, kCFRunLoopCommonModes );
   354     IONotificationPortDestroy( notify_port );
   355     notify_port = 0;
   356 }
.