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, 2 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

# Content
1 /*
2 * $Id: sys_darwin.cpp,v 1.8 2005/06/12 23:36:33 gbeauche 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-2005 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 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_poll_loop)
82 CFRunLoopStop(media_poll_loop);
83 if (media_thread_active)
84 pthread_join(media_thread, NULL);
85 }
86
87
88 /*
89 * Get the BSD-style path of specified object
90 */
91
92 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 }
107 return kernResult;
108 }
109
110
111 /*
112 * kIOMatchedNotification handler
113 */
114
115 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
132
133 /*
134 * kIOTerminatedNotification handler
135 */
136
137 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 }
151 }
152 }
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
202 CFRunLoopRun();
203 return NULL;
204 }
205
206
207 void DarwinAddFloppyPrefs(void)
208 {
209 mach_port_t masterPort; // The way to talk to the kernel
210 io_iterator_t allFloppies; // List of possible floppys
211 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 // This selects all partitions of all disks
220 classesToMatch = IOServiceMatching(kIOMediaClass);
221 if ( classesToMatch )
222 {
223 // Skip drivers and partitions
224 CFDictionarySetValue(classesToMatch,
225 CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
226
227 // Skip fixed drives (hard disks?)
228 CFDictionarySetValue(classesToMatch,
229 CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
230 }
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 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 *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 }
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