ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sys_unix.cpp
Revision: 1.29
Committed: 2006-05-08T12:15:58Z (18 years, 6 months ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-19
Changes since 1.28: +273 -75 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 * sys_unix.cpp - System dependent routines, Unix implementation
3 *
4 * Basilisk II (C) 1997-2005 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #include <sys/ioctl.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26
27 #ifdef HAVE_AVAILABILITYMACROS_H
28 #include <AvailabilityMacros.h>
29 #endif
30
31 #ifdef __linux__
32 #include <sys/mount.h>
33 #include <linux/cdrom.h>
34 #include <linux/fd.h>
35 #include <linux/major.h>
36 #include <linux/kdev_t.h>
37 #include <dirent.h>
38 #endif
39
40 #if defined(__FreeBSD__) || defined(__NetBSD__)
41 #include <sys/cdio.h>
42 #endif
43
44 #if defined __APPLE__ && defined __MACH__
45 #include <sys/disk.h>
46 #if (defined AQUA || defined HAVE_FRAMEWORK_COREFOUNDATION)
47 #ifndef __MACOSX__
48 #define __MACOSX__ MAC_OS_X_VERSION_MIN_REQUIRED
49 #endif
50 #endif
51 #endif
52
53 #include "main.h"
54 #include "macos_util.h"
55 #include "prefs.h"
56 #include "user_strings.h"
57 #include "sys.h"
58
59 #define DEBUG 0
60 #include "debug.h"
61
62
63 // File handles are pointers to these structures
64 struct file_handle {
65 char *name; // Copy of device/file name
66 int fd;
67 bool is_file; // Flag: plain file or /dev/something?
68 bool is_floppy; // Flag: floppy device
69 bool is_cdrom; // Flag: CD-ROM device
70 bool read_only; // Copy of Sys_open() flag
71 loff_t start_byte; // Size of file header (if any)
72 loff_t file_size; // Size of file data (only valid if is_file is true)
73 bool is_media_present; // Flag: media is inserted and available
74
75 #if defined(__linux__)
76 int cdrom_cap; // CD-ROM capability flags (only valid if is_cdrom is true)
77 #elif defined(__FreeBSD__)
78 struct ioc_capability cdrom_cap;
79 #elif defined(__APPLE__) && defined(__MACH__)
80 char *ioctl_name; // For CDs on OS X - a device for special ioctls
81 int ioctl_fd;
82 #endif
83 };
84
85 // Open file handles
86 struct open_file_handle {
87 file_handle *fh;
88 open_file_handle *next;
89 };
90 static open_file_handle *open_file_handles = NULL;
91
92 // File handle of first floppy drive (for SysMountFirstFloppy())
93 static file_handle *first_floppy = NULL;
94
95 // Prototypes
96 static void cdrom_close(file_handle *fh);
97 static bool cdrom_open(file_handle *fh, const char *path = NULL);
98
99
100 /*
101 * Initialization
102 */
103
104 void SysInit(void)
105 {
106 #if defined __MACOSX__
107 extern void DarwinSysInit(void);
108 DarwinSysInit();
109 #endif
110 }
111
112
113 /*
114 * Deinitialization
115 */
116
117 void SysExit(void)
118 {
119 #if defined __MACOSX__
120 extern void DarwinSysExit(void);
121 DarwinSysExit();
122 #endif
123 }
124
125
126 /*
127 * Manage open file handles
128 */
129
130 static void sys_add_file_handle(file_handle *fh)
131 {
132 open_file_handle *p = new open_file_handle;
133 p->fh = fh;
134 p->next = open_file_handles;
135 open_file_handles = p;
136 }
137
138 static void sys_remove_file_handle(file_handle *fh)
139 {
140 open_file_handle *p = open_file_handles;
141 open_file_handle *q = NULL;
142
143 while (p) {
144 if (p->fh == fh) {
145 if (q)
146 q->next = p->next;
147 else
148 open_file_handles = p->next;
149 delete p;
150 break;
151 }
152 q = p;
153 p = p->next;
154 }
155 }
156
157
158 /*
159 * Account for media that has just arrived
160 */
161
162 void SysMediaArrived(const char *path, int type)
163 {
164 // Replace the "cdrom" entry (we are polling, it's unique)
165 if (type == MEDIA_CD && !PrefsFindBool("nocdrom"))
166 PrefsReplaceString("cdrom", path);
167
168 // Wait for media to be available for reading
169 if (open_file_handles) {
170 const int MAX_WAIT = 5;
171 for (int i = 0; i < MAX_WAIT; i++) {
172 if (access(path, R_OK) == 0)
173 break;
174 switch (errno) {
175 case ENOENT: // Unlikely
176 case EACCES: // MacOS X is mounting the media
177 sleep(1);
178 continue;
179 }
180 printf("WARNING: Cannot access %s (%s)\n", path, strerror(errno));
181 return;
182 }
183 }
184
185 for (open_file_handle *p = open_file_handles; p != NULL; p = p->next) {
186 file_handle * const fh = p->fh;
187
188 // Re-open CD-ROM device
189 if (fh->is_cdrom && type == MEDIA_CD) {
190 cdrom_close(fh);
191 if (cdrom_open(fh, path)) {
192 fh->is_media_present = true;
193 MountVolume(fh);
194 }
195 }
196 }
197 }
198
199
200 /*
201 * Account for media that has just been removed
202 */
203
204 void SysMediaRemoved(const char *path, int type)
205 {
206 if ((type & MEDIA_REMOVABLE) != MEDIA_CD)
207 return;
208
209 for (open_file_handle *p = open_file_handles; p != NULL; p = p->next) {
210 file_handle * const fh = p->fh;
211
212 // Mark media as not available
213 if (!fh->is_cdrom || !fh->is_media_present)
214 continue;
215 if (fh->name && strcmp(fh->name, path) == 0) {
216 fh->is_media_present = false;
217 break;
218 }
219 #if defined __MACOSX__
220 if (fh->ioctl_name && strcmp(fh->ioctl_name, path) == 0) {
221 fh->is_media_present = false;
222 break;
223 }
224 #endif
225 }
226 }
227
228
229 /*
230 * Mount first floppy disk
231 */
232
233 void SysMountFirstFloppy(void)
234 {
235 if (first_floppy)
236 MountVolume(first_floppy);
237 }
238
239
240 /*
241 * This gets called when no "floppy" prefs items are found
242 * It scans for available floppy drives and adds appropriate prefs items
243 */
244
245 void SysAddFloppyPrefs(void)
246 {
247 #if defined(__linux__)
248 DIR *fd_dir = opendir("/dev/floppy");
249 if (fd_dir) {
250 struct dirent *floppy_dev;
251 while ((floppy_dev = readdir(fd_dir)) != NULL) {
252 if (strstr(floppy_dev->d_name, "u1440") != NULL) {
253 char fd_dev[20];
254 sprintf(fd_dev, "/dev/floppy/%s", floppy_dev->d_name);
255 PrefsAddString("floppy", fd_dev);
256 }
257 }
258 closedir(fd_dir);
259 } else {
260 PrefsAddString("floppy", "/dev/fd0");
261 PrefsAddString("floppy", "/dev/fd1");
262 }
263 #elif defined(__NetBSD__)
264 PrefsAddString("floppy", "/dev/fd0a");
265 PrefsAddString("floppy", "/dev/fd1a");
266 #elif defined(__APPLE__) && defined(__MACH__)
267 #if defined(AQUA) || defined(HAVE_FRAMEWORK_COREFOUNDATION)
268 extern void DarwinAddFloppyPrefs(void);
269
270 DarwinAddFloppyPrefs();
271 #else
272 // Until I can convince the other guys that my Darwin code is useful,
273 // we just add something safe (a non-existant device):
274 PrefsAddString("floppy", "/dev/null");
275 #endif
276 #else
277 PrefsAddString("floppy", "/dev/fd0");
278 PrefsAddString("floppy", "/dev/fd1");
279 #endif
280 }
281
282
283 /*
284 * This gets called when no "disk" prefs items are found
285 * It scans for available HFS volumes and adds appropriate prefs items
286 * On OS X, we could do the same, but on an OS X machine I think it is
287 * very unlikely that any mounted volumes would contain a system which
288 * is old enough to boot a 68k Mac, so we just do nothing here for now.
289 */
290
291 void SysAddDiskPrefs(void)
292 {
293 #ifdef __linux__
294 FILE *f = fopen("/etc/fstab", "r");
295 if (f) {
296 char line[256];
297 while(fgets(line, 255, f)) {
298 // Read line
299 int len = strlen(line);
300 if (len == 0 || line[0] == '#')
301 continue;
302 line[len-1] = 0;
303
304 // Parse line
305 char *dev, *mnt_point, *fstype;
306 if (sscanf(line, "%as %as %as", &dev, &mnt_point, &fstype) == 3) {
307 if (strcmp(fstype, "hfs") == 0)
308 PrefsAddString("disk", dev);
309 }
310 free(dev); free(mnt_point); free(fstype);
311 }
312 fclose(f);
313 }
314 #endif
315 }
316
317
318 /*
319 * This gets called when no "cdrom" prefs items are found
320 * It scans for available CD-ROM drives and adds appropriate prefs items
321 */
322
323 void SysAddCDROMPrefs(void)
324 {
325 // Don't scan for drives if nocdrom option given
326 if (PrefsFindBool("nocdrom"))
327 return;
328
329 #if defined(__linux__)
330 if (access("/dev/.devfsd", F_OK) < 0)
331 PrefsAddString("cdrom", "/dev/cdrom");
332 else {
333 DIR *cd_dir = opendir("/dev/cdroms");
334 if (cd_dir) {
335 struct dirent *cdrom_dev;
336 while ((cdrom_dev = readdir(cd_dir)) != NULL) {
337 if (strcmp(cdrom_dev->d_name, ".") != 0 && strcmp(cdrom_dev->d_name, "..") != 0) {
338 char cd_dev[20];
339 sprintf(cd_dev, "/dev/cdroms/%s", cdrom_dev->d_name);
340 PrefsAddString("cdrom", cd_dev);
341 }
342 }
343 closedir(cd_dir);
344 }
345 }
346 #elif defined __MACOSX__
347 // There is no predefined path for CD-ROMs on MacOS X. Rather, we
348 // define a single fake CD-ROM entry for the emulated MacOS.
349 // XXX this means we handle only CD-ROM drive at a time, wherever
350 // the disk is, the latest one is used.
351 PrefsAddString("cdrom", "/dev/poll/cdrom");
352 #elif defined(__FreeBSD__) || defined(__NetBSD__)
353 PrefsAddString("cdrom", "/dev/cd0c");
354 #endif
355 }
356
357
358 /*
359 * Add default serial prefs (must be added, even if no ports present)
360 */
361
362 void SysAddSerialPrefs(void)
363 {
364 #if defined(__linux__)
365 if (access("/dev/.devfsd", F_OK) < 0) {
366 PrefsAddString("seriala", "/dev/ttyS0");
367 PrefsAddString("serialb", "/dev/ttyS1");
368 } else {
369 PrefsAddString("seriala", "/dev/tts/0");
370 PrefsAddString("serialb", "/dev/tts/1");
371 }
372 #elif defined(__FreeBSD__)
373 PrefsAddString("seriala", "/dev/cuaa0");
374 PrefsAddString("serialb", "/dev/cuaa1");
375 #elif defined(__NetBSD__)
376 PrefsAddString("seriala", "/dev/tty00");
377 PrefsAddString("serialb", "/dev/tty01");
378 #elif defined(__APPLE__) && defined(__MACH__)
379 #if defined(AQUA) || defined(HAVE_FRAMEWORK_COREFOUNDATION)
380 extern void DarwinAddSerialPrefs(void);
381
382 DarwinAddSerialPrefs();
383 #else
384 // Until I can convince the other guys that my Darwin code is useful,
385 // we just add something safe (non-existant devices):
386 PrefsAddString("seriala", "/dev/null");
387 PrefsAddString("serialb", "/dev/null");
388 #endif
389 #endif
390 }
391
392
393 /*
394 * Open CD-ROM device and initialize internal data
395 */
396
397 static bool cdrom_open_1(file_handle *fh)
398 {
399 #if defined __MACOSX__
400 // In OS X, the device name is OK for sending ioctls to,
401 // but not for reading raw CDROM data from.
402 // (it seems to have extra data padded in)
403 //
404 // So, we keep the already opened file handle,
405 // and open a slightly different file for CDROM data
406 //
407 fh->ioctl_fd = fh->fd;
408 fh->ioctl_name = fh->name;
409 fh->fd = -1;
410 fh->name = (char *)malloc(strlen(fh->ioctl_name) + 3);
411 if (fh->name) {
412 strcpy(fh->name, fh->ioctl_name);
413 strcat(fh->name, "s1");
414 fh->fd = open(fh->name, O_RDONLY, O_NONBLOCK);
415 }
416 if (fh->ioctl_fd < 0)
417 return false;
418 #endif
419 return true;
420 }
421
422 bool cdrom_open(file_handle *fh, const char *path)
423 {
424 if (path)
425 fh->name = strdup(path);
426 fh->fd = open(fh->name, O_RDONLY, O_NONBLOCK);
427 fh->start_byte = 0;
428 if (!cdrom_open_1(fh))
429 return false;
430 return fh->fd >= 0;
431 }
432
433
434 /*
435 * Close a CD-ROM device
436 */
437
438 void cdrom_close(file_handle *fh)
439 {
440 if (fh->fd >= 0) {
441 close(fh->fd);
442 fh->fd = -1;
443 }
444 if (fh->name) {
445 free(fh->name);
446 fh->name = NULL;
447 }
448 #if defined __MACOSX__
449 if (fh->ioctl_fd >= 0) {
450 close(fh->ioctl_fd);
451 fh->ioctl_fd = -1;
452 }
453 if (fh->ioctl_name) {
454 free(fh->ioctl_name);
455 fh->ioctl_name = NULL;
456 }
457 #endif
458 }
459
460
461 /*
462 * Check if device is a mounted HFS volume, get mount name
463 */
464
465 static bool is_drive_mounted(const char *dev_name, char *mount_name)
466 {
467 #ifdef __linux__
468 FILE *f = fopen("/proc/mounts", "r");
469 if (f) {
470 char line[256];
471 while(fgets(line, 255, f)) {
472 // Read line
473 int len = strlen(line);
474 if (len == 0)
475 continue;
476 line[len-1] = 0;
477
478 // Parse line
479 if (strncmp(line, dev_name, strlen(dev_name)) == 0) {
480 mount_name[0] = 0;
481 char *dummy;
482 sscanf(line, "%as %s", &dummy, mount_name);
483 free(dummy);
484 fclose(f);
485 return true;
486 }
487 }
488 fclose(f);
489 }
490 #endif
491 return false;
492 }
493
494
495 /*
496 * Open file/device, create new file handle (returns NULL on error)
497 */
498
499 void *Sys_open(const char *name, bool read_only)
500 {
501 bool is_file = strncmp(name, "/dev/", 5) != 0;
502 #if defined(__FreeBSD__)
503 // SCSI IDE
504 bool is_cdrom = strncmp(name, "/dev/cd", 7) == 0 || strncmp(name, "/dev/acd", 8) == 0;
505 #else
506 bool is_cdrom = strncmp(name, "/dev/cd", 7) == 0;
507 #endif
508 bool is_floppy = strncmp(name, "/dev/fd", 7) == 0;
509
510 bool is_polled_media = strncmp(name, "/dev/poll/", 10) == 0;
511 if (is_floppy) // Floppy open fails if there's no disk inserted
512 is_polled_media = true;
513
514 #if defined __MACOSX__
515 // There is no set filename in /dev which is the cdrom,
516 // so we have to see if it is any of the devices that we found earlier
517 {
518 int index = 0;
519 const char *str;
520 while ((str = PrefsFindString("cdrom", index++)) != NULL) {
521 if (is_polled_media || strcmp(str, name) == 0) {
522 is_cdrom = true;
523 read_only = true;
524 break;
525 }
526 }
527 }
528 #endif
529
530 D(bug("Sys_open(%s, %s)\n", name, read_only ? "read-only" : "read/write"));
531
532 // Check if write access is allowed, set read-only flag if not
533 if (!read_only && access(name, W_OK))
534 read_only = true;
535
536 // Print warning message and eventually unmount drive when this is an HFS volume mounted under Linux (double mounting will corrupt the volume)
537 char mount_name[256];
538 if (!is_file && !read_only && is_drive_mounted(name, mount_name)) {
539 char str[512];
540 sprintf(str, GetString(STR_VOLUME_IS_MOUNTED_WARN), mount_name);
541 WarningAlert(str);
542 sprintf(str, "umount %s", mount_name);
543 if (system(str)) {
544 sprintf(str, GetString(STR_CANNOT_UNMOUNT_WARN), mount_name, strerror(errno));
545 WarningAlert(str);
546 return NULL;
547 }
548 }
549
550 // Open file/device
551 #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACOSX__)
552 int fd = open(name, (read_only ? O_RDONLY : O_RDWR) | (is_cdrom ? O_NONBLOCK : 0));
553 #else
554 int fd = open(name, read_only ? O_RDONLY : O_RDWR);
555 #endif
556 if (fd < 0 && !read_only) {
557 // Read-write failed, try read-only
558 read_only = true;
559 fd = open(name, O_RDONLY);
560 }
561 if (fd >= 0 || is_polled_media) {
562 file_handle *fh = new file_handle;
563 fh->name = strdup(name);
564 fh->fd = fd;
565 fh->is_file = is_file;
566 fh->read_only = read_only;
567 fh->start_byte = 0;
568 fh->is_floppy = is_floppy;
569 fh->is_cdrom = is_cdrom;
570 fh->is_media_present = false;
571 #if defined __MACOSX__
572 fh->ioctl_fd = -1;
573 fh->ioctl_name = NULL;
574 #endif
575 if (fh->is_file) {
576 fh->is_media_present = true;
577 // Detect disk image file layout
578 loff_t size = 0;
579 size = lseek(fd, 0, SEEK_END);
580 uint8 data[256];
581 lseek(fd, 0, SEEK_SET);
582 read(fd, data, 256);
583 FileDiskLayout(size, data, fh->start_byte, fh->file_size);
584 } else {
585 struct stat st;
586 if (fstat(fd, &st) == 0) {
587 fh->is_media_present = true;
588 if (S_ISBLK(st.st_mode)) {
589 fh->is_cdrom = is_cdrom;
590 #if defined(__linux__)
591 fh->is_floppy = (MAJOR(st.st_rdev) == FLOPPY_MAJOR);
592 #ifdef CDROM_GET_CAPABILITY
593 if (is_cdrom) {
594 fh->cdrom_cap = ioctl(fh->fd, CDROM_GET_CAPABILITY);
595 if (fh->cdrom_cap < 0)
596 fh->cdrom_cap = 0;
597 }
598 #else
599 fh->cdrom_cap = 0;
600 #endif
601 #elif defined(__FreeBSD__)
602 fh->is_floppy = ((st.st_rdev >> 16) == 2);
603 #ifdef CDIOCCAPABILITY
604 if (is_cdrom) {
605 if (ioctl(fh->fd, CDIOCCAPABILITY, &fh->cdrom_cap) < 0)
606 memset(&fh->cdrom_cap, 0, sizeof(fh->cdrom_cap));
607 }
608 #else
609 fh->cdrom_cap = 0;
610 #endif
611 #elif defined(__NetBSD__)
612 fh->is_floppy = ((st.st_rdev >> 16) == 2);
613 #endif
614 }
615 #if defined __MACOSX__
616 if (is_cdrom) {
617 fh->is_cdrom = true;
618 fh->is_floppy = false;
619 if (cdrom_open_1(fh))
620 fh->is_media_present = true;
621 }
622 #endif
623 }
624 }
625 if (fh->is_floppy && first_floppy == NULL)
626 first_floppy = fh;
627 sys_add_file_handle(fh);
628 return fh;
629 } else {
630 printf("WARNING: Cannot open %s (%s)\n", name, strerror(errno));
631 return NULL;
632 }
633 }
634
635
636 /*
637 * Close file/device, delete file handle
638 */
639
640 void Sys_close(void *arg)
641 {
642 file_handle *fh = (file_handle *)arg;
643 if (!fh)
644 return;
645
646 sys_remove_file_handle(fh);
647
648 if (fh->is_cdrom)
649 cdrom_close(fh);
650 if (fh->fd >= 0)
651 close(fh->fd);
652 if (fh->name)
653 free(fh->name);
654 delete fh;
655 }
656
657
658 /*
659 * Read "length" bytes from file/device, starting at "offset", to "buffer",
660 * returns number of bytes read (or 0)
661 */
662
663 size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length)
664 {
665 file_handle *fh = (file_handle *)arg;
666 if (!fh)
667 return 0;
668
669 // Seek to position
670 if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
671 return 0;
672
673 // Read data
674 return read(fh->fd, buffer, length);
675 }
676
677
678 /*
679 * Write "length" bytes from "buffer" to file/device, starting at "offset",
680 * returns number of bytes written (or 0)
681 */
682
683 size_t Sys_write(void *arg, void *buffer, loff_t offset, size_t length)
684 {
685 file_handle *fh = (file_handle *)arg;
686 if (!fh)
687 return 0;
688
689 // Seek to position
690 if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
691 return 0;
692
693 // Write data
694 return write(fh->fd, buffer, length);
695 }
696
697
698 /*
699 * Return size of file/device (minus header)
700 */
701
702 loff_t SysGetFileSize(void *arg)
703 {
704 file_handle *fh = (file_handle *)arg;
705 if (!fh)
706 return true;
707
708 if (fh->is_file)
709 return fh->file_size;
710 else {
711 #if defined(__linux__)
712 long blocks;
713 if (ioctl(fh->fd, BLKGETSIZE, &blocks) < 0)
714 return 0;
715 D(bug(" BLKGETSIZE returns %d blocks\n", blocks));
716 return (loff_t)blocks * 512;
717 #elif defined __MACOSX__
718 uint32 block_size;
719 if (ioctl(fh->ioctl_fd, DKIOCGETBLOCKSIZE, &block_size) < 0)
720 return 0;
721 D(bug(" DKIOCGETBLOCKSIZE returns %lu bytes\n", (unsigned long)block_size));
722 uint64 block_count;
723 if (ioctl(fh->ioctl_fd, DKIOCGETBLOCKCOUNT, &block_count) < 0)
724 return 0;
725 D(bug(" DKIOCGETBLOCKCOUNT returns %llu blocks\n", (unsigned long long)block_count));
726 return block_count * block_size;
727 #else
728 return lseek(fh->fd, 0, SEEK_END) - fh->start_byte;
729 #endif
730 }
731 }
732
733
734 /*
735 * Eject volume (if applicable)
736 */
737
738 void SysEject(void *arg)
739 {
740 file_handle *fh = (file_handle *)arg;
741 if (!fh)
742 return;
743
744 #if defined(__linux__)
745 if (fh->is_floppy) {
746 if (fh->fd >= 0) {
747 fsync(fh->fd);
748 ioctl(fh->fd, FDFLUSH);
749 ioctl(fh->fd, FDEJECT);
750 close(fh->fd); // Close and reopen so the driver will see the media change
751 }
752 fh->fd = open(fh->name, fh->read_only ? O_RDONLY : O_RDWR);
753 } else if (fh->is_cdrom) {
754 ioctl(fh->fd, CDROMEJECT);
755 close(fh->fd); // Close and reopen so the driver will see the media change
756 fh->fd = open(fh->name, O_RDONLY | O_NONBLOCK);
757 }
758 #elif defined(__FreeBSD__) || defined(__NetBSD__)
759 if (fh->is_floppy) {
760 fsync(fh->fd);
761 } else if (fh->is_cdrom) {
762 ioctl(fh->fd, CDIOCEJECT);
763 close(fh->fd); // Close and reopen so the driver will see the media change
764 fh->fd = open(fh->name, O_RDONLY | O_NONBLOCK);
765 }
766 #elif defined(__APPLE__) && defined(__MACH__)
767 if (fh->is_cdrom && fh->is_media_present) {
768 close(fh->fd);
769 fh->fd = -1;
770 if (ioctl(fh->ioctl_fd, DKIOCEJECT) < 0) {
771 D(bug(" DKIOCEJECT failed on file %s: %s\n",
772 fh->ioctl_name, strerror(errno)));
773
774 // If we are running MacOS X, the device may be in busy
775 // state because the Finder has mounted the disk
776 close(fh->ioctl_fd);
777 fh->ioctl_fd = -1;
778
779 // Try to use "diskutil eject" but it can take up to 5
780 // seconds to complete
781 static const char eject_cmd[] = "/usr/sbin/diskutil eject %s 2>&1 >/dev/null";
782 char *cmd = (char *)alloca(strlen(eject_cmd) + strlen(fh->ioctl_name) + 1);
783 sprintf(cmd, eject_cmd, fh->ioctl_name);
784 system(cmd);
785 }
786 fh->is_media_present = false;
787 }
788 #endif
789 }
790
791
792 /*
793 * Format volume (if applicable)
794 */
795
796 bool SysFormat(void *arg)
797 {
798 file_handle *fh = (file_handle *)arg;
799 if (!fh)
800 return false;
801
802 //!!
803 return true;
804 }
805
806
807 /*
808 * Check if file/device is read-only (this includes the read-only flag on Sys_open())
809 */
810
811 bool SysIsReadOnly(void *arg)
812 {
813 file_handle *fh = (file_handle *)arg;
814 if (!fh)
815 return true;
816
817 #if defined(__linux__)
818 if (fh->is_floppy) {
819 if (fh->fd >= 0) {
820 struct floppy_drive_struct stat;
821 ioctl(fh->fd, FDGETDRVSTAT, &stat);
822 return !(stat.flags & FD_DISK_WRITABLE);
823 } else
824 return true;
825 } else
826 #endif
827 return fh->read_only;
828 }
829
830
831 /*
832 * Check if the given file handle refers to a fixed or a removable disk
833 */
834
835 bool SysIsFixedDisk(void *arg)
836 {
837 file_handle *fh = (file_handle *)arg;
838 if (!fh)
839 return true;
840
841 if (fh->is_file)
842 return true;
843 else if (fh->is_floppy || fh->is_cdrom)
844 return false;
845 else
846 return true;
847 }
848
849
850 /*
851 * Check if a disk is inserted in the drive (always true for files)
852 */
853
854 bool SysIsDiskInserted(void *arg)
855 {
856 file_handle *fh = (file_handle *)arg;
857 if (!fh)
858 return false;
859
860 if (fh->is_file) {
861 return true;
862
863 #if defined(__linux__)
864 } else if (fh->is_floppy) {
865 char block[512];
866 lseek(fh->fd, 0, SEEK_SET);
867 ssize_t actual = read(fh->fd, block, 512);
868 if (actual < 0) {
869 close(fh->fd); // Close and reopen so the driver will see the media change
870 fh->fd = open(fh->name, fh->read_only ? O_RDONLY : O_RDWR);
871 actual = read(fh->fd, block, 512);
872 }
873 return actual == 512;
874 } else if (fh->is_cdrom) {
875 #ifdef CDROM_MEDIA_CHANGED
876 if (fh->cdrom_cap & CDC_MEDIA_CHANGED) {
877 // If we don't do this, all attempts to read from a disc fail
878 // once the tray has been opened (altough the TOC reads fine).
879 // Can somebody explain this to me?
880 if (ioctl(fh->fd, CDROM_MEDIA_CHANGED) == 1) {
881 close(fh->fd);
882 fh->fd = open(fh->name, O_RDONLY | O_NONBLOCK);
883 }
884 }
885 #endif
886 #ifdef CDROM_DRIVE_STATUS
887 if (fh->cdrom_cap & CDC_DRIVE_STATUS) {
888 return ioctl(fh->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) == CDS_DISC_OK;
889 }
890 #endif
891 cdrom_tochdr header;
892 return ioctl(fh->fd, CDROMREADTOCHDR, &header) == 0;
893 #elif defined(__FreeBSD__) || defined(__NetBSD__)
894 } else if (fh->is_floppy) {
895 return false; //!!
896 } else if (fh->is_cdrom) {
897 struct ioc_toc_header header;
898 return ioctl(fh->fd, CDIOREADTOCHEADER, &header) == 0;
899 #elif defined __MACOSX__
900 } else if (fh->is_cdrom || fh->is_floppy) {
901 return fh->is_media_present;
902 #endif
903
904 } else
905 return true;
906 }
907
908
909 /*
910 * Prevent medium removal (if applicable)
911 */
912
913 void SysPreventRemoval(void *arg)
914 {
915 file_handle *fh = (file_handle *)arg;
916 if (!fh)
917 return;
918
919 #if defined(__linux__) && defined(CDROM_LOCKDOOR)
920 if (fh->is_cdrom)
921 ioctl(fh->fd, CDROM_LOCKDOOR, 1);
922 #endif
923 }
924
925
926 /*
927 * Allow medium removal (if applicable)
928 */
929
930 void SysAllowRemoval(void *arg)
931 {
932 file_handle *fh = (file_handle *)arg;
933 if (!fh)
934 return;
935
936 #if defined(__linux__) && defined(CDROM_LOCKDOOR)
937 if (fh->is_cdrom)
938 ioctl(fh->fd, CDROM_LOCKDOOR, 0);
939 #endif
940 }
941
942
943 /*
944 * Read CD-ROM TOC (binary MSF format, 804 bytes max.)
945 */
946
947 bool SysCDReadTOC(void *arg, uint8 *toc)
948 {
949 file_handle *fh = (file_handle *)arg;
950 if (!fh)
951 return false;
952
953 if (fh->is_cdrom) {
954 #if defined(__linux__)
955 uint8 *p = toc + 2;
956
957 // Header
958 cdrom_tochdr header;
959 if (ioctl(fh->fd, CDROMREADTOCHDR, &header) < 0)
960 return false;
961 *p++ = header.cdth_trk0;
962 *p++ = header.cdth_trk1;
963
964 // Tracks
965 cdrom_tocentry entry;
966 for (int i=header.cdth_trk0; i<=header.cdth_trk1; i++) {
967 entry.cdte_track = i;
968 entry.cdte_format = CDROM_MSF;
969 if (ioctl(fh->fd, CDROMREADTOCENTRY, &entry) < 0)
970 return false;
971 *p++ = 0;
972 *p++ = (entry.cdte_adr << 4) | entry.cdte_ctrl;
973 *p++ = entry.cdte_track;
974 *p++ = 0;
975 *p++ = 0;
976 *p++ = entry.cdte_addr.msf.minute;
977 *p++ = entry.cdte_addr.msf.second;
978 *p++ = entry.cdte_addr.msf.frame;
979 }
980
981 // Leadout track
982 entry.cdte_track = CDROM_LEADOUT;
983 entry.cdte_format = CDROM_MSF;
984 if (ioctl(fh->fd, CDROMREADTOCENTRY, &entry) < 0)
985 return false;
986 *p++ = 0;
987 *p++ = (entry.cdte_adr << 4) | entry.cdte_ctrl;
988 *p++ = entry.cdte_track;
989 *p++ = 0;
990 *p++ = 0;
991 *p++ = entry.cdte_addr.msf.minute;
992 *p++ = entry.cdte_addr.msf.second;
993 *p++ = entry.cdte_addr.msf.frame;
994
995 // TOC size
996 int toc_size = p - toc;
997 *toc++ = toc_size >> 8;
998 *toc++ = toc_size & 0xff;
999 return true;
1000 #elif defined __MACOSX__ && defined MAC_OS_X_VERSION_10_2
1001 if (fh->is_media_present) {
1002 extern bool DarwinCDReadTOC(char *name, uint8 *toc);
1003 return DarwinCDReadTOC(fh->name, toc);
1004 }
1005 return false;
1006 #elif defined(__FreeBSD__)
1007 uint8 *p = toc + 2;
1008
1009 // Header
1010 struct ioc_toc_header header;
1011 if (ioctl(fh->fd, CDIOREADTOCHEADER, &header) < 0)
1012 return false;
1013 *p++ = header.starting_track;
1014 *p++ = header.ending_track;
1015
1016 // Tracks
1017 struct ioc_read_toc_single_entry entry;
1018 for (int i=header.starting_track; i<=header.ending_track; i++) {
1019 entry.track = i;
1020 entry.address_format = CD_MSF_FORMAT;
1021 if (ioctl(fh->fd, CDIOREADTOCENTRY, &entry) < 0)
1022 return false;
1023 *p++ = 0;
1024 *p++ = (entry.entry.addr_type << 4) | entry.entry.control;
1025 *p++ = entry.entry.track;
1026 *p++ = 0;
1027 *p++ = 0;
1028 *p++ = entry.entry.addr.msf.minute;
1029 *p++ = entry.entry.addr.msf.second;
1030 *p++ = entry.entry.addr.msf.frame;
1031 }
1032
1033 // Leadout track
1034 entry.track = CD_TRACK_INFO;
1035 entry.address_format = CD_MSF_FORMAT;
1036 if (ioctl(fh->fd, CDIOREADTOCENTRY, &entry) < 0)
1037 return false;
1038 *p++ = 0;
1039 *p++ = (entry.entry.addr_type << 4) | entry.entry.control;
1040 *p++ = entry.entry.track;
1041 *p++ = 0;
1042 *p++ = 0;
1043 *p++ = entry.entry.addr.msf.minute;
1044 *p++ = entry.entry.addr.msf.second;
1045 *p++ = entry.entry.addr.msf.frame;
1046
1047 // TOC size
1048 int toc_size = p - toc;
1049 *toc++ = toc_size >> 8;
1050 *toc++ = toc_size & 0xff;
1051 return true;
1052 #elif defined(__NetBSD__)
1053 uint8 *p = toc + 2;
1054
1055 // Header
1056 struct ioc_toc_header header;
1057 if (ioctl(fh->fd, CDIOREADTOCHEADER, &header) < 0)
1058 return false;
1059 *p++ = header.starting_track;
1060 *p++ = header.ending_track;
1061
1062 // Tracks (this is nice... :-)
1063 struct ioc_read_toc_entry entries;
1064 entries.address_format = CD_MSF_FORMAT;
1065 entries.starting_track = 1;
1066 entries.data_len = 800;
1067 entries.data = (cd_toc_entry *)p;
1068 if (ioctl(fh->fd, CDIOREADTOCENTRIES, &entries) < 0)
1069 return false;
1070
1071 // TOC size
1072 int toc_size = p - toc;
1073 *toc++ = toc_size >> 8;
1074 *toc++ = toc_size & 0xff;
1075 return true;
1076 #else
1077 return false;
1078 #endif
1079 } else
1080 return false;
1081 }
1082
1083
1084 /*
1085 * Read CD-ROM position data (Sub-Q Channel, 16 bytes, see SCSI standard)
1086 */
1087
1088 bool SysCDGetPosition(void *arg, uint8 *pos)
1089 {
1090 file_handle *fh = (file_handle *)arg;
1091 if (!fh)
1092 return false;
1093
1094 if (fh->is_cdrom) {
1095 #if defined(__linux__)
1096 cdrom_subchnl chan;
1097 chan.cdsc_format = CDROM_MSF;
1098 if (ioctl(fh->fd, CDROMSUBCHNL, &chan) < 0)
1099 return false;
1100 *pos++ = 0;
1101 *pos++ = chan.cdsc_audiostatus;
1102 *pos++ = 0;
1103 *pos++ = 12; // Sub-Q data length
1104 *pos++ = 0;
1105 *pos++ = (chan.cdsc_adr << 4) | chan.cdsc_ctrl;
1106 *pos++ = chan.cdsc_trk;
1107 *pos++ = chan.cdsc_ind;
1108 *pos++ = 0;
1109 *pos++ = chan.cdsc_absaddr.msf.minute;
1110 *pos++ = chan.cdsc_absaddr.msf.second;
1111 *pos++ = chan.cdsc_absaddr.msf.frame;
1112 *pos++ = 0;
1113 *pos++ = chan.cdsc_reladdr.msf.minute;
1114 *pos++ = chan.cdsc_reladdr.msf.second;
1115 *pos++ = chan.cdsc_reladdr.msf.frame;
1116 return true;
1117 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1118 struct ioc_read_subchannel chan;
1119 chan.data_format = CD_MSF_FORMAT;
1120 chan.address_format = CD_MSF_FORMAT;
1121 chan.track = CD_CURRENT_POSITION;
1122 if (ioctl(fh->fd, CDIOCREADSUBCHANNEL, &chan) < 0)
1123 return false;
1124 *pos++ = 0;
1125 *pos++ = chan.data->header.audio_status;
1126 *pos++ = 0;
1127 *pos++ = 12; // Sub-Q data length
1128 *pos++ = 0;
1129 *pos++ = (chan.data->what.position.addr_type << 4) | chan.data->what.position.control;
1130 *pos++ = chan.data->what.position.track_number;
1131 *pos++ = chan.data->what.position.index_number;
1132 *pos++ = 0;
1133 *pos++ = chan.data->what.position.absaddr.msf.minute;
1134 *pos++ = chan.data->what.position.absaddr.msf.second;
1135 *pos++ = chan.data->what.position.absaddr.msf.frame;
1136 *pos++ = 0;
1137 *pos++ = chan.data->what.position.reladdr.msf.minute;
1138 *pos++ = chan.data->what.position.reladdr.msf.second;
1139 *pos++ = chan.data->what.position.reladdr.msf.frame;
1140 return true;
1141 #else
1142 return false;
1143 #endif
1144 } else
1145 return false;
1146 }
1147
1148
1149 /*
1150 * Play CD audio
1151 */
1152
1153 bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end_m, uint8 end_s, uint8 end_f)
1154 {
1155 file_handle *fh = (file_handle *)arg;
1156 if (!fh)
1157 return false;
1158
1159 if (fh->is_cdrom) {
1160 #if defined(__linux__)
1161 cdrom_msf play;
1162 play.cdmsf_min0 = start_m;
1163 play.cdmsf_sec0 = start_s;
1164 play.cdmsf_frame0 = start_f;
1165 play.cdmsf_min1 = end_m;
1166 play.cdmsf_sec1 = end_s;
1167 play.cdmsf_frame1 = end_f;
1168 return ioctl(fh->fd, CDROMPLAYMSF, &play) == 0;
1169 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1170 struct ioc_play_msf play;
1171 play.start_m = start_m;
1172 play.start_s = start_s;
1173 play.start_f = start_f;
1174 play.end_m = end_m;
1175 play.end_s = end_s;
1176 play.end_f = end_f;
1177 return ioctl(fh->fd, CDIOCPLAYMSF, &play) == 0;
1178 #else
1179 return false;
1180 #endif
1181 } else
1182 return false;
1183 }
1184
1185
1186 /*
1187 * Pause CD audio
1188 */
1189
1190 bool SysCDPause(void *arg)
1191 {
1192 file_handle *fh = (file_handle *)arg;
1193 if (!fh)
1194 return false;
1195
1196 if (fh->is_cdrom) {
1197 #if defined(__linux__)
1198 return ioctl(fh->fd, CDROMPAUSE) == 0;
1199 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1200 return ioctl(fh->fd, CDIOCPAUSE) == 0;
1201 #else
1202 return false;
1203 #endif
1204 } else
1205 return false;
1206 }
1207
1208
1209 /*
1210 * Resume paused CD audio
1211 */
1212
1213 bool SysCDResume(void *arg)
1214 {
1215 file_handle *fh = (file_handle *)arg;
1216 if (!fh)
1217 return false;
1218
1219 if (fh->is_cdrom) {
1220 #if defined(__linux__)
1221 return ioctl(fh->fd, CDROMRESUME) == 0;
1222 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1223 return ioctl(fh->fd, CDIOCRESUME) == 0;
1224 #else
1225 return false;
1226 #endif
1227 } else
1228 return false;
1229 }
1230
1231
1232 /*
1233 * Stop CD audio
1234 */
1235
1236 bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f)
1237 {
1238 file_handle *fh = (file_handle *)arg;
1239 if (!fh)
1240 return false;
1241
1242 if (fh->is_cdrom) {
1243 #if defined(__linux__)
1244 return ioctl(fh->fd, CDROMSTOP) == 0;
1245 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1246 return ioctl(fh->fd, CDIOCSTOP) == 0;
1247 #else
1248 return false;
1249 #endif
1250 } else
1251 return false;
1252 }
1253
1254
1255 /*
1256 * Perform CD audio fast-forward/fast-reverse operation starting from specified address
1257 */
1258
1259 bool SysCDScan(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, bool reverse)
1260 {
1261 file_handle *fh = (file_handle *)arg;
1262 if (!fh)
1263 return false;
1264
1265 // Not supported under Linux
1266 return false;
1267 }
1268
1269
1270 /*
1271 * Set CD audio volume (0..255 each channel)
1272 */
1273
1274 void SysCDSetVolume(void *arg, uint8 left, uint8 right)
1275 {
1276 file_handle *fh = (file_handle *)arg;
1277 if (!fh)
1278 return;
1279
1280 if (fh->is_cdrom) {
1281 #if defined(__linux__)
1282 cdrom_volctrl vol;
1283 vol.channel0 = vol.channel2 = left;
1284 vol.channel1 = vol.channel3 = right;
1285 ioctl(fh->fd, CDROMVOLCTRL, &vol);
1286 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1287 struct ioc_vol vol;
1288 vol.vol[0] = vol.vol[2] = left;
1289 vol.vol[1] = vol.vol[3] = right;
1290 ioctl(fh->fd, CDIOCSETVOL, &vol);
1291 #endif
1292 }
1293 }
1294
1295
1296 /*
1297 * Get CD audio volume (0..255 each channel)
1298 */
1299
1300 void SysCDGetVolume(void *arg, uint8 &left, uint8 &right)
1301 {
1302 file_handle *fh = (file_handle *)arg;
1303 if (!fh)
1304 return;
1305
1306 left = right = 0;
1307 if (fh->is_cdrom) {
1308 #if defined(__linux__)
1309 cdrom_volctrl vol;
1310 ioctl(fh->fd, CDROMVOLREAD, &vol);
1311 left = vol.channel0;
1312 right = vol.channel1;
1313 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1314 struct ioc_vol vol;
1315 ioctl(fh->fd, CDIOCGETVOL, &vol);
1316 left = vol.vol[0];
1317 right = vol.vol[1];
1318 #endif
1319 }
1320 }