ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sys_unix.cpp
Revision: 1.19
Committed: 2003-03-21T07:03:05Z (21 years, 7 months ago) by nigel
Branch: MAIN
CVS Tags: nigel-build-12, nigel-build-13
Changes since 1.18: +99 -1 lines
Log Message:
Darwin-specific prefs and initial CD code. Since some of the CD code is just too weird to include here, I just call extern functions which I currently implement in a file sys_darwin.cpp

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * sys_unix.cpp - System dependent routines, Unix implementation
3     *
4 nigel 1.19 * Basilisk II (C) 1997-2003 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     #ifdef __linux__
28 cebix 1.6 #include <sys/mount.h>
29 cebix 1.1 #include <linux/cdrom.h>
30     #include <linux/fd.h>
31     #include <linux/major.h>
32     #include <linux/kdev_t.h>
33     #include <linux/unistd.h>
34 cebix 1.16 #include <dirent.h>
35 cebix 1.1
36     #ifdef __NR__llseek
37 cebix 1.12 _syscall5(int, _llseek, unsigned int, fd, unsigned long, hi, unsigned long, lo, loff_t *, res, unsigned int, wh);
38 cebix 1.1 #else
39 cebix 1.12 static int _llseek(unsigned int fd, unsigned long hi, unsigned long lo, loff_t *res, unsigned int wh)
40 cebix 1.1 {
41     if (hi)
42     return -1;
43     *res = lseek(fd, lo, wh);
44     if (*res == -1)
45     return -1;
46     return 0;
47     }
48     #endif
49     #endif
50    
51 cebix 1.3 #if defined(__FreeBSD__) || defined(__NetBSD__)
52 cebix 1.1 #include <sys/cdio.h>
53     #endif
54    
55     #include "main.h"
56     #include "macos_util.h"
57     #include "prefs.h"
58     #include "user_strings.h"
59     #include "sys.h"
60    
61     #define DEBUG 0
62     #include "debug.h"
63    
64    
65     // File handles are pointers to these structures
66     struct file_handle {
67     char *name; // Copy of device/file name
68     int fd;
69     bool is_file; // Flag: plain file or /dev/something?
70     bool is_floppy; // Flag: floppy device
71     bool is_cdrom; // Flag: CD-ROM device
72     bool read_only; // Copy of Sys_open() flag
73     loff_t start_byte; // Size of file header (if any)
74     loff_t file_size; // Size of file data (only valid if is_file is true)
75    
76     #if defined(__linux__)
77     int cdrom_cap; // CD-ROM capability flags (only valid if is_cdrom is true)
78     #elif defined(__FreeBSD__)
79     struct ioc_capability cdrom_cap;
80 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
81     char *ioctl_name; // For CDs on OS X - a device for special ioctls
82     int ioctl_fd;
83 cebix 1.1 #endif
84     };
85    
86     // File handle of first floppy drive (for SysMountFirstFloppy())
87     static file_handle *first_floppy = NULL;
88    
89    
90     /*
91     * Initialization
92     */
93    
94     void SysInit(void)
95     {
96     }
97    
98    
99     /*
100     * Deinitialization
101     */
102    
103     void SysExit(void)
104     {
105     }
106    
107    
108     /*
109     * Mount first floppy disk
110     */
111    
112     void SysMountFirstFloppy(void)
113     {
114     if (first_floppy)
115     MountVolume(first_floppy);
116     }
117    
118    
119     /*
120     * This gets called when no "floppy" prefs items are found
121     * It scans for available floppy drives and adds appropriate prefs items
122     */
123    
124     void SysAddFloppyPrefs(void)
125     {
126     #if defined(__linux__)
127 gbeauche 1.17 if (access("/dev/.devfsd", F_OK) < 0) {
128 cebix 1.16 PrefsAddString("floppy", "/dev/fd0u1440");
129     PrefsAddString("floppy", "/dev/fd1u1440");
130     } else {
131     DIR *fd_dir = opendir("/dev/floppy");
132     if (fd_dir) {
133     struct dirent *floppy_dev;
134     while ((floppy_dev = readdir(fd_dir)) != NULL) {
135     if (strstr(floppy_dev->d_name, "u1440") != NULL) {
136 gbeauche 1.17 char fd_dev[20];
137 cebix 1.16 sprintf(fd_dev, "/dev/floppy/%s", floppy_dev->d_name);
138     PrefsAddString("floppy", fd_dev);
139     }
140     }
141     closedir(fd_dir);
142     }
143     }
144 cebix 1.3 #elif defined(__NetBSD__)
145     PrefsAddString("floppy", "/dev/fd0a");
146     PrefsAddString("floppy", "/dev/fd1a");
147 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
148     PrefsAddString("floppy", "/dev/fd/0");
149     PrefsAddString("floppy", "/dev/fd/1");
150 cebix 1.1 #else
151     PrefsAddString("floppy", "/dev/fd0");
152     PrefsAddString("floppy", "/dev/fd1");
153     #endif
154     }
155    
156    
157     /*
158     * This gets called when no "disk" prefs items are found
159     * It scans for available HFS volumes and adds appropriate prefs items
160 nigel 1.19 * On OS X, we could do the same, but on an OS X machine I think it is
161     * very unlikely that any mounted volumes would contain a system which
162     * is old enough to boot a 68k Mac, so we just do nothing here for now.
163 cebix 1.1 */
164    
165     void SysAddDiskPrefs(void)
166     {
167     #ifdef __linux__
168     FILE *f = fopen("/etc/fstab", "r");
169     if (f) {
170     char line[256];
171     while(fgets(line, 255, f)) {
172     // Read line
173     int len = strlen(line);
174 cebix 1.10 if (len == 0 || line[0] == '#')
175 cebix 1.1 continue;
176     line[len-1] = 0;
177    
178     // Parse line
179     char *dev, *mnt_point, *fstype;
180 cebix 1.3 if (sscanf(line, "%as %as %as", &dev, &mnt_point, &fstype) == 3) {
181 cebix 1.1 if (strcmp(fstype, "hfs") == 0)
182     PrefsAddString("disk", dev);
183     }
184 cebix 1.3 free(dev); free(mnt_point); free(fstype);
185 cebix 1.1 }
186     fclose(f);
187     }
188     #endif
189     }
190    
191    
192     /*
193     * This gets called when no "cdrom" prefs items are found
194     * It scans for available CD-ROM drives and adds appropriate prefs items
195     */
196    
197     void SysAddCDROMPrefs(void)
198     {
199     // Don't scan for drives if nocdrom option given
200     if (PrefsFindBool("nocdrom"))
201     return;
202    
203     #if defined(__linux__)
204 gbeauche 1.17 if (access("/dev/.devfsd", F_OK) < 0)
205 cebix 1.16 PrefsAddString("cdrom", "/dev/cdrom");
206     else {
207     DIR *cd_dir = opendir("/dev/cdroms");
208     if (cd_dir) {
209     struct dirent *cdrom_dev;
210     while ((cdrom_dev = readdir(cd_dir)) != NULL) {
211     if (strcmp(cdrom_dev->d_name, ".") != 0 && strcmp(cdrom_dev->d_name, "..") != 0) {
212 gbeauche 1.17 char cd_dev[20];
213     sprintf(cd_dev, "/dev/cdroms/%s", cdrom_dev->d_name);
214 cebix 1.16 PrefsAddString("cdrom", cd_dev);
215     }
216     }
217     closedir(cd_dir);
218     }
219     }
220 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
221     extern void DarwinAddCDROMPrefs(void);
222    
223     DarwinAddCDROMPrefs();
224 cebix 1.18 #elif defined(__FreeBSD__) || defined(__NetBSD__)
225 cebix 1.1 PrefsAddString("cdrom", "/dev/cd0c");
226     #endif
227     }
228    
229    
230     /*
231     * Add default serial prefs (must be added, even if no ports present)
232     */
233    
234     void SysAddSerialPrefs(void)
235     {
236     #if defined(__linux__)
237 gbeauche 1.17 if (access("/dev/.devfsd", F_OK) < 0) {
238 cebix 1.16 PrefsAddString("seriala", "/dev/ttyS0");
239     PrefsAddString("serialb", "/dev/ttyS1");
240     } else {
241     PrefsAddString("seriala", "/dev/tts/0");
242     PrefsAddString("serialb", "/dev/tts/1");
243     }
244 cebix 1.1 #elif defined(__FreeBSD__)
245     PrefsAddString("seriala", "/dev/cuaa0");
246     PrefsAddString("serialb", "/dev/cuaa1");
247 cebix 1.3 #elif defined(__NetBSD__)
248     PrefsAddString("seriala", "/dev/tty00");
249     PrefsAddString("serialb", "/dev/tty01");
250 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
251     PrefsAddString("seriala", "/dev/ttys0");
252     PrefsAddString("serialb", "/dev/ttys1");
253     // PrefsAddString("seriala", "/dev/cu.modem");
254     // PrefsAddString("serialb", "/dev/cu.IrDA-IrCOMMch-b");
255 cebix 1.1 #endif
256     }
257    
258    
259     /*
260     * Check if device is a mounted HFS volume, get mount name
261     */
262    
263     static bool is_drive_mounted(const char *dev_name, char *mount_name)
264     {
265     #ifdef __linux__
266     FILE *f = fopen("/proc/mounts", "r");
267     if (f) {
268     char line[256];
269     while(fgets(line, 255, f)) {
270     // Read line
271     int len = strlen(line);
272     if (len == 0)
273     continue;
274     line[len-1] = 0;
275    
276     // Parse line
277     if (strncmp(line, dev_name, strlen(dev_name)) == 0) {
278     mount_name[0] = 0;
279 cebix 1.3 char *dummy;
280     sscanf(line, "%as %s", &dummy, mount_name);
281     free(dummy);
282 cebix 1.1 fclose(f);
283     return true;
284     }
285     }
286     fclose(f);
287     }
288     #endif
289     return false;
290     }
291    
292    
293     /*
294     * Open file/device, create new file handle (returns NULL on error)
295     */
296    
297     void *Sys_open(const char *name, bool read_only)
298     {
299     bool is_file = strncmp(name, "/dev/", 5) != 0;
300 cebix 1.3 #if defined(__FreeBSD__)
301 cebix 1.1 // SCSI IDE
302     bool is_cdrom = strncmp(name, "/dev/cd", 7) == 0 || strncmp(name, "/dev/acd", 8) == 0;
303     #else
304     bool is_cdrom = strncmp(name, "/dev/cd", 7) == 0;
305     #endif
306    
307 nigel 1.19 #if defined(__APPLE__) && defined(__MACH__)
308     //
309     // There is no set filename in /dev which is the cdrom,
310     // so we have to see if it is any of the devices that we found earlier
311     //
312     const char *cdrom;
313     int tmp = 0;
314    
315     while ( (cdrom = PrefsFindString("cdrom", tmp) ) != NULL )
316     {
317     if ( strcmp(name, cdrom) == 0 )
318     {
319     is_cdrom = 1;
320     read_only = 1;
321     break;
322     }
323     ++tmp;
324     }
325     #endif
326    
327 cebix 1.1 D(bug("Sys_open(%s, %s)\n", name, read_only ? "read-only" : "read/write"));
328    
329     // Check if write access is allowed, set read-only flag if not
330     if (!read_only && access(name, W_OK))
331     read_only = true;
332    
333     // Print warning message and eventually unmount drive when this is an HFS volume mounted under Linux (double mounting will corrupt the volume)
334     char mount_name[256];
335     if (!is_file && !read_only && is_drive_mounted(name, mount_name)) {
336     char str[512];
337     sprintf(str, GetString(STR_VOLUME_IS_MOUNTED_WARN), mount_name);
338     WarningAlert(str);
339     sprintf(str, "umount %s", mount_name);
340     if (system(str)) {
341     sprintf(str, GetString(STR_CANNOT_UNMOUNT_WARN), mount_name, strerror(errno));
342     WarningAlert(str);
343     return NULL;
344     }
345     }
346    
347     // Open file/device
348 cebix 1.8 #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
349 cebix 1.1 int fd = open(name, (read_only ? O_RDONLY : O_RDWR) | (is_cdrom ? O_NONBLOCK : 0));
350     #else
351     int fd = open(name, read_only ? O_RDONLY : O_RDWR);
352     #endif
353     if (fd < 0 && !read_only) {
354     // Read-write failed, try read-only
355     read_only = true;
356     fd = open(name, O_RDONLY);
357     }
358     if (fd >= 0) {
359     file_handle *fh = new file_handle;
360     fh->name = strdup(name);
361     fh->fd = fd;
362     fh->is_file = is_file;
363     fh->read_only = read_only;
364     fh->start_byte = 0;
365     fh->is_floppy = false;
366     fh->is_cdrom = false;
367     if (fh->is_file) {
368     // Detect disk image file layout
369     loff_t size = 0;
370 cebix 1.3 #if defined(__linux__)
371 cebix 1.1 _llseek(fh->fd, 0, 0, &size, SEEK_END);
372     #else
373     size = lseek(fd, 0, SEEK_END);
374     #endif
375     uint8 data[256];
376     lseek(fd, 0, SEEK_SET);
377     read(fd, data, 256);
378     FileDiskLayout(size, data, fh->start_byte, fh->file_size);
379     } else {
380     struct stat st;
381     if (fstat(fd, &st) == 0) {
382     if (S_ISBLK(st.st_mode)) {
383     fh->is_cdrom = is_cdrom;
384     #if defined(__linux__)
385     fh->is_floppy = (MAJOR(st.st_rdev) == FLOPPY_MAJOR);
386     #ifdef CDROM_GET_CAPABILITY
387     if (is_cdrom) {
388     fh->cdrom_cap = ioctl(fh->fd, CDROM_GET_CAPABILITY);
389     if (fh->cdrom_cap < 0)
390     fh->cdrom_cap = 0;
391     }
392     #else
393     fh->cdrom_cap = 0;
394     #endif
395 cebix 1.4 #elif defined(__FreeBSD__)
396 cebix 1.1 fh->is_floppy = ((st.st_rdev >> 16) == 2);
397     #ifdef CDIOCCAPABILITY
398     if (is_cdrom) {
399     if (ioctl(fh->fd, CDIOCCAPABILITY, &fh->cdrom_cap) < 0)
400     memset(&fh->cdrom_cap, 0, sizeof(fh->cdrom_cap));
401     }
402     #else
403     fh->cdrom_cap = 0;
404     #endif
405 cebix 1.4 #elif defined(__NetBSD__)
406     fh->is_floppy = ((st.st_rdev >> 16) == 2);
407 cebix 1.1 #endif
408     }
409 nigel 1.19 #if defined(__APPLE__) && defined(__MACH__)
410    
411     // In OS X, the device name is OK for sending ioctls to,
412     // but not for reading raw CDROM data from.
413     // (it seems to have extra data padded in)
414     //
415     // So, we keep the already opened fiole handle,
416     // and open a slightly different file for CDROM data
417     //
418     if ( is_cdrom )
419     {
420     fh->ioctl_name = fh->name;
421     fh->ioctl_fd = fh->fd;
422    
423     fh->name = (char *) malloc(strlen(name) + 2);
424     if ( fh->name )
425     {
426     *fh->name = '\0';
427     strcat(fh->name, name);
428     strcat(fh->name, "s1");
429     fh->fd = open(fh->name, (read_only ? O_RDONLY : O_RDWR));
430     if ( fh->fd < 0 ) {
431     printf("WARNING: Cannot open %s (%s)\n",
432     fh->name, strerror(errno));
433     return NULL;
434     }
435     }
436     }
437     #endif
438 cebix 1.1 }
439     }
440     if (fh->is_floppy && first_floppy == NULL)
441     first_floppy = fh;
442     return fh;
443     } else {
444     printf("WARNING: Cannot open %s (%s)\n", name, strerror(errno));
445     return NULL;
446     }
447     }
448    
449    
450     /*
451     * Close file/device, delete file handle
452     */
453    
454     void Sys_close(void *arg)
455     {
456     file_handle *fh = (file_handle *)arg;
457     if (!fh)
458     return;
459    
460     close(fh->fd);
461     if (fh->name)
462     free(fh->name);
463     delete fh;
464     }
465    
466    
467     /*
468     * Read "length" bytes from file/device, starting at "offset", to "buffer",
469     * returns number of bytes read (or 0)
470     */
471    
472     size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length)
473     {
474     file_handle *fh = (file_handle *)arg;
475     if (!fh)
476     return 0;
477    
478     // Seek to position
479 cebix 1.3 #if defined(__linux__)
480 cebix 1.1 loff_t pos = offset + fh->start_byte, res;
481     if (_llseek(fh->fd, pos >> 32, pos, &res, SEEK_SET) < 0)
482     return 0;
483     #else
484     if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
485     return 0;
486     #endif
487    
488     // Read data
489     return read(fh->fd, buffer, length);
490     }
491    
492    
493     /*
494     * Write "length" bytes from "buffer" to file/device, starting at "offset",
495     * returns number of bytes written (or 0)
496     */
497    
498     size_t Sys_write(void *arg, void *buffer, loff_t offset, size_t length)
499     {
500     file_handle *fh = (file_handle *)arg;
501     if (!fh)
502     return 0;
503    
504     // Seek to position
505 cebix 1.3 #if defined(__linux__)
506 cebix 1.1 loff_t pos = offset + fh->start_byte, res;
507     if (_llseek(fh->fd, pos >> 32, pos, &res, SEEK_SET) < 0)
508     return 0;
509     #else
510     if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
511     return 0;
512     #endif
513    
514     // Write data
515     return write(fh->fd, buffer, length);
516     }
517    
518    
519     /*
520     * Return size of file/device (minus header)
521     */
522    
523     loff_t SysGetFileSize(void *arg)
524     {
525     file_handle *fh = (file_handle *)arg;
526     if (!fh)
527     return true;
528    
529     if (fh->is_file)
530     return fh->file_size;
531     else {
532 cebix 1.3 #if defined(__linux__)
533 cebix 1.5 long blocks;
534     if (ioctl(fh->fd, BLKGETSIZE, &blocks) < 0)
535     return 0;
536     D(bug(" BLKGETSIZE returns %d blocks\n", blocks));
537     return (loff_t)blocks * 512;
538 cebix 1.1 #else
539     return lseek(fh->fd, 0, SEEK_END) - fh->start_byte;
540     #endif
541     }
542     }
543    
544    
545     /*
546     * Eject volume (if applicable)
547     */
548    
549     void SysEject(void *arg)
550     {
551     file_handle *fh = (file_handle *)arg;
552     if (!fh)
553     return;
554    
555     #if defined(__linux__)
556     if (fh->is_floppy) {
557     fsync(fh->fd);
558     ioctl(fh->fd, FDFLUSH);
559     ioctl(fh->fd, FDEJECT);
560 cebix 1.14 close(fh->fd); // Close and reopen so the driver will see the media change
561     fh->fd = open(fh->name, fh->read_only ? O_RDONLY : O_RDWR);
562 cebix 1.1 } else if (fh->is_cdrom) {
563     ioctl(fh->fd, CDROMEJECT);
564     close(fh->fd); // Close and reopen so the driver will see the media change
565     fh->fd = open(fh->name, O_RDONLY | O_NONBLOCK);
566     }
567 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
568 cebix 1.1 if (fh->is_floppy) {
569     fsync(fh->fd);
570     } else if (fh->is_cdrom) {
571     ioctl(fh->fd, CDIOCEJECT);
572     close(fh->fd); // Close and reopen so the driver will see the media change
573     fh->fd = open(fh->name, O_RDONLY | O_NONBLOCK);
574     }
575 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__)
576     if ( fh->is_cdrom ) {
577    
578     // Stolen from IOKit/storage/IOMediaBSDClient.h
579     #define DKIOCEJECT _IO('d', 21)
580    
581     close(fh->fd);
582     if ( ioctl(fh->ioctl_fd, DKIOCEJECT) < 0 )
583     {
584     printf("ioctl(DKIOCEJECT) failed on file %s: %s\n",
585     fh->ioctl_name, strerror(errno));
586    
587     // If we are running OSX, the device may be is busy
588     // due to the Finder having the disk mounted and open,
589     // so we have to use another method.
590    
591     // The only problem is that this takes about 5 seconds:
592    
593     char *cmd = (char *) malloc(30+sizeof(fh->name));
594    
595     if ( ! cmd )
596     return;
597     close(fh->ioctl_fd);
598     sprintf(cmd, "diskutil eject %s 2>&1 >/dev/null", fh->name);
599     system(cmd);
600     }
601     }
602 cebix 1.1 #endif
603     }
604    
605    
606     /*
607     * Format volume (if applicable)
608     */
609    
610     bool SysFormat(void *arg)
611     {
612     file_handle *fh = (file_handle *)arg;
613     if (!fh)
614     return false;
615    
616     //!!
617     return true;
618     }
619    
620    
621     /*
622     * Check if file/device is read-only (this includes the read-only flag on Sys_open())
623     */
624    
625     bool SysIsReadOnly(void *arg)
626     {
627     file_handle *fh = (file_handle *)arg;
628     if (!fh)
629     return true;
630    
631 cebix 1.3 #if defined(__linux__)
632 cebix 1.1 if (fh->is_floppy) {
633     struct floppy_drive_struct stat;
634     ioctl(fh->fd, FDGETDRVSTAT, &stat);
635     return !(stat.flags & FD_DISK_WRITABLE);
636     } else
637     #endif
638     return fh->read_only;
639     }
640    
641    
642     /*
643     * Check if the given file handle refers to a fixed or a removable disk
644     */
645    
646     bool SysIsFixedDisk(void *arg)
647     {
648     file_handle *fh = (file_handle *)arg;
649     if (!fh)
650     return true;
651    
652     if (fh->is_file)
653     return true;
654     else if (fh->is_floppy || fh->is_cdrom)
655     return false;
656     else
657     return true;
658     }
659    
660    
661     /*
662     * Check if a disk is inserted in the drive (always true for files)
663     */
664    
665     bool SysIsDiskInserted(void *arg)
666     {
667     file_handle *fh = (file_handle *)arg;
668     if (!fh)
669     return false;
670    
671     if (fh->is_file) {
672     return true;
673    
674     #if defined(__linux__)
675     } else if (fh->is_floppy) {
676     char block[512];
677     lseek(fh->fd, 0, SEEK_SET);
678 cebix 1.14 ssize_t actual = read(fh->fd, block, 512);
679     if (actual < 0) {
680     close(fh->fd); // Close and reopen so the driver will see the media change
681     fh->fd = open(fh->name, fh->read_only ? O_RDONLY : O_RDWR);
682     actual = read(fh->fd, block, 512);
683     }
684     return actual == 512;
685 cebix 1.1 } else if (fh->is_cdrom) {
686 cebix 1.8 #ifdef CDROM_MEDIA_CHANGED
687     if (fh->cdrom_cap & CDC_MEDIA_CHANGED) {
688     // If we don't do this, all attempts to read from a disc fail
689 cebix 1.14 // once the tray has been opened (altough the TOC reads fine).
690 cebix 1.8 // Can somebody explain this to me?
691     if (ioctl(fh->fd, CDROM_MEDIA_CHANGED) == 1) {
692     close(fh->fd);
693     fh->fd = open(fh->name, O_RDONLY | O_NONBLOCK);
694     }
695     }
696     #endif
697 cebix 1.1 #ifdef CDROM_DRIVE_STATUS
698     if (fh->cdrom_cap & CDC_DRIVE_STATUS) {
699     return ioctl(fh->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) == CDS_DISC_OK;
700     }
701     #endif
702     cdrom_tochdr header;
703     return ioctl(fh->fd, CDROMREADTOCHDR, &header) == 0;
704 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
705 cebix 1.1 } else if (fh->is_floppy) {
706     return false; //!!
707     } else if (fh->is_cdrom) {
708     struct ioc_toc_header header;
709     return ioctl(fh->fd, CDIOREADTOCHEADER, &header) == 0;
710     #endif
711    
712     } else
713     return true;
714     }
715    
716    
717     /*
718     * Prevent medium removal (if applicable)
719     */
720    
721     void SysPreventRemoval(void *arg)
722     {
723     file_handle *fh = (file_handle *)arg;
724     if (!fh)
725     return;
726    
727     #if defined(__linux__) && defined(CDROM_LOCKDOOR)
728     if (fh->is_cdrom)
729     ioctl(fh->fd, CDROM_LOCKDOOR, 1);
730     #endif
731     }
732    
733    
734     /*
735     * Allow medium removal (if applicable)
736     */
737    
738     void SysAllowRemoval(void *arg)
739     {
740     file_handle *fh = (file_handle *)arg;
741     if (!fh)
742     return;
743    
744 cebix 1.2 #if defined(__linux__) && defined(CDROM_LOCKDOOR)
745 cebix 1.1 if (fh->is_cdrom)
746     ioctl(fh->fd, CDROM_LOCKDOOR, 0);
747     #endif
748     }
749    
750    
751     /*
752     * Read CD-ROM TOC (binary MSF format, 804 bytes max.)
753     */
754    
755     bool SysCDReadTOC(void *arg, uint8 *toc)
756     {
757     file_handle *fh = (file_handle *)arg;
758     if (!fh)
759     return false;
760    
761     if (fh->is_cdrom) {
762     #if defined(__linux__)
763     uint8 *p = toc + 2;
764    
765     // Header
766     cdrom_tochdr header;
767     if (ioctl(fh->fd, CDROMREADTOCHDR, &header) < 0)
768     return false;
769     *p++ = header.cdth_trk0;
770     *p++ = header.cdth_trk1;
771    
772     // Tracks
773     cdrom_tocentry entry;
774     for (int i=header.cdth_trk0; i<=header.cdth_trk1; i++) {
775     entry.cdte_track = i;
776     entry.cdte_format = CDROM_MSF;
777     if (ioctl(fh->fd, CDROMREADTOCENTRY, &entry) < 0)
778     return false;
779     *p++ = 0;
780     *p++ = (entry.cdte_adr << 4) | entry.cdte_ctrl;
781     *p++ = entry.cdte_track;
782     *p++ = 0;
783     *p++ = 0;
784     *p++ = entry.cdte_addr.msf.minute;
785     *p++ = entry.cdte_addr.msf.second;
786     *p++ = entry.cdte_addr.msf.frame;
787     }
788    
789     // Leadout track
790     entry.cdte_track = CDROM_LEADOUT;
791     entry.cdte_format = CDROM_MSF;
792     if (ioctl(fh->fd, CDROMREADTOCENTRY, &entry) < 0)
793     return false;
794     *p++ = 0;
795     *p++ = (entry.cdte_adr << 4) | entry.cdte_ctrl;
796     *p++ = entry.cdte_track;
797     *p++ = 0;
798     *p++ = 0;
799     *p++ = entry.cdte_addr.msf.minute;
800     *p++ = entry.cdte_addr.msf.second;
801     *p++ = entry.cdte_addr.msf.frame;
802    
803     // TOC size
804     int toc_size = p - toc;
805     *toc++ = toc_size >> 8;
806     *toc++ = toc_size & 0xff;
807     return true;
808 nigel 1.19 #elif defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_2)
809     extern bool DarwinCDReadTOC(char *name, uint8 *toc);
810    
811     return DarwinCDReadTOC(fh->name, toc);
812 cebix 1.4 #elif defined(__FreeBSD__)
813 cebix 1.1 uint8 *p = toc + 2;
814    
815     // Header
816     struct ioc_toc_header header;
817     if (ioctl(fh->fd, CDIOREADTOCHEADER, &header) < 0)
818     return false;
819     *p++ = header.starting_track;
820     *p++ = header.ending_track;
821    
822     // Tracks
823     struct ioc_read_toc_single_entry entry;
824     for (int i=header.starting_track; i<=header.ending_track; i++) {
825     entry.track = i;
826     entry.address_format = CD_MSF_FORMAT;
827     if (ioctl(fh->fd, CDIOREADTOCENTRY, &entry) < 0)
828     return false;
829     *p++ = 0;
830     *p++ = (entry.entry.addr_type << 4) | entry.entry.control;
831     *p++ = entry.entry.track;
832     *p++ = 0;
833     *p++ = 0;
834     *p++ = entry.entry.addr.msf.minute;
835     *p++ = entry.entry.addr.msf.second;
836     *p++ = entry.entry.addr.msf.frame;
837     }
838    
839     // Leadout track
840     entry.track = CD_TRACK_INFO;
841     entry.address_format = CD_MSF_FORMAT;
842     if (ioctl(fh->fd, CDIOREADTOCENTRY, &entry) < 0)
843     return false;
844     *p++ = 0;
845     *p++ = (entry.entry.addr_type << 4) | entry.entry.control;
846     *p++ = entry.entry.track;
847     *p++ = 0;
848     *p++ = 0;
849     *p++ = entry.entry.addr.msf.minute;
850     *p++ = entry.entry.addr.msf.second;
851     *p++ = entry.entry.addr.msf.frame;
852 cebix 1.4
853     // TOC size
854     int toc_size = p - toc;
855     *toc++ = toc_size >> 8;
856     *toc++ = toc_size & 0xff;
857     return true;
858     #elif defined(__NetBSD__)
859     uint8 *p = toc + 2;
860    
861     // Header
862     struct ioc_toc_header header;
863     if (ioctl(fh->fd, CDIOREADTOCHEADER, &header) < 0)
864     return false;
865     *p++ = header.starting_track;
866     *p++ = header.ending_track;
867    
868     // Tracks (this is nice... :-)
869     struct ioc_read_toc_entry entries;
870     entries.address_format = CD_MSF_FORMAT;
871     entries.starting_track = 1;
872     entries.data_len = 800;
873     entries.data = (cd_toc_entry *)p;
874     if (ioctl(fh->fd, CDIOREADTOCENTRIES, &entries) < 0)
875     return false;
876 cebix 1.1
877     // TOC size
878     int toc_size = p - toc;
879     *toc++ = toc_size >> 8;
880     *toc++ = toc_size & 0xff;
881     return true;
882 cebix 1.15 #else
883     return false;
884 cebix 1.1 #endif
885     } else
886     return false;
887     }
888    
889    
890     /*
891     * Read CD-ROM position data (Sub-Q Channel, 16 bytes, see SCSI standard)
892     */
893    
894     bool SysCDGetPosition(void *arg, uint8 *pos)
895     {
896     file_handle *fh = (file_handle *)arg;
897     if (!fh)
898     return false;
899    
900     if (fh->is_cdrom) {
901     #if defined(__linux__)
902     cdrom_subchnl chan;
903     chan.cdsc_format = CDROM_MSF;
904     if (ioctl(fh->fd, CDROMSUBCHNL, &chan) < 0)
905     return false;
906     *pos++ = 0;
907     *pos++ = chan.cdsc_audiostatus;
908     *pos++ = 0;
909     *pos++ = 12; // Sub-Q data length
910     *pos++ = 0;
911     *pos++ = (chan.cdsc_adr << 4) | chan.cdsc_ctrl;
912     *pos++ = chan.cdsc_trk;
913     *pos++ = chan.cdsc_ind;
914     *pos++ = 0;
915     *pos++ = chan.cdsc_absaddr.msf.minute;
916     *pos++ = chan.cdsc_absaddr.msf.second;
917     *pos++ = chan.cdsc_absaddr.msf.frame;
918     *pos++ = 0;
919     *pos++ = chan.cdsc_reladdr.msf.minute;
920     *pos++ = chan.cdsc_reladdr.msf.second;
921     *pos++ = chan.cdsc_reladdr.msf.frame;
922     return true;
923 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
924 cebix 1.1 struct ioc_read_subchannel chan;
925     chan.data_format = CD_MSF_FORMAT;
926     chan.address_format = CD_MSF_FORMAT;
927     chan.track = CD_CURRENT_POSITION;
928     if (ioctl(fh->fd, CDIOCREADSUBCHANNEL, &chan) < 0)
929     return false;
930     *pos++ = 0;
931     *pos++ = chan.data->header.audio_status;
932     *pos++ = 0;
933     *pos++ = 12; // Sub-Q data length
934     *pos++ = 0;
935     *pos++ = (chan.data->what.position.addr_type << 4) | chan.data->what.position.control;
936     *pos++ = chan.data->what.position.track_number;
937     *pos++ = chan.data->what.position.index_number;
938     *pos++ = 0;
939     *pos++ = chan.data->what.position.absaddr.msf.minute;
940     *pos++ = chan.data->what.position.absaddr.msf.second;
941     *pos++ = chan.data->what.position.absaddr.msf.frame;
942     *pos++ = 0;
943     *pos++ = chan.data->what.position.reladdr.msf.minute;
944     *pos++ = chan.data->what.position.reladdr.msf.second;
945     *pos++ = chan.data->what.position.reladdr.msf.frame;
946     return true;
947 cebix 1.15 #else
948     return false;
949 cebix 1.1 #endif
950     } else
951     return false;
952     }
953    
954    
955     /*
956     * Play CD audio
957     */
958    
959     bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end_m, uint8 end_s, uint8 end_f)
960     {
961     file_handle *fh = (file_handle *)arg;
962     if (!fh)
963     return false;
964    
965     if (fh->is_cdrom) {
966     #if defined(__linux__)
967     cdrom_msf play;
968     play.cdmsf_min0 = start_m;
969     play.cdmsf_sec0 = start_s;
970     play.cdmsf_frame0 = start_f;
971     play.cdmsf_min1 = end_m;
972     play.cdmsf_sec1 = end_s;
973     play.cdmsf_frame1 = end_f;
974     return ioctl(fh->fd, CDROMPLAYMSF, &play) == 0;
975 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
976 cebix 1.1 struct ioc_play_msf play;
977     play.start_m = start_m;
978     play.start_s = start_s;
979     play.start_f = start_f;
980     play.end_m = end_m;
981     play.end_s = end_s;
982     play.end_f = end_f;
983     return ioctl(fh->fd, CDIOCPLAYMSF, &play) == 0;
984 cebix 1.15 #else
985     return false;
986 cebix 1.1 #endif
987     } else
988     return false;
989     }
990    
991    
992     /*
993     * Pause CD audio
994     */
995    
996     bool SysCDPause(void *arg)
997     {
998     file_handle *fh = (file_handle *)arg;
999     if (!fh)
1000     return false;
1001    
1002     if (fh->is_cdrom) {
1003     #if defined(__linux__)
1004     return ioctl(fh->fd, CDROMPAUSE) == 0;
1005 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1006 cebix 1.1 return ioctl(fh->fd, CDIOCPAUSE) == 0;
1007 cebix 1.15 #else
1008     return false;
1009 cebix 1.1 #endif
1010     } else
1011     return false;
1012     }
1013    
1014    
1015     /*
1016     * Resume paused CD audio
1017     */
1018    
1019     bool SysCDResume(void *arg)
1020     {
1021     file_handle *fh = (file_handle *)arg;
1022     if (!fh)
1023     return false;
1024    
1025     if (fh->is_cdrom) {
1026     #if defined(__linux__)
1027     return ioctl(fh->fd, CDROMRESUME) == 0;
1028 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1029 cebix 1.1 return ioctl(fh->fd, CDIOCRESUME) == 0;
1030 cebix 1.15 #else
1031     return false;
1032 cebix 1.1 #endif
1033     } else
1034     return false;
1035     }
1036    
1037    
1038     /*
1039     * Stop CD audio
1040     */
1041    
1042     bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f)
1043     {
1044     file_handle *fh = (file_handle *)arg;
1045     if (!fh)
1046     return false;
1047    
1048     if (fh->is_cdrom) {
1049     #if defined(__linux__)
1050     return ioctl(fh->fd, CDROMSTOP) == 0;
1051 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1052 cebix 1.1 return ioctl(fh->fd, CDIOCSTOP) == 0;
1053 cebix 1.15 #else
1054     return false;
1055 cebix 1.1 #endif
1056     } else
1057     return false;
1058     }
1059    
1060    
1061     /*
1062     * Perform CD audio fast-forward/fast-reverse operation starting from specified address
1063     */
1064    
1065     bool SysCDScan(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, bool reverse)
1066     {
1067     file_handle *fh = (file_handle *)arg;
1068     if (!fh)
1069     return false;
1070    
1071     // Not supported under Linux
1072     return false;
1073     }
1074    
1075    
1076     /*
1077     * Set CD audio volume (0..255 each channel)
1078     */
1079    
1080     void SysCDSetVolume(void *arg, uint8 left, uint8 right)
1081     {
1082     file_handle *fh = (file_handle *)arg;
1083     if (!fh)
1084     return;
1085    
1086     if (fh->is_cdrom) {
1087     #if defined(__linux__)
1088     cdrom_volctrl vol;
1089     vol.channel0 = vol.channel2 = left;
1090     vol.channel1 = vol.channel3 = right;
1091     ioctl(fh->fd, CDROMVOLCTRL, &vol);
1092 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1093 cebix 1.1 struct ioc_vol vol;
1094     vol.vol[0] = vol.vol[2] = left;
1095     vol.vol[1] = vol.vol[3] = right;
1096     ioctl(fh->fd, CDIOCSETVOL, &vol);
1097     #endif
1098     }
1099     }
1100    
1101    
1102     /*
1103     * Get CD audio volume (0..255 each channel)
1104     */
1105    
1106     void SysCDGetVolume(void *arg, uint8 &left, uint8 &right)
1107     {
1108     file_handle *fh = (file_handle *)arg;
1109     if (!fh)
1110     return;
1111    
1112     left = right = 0;
1113     if (fh->is_cdrom) {
1114     #if defined(__linux__)
1115     cdrom_volctrl vol;
1116     ioctl(fh->fd, CDROMVOLREAD, &vol);
1117     left = vol.channel0;
1118     right = vol.channel1;
1119 cebix 1.3 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1120 cebix 1.1 struct ioc_vol vol;
1121     ioctl(fh->fd, CDIOCGETVOL, &vol);
1122     left = vol.vol[0];
1123     right = vol.vol[1];
1124     #endif
1125     }
1126     }