ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sys_unix.cpp
Revision: 1.24
Committed: 2005-01-30T21:42:14Z (19 years, 9 months ago) by gbeauche
Branch: MAIN
Changes since 1.23: +1 -1 lines
Log Message:
Happy New Year!

File Contents

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