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, 5 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

# Content
1 /*
2 * sys_unix.cpp - System dependent routines, Unix implementation
3 *
4 * Basilisk II (C) 1997-2004 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 #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 #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 * 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 */
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 if (len == 0 || line[0] == '#')
182 continue;
183 line[len-1] = 0;
184
185 // Parse line
186 char *dev, *mnt_point, *fstype;
187 if (sscanf(line, "%as %as %as", &dev, &mnt_point, &fstype) == 3) {
188 if (strcmp(fstype, "hfs") == 0)
189 PrefsAddString("disk", dev);
190 }
191 free(dev); free(mnt_point); free(fstype);
192 }
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 if (access("/dev/.devfsd", F_OK) < 0)
212 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 char cd_dev[20];
220 sprintf(cd_dev, "/dev/cdroms/%s", cdrom_dev->d_name);
221 PrefsAddString("cdrom", cd_dev);
222 }
223 }
224 closedir(cd_dir);
225 }
226 }
227 #elif defined(__APPLE__) && defined(__MACH__)
228 #ifdef AQUA
229 extern void DarwinAddCDROMPrefs(void);
230
231 DarwinAddCDROMPrefs();
232 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
237 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 if (access("/dev/.devfsd", F_OK) < 0) {
250 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 #elif defined(__FreeBSD__)
257 PrefsAddString("seriala", "/dev/cuaa0");
258 PrefsAddString("serialb", "/dev/cuaa1");
259 #elif defined(__NetBSD__)
260 PrefsAddString("seriala", "/dev/tty00");
261 PrefsAddString("serialb", "/dev/tty01");
262 #elif defined(__APPLE__) && defined(__MACH__)
263 #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 #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 char *dummy;
298 sscanf(line, "%as %s", &dummy, mount_name);
299 free(dummy);
300 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 #if defined(__FreeBSD__)
319 // 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 #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 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 #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
367 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 #if defined(__linux__)
389 _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 #elif defined(__FreeBSD__)
414 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 #elif defined(__NetBSD__)
424 fh->is_floppy = ((st.st_rdev >> 16) == 2);
425 #endif
426 }
427 #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 }
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 #if defined(__linux__)
498 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 #if defined(__linux__)
524 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 #if defined(__linux__)
551 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 #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 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 } 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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
586 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 #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 #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 #if defined(__linux__)
650 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 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 } else if (fh->is_cdrom) {
704 #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 // once the tray has been opened (altough the TOC reads fine).
708 // 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 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
723 } 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 #if defined(__linux__) && defined(CDROM_LOCKDOOR)
763 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 #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 #elif defined(__FreeBSD__)
831 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
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
895 // TOC size
896 int toc_size = p - toc;
897 *toc++ = toc_size >> 8;
898 *toc++ = toc_size & 0xff;
899 return true;
900 #else
901 return false;
902 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
942 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 #else
966 return false;
967 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
994 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 #else
1003 return false;
1004 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1024 return ioctl(fh->fd, CDIOCPAUSE) == 0;
1025 #else
1026 return false;
1027 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1047 return ioctl(fh->fd, CDIOCRESUME) == 0;
1048 #else
1049 return false;
1050 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1070 return ioctl(fh->fd, CDIOCSTOP) == 0;
1071 #else
1072 return false;
1073 #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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1111 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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1138 struct ioc_vol vol;
1139 ioctl(fh->fd, CDIOCGETVOL, &vol);
1140 left = vol.vol[0];
1141 right = vol.vol[1];
1142 #endif
1143 }
1144 }