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

# Content
1 /*
2 * $Id: sys_darwin.cpp,v 1.14 2008/02/04 01:02:58 nigel Exp $
3 *
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 * Basilisk II (C) 1997-2008 Christian Bauer
11 *
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 #import <IOKit/serial/IOSerialKeys.h>
32 #import <IOKit/storage/IOMedia.h>
33 #import <IOKit/storage/IOMediaBSDClient.h>
34 #ifdef HAVE_IOKIT_STORAGE_IOBLOCKSTORAGEDEVICE_H
35 #import <IOKit/storage/IOBlockStorageDevice.h>
36 #endif
37 #import <IOKit/storage/IOCDMedia.h>
38 #import <IOKit/storage/IOCDMediaBSDClient.h>
39 #import <CoreFoundation/CoreFoundation.h>
40
41 #include "sysdeps.h"
42
43 #include "sys.h"
44 #include "prefs.h"
45
46 #define DEBUG 0
47 #import "debug.h"
48
49
50 // Global variables
51 static volatile CFRunLoopRef media_poll_loop = NULL;
52 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
63 /*
64 * Initialization
65 */
66
67 void DarwinSysInit(void)
68 {
69 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
73
74 /*
75 * Deinitialization
76 */
77
78 void DarwinSysExit(void)
79 {
80 // Stop media poll thread
81 if (media_thread_active) {
82 while (media_poll_loop == NULL || !CFRunLoopIsWaiting(media_poll_loop))
83 usleep(0);
84 CFRunLoopStop(media_poll_loop);
85 pthread_join(media_thread, NULL);
86 media_poll_loop = NULL;
87 media_thread_active = false;
88 }
89 }
90
91
92 /*
93 * Get the BSD-style path of specified object
94 */
95
96 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 }
111 return kernResult;
112 }
113
114
115 /*
116 * kIOMatchedNotification handler
117 */
118
119 static void media_arrived(int type, io_iterator_t iterator)
120 {
121 io_object_t obj;
122 while ((obj = IOIteratorNext(iterator))) {
123 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
136
137 /*
138 * kIOTerminatedNotification handler
139 */
140
141 static void media_removed(int type, io_iterator_t iterator)
142 {
143 io_object_t obj;
144 while ((obj = IOIteratorNext(iterator))) {
145 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 }
155 }
156 }
157
158
159 /*
160 * Media poll function
161 *
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 */
165
166 static void dummy(void *) { }; // stub for dummy runloop source
167
168 static void *media_poll_func(void *)
169 {
170 media_poll_loop = CFRunLoopGetCurrent();
171
172 mach_port_t masterPort;
173 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 fprintf(stderr, "IOMasterPort() returned %d\n", kernResult);
180 else if ((matchingDictionary = IOServiceMatching(kIOCDMediaClass)) == NULL)
181 fprintf(stderr, "IOServiceMatching() returned a NULL dictionary\n");
182 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 }
215
216 CFRunLoopRun();
217
218 if (dummySource != NULL)
219 CFRelease(dummySource);
220 return NULL;
221 }
222
223
224 void DarwinAddFloppyPrefs(void)
225 {
226 mach_port_t masterPort; // The way to talk to the kernel
227 io_iterator_t allFloppies; // List of possible floppys
228 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 // This selects all partitions of all disks
237 classesToMatch = IOServiceMatching(kIOMediaClass);
238 if ( classesToMatch )
239 {
240 // Skip drivers and partitions
241 CFDictionarySetValue(classesToMatch,
242 CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
243
244 // Skip fixed drives (hard disks?)
245 CFDictionarySetValue(classesToMatch,
246 CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
247 }
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 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 D(puts("Device does not appear to be 800k or 1440k"));
273 continue;
274 }
275 }
276 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 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 }
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 }
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