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