ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/sys_darwin.cpp
Revision: 1.17
Committed: 2012-04-21T16:04:51Z (12 years, 6 months ago) by asvitkine
Branch: MAIN
CVS Tags: HEAD
Changes since 1.16: +2 -2 lines
Log Message:
Fix inverted check from my previous commit.

File Contents

# Content
1 /*
2 * $Id: sys_darwin.cpp,v 1.16 2012/04/21 16:02:05 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