ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sys_unix.cpp
Revision: 1.33
Committed: 2010-10-19T03:21:52Z (13 years, 10 months ago) by asvitkine
Branch: MAIN
Changes since 1.32: +100 -69 lines
Log Message:
[Geoffrey Brown]
For my work on digital preservation it's important to have "golden"
disk images that are not corrupted by user action.  In order to enable
this, I've added support for VHD virtual disks (especially snapshots !)
to the Linux and OS X versions of BasiliskII and SheepShaver.

The support uses the open source libvhd library which is part of xen,
available here:
  http://www.xen.org/products/xen_source.html

The piece that's needed is libvhd which is in tools/blktap2 and it can
be separately compiled.
The vhd-util enables creation of vhd disks and snapshots.

Compiling libvhd for OS X is non-trivial and required  1) a new config
and 2) a number of small changes to the include files and c files.
Compiling for linux is a snap.

I use this as follows.

1) create my "golden image"  gold.dsk in the usual way
2) create a snapshot:  vhd-util snapshot -n gold.vhd -p gold.dsk -m
3) use the snapshot in my prefs file

In my work the golden images are in an AFS system which means the golden
images can reside at "universal" addresses.   The snapshots are initially
tiny, so a complete virtual machine configuration -- prefs + snapshot is
quick to download for the end user.

