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

# Content
1 /*
2 * $Id: sys_darwin.cpp,v 1.15 2008/07/20 07:38:27 asvitkine 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 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 }
74
75
76 /*
77 * Deinitialization
78 */
79
80 void DarwinSysExit(void)
81 {
82 // Stop media poll thread
83 if (media_thread_active) {
84 while (media_poll_loop == NULL || !CFRunLoopIsWaiting(media_poll_loop))
85 usleep(0);
86 CFRunLoopStop(media_poll_loop);
87 pthread_join(media_thread, NULL);
88 media_poll_loop = NULL;
89 media_thread_active = false;
90 }
91 }
92
93
94 /*
95 * Get the BSD-style path of specified object
96 */
97
98 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 }
113 return kernResult;
114 }
115
116
117 /*
118 * kIOMatchedNotification handler
119 */
120
121 static void media_arrived(int type, io_iterator_t iterator)
122 {
123 io_object_t obj;
124 while ((obj = IOIteratorNext(iterator))) {
125 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
138
139 /*
140 * kIOTerminatedNotification handler
141 */
142
143 static void media_removed(int type, io_iterator_t iterator)
144 {
145 io_object_t obj;
146 while ((obj = IOIteratorNext(iterator))) {
147 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 }
157 }
158 }
159
160
161 /*
162 * Media poll function
163 *
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 */
167
168 static void dummy(void *) { }; // stub for dummy runloop source
169
170 static void *media_poll_func(void *)
171 {
172 media_poll_loop = CFRunLoopGetCurrent();
173
174 mach_port_t masterPort;
175 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 fprintf(stderr, "IOMasterPort() returned %d\n", kernResult);
182 else if ((matchingDictionary = IOServiceMatching(kIOCDMediaClass)) == NULL)
183 fprintf(stderr, "IOServiceMatching() returned a NULL dictionary\n");
184 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 }
217
218 CFRunLoopRun();
219
220 if (dummySource != NULL)
221 CFRelease(dummySource);
222 return NULL;
223 }
224
225
226 void DarwinAddFloppyPrefs(void)
227 {
228 mach_port_t masterPort; // The way to talk to the kernel
229 io_iterator_t allFloppies; // List of possible floppys
230 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 // This selects all partitions of all disks
239 classesToMatch = IOServiceMatching(kIOMediaClass);
240 if ( classesToMatch )
241 {
242 // Skip drivers and partitions
243 CFDictionarySetValue(classesToMatch,
244 CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
245
246 // Skip fixed drives (hard disks?)
247 CFDictionarySetValue(classesToMatch,
248 CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
249 }
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 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 D(puts("Device does not appear to be 800k or 1440k"));
275 continue;
276 }
277 }
278 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 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 }
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 }
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