ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sys_unix.cpp
Revision: 1.22
Committed: 2004-01-26T11:08:52Z (20 years, 7 months ago) by nigel
Branch: MAIN
CVS Tags: nigel-build-16, nigel-build-15
Changes since 1.21: +25 -17 lines
Log Message:
Tidy up Aqua/Darwin specific code

File Contents

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