ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/AmigaOS/sys_amiga.cpp
Revision: 1.2
Committed: 1999-11-01T16:24:12Z (25 years ago) by cebix
Branch: MAIN
CVS Tags: snapshot-22121999, release-0_8-1, snapshot-02111999
Changes since 1.1: +87 -27 lines
Log Message:
- AmigaOS: removed support for SAS/C
- AmigaOS: sys_amiga.cpp: supports 64-bit device access and respects
  device block size on writes
- AmigaOS: added support for resource forks and Finder info for ExtFS
- AmigaOS: added "ExtFS" gadget to prefs editor
- protection mask for all open()/creat()/mkdir() calls is now 0666 or
  0777

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * sys_amiga.cpp - System dependent routines, Amiga implementation
3     *
4     * Basilisk II (C) 1997-1999 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 <exec/types.h>
22     #include <exec/memory.h>
23     #include <devices/newstyle.h>
24     #include <devices/trackdisk.h>
25     #include <devices/scsidisk.h>
26     #include <resources/disk.h>
27     #include <proto/dos.h>
28     #include <proto/exec.h>
29     #include <proto/disk.h>
30    
31     #include "sysdeps.h"
32     #include "main.h"
33     #include "macos_util.h"
34     #include "prefs.h"
35     #include "user_strings.h"
36     #include "sys.h"
37    
38     #define DEBUG 0
39     #include "debug.h"
40    
41    
42     // File handles are pointers to these structures
43     struct file_handle {
44     bool is_file; // Flag: plain file or /dev/something?
45     bool read_only; // Copy of Sys_open() flag
46     loff_t start_byte; // Size of file header (if any)
47     loff_t size; // Size of file/device (minus header)
48    
49     BPTR f; // AmigaDOS file handle (if is_file == true)
50    
51     struct IOStdReq *io; // Pointer to IORequest (if is_file == false)
52     ULONG block_size; // Block size of device (must be a power of two)
53     bool is_nsd; // New style device?
54     bool does_64bit; // Supports 64 bit trackdisk commands?
55     };
56    
57    
58     // FileInfoBlock (must be global because it has to be on a longword boundary)
59     static struct FileInfoBlock FIB;
60    
61     // Message port for device communication
62     static struct MsgPort *the_port = NULL;
63    
64     // Temporary buffer in chip memory
65     const int TMP_BUF_SIZE = 0x10000;
66     static UBYTE *tmp_buf = NULL;
67    
68    
69     /*
70     * Initialization
71     */
72    
73     void SysInit(void)
74     {
75     // Create port and temporary buffer
76     the_port = CreateMsgPort();
77     tmp_buf = (UBYTE *)AllocMem(TMP_BUF_SIZE, MEMF_CHIP | MEMF_PUBLIC);
78     if (the_port == NULL || tmp_buf == NULL) {
79     ErrorAlert(GetString(STR_NO_MEM_ERR));
80     QuitEmulator();
81     }
82     }
83    
84    
85     /*
86     * Deinitialization
87     */
88    
89     void SysExit(void)
90     {
91     // Delete port and temporary buffer
92     if (the_port)
93     DeleteMsgPort(the_port);
94     if (tmp_buf)
95     FreeMem(tmp_buf, TMP_BUF_SIZE);
96     }
97    
98    
99     /*
100     * This gets called when no "floppy" prefs items are found
101     * It scans for available floppy drives and adds appropriate prefs items
102     */
103    
104     void SysAddFloppyPrefs(void)
105     {
106     #if 0
107     for (int i=0; i<4; i++) {
108     ULONG id = GetUnitID(i);
109     if (id == DRT_150RPM) { // We need an HD drive
110     char str[256];
111     sprintf(str, "/dev/mfm.device/%d/0/0/1474560/512", i);
112     PrefsAddString("floppy", str);
113     }
114     }
115     #endif
116     }
117    
118    
119     /*
120     * This gets called when no "disk" prefs items are found
121     * It scans for available HFS volumes and adds appropriate prefs items
122     */
123    
124     void SysAddDiskPrefs(void)
125     {
126     // AmigaOS doesn't support MacOS partitioning, so this probably doesn't make much sense...
127     }
128    
129    
130     /*
131     * This gets called when no "cdrom" prefs items are found
132     * It scans for available CD-ROM drives and adds appropriate prefs items
133     */
134    
135     void SysAddCDROMPrefs(void)
136     {
137     // Don't scan for drives if nocdrom option given
138     if (PrefsFindBool("nocdrom"))
139     return;
140    
141     //!!
142     }
143    
144    
145     /*
146     * Add default serial prefs (must be added, even if no ports present)
147     */
148    
149     void SysAddSerialPrefs(void)
150     {
151     PrefsAddString("seriala", "serial.device/0");
152     PrefsAddString("serialb", "*parallel.device/0");
153     }
154    
155    
156     /*
157     * Open file/device, create new file handle (returns NULL on error)
158     *
159     * Format for device names: /dev/<name>/<unit>/<open flags>/<start block>/<size (blocks)>/<block size>
160     */
161    
162     void *Sys_open(const char *name, bool read_only)
163     {
164     bool is_file = (strstr(name, "/dev/") != name);
165    
166     D(bug("Sys_open(%s, %s)\n", name, read_only ? "read-only" : "read/write"));
167    
168     // File or device?
169     if (is_file) {
170    
171     // File, open it and get stats
172     BPTR f = Open((char *)name, MODE_OLDFILE);
173     if (!f)
174     return NULL;
175     if (!ExamineFH(f, &FIB)) {
176     Close(f);
177     return NULL;
178     }
179    
180     // Check if file is write protected
181     if (FIB.fib_Protection & FIBF_WRITE)
182     read_only = true;
183    
184     // Create file_handle
185     file_handle *fh = new file_handle;
186     fh->f = f;
187     fh->is_file = true;
188     fh->read_only = read_only;
189    
190     // Detect disk image file layout
191     loff_t size = FIB.fib_Size;
192     Seek(fh->f, 0, OFFSET_BEGINNING);
193     Read(fh->f, tmp_buf, 256);
194     FileDiskLayout(size, tmp_buf, fh->start_byte, fh->size);
195     return fh;
196    
197     } else {
198    
199     // Device, parse string
200     char dev_name[256];
201     ULONG dev_unit = 0, dev_flags = 0, dev_start = 0, dev_size = 16, dev_bsize = 512;
202     if (sscanf(name, "/dev/%[^/]/%ld/%ld/%ld/%ld/%ld", dev_name, &dev_unit, &dev_flags, &dev_start, &dev_size, &dev_bsize) < 2)
203     return NULL;
204    
205     // Create IORequest
206     struct IOStdReq *io = (struct IOStdReq *)CreateIORequest(the_port, sizeof(struct IOExtTD));
207     if (io == NULL)
208     return NULL;
209    
210     // Open device
211     if (OpenDevice((UBYTE *)dev_name, dev_unit, (struct IORequest *)io, dev_flags)) {
212     D(bug(" couldn't open device\n"));
213     DeleteIORequest(io);
214     return NULL;
215     }
216    
217     // Check for new style device
218     bool is_nsd = false, does_64bit = false;
219     struct NSDeviceQueryResult nsdqr;
220     nsdqr.DevQueryFormat = 0;
221     nsdqr.SizeAvailable = 0;
222     io->io_Command = NSCMD_DEVICEQUERY;
223     io->io_Length = sizeof(nsdqr);
224     io->io_Data = (APTR)&nsdqr;
225     LONG error = DoIO((struct IORequest *)io);
226     D(bug("DEVICEQUERY returned %ld (length %ld, actual %ld)\n", error, io->io_Length, io->io_Actual));
227     if ((!error) && (io->io_Actual >= 16) && (io->io_Actual <= sizeof(nsdqr)) && (nsdqr.SizeAvailable == io->io_Actual)) {
228    
229     // Looks like an NSD
230     is_nsd = true;
231     D(bug(" new style device, type %ld\n", nsdqr.DeviceType));
232    
233     // We only work with trackdisk-like devices
234     if (nsdqr.DeviceType != NSDEVTYPE_TRACKDISK) {
235     CloseDevice((struct IORequest *)io);
236     DeleteIORequest(io);
237     return NULL;
238     }
239    
240     // Check whether device is 64 bit capable
241     UWORD *cmdcheck;
242     for (cmdcheck = nsdqr.SupportedCommands; *cmdcheck; cmdcheck++) {
243     if (*cmdcheck == NSCMD_TD_READ64) {
244     D(bug(" supports 64 bit commands\n"));
245     does_64bit = true;
246     }
247     }
248     }
249    
250     // Create file_handle
251     file_handle *fh = new file_handle;
252     fh->io = io;
253     fh->is_file = false;
254     fh->read_only = read_only;
255     fh->start_byte = (loff_t)dev_start * dev_bsize;
256     fh->size = (loff_t)dev_size * dev_bsize;
257     fh->block_size = dev_bsize;
258     fh->is_nsd = is_nsd;
259     fh->does_64bit = does_64bit;
260     return fh;
261     }
262     }
263    
264    
265     /*
266     * Close file/device, delete file handle
267     */
268    
269     void Sys_close(void *arg)
270     {
271     file_handle *fh = (file_handle *)arg;
272     if (!fh)
273     return;
274    
275     D(bug("Sys_close(%08lx)\n", arg));
276    
277     // File or device?
278     if (fh->is_file) {
279    
280     // File, simply close it
281     Close(fh->f);
282    
283     } else {
284    
285     // Device, close it and delete IORequest
286     fh->io->io_Command = CMD_UPDATE;
287     DoIO((struct IORequest *)fh->io);
288    
289     fh->io->io_Command = TD_MOTOR;
290     fh->io->io_Length = 0;
291     DoIO((struct IORequest *)fh->io);
292    
293     CloseDevice((struct IORequest *)fh->io);
294     DeleteIORequest(fh->io);
295     }
296     delete fh;
297     }
298    
299    
300     /*
301 cebix 1.2 * Send one I/O request, using 64-bit addressing if the device supports it
302     */
303    
304     static loff_t send_io_request(file_handle *fh, bool writing, ULONG length, loff_t offset, APTR data)
305     {
306     if (fh->does_64bit) {
307     fh->io->io_Command = writing ? NSCMD_TD_WRITE64 : NSCMD_TD_READ64;
308     fh->io->io_Actual = offset >> 32;
309     } else {
310     fh->io->io_Command = writing ? CMD_WRITE : CMD_READ;
311     fh->io->io_Actual = 0;
312     }
313     fh->io->io_Length = length;
314     fh->io->io_Offset = offset;
315     fh->io->io_Data = data;
316     if (DoIO((struct IORequest *)fh->io) || fh->io->io_Actual != length)
317     return 0;
318     return fh->io->io_Actual;
319     }
320    
321    
322     /*
323 cebix 1.1 * Read "length" bytes from file/device, starting at "offset", to "buffer",
324     * returns number of bytes read (or 0)
325     */
326    
327     size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length)
328     {
329     file_handle *fh = (file_handle *)arg;
330     if (!fh)
331     return 0;
332    
333     // File or device?
334     if (fh->is_file) {
335    
336     // File, seek to position
337     if (Seek(fh->f, offset + fh->start_byte, OFFSET_BEGINNING) == -1)
338     return 0;
339    
340     // Read data
341     LONG actual = Read(fh->f, buffer, length);
342     if (actual == -1)
343     return 0;
344     else
345     return actual;
346    
347     } else {
348    
349 cebix 1.2 // Device, pre-read (partial read of first block) necessary?
350 cebix 1.1 loff_t pos = offset + fh->start_byte;
351     size_t actual = 0;
352     uint32 pre_offset = pos % fh->block_size;
353     if (pre_offset) {
354    
355     // Yes, read one block
356 cebix 1.2 if (send_io_request(fh, false, fh->block_size, pos - pre_offset, tmp_buf) == 0)
357 cebix 1.1 return 0;
358    
359     // Copy data to destination buffer
360     size_t pre_length = fh->block_size - pre_offset;
361     if (pre_length > length)
362     pre_length = length;
363     memcpy(buffer, tmp_buf + pre_offset, pre_length);
364    
365     // Adjust data pointers
366     buffer = (uint8 *)buffer + pre_length;
367     pos += pre_length;
368     length -= pre_length;
369     actual += pre_length;
370     }
371    
372     // Main read (complete reads of middle blocks) possible?
373     if (length >= fh->block_size) {
374    
375     // Yes, read blocks
376     size_t main_length = length & ~(fh->block_size - 1);
377 cebix 1.2 if (send_io_request(fh, false, main_length, pos, buffer) == 0)
378 cebix 1.1 return 0;
379    
380     // Adjust data pointers
381     buffer = (uint8 *)buffer + main_length;
382     pos += main_length;
383     length -= main_length;
384     actual += main_length;
385     }
386    
387 cebix 1.2 // Post-read (partial read of last block) necessary?
388 cebix 1.1 if (length) {
389    
390     // Yes, read one block
391 cebix 1.2 if (send_io_request(fh, false, fh->block_size, pos, tmp_buf) == 0)
392 cebix 1.1 return 0;
393    
394     // Copy data to destination buffer
395     memcpy(buffer, tmp_buf, length);
396     actual += length;
397     }
398    
399     return actual;
400     }
401     }
402    
403    
404     /*
405     * Write "length" bytes from "buffer" to file/device, starting at "offset",
406     * returns number of bytes written (or 0)
407     */
408    
409     size_t Sys_write(void *arg, void *buffer, loff_t offset, size_t length)
410     {
411     file_handle *fh = (file_handle *)arg;
412     if (!fh)
413     return 0;
414    
415     // File or device?
416     if (fh->is_file) {
417    
418 cebix 1.2 // File, seek to position if necessary
419 cebix 1.1 if (Seek(fh->f, offset + fh->start_byte, OFFSET_BEGINNING) == -1)
420     return 0;
421    
422     // Write data
423     LONG actual = Write(fh->f, buffer, length);
424     if (actual == -1)
425     return 0;
426     else
427     return actual;
428    
429     } else {
430    
431 cebix 1.2 // Device, pre-write (partial write of first block) necessary
432     loff_t pos = offset + fh->start_byte;
433     size_t actual = 0;
434     uint32 pre_offset = pos % fh->block_size;
435     if (pre_offset) {
436    
437     // Yes, read one block
438     if (send_io_request(fh, false, fh->block_size, pos - pre_offset, tmp_buf) == 0)
439     return 0;
440    
441     // Copy data from source buffer
442     size_t pre_length = fh->block_size - pre_offset;
443     if (pre_length > length)
444     pre_length = length;
445     memcpy(tmp_buf + pre_offset, buffer, pre_length);
446    
447     // Write block back
448     if (send_io_request(fh, true, fh->block_size, pos - pre_offset, tmp_buf) == 0)
449     return 0;
450    
451     // Adjust data pointers
452     buffer = (uint8 *)buffer + pre_length;
453     pos += pre_length;
454     length -= pre_length;
455     actual += pre_length;
456     }
457    
458     // Main write (complete writes of middle blocks) possible?
459     if (length >= fh->block_size) {
460    
461     // Yes, write blocks
462     size_t main_length = length & ~(fh->block_size - 1);
463     if (send_io_request(fh, true, main_length, pos, buffer) == 0)
464     return 0;
465    
466     // Adjust data pointers
467     buffer = (uint8 *)buffer + main_length;
468     pos += main_length;
469     length -= main_length;
470     actual += main_length;
471     }
472    
473     // Post-write (partial write of last block) necessary?
474     if (length) {
475    
476     // Yes, read one block
477     if (send_io_request(fh, false, fh->block_size, pos, tmp_buf) == 0)
478     return 0;
479    
480     // Copy data from source buffer
481     memcpy(buffer, tmp_buf, length);
482    
483     // Write block back
484     if (send_io_request(fh, true, fh->block_size, pos, tmp_buf) == 0)
485     return 0;
486     actual += length;
487     }
488    
489     return actual;
490 cebix 1.1 }
491     }
492    
493    
494     /*
495     * Return size of file/device (minus header)
496     */
497    
498     loff_t SysGetFileSize(void *arg)
499     {
500     file_handle *fh = (file_handle *)arg;
501     if (!fh)
502     return true;
503    
504     return fh->size;
505     }
506    
507    
508     /*
509     * Eject volume (if applicable)
510     */
511    
512     void SysEject(void *arg)
513     {
514     file_handle *fh = (file_handle *)arg;
515     if (!fh)
516     return;
517    
518     if (!fh->is_file) {
519    
520     // Flush buffer, turn off the drive motor and eject volume
521     fh->io->io_Command = CMD_UPDATE;
522     DoIO((struct IORequest *)fh->io);
523    
524     fh->io->io_Command = TD_MOTOR;
525     fh->io->io_Length = 0;
526     DoIO((struct IORequest *)fh->io);
527    
528     fh->io->io_Command = TD_EJECT;
529     fh->io->io_Length = 1;
530     DoIO((struct IORequest *)fh->io);
531     }
532     }
533    
534    
535     /*
536     * Format volume (if applicable)
537     */
538    
539     bool SysFormat(void *arg)
540     {
541     file_handle *fh = (file_handle *)arg;
542     if (!fh)
543     return false;
544    
545     //!!
546     return true;
547     }
548    
549    
550     /*
551     * Check if file/device is read-only (this includes the read-only flag on Sys_open())
552     */
553    
554     bool SysIsReadOnly(void *arg)
555     {
556     file_handle *fh = (file_handle *)arg;
557     if (!fh)
558     return true;
559    
560     if (fh->is_file) {
561    
562     // File, return flag given to Sys_open
563     return fh->read_only;
564    
565     } else {
566    
567     // Device, check write protection
568     fh->io->io_Flags = IOF_QUICK;
569     fh->io->io_Command = TD_PROTSTATUS;
570     BeginIO((struct IORequest *)fh->io);
571     if (fh->io->io_Actual)
572     return true;
573     else
574     return fh->read_only;
575     }
576     }
577    
578    
579     /*
580     * Check if the given file handle refers to a fixed or a removable disk
581     */
582    
583     bool SysIsFixedDisk(void *arg)
584     {
585     file_handle *fh = (file_handle *)arg;
586     if (!fh)
587     return true;
588    
589     return true;
590     }
591    
592    
593     /*
594     * Check if a disk is inserted in the drive (always true for files)
595     */
596    
597     bool SysIsDiskInserted(void *arg)
598     {
599     file_handle *fh = (file_handle *)arg;
600     if (!fh)
601     return false;
602    
603     if (fh->is_file)
604     return true;
605     else {
606    
607     // Check medium status
608     fh->io->io_Flags = IOF_QUICK;
609     fh->io->io_Command = TD_CHANGESTATE;
610     fh->io->io_Actual = 0;
611     BeginIO((struct IORequest *)fh->io);
612     return fh->io->io_Actual == 0;
613     }
614     }
615    
616    
617     /*
618     * Prevent medium removal (if applicable)
619     */
620    
621     void SysPreventRemoval(void *arg)
622     {
623     file_handle *fh = (file_handle *)arg;
624     if (!fh)
625     return;
626    
627     if (!fh->is_file) {
628    
629     // Send PREVENT ALLOW MEDIUM REMOVAL SCSI command
630     struct SCSICmd scsi;
631     static const UBYTE the_cmd[6] = {0x1e, 0, 0, 0, 1, 0};
632     scsi.scsi_Length = 0;
633     scsi.scsi_Command = (UBYTE *)the_cmd;
634     scsi.scsi_CmdLength = 6;
635     scsi.scsi_Flags = SCSIF_READ;
636     scsi.scsi_Status = 0;
637     fh->io->io_Data = &scsi;
638     fh->io->io_Length = sizeof(scsi);
639     fh->io->io_Command = HD_SCSICMD;
640     DoIO((struct IORequest *)fh->io);
641     }
642     }
643    
644    
645     /*
646     * Allow medium removal (if applicable)
647     */
648    
649     void SysAllowRemoval(void *arg)
650     {
651     file_handle *fh = (file_handle *)arg;
652     if (!fh)
653     return;
654    
655     if (!fh->is_file) {
656    
657     // Send PREVENT ALLOW MEDIUM REMOVAL SCSI command
658     struct SCSICmd scsi;
659     static const UBYTE the_cmd[6] = {0x1e, 0, 0, 0, 0, 0};
660     scsi.scsi_Length = 0;
661     scsi.scsi_Command = (UBYTE *)the_cmd;
662     scsi.scsi_CmdLength = 6;
663     scsi.scsi_Flags = SCSIF_READ;
664     scsi.scsi_Status = 0;
665     fh->io->io_Data = &scsi;
666     fh->io->io_Length = sizeof(scsi);
667     fh->io->io_Command = HD_SCSICMD;
668     DoIO((struct IORequest *)fh->io);
669     }
670     }
671    
672    
673     /*
674     * Read CD-ROM TOC (binary MSF format, 804 bytes max.)
675     */
676    
677     bool SysCDReadTOC(void *arg, uint8 *toc)
678     {
679     file_handle *fh = (file_handle *)arg;
680     if (!fh)
681     return false;
682    
683     if (fh->is_file)
684     return false;
685     else {
686    
687     // Send READ TOC MSF SCSI command
688     struct SCSICmd scsi;
689     static const UBYTE read_toc_cmd[10] = {0x43, 0x02, 0, 0, 0, 0, 0, 0x03, 0x24, 0};
690     scsi.scsi_Data = (UWORD *)tmp_buf;
691     scsi.scsi_Length = 804;
692     scsi.scsi_Command = (UBYTE *)read_toc_cmd;
693     scsi.scsi_CmdLength = 10;
694     scsi.scsi_Flags = SCSIF_READ;
695     scsi.scsi_Status = 0;
696     fh->io->io_Data = &scsi;
697     fh->io->io_Length = sizeof(scsi);
698     fh->io->io_Command = HD_SCSICMD;
699     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
700     return false;
701     memcpy(toc, tmp_buf, 804);
702     return true;
703     }
704     }
705    
706    
707     /*
708     * Read CD-ROM position data (Sub-Q Channel, 16 bytes, see SCSI standard)
709     */
710    
711     bool SysCDGetPosition(void *arg, uint8 *pos)
712     {
713     file_handle *fh = (file_handle *)arg;
714     if (!fh)
715     return false;
716    
717     if (fh->is_file)
718     return false;
719     else {
720    
721     // Send READ SUB-CHANNEL SCSI command
722     struct SCSICmd scsi;
723     static const UBYTE read_subq_cmd[10] = {0x42, 0x02, 0x40, 0x01, 0, 0, 0, 0, 0x10, 0};
724     scsi.scsi_Data = (UWORD *)tmp_buf;
725     scsi.scsi_Length = 16;
726     scsi.scsi_Command = (UBYTE *)read_subq_cmd;
727     scsi.scsi_CmdLength = 10;
728     scsi.scsi_Flags = SCSIF_READ;
729     scsi.scsi_Status = 0;
730     fh->io->io_Data = &scsi;
731     fh->io->io_Length = sizeof(scsi);
732     fh->io->io_Command = HD_SCSICMD;
733     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
734     return false;
735     memcpy(pos, tmp_buf, 16);
736     return true;
737     }
738     }
739    
740    
741     /*
742     * Play CD audio
743     */
744    
745     bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end_m, uint8 end_s, uint8 end_f)
746     {
747     file_handle *fh = (file_handle *)arg;
748     if (!fh)
749     return false;
750    
751     if (fh->is_file)
752     return false;
753     else {
754    
755     // Send PLAY AUDIO MSF SCSI command
756     struct SCSICmd scsi;
757     UBYTE play_cmd[10] = {0x47, 0, 0, start_m, start_s, start_f, end_m, end_s, end_f, 0};
758     scsi.scsi_Data = (UWORD *)tmp_buf;
759     scsi.scsi_Length = 0;
760     scsi.scsi_Command = play_cmd;
761     scsi.scsi_CmdLength = 10;
762     scsi.scsi_Flags = SCSIF_READ;
763     scsi.scsi_Status = 0;
764     fh->io->io_Data = &scsi;
765     fh->io->io_Length = sizeof(scsi);
766     fh->io->io_Command = HD_SCSICMD;
767     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
768     return false;
769     return true;
770     }
771     }
772    
773    
774     /*
775     * Pause CD audio
776     */
777    
778     bool SysCDPause(void *arg)
779     {
780     file_handle *fh = (file_handle *)arg;
781     if (!fh)
782     return false;
783    
784     if (fh->is_file)
785     return false;
786     else {
787    
788     // Send PAUSE RESUME SCSI command
789     struct SCSICmd scsi;
790     static const UBYTE pause_cmd[10] = {0x4b, 0, 0, 0, 0, 0, 0, 0, 0, 0};
791     scsi.scsi_Data = (UWORD *)tmp_buf;
792     scsi.scsi_Length = 0;
793     scsi.scsi_Command = (UBYTE *)pause_cmd;
794     scsi.scsi_CmdLength = 10;
795     scsi.scsi_Flags = SCSIF_READ;
796     scsi.scsi_Status = 0;
797     fh->io->io_Data = &scsi;
798     fh->io->io_Length = sizeof(scsi);
799     fh->io->io_Command = HD_SCSICMD;
800     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
801     return false;
802     return true;
803     }
804     }
805    
806    
807     /*
808     * Resume paused CD audio
809     */
810    
811     bool SysCDResume(void *arg)
812     {
813     file_handle *fh = (file_handle *)arg;
814     if (!fh)
815     return false;
816    
817     if (fh->is_file)
818     return false;
819     else {
820    
821     // Send PAUSE RESUME SCSI command
822     struct SCSICmd scsi;
823     static const UBYTE resume_cmd[10] = {0x4b, 0, 0, 0, 0, 0, 0, 0, 1, 0};
824     scsi.scsi_Data = (UWORD *)tmp_buf;
825     scsi.scsi_Length = 0;
826     scsi.scsi_Command = (UBYTE *)resume_cmd;
827     scsi.scsi_CmdLength = 10;
828     scsi.scsi_Flags = SCSIF_READ;
829     scsi.scsi_Status = 0;
830     fh->io->io_Data = &scsi;
831     fh->io->io_Length = sizeof(scsi);
832     fh->io->io_Command = HD_SCSICMD;
833     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
834     return false;
835     return true;
836     }
837     }
838    
839    
840     /*
841     * Stop CD audio
842     */
843    
844     bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f)
845     {
846     file_handle *fh = (file_handle *)arg;
847     if (!fh)
848     return false;
849    
850     if (fh->is_file)
851     return false;
852     else {
853    
854     uint8 end_m = lead_out_m;
855     uint8 end_s = lead_out_s;
856     uint8 end_f = lead_out_f + 1;
857     if (end_f >= 75) {
858     end_f = 0;
859     end_s++;
860     if (end_s >= 60) {
861     end_s = 0;
862     end_m++;
863     }
864     }
865    
866     // Send PLAY AUDIO MSF SCSI command (play first frame of lead-out area)
867     struct SCSICmd scsi;
868     UBYTE play_cmd[10] = {0x47, 0, 0, lead_out_m, lead_out_s, lead_out_f, end_m, end_s, end_f, 0};
869     scsi.scsi_Data = (UWORD *)tmp_buf;
870     scsi.scsi_Length = 0;
871     scsi.scsi_Command = play_cmd;
872     scsi.scsi_CmdLength = 10;
873     scsi.scsi_Flags = SCSIF_READ;
874     scsi.scsi_Status = 0;
875     fh->io->io_Data = &scsi;
876     fh->io->io_Length = sizeof(scsi);
877     fh->io->io_Command = HD_SCSICMD;
878     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
879     return false;
880     return true;
881     }
882     }
883    
884    
885     /*
886     * Perform CD audio fast-forward/fast-reverse operation starting from specified address
887     */
888    
889     bool SysCDScan(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, bool reverse)
890     {
891     file_handle *fh = (file_handle *)arg;
892     if (!fh)
893     return false;
894    
895     //!!
896     return false;
897     }
898    
899    
900     /*
901     * Set CD audio volume (0..255 each channel)
902     */
903    
904     void SysCDSetVolume(void *arg, uint8 left, uint8 right)
905     {
906     file_handle *fh = (file_handle *)arg;
907     if (!fh)
908     return;
909    
910     if (!fh->is_file) {
911    
912     // Send MODE SENSE (CD-ROM Audio Control Parameters Page) SCSI command
913     struct SCSICmd scsi;
914     static const UBYTE mode_sense_cmd[6] = {0x1a, 0x08, 0x0e, 0, 20, 0};
915     scsi.scsi_Data = (UWORD *)tmp_buf;
916     scsi.scsi_Length = 20;
917     scsi.scsi_Command = (UBYTE *)mode_sense_cmd;
918     scsi.scsi_CmdLength = 6;
919     scsi.scsi_Flags = SCSIF_READ;
920     scsi.scsi_Status = 0;
921     fh->io->io_Data = &scsi;
922     fh->io->io_Length = sizeof(scsi);
923     fh->io->io_Command = HD_SCSICMD;
924     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
925     return;
926    
927     tmp_buf[6] = 0x04; // Immed
928     tmp_buf[9] = 0; // LBA/sec format
929     tmp_buf[10] = 0; // LBA/sec
930     tmp_buf[11] = 0;
931     tmp_buf[13] = left; // Port 0 volume
932     tmp_buf[15] = right; // Port 1 volume
933    
934     // Send MODE SELECT (CD-ROM Audio Control Parameters Page) SCSI command
935     static const UBYTE mode_select_cmd[6] = {0x15, 0x10, 0, 0, 20, 0};
936     scsi.scsi_Data = (UWORD *)tmp_buf;
937     scsi.scsi_Length = 20;
938     scsi.scsi_Command = (UBYTE *)mode_select_cmd;
939     scsi.scsi_CmdLength = 6;
940     scsi.scsi_Flags = SCSIF_WRITE;
941     scsi.scsi_Status = 0;
942     fh->io->io_Data = &scsi;
943     fh->io->io_Length = sizeof(scsi);
944     fh->io->io_Command = HD_SCSICMD;
945     DoIO((struct IORequest *)fh->io);
946     }
947     }
948    
949    
950     /*
951     * Get CD audio volume (0..255 each channel)
952     */
953    
954     void SysCDGetVolume(void *arg, uint8 &left, uint8 &right)
955     {
956     file_handle *fh = (file_handle *)arg;
957     if (!fh)
958     return;
959    
960     if (!fh->is_file) {
961    
962     // Send MODE SENSE (CD-ROM Audio Control Parameters Page) SCSI command
963     struct SCSICmd scsi;
964     static const UBYTE mode_sense_cmd[6] = {0x1a, 0x08, 0x0e, 0, 20, 0};
965     scsi.scsi_Data = (UWORD *)tmp_buf;
966     scsi.scsi_Length = 20;
967     scsi.scsi_Command = (UBYTE *)mode_sense_cmd;
968     scsi.scsi_CmdLength = 6;
969     scsi.scsi_Flags = SCSIF_READ;
970     scsi.scsi_Status = 0;
971     fh->io->io_Data = &scsi;
972     fh->io->io_Length = sizeof(scsi);
973     fh->io->io_Command = HD_SCSICMD;
974     if (DoIO((struct IORequest *)fh->io) || scsi.scsi_Status)
975     return;
976     left = tmp_buf[13]; // Port 0 volume
977     right = tmp_buf[15]; // Port 1 volume
978     }
979     }