ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/sys_darwin.cpp
Revision: 1.15
Committed: 2008-07-20T07:38:27Z (16 years, 4 months ago) by asvitkine
Branch: MAIN
Changes since 1.14: +55 -38 lines
Log Message:
[patch from Kelvin Delbarre]
Previously, SheepShaver would usually hang if it was unable to access the ROM
file on startup, due to a race between media_poll_func() and DarwinSysExit().
This change eliminates the race by ensuring that media_poll_func() always ends
up waiting in CFRunLoopRun(), which allows us to terminate the polling thread
in a consistent way.

File Contents

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