home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 25 / AACD 25.iso / AACD / Utilities / BasiliskII / src / AmigaOS / sys_amiga.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-02-02  |  24.1 KB  |  1,061 lines

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