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

# User Rev Content
1 nigel 1.1 /*
2 asvitkine 1.17 * $Id: sys_darwin.cpp,v 1.16 2012/04/21 16:02:05 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.17 if (PrefsFindBool("nocdrom")) {
70 asvitkine 1.16 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