macemu/BasiliskII/src/MacOSX/sys_darwin.cpp

442 lines
12 KiB
C++

/*
* $Id$
*
* sys_darwin.cpp - Extra Darwin system dependant routines. Called by:
*
* sys_unix.cpp - System dependent routines, Unix implementation
*
* Based on Apple's CDROMSample.c and Evan Jones' cd-discid.c patches
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#import <errno.h>
#import <sys/param.h>
#import <IOKit/IOKitLib.h>
#import <IOKit/IOBSD.h>
#import <IOKit/serial/IOSerialKeys.h>
#import <IOKit/storage/IOMedia.h>
#import <IOKit/storage/IOMediaBSDClient.h>
#ifdef HAVE_IOKIT_STORAGE_IOBLOCKSTORAGEDEVICE_H
#import <IOKit/storage/IOBlockStorageDevice.h>
#endif
#import <IOKit/storage/IOCDMedia.h>
#import <IOKit/storage/IOCDMediaBSDClient.h>
#import <CoreFoundation/CoreFoundation.h>
#include "sysdeps.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);
/*
* Initialization
*/
void DarwinSysInit(void)
{
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));
}
}
/*
* 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;
if ( IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS )
bug("IOMasterPort failed. Won't be able to do anything with floppy drives\n");
// This selects all partitions of all disks
classesToMatch = IOServiceMatching(kIOMediaClass);
if ( classesToMatch )
{
// Skip drivers and partitions
CFDictionarySetValue(classesToMatch,
CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
// Skip fixed drives (hard disks?)
CFDictionarySetValue(classesToMatch,
CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
}
if ( IOServiceGetMatchingServices(masterPort,
classesToMatch, &allFloppies) != KERN_SUCCESS )
{
D(bug("IOServiceGetMatchingServices failed. No removable drives found?\n"));
return;
}
// Iterate through each floppy
while ((nextFloppy = IOIteratorNext(allFloppies)))
{
char bsdPath[MAXPATHLEN];
long size = 0;
Boolean gotSize = FALSE;
CFTypeRef sizeAsCFNumber =
IORegistryEntryCreateCFProperty(nextFloppy,
CFSTR(kIOMediaSizeKey),
kCFAllocatorDefault, 0);
if (sizeAsCFNumber)
{
gotSize = CFNumberGetValue((CFNumberRef)sizeAsCFNumber, kCFNumberSInt32Type, &size);
CFRelease(sizeAsCFNumber);
}
if (gotSize)
{
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 modem
while ((nextModem = IOIteratorNext(allModems)))
{
char bsdPath[MAXPATHLEN];
CFTypeRef bsdPathAsCFString =
IORegistryEntryCreateCFProperty(nextModem,
CFSTR(kIOCalloutDeviceKey),
// kIODialinDeviceKey?
kCFAllocatorDefault, 0);
*bsdPath = '\0';
if ( bsdPathAsCFString )
{
if ( CFStringGetCString((const __CFString *)bsdPathAsCFString,
bsdPath, MAXPATHLEN,
kCFStringEncodingASCII) )
{
D(bug("Modem BSD path: %s\n", bsdPath));
// Note that if there are multiple modems, we only get the first
PrefsAddString("seriala", bsdPath);
break;
}
else
D(bug("Could not get BSD device path for modem\n"));
CFRelease(bsdPathAsCFString);
}
else
D(puts("Cannot determine bsdPath for modem\n"));
}
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");
}
#ifdef MAC_OS_X_VERSION_10_2
/*
* Read CD-ROM TOC (binary MSF format, 804 bytes max.)
*/
bool DarwinCDReadTOC(char *name, uint8 *toc)
{
char *c, *devname;
int fd;
// The open filehandle is something like /dev/disk5s1
// The DKIOCCDREADTOC ioctl needs the original cd file,
// so we strip the s1 suffix off it, and open the file just for this ioctl
devname = strdup(name);
if ( ! devname )
return false;
for ( c = devname; *c; ++c ) ; // Go to the end of the name,
--c, --c; // point to the 's1' on the end,
*c = '\0'; // and truncate the string
fd = open(devname, O_RDONLY);
if ( ! fd )
{
printf("Failed to open CD device %s for ioctl\n", devname);
free(devname);
return false;
}
D(bug("Opened %s for ioctl()\n", devname));
dk_cd_read_toc_t TOCrequest;
// Setup the ioctl request structure:
memset(&TOCrequest, 0, sizeof(TOCrequest));
TOCrequest.buffer = toc;
TOCrequest.bufferLength = 804;
TOCrequest.formatAsTime = kCDTrackInfoAddressTypeTrackNumber;
if ( ioctl(fd, DKIOCCDREADTOC, &TOCrequest) < 0 )
{
printf("ioctl(DKIOCCDREADTOC) failed: %s\n", strerror(errno));
close(fd);
free(devname);
return false;
}
if ( TOCrequest.bufferLength < sizeof(CDTOC) )
{
printf("ioctl(DKIOCCDREADTOC): only read %d bytes (a CDTOC is at least %d)\n",
TOCrequest.bufferLength, (int)sizeof(CDTOC));
close(fd);
free(devname);
return false;
}
D(bug("ioctl(DKIOCCDREADTOC) read %d bytes\n", TOCrequest.bufferLength));
close(fd);
free(devname);
return true;
}
#endif