ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/sys_darwin.cpp
Revision: 1.9
Committed: 2006-05-08T12:15:58Z (18 years, 6 months ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-19
Changes since 1.8: +137 -60 lines
Log Message:
Rewrite MacOS X CD-ROM support.

Other bugs fixed:
- CD-ROM media are polled and now can be changed without rebooting
- Buffer overflow, memory leak and extra wait in CD-ROM ejection code

File Contents

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