--- BasiliskII/src/MacOSX/sys_darwin.cpp 2003/03/21 07:05:28 1.1 +++ BasiliskII/src/MacOSX/sys_darwin.cpp 2012/04/21 16:04:51 1.17 @@ -1,5 +1,5 @@ /* - * $Id: sys_darwin.cpp,v 1.1 2003/03/21 07:05:28 nigel Exp $ + * $Id: sys_darwin.cpp,v 1.17 2012/04/21 16:04:51 asvitkine Exp $ * * sys_darwin.cpp - Extra Darwin system dependant routines. Called by: * @@ -7,7 +7,7 @@ * * Based on Apple's CDROMSample.c and Evan Jones' cd-discid.c patches * - * Basilisk II (C) 1997-2003 Christian Bauer + * Basilisk II (C) 1997-2008 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,100 +28,343 @@ #import #import #import +#import #import #import +#ifdef HAVE_IOKIT_STORAGE_IOBLOCKSTORAGEDEVICE_H + #import +#endif #import #import #import -#import "sysdeps.h" +#include "sysdeps.h" -#import "prefs.h" +#include "sys.h" +#include "prefs.h" #define DEBUG 0 #import "debug.h" +// Global variables +static volatile CFRunLoopRef media_poll_loop = NULL; +static bool media_thread_active = false; +static pthread_t media_thread; + +// Prototypes +static void *media_poll_func(void *); + +// From sys_unix.cpp +extern void SysMediaArrived(const char *path, int type); +extern void SysMediaRemoved(const char *path, int type); + /* - * This gets called when no "cdrom" prefs items are found - * It scans for available CD-ROM drives and adds appropriate prefs items + * Initialization */ -void DarwinAddCDROMPrefs(void) +void DarwinSysInit(void) { - mach_port_t masterPort; // The way to talk to the kernel - io_iterator_t allCDs; // List of CD drives on the system - CFMutableDictionaryRef classesToMatch; - io_object_t nextCD; + if (PrefsFindBool("nocdrom")) { + media_thread_active = (pthread_create(&media_thread, NULL, media_poll_func, NULL) == 0); + D(bug("Media poll thread installed (%ld)\n", media_thread)); + } +} - // Don't scan for drives if nocdrom option given - if ( PrefsFindBool("nocdrom") ) - return; +/* + * Deinitialization + */ + +void DarwinSysExit(void) +{ + // Stop media poll thread + if (media_thread_active) { + while (media_poll_loop == NULL || !CFRunLoopIsWaiting(media_poll_loop)) + usleep(0); + CFRunLoopStop(media_poll_loop); + pthread_join(media_thread, NULL); + media_poll_loop = NULL; + media_thread_active = false; + } +} + + +/* + * Get the BSD-style path of specified object + */ + +static kern_return_t get_device_path(io_object_t obj, char *path, size_t maxPathLength) +{ + kern_return_t kernResult = KERN_FAILURE; + CFTypeRef pathAsCFString = IORegistryEntryCreateCFProperty(obj, CFSTR(kIOBSDNameKey), + kCFAllocatorDefault, 0); + if (pathAsCFString) { + strcpy(path, "/dev/"); + size_t pathLength = strlen(path); + if (CFStringGetCString((const __CFString *)pathAsCFString, + path + pathLength, + maxPathLength - pathLength, + kCFStringEncodingASCII)) + kernResult = KERN_SUCCESS; + CFRelease(pathAsCFString); + } + return kernResult; +} + + +/* + * kIOMatchedNotification handler + */ + +static void media_arrived(int type, io_iterator_t iterator) +{ + io_object_t obj; + while ((obj = IOIteratorNext(iterator))) { + char path[MAXPATHLEN]; + kern_return_t kernResult = get_device_path(obj, path, sizeof(path)); + if (kernResult == KERN_SUCCESS) { + D(bug("Media Arrived: %s\n", path)); + SysMediaArrived(path, type); + } + kernResult = IOObjectRelease(obj); + if (kernResult != KERN_SUCCESS) { + fprintf(stderr, "IOObjectRelease() returned %d\n", kernResult); + } + } +} + + +/* + * kIOTerminatedNotification handler + */ + +static void media_removed(int type, io_iterator_t iterator) +{ + io_object_t obj; + while ((obj = IOIteratorNext(iterator))) { + char path[MAXPATHLEN]; + kern_return_t kernResult = get_device_path(obj, path, sizeof(path)); + if (kernResult == KERN_SUCCESS) { + D(bug("Media Removed: %s\n", path)); + SysMediaRemoved(path, type); + } + kernResult = IOObjectRelease(obj); + if (kernResult != KERN_SUCCESS) { + fprintf(stderr, "IOObjectRelease() returned %d\n", kernResult); + } + } +} + + +/* + * Media poll function + * + * NOTE: to facilitate orderly thread termination, media_poll_func MUST end up waiting in CFRunLoopRun. + * Early returns must be avoided, even if there is nothing useful to be done here. See DarwinSysExit. + */ + +static void dummy(void *) { }; // stub for dummy runloop source + +static void *media_poll_func(void *) +{ + media_poll_loop = CFRunLoopGetCurrent(); + + mach_port_t masterPort; + kern_return_t kernResult; + CFMutableDictionaryRef matchingDictionary; + CFRunLoopSourceRef loopSource = NULL; + CFRunLoopSourceRef dummySource = NULL; + + if ((kernResult = IOMasterPort(bootstrap_port, &masterPort)) != KERN_SUCCESS) + fprintf(stderr, "IOMasterPort() returned %d\n", kernResult); + else if ((matchingDictionary = IOServiceMatching(kIOCDMediaClass)) == NULL) + fprintf(stderr, "IOServiceMatching() returned a NULL dictionary\n"); + else { + matchingDictionary = (CFMutableDictionaryRef)CFRetain(matchingDictionary); + IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault); + loopSource = IONotificationPortGetRunLoopSource(notificationPort); + CFRunLoopAddSource(media_poll_loop, loopSource, kCFRunLoopDefaultMode); + + io_iterator_t mediaArrivedIterator; + kernResult = IOServiceAddMatchingNotification(notificationPort, + kIOMatchedNotification, + matchingDictionary, + (IOServiceMatchingCallback)media_arrived, + (void *)MEDIA_CD, &mediaArrivedIterator); + if (kernResult != KERN_SUCCESS) + fprintf(stderr, "IOServiceAddMatchingNotification() returned %d\n", kernResult); + media_arrived(MEDIA_CD, mediaArrivedIterator); + + io_iterator_t mediaRemovedIterator; + kernResult = IOServiceAddMatchingNotification(notificationPort, + kIOTerminatedNotification, + matchingDictionary, + (IOServiceMatchingCallback)media_removed, + (void *)MEDIA_CD, &mediaRemovedIterator); + if (kernResult != KERN_SUCCESS) + fprintf(stderr, "IOServiceAddMatchingNotification() returned %d\n", kernResult); + media_removed(MEDIA_CD, mediaRemovedIterator); + } + + if (loopSource == NULL) { + // add a dummy runloop source to prevent premature return from CFRunLoopRun + CFRunLoopSourceContext context = { 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dummy }; + dummySource = CFRunLoopSourceCreate(NULL, 0, &context); + CFRunLoopAddSource(media_poll_loop, dummySource, kCFRunLoopDefaultMode); + } + + CFRunLoopRun(); + + if (dummySource != NULL) + CFRelease(dummySource); + return NULL; +} + + +void DarwinAddFloppyPrefs(void) +{ + mach_port_t masterPort; // The way to talk to the kernel + io_iterator_t allFloppies; // List of possible floppys + CFMutableDictionaryRef classesToMatch; + io_object_t nextFloppy; - // Let this task talk to the guts of the kernel: if ( IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS ) - bug("IOMasterPort failed. Won't be able to do anything with CD drives"); + bug("IOMasterPort failed. Won't be able to do anything with floppy drives\n"); - // CD media are instances of class kIOCDMediaClass - classesToMatch = IOServiceMatching(kIOCDMediaClass); + // This selects all partitions of all disks + classesToMatch = IOServiceMatching(kIOMediaClass); if ( classesToMatch ) { - // Narrow the search a little further. Each IOMedia object - // has a property with key kIOMediaEjectable. We limit - // the match only to those CDs that are actually ejectable + // Skip drivers and partitions CFDictionarySetValue(classesToMatch, - CFSTR(kIOMediaEjectable), kCFBooleanTrue); + CFSTR(kIOMediaWholeKey), kCFBooleanTrue); + + // Skip fixed drives (hard disks?) + CFDictionarySetValue(classesToMatch, + CFSTR(kIOMediaEjectableKey), kCFBooleanTrue); } if ( IOServiceGetMatchingServices(masterPort, - classesToMatch, &allCDs) != KERN_SUCCESS ) + classesToMatch, &allFloppies) != KERN_SUCCESS ) { - D(bug("IOServiceGetMatchingServices failed. No CD media drives found?\n")); + D(bug("IOServiceGetMatchingServices failed. No removable drives found?\n")); return; } + // Iterate through each floppy + while ( nextFloppy = IOIteratorNext(allFloppies)) + { + char bsdPath[MAXPATHLEN]; + long size; + CFTypeRef sizeAsCFNumber = + IORegistryEntryCreateCFProperty(nextFloppy, + CFSTR(kIOMediaSizeKey), + kCFAllocatorDefault, 0); + + if ( CFNumberGetValue((CFNumberRef)sizeAsCFNumber, + kCFNumberSInt32Type, &size) ) + { + D(bug("Got size of %ld\n", size)); + if ( size < 800 * 1024 || size > 1440 * 1024 ) + { + D(puts("Device does not appear to be 800k or 1440k")); + continue; + } + } + else { + D(puts("Couldn't get kIOMediaSizeKey of device")); + continue; // if kIOMediaSizeKey is unavailable, we shouldn't use it anyway + } + + if (get_device_path(nextFloppy, bsdPath, sizeof(bsdPath)) == KERN_SUCCESS) { + PrefsAddString("floppy", bsdPath); + } else { + D(bug("Could not get BSD device path for floppy\n")); + } + } + + IOObjectRelease(nextFloppy); + IOObjectRelease(allFloppies); +} + + +void DarwinAddSerialPrefs(void) +{ + mach_port_t masterPort; // The way to talk to the kernel + io_iterator_t allModems; // List of modems on the system + CFMutableDictionaryRef classesToMatch; + io_object_t nextModem; + + + if ( IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS ) + bug("IOMasterPort failed. Won't be able to do anything with modems\n"); + + + // Serial devices are instances of class IOSerialBSDClient + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if ( classesToMatch ) + { + // Narrow the search a little further. Each serial device object has + // a property with key kIOSerialBSDTypeKey and a value that is one of + // kIOSerialBSDAllTypes, kIOSerialBSDModemType, or kIOSerialBSDRS232Type. + + CFDictionarySetValue(classesToMatch, + CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDModemType)); + + // This will find built-in and USB modems, but not serial modems. + } + + if ( IOServiceGetMatchingServices(masterPort, + classesToMatch, &allModems) != KERN_SUCCESS ) + { + D(bug("IOServiceGetMatchingServices failed. No modems found?\n")); + return; + } - // Iterate through each CD drive - while ( nextCD = IOIteratorNext(allCDs)) + // Iterate through each modem + while ( nextModem = IOIteratorNext(allModems)) { char bsdPath[MAXPATHLEN]; CFTypeRef bsdPathAsCFString = - IORegistryEntryCreateCFProperty(nextCD, CFSTR(kIOBSDName), - kCFAllocatorDefault, 0); + IORegistryEntryCreateCFProperty(nextModem, + CFSTR(kIOCalloutDeviceKey), + // kIODialinDeviceKey? + kCFAllocatorDefault, 0); *bsdPath = '\0'; if ( bsdPathAsCFString ) { - size_t devPathLength; - - strcpy(bsdPath, "/dev/"); - devPathLength = strlen(bsdPath); - if ( CFStringGetCString((const __CFString *)bsdPathAsCFString, - bsdPath + devPathLength, - MAXPATHLEN - devPathLength, + bsdPath, MAXPATHLEN, kCFStringEncodingASCII) ) { - // If we try to do raw reads on the file bsdPath (e.g. /dev/disk5), - // we get a lot of extra padding in the data. For some reason, - // the device we need has a different name (e.g. /dev/disk5s1) - //strcat(bsdPath, "s1"); - D(bug("CDROM BSD path: %s\n", bsdPath)); + D(bug("Modem BSD path: %s\n", bsdPath)); + + // Note that if there are multiple modems, we only get the last + PrefsAddString("seriala", bsdPath); } else - D(bug("Could not get BSD device path for CD\n")); + D(bug("Could not get BSD device path for modem\n")); CFRelease(bsdPathAsCFString); } - - PrefsAddString("cdrom", bsdPath); + else + D(puts("Cannot determine bsdPath for modem\n")); } - IOObjectRelease(nextCD); - IOObjectRelease(allCDs); + IOObjectRelease(nextModem); + IOObjectRelease(allModems); + + + // Getting a printer device is a bit harder. Creating a fake device + // that emulates a simple printer (e.g. a HP DeskJet) is one possibility, + // but for now I will just create a fake, safe, device entry: + + PrefsAddString("serialb", "/dev/null"); }