ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/sys_darwin.cpp
Revision: 1.16
Committed: 2012-04-21T16:02:05Z (12 years, 6 months ago) by asvitkine
Branch: MAIN
Changes since 1.15: +5 -3 lines
Log Message:
Don't start the Darwin media_thread if "nocdrom" pref is set. The media
thread is currently only used to poll for CDROM devices and is not useful
when "nocdrom" is set. This change also fixes the problem of the emulator
preventing the CD to be ejected at the host level despite "nocdrom" being
set in prefs.

Thanks to Robert Munafo <mrob27@gmail.com> for investigating this problem!

File Contents

# User Rev Content
1 nigel 1.1 /*
2 asvitkine 1.16 * $Id: sys_darwin.cpp,v 1.15 2008/07/20 07:38:27 asvitkine Exp $
3 nigel 1.1 *
4     * sys_darwin.cpp - Extra Darwin system dependant routines. Called by:
5     *
6     * sys_unix.cpp - System dependent routines, Unix implementation
7     *
8     * Based on Apple's CDROMSample.c and Evan Jones' cd-discid.c patches
9     *
10 gbeauche 1.13 * Basilisk II (C) 1997-2008 Christian Bauer
11 nigel 1.1 *
12     * This program is free software; you can redistribute it and/or modify
13     * it under the terms of the GNU General Public License as published by
14     * the Free Software Foundation; either version 2 of the License, or
15     * (at your option) any later version.
16     *
17     * This program is distributed in the hope that it will be useful,
18     * but WITHOUT ANY WARRANTY; without even the implied warranty of
19     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20     * GNU General Public License for more details.
21     *
22     * You should have received a copy of the GNU General Public License
23     * along with this program; if not, write to the Free Software
24     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25     */
26    
27     #import <errno.h>
28     #import <sys/param.h>
29     #import <IOKit/IOKitLib.h>
30     #import <IOKit/IOBSD.h>
31 nigel 1.4 #import <IOKit/serial/IOSerialKeys.h>
32 nigel 1.1 #import <IOKit/storage/IOMedia.h>
33     #import <IOKit/storage/IOMediaBSDClient.h>
34 gbeauche 1.8 #ifdef HAVE_IOKIT_STORAGE_IOBLOCKSTORAGEDEVICE_H
35 nigel 1.5 #import <IOKit/storage/IOBlockStorageDevice.h>
36     #endif
37 nigel 1.1 #import <IOKit/storage/IOCDMedia.h>
38     #import <IOKit/storage/IOCDMediaBSDClient.h>
39     #import <CoreFoundation/CoreFoundation.h>
40    
41 gbeauche 1.9 #include "sysdeps.h"
42 nigel 1.1
43 gbeauche 1.9 #include "sys.h"
44     #include "prefs.h"
45 nigel 1.1
46     #define DEBUG 0
47     #import "debug.h"
48    
49    
50 gbeauche 1.9 // Global variables
51 asvitkine 1.15 static volatile CFRunLoopRef media_poll_loop = NULL;
52 gbeauche 1.9 static bool media_thread_active = false;
53     static pthread_t media_thread;
54    
55     // Prototypes
56     static void *media_poll_func(void *);
57    
58     // From sys_unix.cpp
59     extern void SysMediaArrived(const char *path, int type);
60     extern void SysMediaRemoved(const char *path, int type);
61    
62 nigel 1.1
63     /*
64 gbeauche 1.9 * Initialization
65 nigel 1.1 */
66    
67 gbeauche 1.9 void DarwinSysInit(void)
68 nigel 1.1 {
69 asvitkine 1.16 if (!PrefsFindBool("nocdrom")) {
70     media_thread_active = (pthread_create(&media_thread, NULL, media_poll_func, NULL) == 0);
71     D(bug("Media poll thread installed (%ld)\n", media_thread));
72     }
73 gbeauche 1.9 }
74 nigel 1.1
75    
76 gbeauche 1.9 /*
77     * Deinitialization
78     */
79 nigel 1.1
80 gbeauche 1.9 void DarwinSysExit(void)
81     {
82     // Stop media poll thread
83 asvitkine 1.15 if (media_thread_active) {
84     while (media_poll_loop == NULL || !CFRunLoopIsWaiting(media_poll_loop))
85     usleep(0);
86 gbeauche 1.9 CFRunLoopStop(media_poll_loop);
87     pthread_join(media_thread, NULL);
88 asvitkine 1.15 media_poll_loop = NULL;
89     media_thread_active = false;
90     }
91 gbeauche 1.9 }
92 nigel 1.1
93    
94 gbeauche 1.9 /*
95     * Get the BSD-style path of specified object
96     */
97 nigel 1.1
98 gbeauche 1.9 static kern_return_t get_device_path(io_object_t obj, char *path, size_t maxPathLength)
99     {
100     kern_return_t kernResult = KERN_FAILURE;
101     CFTypeRef pathAsCFString = IORegistryEntryCreateCFProperty(obj, CFSTR(kIOBSDNameKey),
102     kCFAllocatorDefault, 0);
103     if (pathAsCFString) {
104     strcpy(path, "/dev/");
105     size_t pathLength = strlen(path);
106     if (CFStringGetCString((const __CFString *)pathAsCFString,
107     path + pathLength,
108     maxPathLength - pathLength,
109     kCFStringEncodingASCII))
110     kernResult = KERN_SUCCESS;
111     CFRelease(pathAsCFString);
112 nigel 1.1 }
113 gbeauche 1.9 return kernResult;
114     }
115 nigel 1.1
116    
117 gbeauche 1.9 /*
118     * kIOMatchedNotification handler
119     */
120 nigel 1.1
121 gbeauche 1.9 static void media_arrived(int type, io_iterator_t iterator)
122     {
123     io_object_t obj;
124 nigel 1.14 while ((obj = IOIteratorNext(iterator))) {
125 gbeauche 1.9 char path[MAXPATHLEN];
126     kern_return_t kernResult = get_device_path(obj, path, sizeof(path));
127     if (kernResult == KERN_SUCCESS) {
128     D(bug("Media Arrived: %s\n", path));
129     SysMediaArrived(path, type);
130     }
131     kernResult = IOObjectRelease(obj);
132     if (kernResult != KERN_SUCCESS) {
133     fprintf(stderr, "IOObjectRelease() returned %d\n", kernResult);
134     }
135     }
136     }
137 nigel 1.1
138    
139 gbeauche 1.9 /*
140     * kIOTerminatedNotification handler
141     */
142 nigel 1.1
143 gbeauche 1.9 static void media_removed(int type, io_iterator_t iterator)
144     {
145     io_object_t obj;
146 nigel 1.14 while ((obj = IOIteratorNext(iterator))) {
147 gbeauche 1.9 char path[MAXPATHLEN];
148     kern_return_t kernResult = get_device_path(obj, path, sizeof(path));
149     if (kernResult == KERN_SUCCESS) {
150     D(bug("Media Removed: %s\n", path));
151     SysMediaRemoved(path, type);
152     }
153     kernResult = IOObjectRelease(obj);
154     if (kernResult != KERN_SUCCESS) {
155     fprintf(stderr, "IOObjectRelease() returned %d\n", kernResult);
156 nigel 1.1 }
157     }
158 gbeauche 1.9 }
159    
160    
161     /*
162     * Media poll function
163 asvitkine 1.15 *
164     * NOTE: to facilitate orderly thread termination, media_poll_func MUST end up waiting in CFRunLoopRun.
165     * Early returns must be avoided, even if there is nothing useful to be done here. See DarwinSysExit.
166 gbeauche 1.9 */
167    
168 asvitkine 1.15 static void dummy(void *) { }; // stub for dummy runloop source
169    
170 gbeauche 1.9 static void *media_poll_func(void *)
171     {
172     media_poll_loop = CFRunLoopGetCurrent();
173    
174     mach_port_t masterPort;
175 asvitkine 1.15 kern_return_t kernResult;
176     CFMutableDictionaryRef matchingDictionary;
177     CFRunLoopSourceRef loopSource = NULL;
178     CFRunLoopSourceRef dummySource = NULL;
179    
180     if ((kernResult = IOMasterPort(bootstrap_port, &masterPort)) != KERN_SUCCESS)
181 gbeauche 1.9 fprintf(stderr, "IOMasterPort() returned %d\n", kernResult);
182 asvitkine 1.15 else if ((matchingDictionary = IOServiceMatching(kIOCDMediaClass)) == NULL)
183 gbeauche 1.9 fprintf(stderr, "IOServiceMatching() returned a NULL dictionary\n");
184 asvitkine 1.15 else {
185     matchingDictionary = (CFMutableDictionaryRef)CFRetain(matchingDictionary);
186     IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
187     loopSource = IONotificationPortGetRunLoopSource(notificationPort);
188     CFRunLoopAddSource(media_poll_loop, loopSource, kCFRunLoopDefaultMode);
189    
190     io_iterator_t mediaArrivedIterator;
191     kernResult = IOServiceAddMatchingNotification(notificationPort,
192     kIOMatchedNotification,
193     matchingDictionary,
194     (IOServiceMatchingCallback)media_arrived,
195     (void *)MEDIA_CD, &mediaArrivedIterator);
196     if (kernResult != KERN_SUCCESS)
197     fprintf(stderr, "IOServiceAddMatchingNotification() returned %d\n", kernResult);
198     media_arrived(MEDIA_CD, mediaArrivedIterator);
199    
200     io_iterator_t mediaRemovedIterator;
201     kernResult = IOServiceAddMatchingNotification(notificationPort,
202     kIOTerminatedNotification,
203     matchingDictionary,
204     (IOServiceMatchingCallback)media_removed,
205     (void *)MEDIA_CD, &mediaRemovedIterator);
206     if (kernResult != KERN_SUCCESS)
207     fprintf(stderr, "IOServiceAddMatchingNotification() returned %d\n", kernResult);
208     media_removed(MEDIA_CD, mediaRemovedIterator);
209     }
210    
211     if (loopSource == NULL) {
212     // add a dummy runloop source to prevent premature return from CFRunLoopRun
213     CFRunLoopSourceContext context = { 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dummy };
214     dummySource = CFRunLoopSourceCreate(NULL, 0, &context);
215     CFRunLoopAddSource(media_poll_loop, dummySource, kCFRunLoopDefaultMode);
216 gbeauche 1.9 }
217    
218 asvitkine 1.15 CFRunLoopRun();
219 nigel 1.1
220 asvitkine 1.15 if (dummySource != NULL)
221     CFRelease(dummySource);
222 gbeauche 1.9 return NULL;
223 nigel 1.4 }
224    
225    
226     void DarwinAddFloppyPrefs(void)
227     {
228     mach_port_t masterPort; // The way to talk to the kernel
229 nigel 1.6 io_iterator_t allFloppies; // List of possible floppys
230 nigel 1.4 CFMutableDictionaryRef classesToMatch;
231     io_object_t nextFloppy;
232    
233    
234     if ( IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS )
235     bug("IOMasterPort failed. Won't be able to do anything with floppy drives\n");
236    
237    
238 nigel 1.6 // This selects all partitions of all disks
239 nigel 1.4 classesToMatch = IOServiceMatching(kIOMediaClass);
240     if ( classesToMatch )
241     {
242 nigel 1.6 // Skip drivers and partitions
243     CFDictionarySetValue(classesToMatch,
244     CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
245    
246     // Skip fixed drives (hard disks?)
247 nigel 1.4 CFDictionarySetValue(classesToMatch,
248 nigel 1.6 CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
249 nigel 1.4 }
250    
251     if ( IOServiceGetMatchingServices(masterPort,
252     classesToMatch, &allFloppies) != KERN_SUCCESS )
253     {
254     D(bug("IOServiceGetMatchingServices failed. No removable drives found?\n"));
255     return;
256     }
257    
258     // Iterate through each floppy
259     while ( nextFloppy = IOIteratorNext(allFloppies))
260     {
261     char bsdPath[MAXPATHLEN];
262 nigel 1.6 long size;
263     CFTypeRef sizeAsCFNumber =
264     IORegistryEntryCreateCFProperty(nextFloppy,
265     CFSTR(kIOMediaSizeKey),
266     kCFAllocatorDefault, 0);
267    
268     if ( CFNumberGetValue((CFNumberRef)sizeAsCFNumber,
269     kCFNumberSInt32Type, &size) )
270     {
271     D(bug("Got size of %ld\n", size));
272     if ( size < 800 * 1024 || size > 1440 * 1024 )
273     {
274 asvitkine 1.11 D(puts("Device does not appear to be 800k or 1440k"));
275 nigel 1.6 continue;
276     }
277     }
278 asvitkine 1.11 else {
279     D(puts("Couldn't get kIOMediaSizeKey of device"));
280     continue; // if kIOMediaSizeKey is unavailable, we shouldn't use it anyway
281     }
282    
283 asvitkine 1.12 if (get_device_path(nextFloppy, bsdPath, sizeof(bsdPath)) == KERN_SUCCESS) {
284     PrefsAddString("floppy", bsdPath);
285     } else {
286     D(bug("Could not get BSD device path for floppy\n"));
287 nigel 1.4 }
288     }
289    
290     IOObjectRelease(nextFloppy);
291     IOObjectRelease(allFloppies);
292     }
293    
294    
295     void DarwinAddSerialPrefs(void)
296     {
297     mach_port_t masterPort; // The way to talk to the kernel
298     io_iterator_t allModems; // List of modems on the system
299     CFMutableDictionaryRef classesToMatch;
300     io_object_t nextModem;
301    
302    
303     if ( IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS )
304     bug("IOMasterPort failed. Won't be able to do anything with modems\n");
305    
306    
307     // Serial devices are instances of class IOSerialBSDClient
308     classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
309     if ( classesToMatch )
310     {
311     // Narrow the search a little further. Each serial device object has
312     // a property with key kIOSerialBSDTypeKey and a value that is one of
313     // kIOSerialBSDAllTypes, kIOSerialBSDModemType, or kIOSerialBSDRS232Type.
314    
315     CFDictionarySetValue(classesToMatch,
316     CFSTR(kIOSerialBSDTypeKey),
317     CFSTR(kIOSerialBSDModemType));
318    
319     // This will find built-in and USB modems, but not serial modems.
320     }
321    
322     if ( IOServiceGetMatchingServices(masterPort,
323     classesToMatch, &allModems) != KERN_SUCCESS )
324     {
325     D(bug("IOServiceGetMatchingServices failed. No modems found?\n"));
326     return;
327     }
328    
329     // Iterate through each modem
330     while ( nextModem = IOIteratorNext(allModems))
331     {
332     char bsdPath[MAXPATHLEN];
333     CFTypeRef bsdPathAsCFString =
334     IORegistryEntryCreateCFProperty(nextModem,
335     CFSTR(kIOCalloutDeviceKey),
336     // kIODialinDeviceKey?
337     kCFAllocatorDefault, 0);
338     *bsdPath = '\0';
339     if ( bsdPathAsCFString )
340     {
341     if ( CFStringGetCString((const __CFString *)bsdPathAsCFString,
342     bsdPath, MAXPATHLEN,
343     kCFStringEncodingASCII) )
344     {
345     D(bug("Modem BSD path: %s\n", bsdPath));
346    
347     // Note that if there are multiple modems, we only get the last
348     PrefsAddString("seriala", bsdPath);
349     }
350     else
351     D(bug("Could not get BSD device path for modem\n"));
352    
353     CFRelease(bsdPathAsCFString);
354     }
355     else
356     D(puts("Cannot determine bsdPath for modem\n"));
357     }
358    
359     IOObjectRelease(nextModem);
360     IOObjectRelease(allModems);
361    
362    
363     // Getting a printer device is a bit harder. Creating a fake device
364     // that emulates a simple printer (e.g. a HP DeskJet) is one possibility,
365     // but for now I will just create a fake, safe, device entry:
366    
367     PrefsAddString("serialb", "/dev/null");
368 nigel 1.1 }
369    
370    
371     #ifdef MAC_OS_X_VERSION_10_2
372     /*
373     * Read CD-ROM TOC (binary MSF format, 804 bytes max.)
374     */
375    
376     bool DarwinCDReadTOC(char *name, uint8 *toc)
377     {
378     char *c, *devname;
379     int fd;
380    
381    
382     // The open filehandle is something like /dev/disk5s1
383     // The DKIOCCDREADTOC ioctl needs the original cd file,
384     // so we strip the s1 suffix off it, and open the file just for this ioctl
385    
386     devname = strdup(name);
387     if ( ! devname )
388     return false;
389    
390     for ( c = devname; *c; ++c ) ; // Go to the end of the name,
391     --c, --c; // point to the 's1' on the end,
392     *c = '\0'; // and truncate the string
393    
394     fd = open(devname, O_RDONLY);
395     if ( ! fd )
396     {
397     printf("Failed to open CD device %s for ioctl\n", devname);
398     free(devname);
399     return false;
400     }
401    
402     D(bug("Opened %s for ioctl()\n", devname));
403    
404     dk_cd_read_toc_t TOCrequest;
405    
406     // Setup the ioctl request structure:
407    
408     memset(&TOCrequest, 0, sizeof(TOCrequest));
409     TOCrequest.buffer = toc;
410     TOCrequest.bufferLength = 804;
411     TOCrequest.formatAsTime = kCDTrackInfoAddressTypeTrackNumber;
412    
413     if ( ioctl(fd, DKIOCCDREADTOC, &TOCrequest) < 0 )
414     {
415     printf("ioctl(DKIOCCDREADTOC) failed: %s\n", strerror(errno));
416     close(fd);
417     free(devname);
418     return false;
419     }
420     if ( TOCrequest.bufferLength < sizeof(CDTOC) )
421     {
422     printf("ioctl(DKIOCCDREADTOC): only read %d bytes (a CDTOC is at least %d)\n",
423     TOCrequest.bufferLength, (int)sizeof(CDTOC));
424     close(fd);
425     free(devname);
426     return false;
427     }
428     D(bug("ioctl(DKIOCCDREADTOC) read %d bytes\n", TOCrequest.bufferLength));
429    
430     close(fd);
431     free(devname);
432     return true;
433     }
434     #endif