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

File Contents

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