home *** CD-ROM | disk | FTP | other *** search
- /*
- * scsi.cpp - SCSI Manager
- *
- * Basilisk II (C) 1997-2001 Christian Bauer
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
- /*
- * SEE ALSO
- * Inside Macintosh: Devices, chapter 3 "SCSI Manager"
- * Technote DV 24: "Fear No SCSI"
- */
-
- #include <stdio.h>
- #include <string.h>
-
- #include "sysdeps.h"
- #include "cpu_emulation.h"
- #include "main.h"
- #include "user_strings.h"
- #include "scsi.h"
-
- #define DEBUG 0
- #include "debug.h"
-
-
- // Error codes
- enum {
- scCommErr = 2,
- scArbNBErr,
- scBadParmsErr,
- scPhaseErr,
- scCompareErr,
- scMgrBusyErr,
- scSequenceErr,
- scBusTOErr,
- scComplPhaseErr
- };
-
- // TIB opcodes
- enum {
- scInc = 1,
- scNoInc,
- scAdd,
- scMove,
- scLoop,
- scNop,
- scStop,
- scComp
- };
-
- // Logical SCSI phases
- enum {
- PH_FREE, // Bus free
- PH_ARBITRATED, // Bus arbitrated (after SCSIGet())
- PH_SELECTED, // Target selected (after SCSISelect())
- PH_TRANSFER // Command sent (after SCSICmd())
- };
-
- // Global variables
- static int target_id; // ID of active target
- static int phase; // Logical SCSI phase
- static uint16 fake_status; // Faked 5830 status word
- static bool reading; // Flag: reading from device
-
- const int SG_TABLE_SIZE = 1024;
- static int sg_index; // Index of first unused entry in S/G table
- static uint8 *sg_ptr[SG_TABLE_SIZE]; // Scatter/gather table data pointer (host address space)
- static uint32 sg_len[SG_TABLE_SIZE]; // Scatter/gather table data length
- static uint32 sg_total_length; // Total data length
-
-
- /*
- * Execute TIB, constructing S/G table
- */
-
- static int16 exec_tib(uint32 tib)
- {
- for (;;) {
-
- // Read next opcode and parameters
- uint16 cmd = ReadMacInt16(tib); tib += 2;
- uint32 ptr = ReadMacInt32(tib); tib += 4;
- uint32 len = ReadMacInt32(tib); tib += 4;
-
- D(bug(" %d %08x %d\n", cmd, ptr, len));
-
- switch (cmd) {
- case scInc:
- WriteMacInt32(tib - 8, ptr + len);
- case scNoInc:
- if ((sg_index > 0) && (Mac2HostAddr(ptr) == sg_ptr[sg_index-1] + sg_len[sg_index-1]))
- sg_len[sg_index-1] += len; // Merge to previous entry
- else {
- if (sg_index == SG_TABLE_SIZE) {
- ErrorAlert(GetString(STR_SCSI_SG_FULL_ERR));
- return -108;
- }
- sg_ptr[sg_index] = Mac2HostAddr(ptr); // Create new entry
- sg_len[sg_index] = len;
- sg_index++;
- }
- sg_total_length += len;
- break;
-
- case scAdd:
- WriteMacInt32(ptr, ReadMacInt32(ptr) + len);
- break;
-
- case scMove:
- WriteMacInt32(len, ReadMacInt32(ptr));
- break;
-
- case scLoop:
- WriteMacInt32(tib - 4, len - 1);
- if (len - 1 > 0)
- tib += (int32)ptr - 10;
- break;
-
- case scNop:
- break;
-
- case scStop:
- return 0;
-
- case scComp:
- printf("WARNING: Unimplemented scComp opcode\n");
- return scCompareErr;
-
- default:
- printf("FATAL: Unrecognized TIB opcode %d\n", cmd);
- return scBadParmsErr;
- }
- }
- }
-
-
- /*
- * Reset SCSI bus
- */
-
- int16 SCSIReset(void)
- {
- D(bug("SCSIReset\n"));
-
- phase = PH_FREE;
- fake_status = 0x0000; // Bus free
- sg_index = 0;
- target_id = 8;
- return 0;
- }
-
-
- /*
- * Arbitrate bus
- */
-
- int16 SCSIGet(void)
- {
- D(bug("SCSIGet\n"));
- if (phase != PH_FREE)
- return scMgrBusyErr;
-
- phase = PH_ARBITRATED;
- fake_status = 0x0040; // Bus busy
- reading = false;
- sg_index = 0; // Flush S/G table
- sg_total_length = 0;
- return 0;
- }
-
-
- /*
- * Select SCSI device
- */
-
- int16 SCSISelect(int id)
- {
- D(bug("SCSISelect %d\n", id));
- if (phase != PH_ARBITRATED)
- return scSequenceErr;
-
- // ID valid?
- if (id >= 0 && id <= 7) {
- target_id = id;
-
- // Target present?
- if (scsi_is_target_present(target_id)) {
- phase = PH_SELECTED;
- fake_status = 0x006a; // Target selected, command phase
- return 0;
- }
- }
-
- // Error
- phase = PH_FREE;
- fake_status = 0x0000; // Bus free
- return scCommErr;
- }
-
-
- /*
- * Send SCSI command
- */
-
- int16 SCSICmd(int cmd_length, uint8 *cmd)
- {
- D(bug("SCSICmd len %d, cmd %08x%08x%08x\n", cmd_length, ntohl(0[(uint32 *)cmd]), ntohl(1[(uint32 *)cmd]), ntohl(2[(uint32 *)cmd])));
- if (phase != PH_SELECTED)
- return scPhaseErr;
-
- // Commdn length valid?
- if (cmd_length != 6 && cmd_length != 10 && cmd_length != 12)
- return scBadParmsErr;
-
- // Set command, extract LUN
- scsi_set_cmd(cmd_length, cmd);
-
- // Extract LUN, set target
- if (!scsi_set_target(target_id, (cmd[1] >> 5) & 7)) {
- phase = PH_FREE;
- fake_status = 0x0000; // Bus free
- return scCommErr;
- }
-
- phase = PH_TRANSFER;
- fake_status = 0x006e; // Target selected, data phase
- return 0;
- }
-
-
- /*
- * Read data
- */
-
- int16 SCSIRead(uint32 tib)
- {
- D(bug("SCSIRead TIB %08lx\n", tib));
- if (phase != PH_TRANSFER)
- return scPhaseErr;
-
- // Execute TIB, fill S/G table
- reading = true;
- return exec_tib(tib);
- }
-
-
- /*
- * Write data
- */
-
- int16 SCSIWrite(uint32 tib)
- {
- D(bug("SCSIWrite TIB %08lx\n", tib));
- if (phase != PH_TRANSFER)
- return scPhaseErr;
-
- // Execute TIB, fill S/G table
- return exec_tib(tib);
- }
-
-
- /*
- * Wait for command completion (we're actually doing everything in here...)
- */
-
- int16 SCSIComplete(uint32 timeout, uint32 message, uint32 stat)
- {
- D(bug("SCSIComplete wait %d, msg %08lx, stat %08lx\n", timeout, message, stat));
- WriteMacInt16(message, 0);
- if (phase != PH_TRANSFER)
- return scPhaseErr;
-
- // Send command, process S/G table
- uint16 scsi_stat = 0;
- bool success = scsi_send_cmd(sg_total_length, reading, sg_index, sg_ptr, sg_len, &scsi_stat, timeout);
- WriteMacInt16(stat, scsi_stat);
-
- // Complete command
- phase = PH_FREE;
- fake_status = 0x0000; // Bus free
- return success ? 0 : scCommErr;
- }
-
-
- /*
- * Get bus status
- */
-
- uint16 SCSIStat(void)
- {
- D(bug("SCSIStat returns %04x\n", fake_status));
- return fake_status;
- }
-
-
- /*
- * SCSI Manager busy?
- */
-
- int16 SCSIMgrBusy(void)
- {
- // D(bug("SCSIMgrBusy\n"));
- return phase != PH_FREE;
- }
-