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