ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sys_unix.cpp
Revision: 1.35
Committed: 2012-03-01T04:24:45Z (12 years, 2 months ago) by asvitkine
Branch: MAIN
CVS Tags: HEAD
Changes since 1.34: +1 -1 lines
Log Message:
fix a warning

File Contents

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