442 lines
12 KiB
C++
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
|