The snapshots are copy on write which has the pleasant side effect of
letting the end user keep any changes.

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * sys_unix.cpp - System dependent routines, Unix implementation
3     *
4 cebix 1.31 * Basilisk II (C) Christian Bauer
5 cebix 1.1 *
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 gbeauche 1.23 #ifdef HAVE_AVAILABILITYMACROS_H
28     #include <AvailabilityMacros.h>
29     #endif
30    
31 cebix 1.1 #ifdef __linux__
32 cebix 1.6 #include <sys/mount.h>
33 cebix 1.1 #include <linux/cdrom.h>
34     #include <linux/fd.h>
35     #include <linux/major.h>
36     #include <linux/kdev_t.h>
37 cebix 1.16 #include <dirent.h>
38 cebix 1.31 #include <limits.h>
39 cebix 1.1 #endif
40    
41 cebix 1.3 #if defined(__FreeBSD__) || defined(__NetBSD__)
42 cebix 1.1 #include <sys/cdio.h>
43     #endif
44    
45 gbeauche 1.29 #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 cebix 1.1 #include "main.h"
55     #include "macos_util.h"
56     #include "prefs.h"
57     #include "user_strings.h"
58     #include "sys.h"
59    
60 asvitkine 1.32 #if defined(BINCUE)
61     #include "bincue_unix.h"
62     #endif
63    
64 asvitkine 1.33 #if defined(HAVE_LIBVHD)
65     #include "vhd_unix.h"
66     #endif
67    
68    
69 cebix 1.1 #define DEBUG 0
70     #include "debug.h"
71    
72     // File handles are pointers to these structures
73     struct file_handle {
74 asvitkine 1.32 char *name; // Copy of device/file name
75 cebix 1.1 int fd;
76 asvitkine 1.33
77 cebix 1.1 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 asvitkine 1.33
82     loff_t start_byte; // Size of file header (if any)
83 cebix 1.1 loff_t file_size; // Size of file data (only valid if is_file is true)
84 asvitkine 1.33
85 gbeauche 1.29 bool is_media_present; // Flag: media is inserted and available
86 cebix 1.1
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 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
92 asvitkine 1.32 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 asvitkine 1.33 bool is_bincue; // Flag: BIN CUE file
98 asvitkine 1.32 void *bincue_fd;
99 cebix 1.1 #endif
100 asvitkine 1.33
101     #if defined(HAVE_LIBVHD)
102     bool is_vhd; // Flag: VHD file
103     void *vhd_fd;
104     #endif
105 cebix 1.1 };
106    
107 gbeauche 1.29 // Open file handles
108     struct open_file_handle {
109     file_handle *fh;
110     open_file_handle *next;
111     };
112     static open_file_handle *open_file_handles = NULL;
113    
114 cebix 1.1 // File handle of first floppy drive (for SysMountFirstFloppy())
115     static file_handle *first_floppy = NULL;
116    
117 gbeauche 1.29 // Prototypes
118     static void cdrom_close(file_handle *fh);
119     static bool cdrom_open(file_handle *fh, const char *path = NULL);
120    
121 cebix 1.1
122     /*
123     * Initialization
124     */
125    
126     void SysInit(void)
127     {
128 gbeauche 1.29 #if defined __MACOSX__
129     extern void DarwinSysInit(void);
130     DarwinSysInit();
131     #endif
132 cebix 1.1 }
133    
134    
135     /*
136     * Deinitialization
137     */
138    
139     void SysExit(void)
140     {
141 gbeauche 1.29 #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_file_handle(file_handle *fh)
153     {
154     open_file_handle *p = new open_file_handle;
155     p->fh = fh;
156     p->next = open_file_handles;
157     open_file_handles = p;
158     }
159    
160     static void sys_remove_file_handle(file_handle *fh)
161     {
162     open_file_handle *p = open_file_handles;
163     open_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_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_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_file_handle *p = open_file_handles; p != NULL; p = p->next) {
208     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_file_handle *p = open_file_handles; p != NULL; p = p->next) {
232     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 cebix 1.1 }
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 cebix 1.27 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 cebix 1.16 }
279     }
280 cebix 1.27 closedir(fd_dir);
281     } else {
282     PrefsAddString("floppy", "/dev/fd0");
283     PrefsAddString("floppy", "/dev/fd1");
284 cebix 1.16 }
285 cebix 1.3 #elif defined(__NetBSD__)
286     PrefsAddString("floppy", "/dev/fd0a");
287     PrefsAddString("floppy", "/dev/fd1a");
288 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
289 gbeauche 1.25 #if defined(AQUA) || defined(HAVE_FRAMEWORK_COREFOUNDATION)
290 nigel 1.22 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 cebix 1.1 #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 nigel 1.19 * 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 cebix 1.1 */
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 cebix 1.10 if (len == 0 || line[0] == '#')
323 cebix 1.1 continue;
324     line[len-1] = 0;
325    
326     // Parse line
327     char *dev, *mnt_point, *fstype;
328 cebix 1.3 if (sscanf(line, "%as %as %as", &dev, &mnt_point, &fstype) == 3) {
329 cebix 1.1 if (strcmp(fstype, "hfs") == 0)
330     PrefsAddString("disk", dev);
331     }
332 cebix 1.3 free(dev); free(mnt_point); free(fstype);
333 cebix 1.1 }
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 gbeauche 1.17 if (access("/dev/.devfsd", F_OK) < 0)
353 cebix 1.16 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 gbeauche 1.17 char cd_dev[20];
361     sprintf(cd_dev, "/dev/cdroms/%s", cdrom_dev->d_name);
362 cebix 1.16 PrefsAddString("cdrom", cd_dev);
363     }
364     }
365     closedir(cd_dir);
366     }
367     }
368 gbeauche 1.29 #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 cebix 1.18 #elif defined(__FreeBSD__) || defined(__NetBSD__)
375 cebix 1.1 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 gbeauche 1.17 if (access("/dev/.devfsd", F_OK) < 0) {
388 cebix 1.16 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 cebix 1.1 #elif defined(__FreeBSD__)
395     PrefsAddString("seriala", "/dev/cuaa0");
396     PrefsAddString("serialb", "/dev/cuaa1");
397 cebix 1.3 #elif defined(__NetBSD__)
398     PrefsAddString("seriala", "/dev/tty00");
399     PrefsAddString("serialb", "/dev/tty01");
400 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
401 gbeauche 1.25 #if defined(AQUA) || defined(HAVE_FRAMEWORK_COREFOUNDATION)
402 nigel 1.22 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 cebix 1.1 #endif
412     }
413    
414    
415     /*
416 gbeauche 1.29 * Open CD-ROM device and initialize internal data
417     */
418    
419     static bool cdrom_open_1(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(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(file_handle *fh)
461     {
462 asvitkine 1.32
463 gbeauche 1.29 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 cebix 1.1 * 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 cebix 1.3 char *dummy;
505     sscanf(line, "%as %s", &dummy, mount_name);
506     free(dummy);
507 cebix 1.1 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 asvitkine 1.33
522     static file_handle *open_filehandle(const char *name)
523     {
524     file_handle *fh = new file_handle;
525     memset(fh, 0, sizeof(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 cebix 1.1
535     void *Sys_open(const char *name, bool read_only)
536     {
537     bool is_file = strncmp(name, "/dev/", 5) != 0;
538 cebix 1.3 #if defined(__FreeBSD__)
539 cebix 1.1 // 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 cebix 1.28 bool is_floppy = strncmp(name, "/dev/fd", 7) == 0;
545 cebix 1.1
546 gbeauche 1.29 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 nigel 1.19 // 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 gbeauche 1.29 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 nigel 1.19 }
563     }
564     #endif
565    
566 cebix 1.1 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 asvitkine 1.32
588     #if defined(BINCUE)
589     void *binfd = open_bincue(name);
590     if (binfd) {
591 asvitkine 1.33 file_handle *fh = open_filehandle(name);
592     D(bug("opening %s as bincue\n", name));
593 asvitkine 1.32 fh->bincue_fd = binfd;
594     fh->is_bincue = true;
595 asvitkine 1.33 fh->read_only = true;
596 asvitkine 1.32 fh->is_media_present = true;
597 asvitkine 1.33 sys_add_file_handle(fh);
598     return fh;
599     }
600 asvitkine 1.32 #endif
601 asvitkine 1.33
602    
603     #if defined(HAVE_LIBVHD)
604     int vhdsize;
605     void *vhdfd = vhd_unix_open(name, &vhdsize, read_only);
606     if (vhdfd) {
607     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_file_handle(fh);
615     return fh;
616     }
617 asvitkine 1.32 #endif
618    
619 gbeauche 1.29 #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACOSX__)
620 cebix 1.1 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 gbeauche 1.29 if (fd >= 0 || is_polled_media) {
630 asvitkine 1.33 file_handle *fh = open_filehandle(name);
631 cebix 1.1 fh->fd = fd;
632     fh->is_file = is_file;
633     fh->read_only = read_only;
634 cebix 1.28 fh->is_floppy = is_floppy;
635     fh->is_cdrom = is_cdrom;
636 cebix 1.1 if (fh->is_file) {
637 gbeauche 1.29 fh->is_media_present = true;
638 cebix 1.1 // 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 gbeauche 1.29 fh->is_media_present = true;
649 cebix 1.1 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 cebix 1.4 #elif defined(__FreeBSD__)
661 cebix 1.1 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 cebix 1.4 #elif defined(__NetBSD__)
669     fh->is_floppy = ((st.st_rdev >> 16) == 2);
670 cebix 1.1 #endif
671     }
672 gbeauche 1.29 #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 nigel 1.19 }
679     #endif
680 cebix 1.1 }
681     }
682     if (fh->is_floppy && first_floppy == NULL)
683     first_floppy = fh;
684 gbeauche 1.29 sys_add_file_handle(fh);
685 cebix 1.1 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     file_handle *fh = (file_handle *)arg;
700     if (!fh)
701     return;
702    
703 gbeauche 1.29 sys_remove_file_handle(fh);
704    
705 asvitkine 1.33 #if defined(HAVE_LIBVHD)
706     if (fh->is_vhd)
707     vhd_unix_close(fh->vhd_fd);
708     #endif
709    
710 asvitkine 1.32 #if defined(BINCUE)
711 asvitkine 1.33 if (fh->is_bincue)
712     close_bincue(fh->bincue_fd);
713 asvitkine 1.32 #endif
714    
715 gbeauche 1.29 if (fh->is_cdrom)
716     cdrom_close(fh);
717 cebix 1.28 if (fh->fd >= 0)
718     close(fh->fd);
719 cebix 1.1 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     file_handle *fh = (file_handle *)arg;
733     if (!fh)
734     return 0;
735    
736 asvitkine 1.32 #if defined(BINCUE)
737 asvitkine 1.33 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 asvitkine 1.32 #endif
745    
746 cebix 1.1 // 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     file_handle *fh = (file_handle *)arg;
763     if (!fh)
764     return 0;
765    
766 asvitkine 1.33 #if defined(HAVE_LIBVHD)
767     if (fh->is_vhd)
768     return vhd_unix_write(fh->vhd_fd, buffer, offset, length);
769     #endif
770    
771 cebix 1.1 // 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     file_handle *fh = (file_handle *)arg;
787     if (!fh)
788     return true;
789    
790 asvitkine 1.33 #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 cebix 1.1 if (fh->is_file)
801     return fh->file_size;
802     else {
803 asvitkine 1.32 long blocks;
804 cebix 1.3 #if defined(__linux__)
805 cebix 1.5 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 gbeauche 1.29 #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 cebix 1.1 #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     file_handle *fh = (file_handle *)arg;
833     if (!fh)
834     return;
835    
836     #if defined(__linux__)
837     if (fh->is_floppy) {
838 cebix 1.28 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 cebix 1.14 fh->fd = open(fh->name, fh->read_only ? O_RDONLY : O_RDWR);
845 cebix 1.1 } 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 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
851 cebix 1.1 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 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
859 gbeauche 1.29 if (fh->is_cdrom && fh->is_media_present) {
860 nigel 1.19 close(fh->fd);
861 gbeauche 1.29 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 nigel 1.19
866 gbeauche 1.29 // 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 nigel 1.19
871 gbeauche 1.29 // 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 nigel 1.19 system(cmd);
877     }
878 gbeauche 1.29 fh->is_media_present = false;
879 nigel 1.19 }
880 cebix 1.1 #endif
881     }
882    
883    
884     /*
885     * Format volume (if applicable)
886     */
887    
888     bool SysFormat(void *arg)
889     {
890     file_handle *fh = (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     file_handle *fh = (file_handle *)arg;
906     if (!fh)
907     return true;
908    
909 cebix 1.3 #if defined(__linux__)
910 cebix 1.1 if (fh->is_floppy) {
911 cebix 1.28 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 cebix 1.1 } 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     file_handle *fh = (file_handle *)arg;
930     if (!fh)
931     return true;
932    
933 asvitkine 1.33 #if defined(HAVE_LIBVHD)
934     if (fh->is_vhd)
935     return true;
936     #endif
937    
938 cebix 1.1 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     file_handle *fh = (file_handle *)arg;
954     if (!fh)
955     return false;
956    
957 asvitkine 1.33 #if defined(HAVE_LIBVHD)
958     if (fh->is_vhd)
959     return true;
960     #endif
961    
962 cebix 1.1 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 cebix 1.14 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 cebix 1.1 } else if (fh->is_cdrom) {
977 cebix 1.8 #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 cebix 1.14 // once the tray has been opened (altough the TOC reads fine).
981 cebix 1.8 // 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 cebix 1.1 #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 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
996 cebix 1.1 } 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 gbeauche 1.29 #elif defined __MACOSX__
1002     } else if (fh->is_cdrom || fh->is_floppy) {
1003     return fh->is_media_present;
1004 cebix 1.1 #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     file_handle *fh = (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     file_handle *fh = (file_handle *)arg;
1035     if (!fh)
1036     return;
1037    
1038 cebix 1.2 #if defined(__linux__) && defined(CDROM_LOCKDOOR)
1039 cebix 1.1 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     file_handle *fh = (file_handle *)arg;
1052     if (!fh)
1053     return false;
1054    
1055 asvitkine 1.32 #if defined(BINCUE)
1056 asvitkine 1.33 if (fh->is_bincue)
1057 asvitkine 1.32 return readtoc_bincue(fh->bincue_fd, toc);
1058     #endif
1059    
1060 cebix 1.1 if (fh->is_cdrom) {
1061 asvitkine 1.32
1062 cebix 1.1 #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 gbeauche 1.29 #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 cebix 1.4 #elif defined(__FreeBSD__)
1115 cebix 1.1 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 cebix 1.4
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 cebix 1.1
1179     // TOC size
1180     int toc_size = p - toc;
1181     *toc++ = toc_size >> 8;
1182     *toc++ = toc_size & 0xff;
1183     return true;
1184 cebix 1.15 #else
1185     return false;
1186 cebix 1.1 #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     file_handle *fh = (file_handle *)arg;
1199     if (!fh)
1200     return false;
1201    
1202 asvitkine 1.32 #if defined(BINCUE)
1203 asvitkine 1.33 if (fh->is_bincue)
1204 asvitkine 1.32 return GetPosition_bincue(fh->bincue_fd, pos);
1205     #endif
1206    
1207 cebix 1.1 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 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1231 cebix 1.1 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 cebix 1.15 #else
1255     return false;
1256 cebix 1.1 #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     file_handle *fh = (file_handle *)arg;
1269     if (!fh)
1270     return false;
1271    
1272 asvitkine 1.32 #if defined(BINCUE)
1273 asvitkine 1.33 if (fh->is_bincue)
1274     return CDPlay_bincue(fh->bincue_fd, start_m, start_s, start_f, end_m, end_s, end_f);
1275 asvitkine 1.32 #endif
1276    
1277 cebix 1.1 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 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1288 cebix 1.1 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 cebix 1.15 #else
1297     return false;
1298 cebix 1.1 #endif
1299     } else
1300     return false;
1301     }
1302    
1303    
1304     /*
1305     * Pause CD audio
1306     */
1307    
1308     bool SysCDPause(void *arg)
1309     {
1310     file_handle *fh = (file_handle *)arg;
1311     if (!fh)
1312     return false;
1313    
1314 asvitkine 1.32 #if defined(BINCUE)
1315 asvitkine 1.33 if (fh->is_bincue)
1316     return CDPause_bincue(fh->bincue_fd);
1317 asvitkine 1.32 #endif
1318    
1319 cebix 1.1 if (fh->is_cdrom) {
1320     #if defined(__linux__)
1321     return ioctl(fh->fd, CDROMPAUSE) == 0;
1322 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1323 cebix 1.1 return ioctl(fh->fd, CDIOCPAUSE) == 0;
1324 cebix 1.15 #else
1325     return false;
1326 cebix 1.1 #endif
1327     } else
1328     return false;
1329     }
1330    
1331    
1332     /*
1333     * Resume paused CD audio
1334     */
1335    
1336     bool SysCDResume(void *arg)
1337     {
1338     file_handle *fh = (file_handle *)arg;
1339     if (!fh)
1340     return false;
1341    
1342 asvitkine 1.32 #if defined(BINCUE)
1343 asvitkine 1.33 if (fh->is_bincue)
1344     return CDResume_bincue(fh->bincue_fd);
1345 asvitkine 1.32 #endif
1346    
1347    
1348 cebix 1.1 if (fh->is_cdrom) {
1349     #if defined(__linux__)
1350     return ioctl(fh->fd, CDROMRESUME) == 0;
1351 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1352 cebix 1.1 return ioctl(fh->fd, CDIOCRESUME) == 0;
1353 cebix 1.15 #else
1354     return false;
1355 cebix 1.1 #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     file_handle *fh = (file_handle *)arg;
1368     if (!fh)
1369     return false;
1370    
1371 asvitkine 1.32 #if defined(BINCUE)
1372 asvitkine 1.33 if (fh->is_bincue)
1373     return CDStop_bincue(fh->bincue_fd);
1374 asvitkine 1.32 #endif
1375    
1376    
1377 cebix 1.1 if (fh->is_cdrom) {
1378     #if defined(__linux__)
1379     return ioctl(fh->fd, CDROMSTOP) == 0;
1380 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1381 cebix 1.1 return ioctl(fh->fd, CDIOCSTOP) == 0;
1382 cebix 1.15 #else
1383     return false;
1384 cebix 1.1 #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     file_handle *fh = (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     file_handle *fh = (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 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1422 cebix 1.1 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     file_handle *fh = (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 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1449 cebix 1.1 struct ioc_vol vol;
1450     ioctl(fh->fd, CDIOCGETVOL, &vol);
1451     left = vol.vol[0];
1452     right = vol.vol[1];
1453     #endif
1454     }
1455     }