ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sys_unix.cpp
Revision: 1.25
Committed: 2005-07-10T16:42:04Z (19 years, 4 months ago) by gbeauche
Branch: MAIN
Changes since 1.24: +4 -4 lines
Log Message:
sys_darwin.cpp depends on CoreFoundation stuff, disable it for OpenDarwin

File Contents

# Content
1 /*
2 * sys_unix.cpp - System dependent routines, Unix implementation
3 *
4 * Basilisk II (C) 1997-2005 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #include <sys/ioctl.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26
27 #ifdef HAVE_AVAILABILITYMACROS_H
28 #include <AvailabilityMacros.h>
29 #endif
30
31 #ifdef __linux__
32 #include <sys/mount.h>
33 #include <linux/cdrom.h>
34 #include <linux/fd.h>
35 #include <linux/major.h>
36 #include <linux/kdev_t.h>
37 #include <linux/unistd.h>
38 #include <dirent.h>
39
40 #ifdef __NR__llseek
41 _syscall5(int, _llseek, unsigned int, fd, unsigned long, hi, unsigned long, lo, loff_t *, res, unsigned int, wh);
42 #else
43 static int _llseek(unsigned int fd, unsigned long hi, unsigned long lo, loff_t *res, unsigned int wh)
44 {
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 #if defined(__FreeBSD__) || defined(__NetBSD__)
56 #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 #elif defined(__APPLE__) && defined(__MACH__)
85 char *ioctl_name; // For CDs on OS X - a device for special ioctls
86 int ioctl_fd;
87 #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 if (access("/dev/.devfsd", F_OK) < 0) {
132 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 char fd_dev[20];
141 sprintf(fd_dev, "/dev/floppy/%s", floppy_dev->d_name);
142 PrefsAddString("floppy", fd_dev);
143 }
144 }
145 closedir(fd_dir);
146 }
147 }
148 #elif defined(__NetBSD__)
149 PrefsAddString("floppy", "/dev/fd0a");
150 PrefsAddString("floppy", "/dev/fd1a");
151 #elif defined(__APPLE__) && defined(__MACH__)
152 #if defined(AQUA) || defined(HAVE_FRAMEWORK_COREFOUNDATION)
153 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 #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 * 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 */
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 if (len == 0 || line[0] == '#')
186 continue;
187 line[len-1] = 0;
188
189 // Parse line
190 char *dev, *mnt_point, *fstype;
191 if (sscanf(line, "%as %as %as", &dev, &mnt_point, &fstype) == 3) {
192 if (strcmp(fstype, "hfs") == 0)
193 PrefsAddString("disk", dev);
194 }
195 free(dev); free(mnt_point); free(fstype);
196 }
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 if (access("/dev/.devfsd", F_OK) < 0)
216 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 char cd_dev[20];
224 sprintf(cd_dev, "/dev/cdroms/%s", cdrom_dev->d_name);
225 PrefsAddString("cdrom", cd_dev);
226 }
227 }
228 closedir(cd_dir);
229 }
230 }
231 #elif defined(__APPLE__) && defined(__MACH__)
232 #if defined(AQUA) || defined(HAVE_FRAMEWORK_COREFOUNDATION)
233 extern void DarwinAddCDROMPrefs(void);
234
235 DarwinAddCDROMPrefs();
236 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
241 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 if (access("/dev/.devfsd", F_OK) < 0) {
254 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 #elif defined(__FreeBSD__)
261 PrefsAddString("seriala", "/dev/cuaa0");
262 PrefsAddString("serialb", "/dev/cuaa1");
263 #elif defined(__NetBSD__)
264 PrefsAddString("seriala", "/dev/tty00");
265 PrefsAddString("serialb", "/dev/tty01");
266 #elif defined(__APPLE__) && defined(__MACH__)
267 #if defined(AQUA) || defined(HAVE_FRAMEWORK_COREFOUNDATION)
268 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 #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 char *dummy;
302 sscanf(line, "%as %s", &dummy, mount_name);
303 free(dummy);
304 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 #if defined(__FreeBSD__)
323 // 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 #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 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 #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
371 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 #if defined(__linux__)
393 _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 #elif defined(__FreeBSD__)
418 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 #elif defined(__NetBSD__)
428 fh->is_floppy = ((st.st_rdev >> 16) == 2);
429 #endif
430 }
431 #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 }
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 #if defined(__linux__)
502 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 #if defined(__linux__)
528 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 #if defined(__linux__)
555 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 #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 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 } 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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
590 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 #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 #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 #if defined(__linux__)
654 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 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 } else if (fh->is_cdrom) {
708 #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 // once the tray has been opened (altough the TOC reads fine).
712 // 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 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
727 } 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 #if defined(__linux__) && defined(CDROM_LOCKDOOR)
767 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 #elif defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_2) && defined(HAVE_FRAMEWORK_COREFOUNDATION)
831 extern bool DarwinCDReadTOC(char *name, uint8 *toc);
832
833 return DarwinCDReadTOC(fh->name, toc);
834 #elif defined(__FreeBSD__)
835 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
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
899 // TOC size
900 int toc_size = p - toc;
901 *toc++ = toc_size >> 8;
902 *toc++ = toc_size & 0xff;
903 return true;
904 #else
905 return false;
906 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
946 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 #else
970 return false;
971 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
998 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 #else
1007 return false;
1008 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1028 return ioctl(fh->fd, CDIOCPAUSE) == 0;
1029 #else
1030 return false;
1031 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1051 return ioctl(fh->fd, CDIOCRESUME) == 0;
1052 #else
1053 return false;
1054 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1074 return ioctl(fh->fd, CDIOCSTOP) == 0;
1075 #else
1076 return false;
1077 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1115 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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1142 struct ioc_vol vol;
1143 ioctl(fh->fd, CDIOCGETVOL, &vol);
1144 left = vol.vol[0];
1145 right = vol.vol[1];
1146 #endif
1147 }
1148 }