Files
86Box/src/aha154x.c
TC1995 1ca7016a8e Fixed Test Unit Ready being the culprit of the non-working Read 10.
Made Unknown transfers as invalid but with SCSI Status Good/Ok in the Adaptec 1540.
SCSI Commands with no transfers are left with no transfer function.
2016-11-12 16:30:09 +01:00

1112 lines
30 KiB
C

/* Copyright holders: SA1988
see COPYING for more details
*/
/*Adaptec 154x SCSI emulation*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "ibm.h"
#include "device.h"
#include "io.h"
#include "mem.h"
#include "dma.h"
#include "rom.h"
#include "pic.h"
#include "timer.h"
#include "cdrom.h"
#include "scsi.h"
#include "aha154x.h"
typedef struct
{
uint8_t hi;
uint8_t mid;
uint8_t lo;
} addr24;
#define ADDR_TO_U32(x) (((x).hi << 16) | ((x).mid << 8) | (x).lo & 0xFF)
#define U32_TO_ADDR(a, x) do {(a).hi = (x) >> 16; (a).mid = (x) >> 8; (a).lo = (x) & 0xFF;} while(0)
// I/O Port interface
// READ Port x+0: STATUS
// WRITE Port x+0: CONTROL
//
// READ Port x+1: DATA
// WRITE Port x+1: COMMAND
//
// READ Port x+2: INTERRUPT STATUS
// WRITE Port x+2: (undefined?)
//
// R/W Port x+3: (undefined)
// READ STATUS flags
#define STAT_STST 0x80 // self-test in progress
#define STAT_DFAIL 0x40 // internal diagnostic failure
#define STAT_INIT 0x20 // mailbox initialization required
#define STAT_IDLE 0x10 // HBA is idle
#define STAT_CDFULL 0x08 // Command/Data output port is full
#define STAT_DFULL 0x04 // Data input port is full
#define STAT_INVCMD 0x01 // Invalid command
// READ INTERRUPT STATUS flags
#define INTR_ANY 0x80 // any interrupt
#define INTR_SRCD 0x08 // SCSI reset detected
#define INTR_HACC 0x04 // HA command complete
#define INTR_MBOA 0x02 // MBO empty
#define INTR_MBIF 0x01 // MBI full
// WRITE CONTROL commands
#define CTRL_HRST 0x80 // Hard reset
#define CTRL_SRST 0x40 // Soft reset
#define CTRL_IRST 0x20 // interrupt reset
#define CTRL_SCRST 0x10 // SCSI bus reset
// READ/WRITE DATA commands
#define CMD_NOP 0x00 // No operation
#define CMD_MBINIT 0x01 // mailbox initialization
#define CMD_START_SCSI 0x02 // Start SCSI command
#define CMD_INQUIRY 0x04 // Adapter inquiry
#define CMD_EMBOI 0x05 // enable Mailbox Out Interrupt
#define CMD_SELTIMEOUT 0x06 // Set SEL timeout
#define CMD_BUSON_TIME 0x07 // set bus-On time
#define CMD_BUSOFF_TIME 0x08 // set bus-off time
#define CMD_DMASPEED 0x09 // set ISA DMA speed
#define CMD_RETDEVS 0x0A // return installed devices
#define CMD_RETCONF 0x0B // return configuration data
#define CMD_TARGET 0x0C // set HBA to target mode
#define CMD_RETSETUP 0x0D // return setup data
#define CMD_ECHO 0x1F // ECHO command data
typedef struct MailboxInit_t
{
uint8_t Count;
addr24 Address;
} MailboxInit_t;
typedef struct MailboxInitExtended_t
{
uint8_t Count;
uint32_t Address;
} MailboxInitExtended_t;
///////////////////////////////////////////////////////////////////////////////
//
// Mailbox Definitions
//
//
///////////////////////////////////////////////////////////////////////////////
//
// Mailbox Out
//
//
// MBO Command Values
//
#define MBO_FREE 0x00
#define MBO_START 0x01
#define MBO_ABORT 0x02
//
// Mailbox In
//
//
// MBI Status Values
//
#define MBI_FREE 0x00
#define MBI_SUCCESS 0x01
#define MBI_ABORT 0x02
#define MBI_NOT_FOUND 0x03
#define MBI_ERROR 0x04
typedef struct Mailbox_t
{
uint8_t CmdStatus;
addr24 CCBPointer;
} Mailbox_t;
typedef struct Mailbox32_t
{
uint8_t CmdStatus;
uint32_t CCBPointer;
} Mailbox32_t;
///////////////////////////////////////////////////////////////////////////////
//
// CCB - Adaptec SCSI Command Control Block
//
// The CCB is a superset of the CDB (Command Descriptor Block)
// and specifies detailed information about a SCSI command.
//
///////////////////////////////////////////////////////////////////////////////
//
// Byte 0 Command Control Block Operation Code
//
#define SCSI_INITIATOR_COMMAND 0x00
#define TARGET_MODE_COMMAND 0x01
#define SCATTER_GATHER_COMMAND 0x02
#define SCSI_INITIATOR_COMMAND_RES 0x03
#define SCATTER_GATHER_COMMAND_RES 0x04
#define BUS_RESET 0x81
//
// Byte 1 Address and Direction Control
//
#define CCB_TARGET_ID_SHIFT 0x06 // CCB Op Code = 00, 02
#define CCB_INITIATOR_ID_SHIFT 0x06 // CCB Op Code = 01
#define CCB_DATA_XFER_IN 0x01
#define CCB_DATA_XFER_OUT 0x02
#define CCB_LUN_MASK 0x07 // Logical Unit Number
//
// Byte 2 SCSI_Command_Length - Length of SCSI CDB
//
// Byte 3 Request Sense Allocation Length
//
#define FOURTEEN_BYTES 0x00 // Request Sense Buffer size
#define NO_AUTO_REQUEST_SENSE 0x01 // No Request Sense Buffer
//
// Bytes 4, 5 and 6 Data Length // Data transfer byte count
//
// Bytes 7, 8 and 9 Data Pointer // SGD List or Data Buffer
//
// Bytes 10, 11 and 12 Link Pointer // Next CCB in Linked List
//
// Byte 13 Command Link ID // TBD (I don't know yet)
//
// Byte 14 Host Status // Host Adapter status
//
#define CCB_COMPLETE 0x00 // CCB completed without error
#define CCB_LINKED_COMPLETE 0x0A // Linked command completed
#define CCB_LINKED_COMPLETE_INT 0x0B // Linked complete with interrupt
#define CCB_SELECTION_TIMEOUT 0x11 // Set SCSI selection timed out
#define CCB_DATA_OVER_UNDER_RUN 0x12
#define CCB_UNEXPECTED_BUS_FREE 0x13 // Target dropped SCSI BSY
#define CCB_PHASE_SEQUENCE_FAIL 0x14 // Target bus phase sequence failure
#define CCB_BAD_MBO_COMMAND 0x15 // MBO command not 0, 1 or 2
#define CCB_INVALID_OP_CODE 0x16 // CCB invalid operation code
#define CCB_BAD_LINKED_LUN 0x17 // Linked CCB LUN different from first
#define CCB_INVALID_DIRECTION 0x18 // Invalid target direction
#define CCB_DUPLICATE_CCB 0x19 // Duplicate CCB
#define CCB_INVALID_CCB 0x1A // Invalid CCB - bad parameter
//
// Byte 15 Target Status
//
// See SCSI.H files for these statuses.
//
//
// Bytes 16 and 17 Reserved (must be 0)
//
//
// Bytes 18 through 18+n-1, where n=size of CDB Command Descriptor Block
//
typedef struct CCB
{
uint8_t Opcode;
uint8_t Lun:3;
uint8_t ControlByte:2;
uint8_t Id:3;
uint8_t CdbLength;
uint8_t RequestSenseLength;
addr24 DataLength;
addr24 DataPointer;
addr24 LinkPointer;
uint8_t LinkId;
uint8_t HostStatus;
uint8_t TargetStatus;
uint8_t Reserved[2];
uint8_t Cdb[12];
} CCB;
///////////////////////////////////////////////////////////////////////////////
//
// Scatter/Gather Segment List Definitions
//
///////////////////////////////////////////////////////////////////////////////
//
// Adapter limits
//
#define MAX_SG_DESCRIPTORS 17
typedef struct SGE
{
addr24 Segment;
addr24 SegmentPointer;
} SGE;
typedef struct AdaptecRequests_t
{
CCB CmdBlock;
uint8_t *RequestSenseBuffer;
uint32_t CCBPointer;
} AdaptecRequests_t;
typedef struct Adaptec_t
{
AdaptecRequests_t AdaptecRequests;
uint8_t Status;
uint8_t Interrupt;
uint8_t Geometry;
uint8_t Command;
uint8_t CmdBuf[5];
uint8_t CmdParam;
uint8_t CmdParamLeft;
uint8_t DataBuf[64];
uint8_t DataReply;
uint8_t DataReplyLeft;
uint32_t MailboxCount;
uint32_t MailboxOutAddr;
uint32_t MailboxOutPosCur;
uint32_t MailboxInAddr;
uint32_t MailboxInPosCur;
int Irq;
int DmaChannel;
int DmaPort1, DmaData1;
int DmaPort2, DmaData2;
int BusOn;
int BusOff;
int DmaSpeed;
int ScsiBios;
} Adaptec_t;
Adaptec_t AdaptecLUN;
static void AdaptecSCSIRequestSetup(Adaptec_t *Adaptec, uint32_t CCBPointer);
static void AdaptecStartMailbox(Adaptec_t *Adaptec);
typedef void (*AdaptecMemCopyCallback)(Adaptec_t *Adaptec, uint32_t Addr, SGBUF *SegmentBuffer,
uint32_t Copy, uint32_t *Skip);
int aha154x_do_log = 0;
void AdaptecLog(const char *format, ...)
{
if (aha154x_do_log)
{
va_list ap;
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
fflush(stdout);
}
}
static void AdaptecSetDMAChannel(int DmaPort1, int DmaData1, int DmaPort2, int DmaData2)
{
dma_channel_write(DmaPort1, DmaData1);
dma_channel_write(DmaPort2, DmaData2);
}
static void AdaptecClearInterrupt(Adaptec_t *Adaptec)
{
AdaptecLog("Adaptec: Clearing Interrupt 0x%02X\n", Adaptec->Interrupt);
Adaptec->Interrupt = 0;
picintc(1 << Adaptec->Irq);
}
static void AdaptecReset(Adaptec_t *Adaptec)
{
Adaptec->Status = STAT_IDLE | STAT_INIT;
Adaptec->Geometry = 0x80;
Adaptec->Command = 0xFF;
Adaptec->CmdParam = 0;
Adaptec->CmdParamLeft = 0;
Adaptec->MailboxOutPosCur = 0;
Adaptec->MailboxInPosCur = 0;
AdaptecClearInterrupt(Adaptec);
}
static void AdaptecCommandComplete(Adaptec_t *Adaptec)
{
Adaptec->Status |= STAT_IDLE;
Adaptec->DataReply = 0;
if (Adaptec->Command != CMD_START_SCSI)
{
Adaptec->Status &= ~STAT_DFULL;
Adaptec->Interrupt = INTR_ANY | INTR_HACC;
picint(1 << Adaptec->Irq);
}
Adaptec->Command = 0xFF;
Adaptec->CmdParam = 0;
}
static void AdaptecInitReset(Adaptec_t *Adaptec, int Reset)
{
AdaptecReset(Adaptec);
if (Reset)
{
Adaptec->Status |= STAT_STST;
Adaptec->Status &= ~STAT_IDLE;
}
}
static void AdaptecMailboxLogInformation(Mailbox_t *Mailbox)
{
AdaptecLog("Adaptec: Mailbox Dump Log\n");
AdaptecLog("CCB Pointer=%#x\n", ADDR_TO_U32(Mailbox->CCBPointer));
AdaptecLog("Command or Status Code=%02X\n", Mailbox->CmdStatus);
}
static void AdaptecCCBLogInformation(CCB *CmdBlock)
{
pclog("Adaptec: CCB Dump Log\n");
pclog("Opcode=%#x\n", CmdBlock->Opcode);
pclog("Data Direction=%u\n", CmdBlock->ControlByte);
pclog("Cdb Length=%d\n", CmdBlock->CdbLength);
pclog("Sense Length=%u\n", CmdBlock->RequestSenseLength);
pclog("Data Length=%u\n", ADDR_TO_U32(CmdBlock->DataLength));
pclog("Data Pointer=%u\n", ADDR_TO_U32(CmdBlock->DataPointer));
pclog("Host Adapter Status=0x%02X\n", CmdBlock->HostStatus);
pclog("Target Device Status=0x%02X\n", CmdBlock->TargetStatus);
pclog("Cdb[0]=%#x\n", CmdBlock->Cdb[0]);
pclog("Cdb[1]=%#x\n", CmdBlock->Cdb[1]);
}
static void AdaptecMailboxIn(Adaptec_t *Adaptec, uint32_t CCBPointer, CCB *CmdBlock, uint8_t HostStatus, uint8_t TargetStatus, uint8_t MailboxCompletionCode)
{
Mailbox32_t Mailbox32;
Mailbox_t MailboxIn;
Mailbox32.CCBPointer = CCBPointer;
Mailbox32.CmdStatus = MailboxCompletionCode;
uint32_t Incoming = Adaptec->MailboxInAddr + (Adaptec->MailboxInPosCur * sizeof(Mailbox_t));
if (MailboxCompletionCode != MBI_NOT_FOUND)
{
CmdBlock->HostStatus = HostStatus;
CmdBlock->TargetStatus = TargetStatus;
uint32_t CCBSize = offsetof(CCB, Cdb);
const void *Data = (const void *)&CmdBlock;
uint32_t l = PageLengthReadWrite(CCBPointer, CCBSize);
//AdaptecBufferWrite(&Adaptec->AdaptecRequests, Incoming, &MailboxIn, sizeof(Mailbox_t));
memcpy(&ram[CCBPointer], Data, l);
CCBPointer += l;
Data -= l;
CCBSize += l;
}
MailboxIn.CmdStatus = Mailbox32.CmdStatus;
U32_TO_ADDR(MailboxIn.CCBPointer, Mailbox32.CCBPointer);
AdaptecLog("Adaptec: Mailbox: status code=0x%02X, CCB at 0x%08X\n", MailboxIn.CmdStatus, ADDR_TO_U32(MailboxIn.CCBPointer));
uint32_t MailboxSize = sizeof(Mailbox_t);
const void *Data = (const void *)&MailboxIn;
uint32_t l = PageLengthReadWrite(Incoming, MailboxSize);
//AdaptecBufferWrite(&Adaptec->AdaptecRequests, Incoming, &MailboxIn, sizeof(Mailbox_t));
memcpy(&ram[Incoming], Data, l);
Incoming += l;
Data -= l;
MailboxSize += l;
Adaptec->MailboxInPosCur++;
if (Adaptec->MailboxInPosCur > Adaptec->MailboxCount)
Adaptec->MailboxInPosCur = 0;
Adaptec->Interrupt = INTR_MBIF | INTR_ANY;
picint(1 << Adaptec->Irq);
}
static void AdaptecReadSGEntries(uint32_t SGList, uint32_t Entries, SGE *SG)
{
uint32_t SGSize = Entries * sizeof(SGE);
void *Data = (void *)SG;
uint32_t l = PageLengthReadWrite(SGList, SGSize);
//AdaptecBufferRead(&Adaptec->AdaptecRequests, SGList, SG, Entries * sizeof(SGE));
memcpy(Data, &ram[SGList], l);
SGList += l;
Data -= l;
SGSize += l;
}
static void AdaptecQueryDataBufferSize(Adaptec_t *Adaptec, CCB *CmdBlock, uint32_t *pBufferSize)
{
uint32_t BufferSize = 0;
DataPointer = ADDR_TO_U32(CmdBlock->DataPointer);
DataLength = ADDR_TO_U32(CmdBlock->DataLength);
if (DataLength)
{
if (CmdBlock->Opcode == SCATTER_GATHER_COMMAND ||
CmdBlock->Opcode == SCATTER_GATHER_COMMAND_RES)
{
uint32_t ScatterGatherRead;
uint32_t ScatterEntry;
SGE ScatterGatherBuffer[MAX_SG_DESCRIPTORS];
uint32_t ScatterGatherLeft = DataLength / sizeof(SGE);
uint32_t ScatterGatherAddrCurrent = DataPointer;
do
{
ScatterGatherRead = (ScatterGatherLeft < ELEMENTS(ScatterGatherBuffer))
? ScatterGatherLeft : ELEMENTS(ScatterGatherBuffer);
ScatterGatherLeft -= ScatterGatherRead;
AdaptecReadSGEntries(ScatterGatherAddrCurrent, ScatterGatherRead, ScatterGatherBuffer);
for (ScatterEntry = 0; ScatterEntry < ScatterGatherRead; ScatterEntry++)
BufferSize += ADDR_TO_U32(ScatterGatherBuffer[ScatterEntry].Segment);
ScatterGatherAddrCurrent += ScatterGatherRead * sizeof(SGE);
} while (ScatterGatherLeft > 0);
AdaptecLog("Adaptec: Data Buffer Size=%u\n", BufferSize);
}
else if (CmdBlock->Opcode == SCSI_INITIATOR_COMMAND ||
CmdBlock->Opcode == SCSI_INITIATOR_COMMAND_RES)
BufferSize = DataLength;
}
*pBufferSize = BufferSize;
}
static void AdaptecCopyBufferFromGuestWorker(Adaptec_t *Adaptec, uint32_t Address, SGBUF *SegmentBuffer,
uint32_t Copy, uint32_t *pSkip)
{
uint32_t Skipped = MIN(Copy, *pSkip);
Copy -= Skipped;
Address += Skipped;
*pSkip -= Skipped;
while (Copy)
{
uint32_t Segment = Copy;
uint8_t *SegmentPointer = SegmentBufferGetNextSegment(SegmentBuffer, Segment);
uint32_t SegmentSize = Segment;
void *Data = (void *)SegmentPointer;
uint32_t l = PageLengthReadWrite(Address, SegmentSize);
//AdaptecBufferRead(&Adaptec->AdaptecRequests, Address, SegmentPointer, Segment);
memcpy(Data, &ram[Address], l);
Address += l;
Data -= l;
SegmentSize += l;
Address += Segment;
Copy -= Segment;
}
}
static void AdaptecCopyBufferToGuestWorker(Adaptec_t *Adaptec, uint32_t Address, SGBUF *SegmentBuffer,
uint32_t Copy, uint32_t *pSkip)
{
uint32_t Skipped = MIN(Copy, *pSkip);
Copy -= Skipped;
Address += Skipped;
*pSkip -= Skipped;
while (Copy)
{
uint32_t Segment = Copy;
uint8_t *SegmentPointer = SegmentBufferGetNextSegment(SegmentBuffer, Segment);
uint32_t SegmentSize = Segment;
const void *Data = (const void *)SegmentPointer;
uint32_t l = PageLengthReadWrite(Address, SegmentSize);
//AdaptecBufferWrite(&Adaptec->AdaptecRequests, Address, SegmentPointer, Segment);
memcpy(&ram[Address], Data, l);
Address += l;
Data -= l;
SegmentSize += l;
Address += Segment;
Copy -= Segment;
}
}
static int AdaptecScatterGatherBufferWalker(Adaptec_t *Adaptec, AdaptecRequests_t *AdaptecRequests,
AdaptecMemCopyCallback IoCopyWorker, SGBUF *SegmentBuffer,
uint32_t Skip, uint32_t Copy)
{
uint32_t Copied = 0;
Copy += Skip;
DataPointer = ADDR_TO_U32(AdaptecRequests->CmdBlock.DataPointer);
DataLength = ADDR_TO_U32(AdaptecRequests->CmdBlock.DataLength);
AdaptecLog("Adaptec: S/G Buffer Walker\n");
AdaptecLog("Data Length=%u\n", DataLength);
AdaptecLog("Data Buffer Copy=%u\n", Copy);
if ((DataLength > 0) && (AdaptecRequests->CmdBlock.ControlByte == CCB_DATA_XFER_IN ||
AdaptecRequests->CmdBlock.ControlByte == CCB_DATA_XFER_OUT))
{
if (AdaptecRequests->CmdBlock.Opcode == SCATTER_GATHER_COMMAND ||
AdaptecRequests->CmdBlock.Opcode == SCATTER_GATHER_COMMAND_RES)
{
uint32_t ScatterGatherRead;
uint32_t ScatterEntry;
SGE ScatterGatherBuffer[MAX_SG_DESCRIPTORS];
uint32_t ScatterGatherLeft = DataLength / sizeof(SGE);
uint32_t ScatterGatherAddrCurrent = DataPointer;
do
{
ScatterGatherRead = (ScatterGatherLeft < ELEMENTS(ScatterGatherBuffer))
? ScatterGatherLeft : ELEMENTS(ScatterGatherBuffer);
ScatterGatherLeft -= ScatterGatherRead;
AdaptecReadSGEntries(ScatterGatherAddrCurrent, ScatterGatherRead, ScatterGatherBuffer);
for (ScatterEntry = 0; ScatterEntry < ScatterGatherRead; ScatterEntry++)
{
uint32_t Address;
uint32_t CopyThis;
AdaptecLog("Adaptec: Scatter Entry=%u\n", ScatterEntry);
Address = ADDR_TO_U32(ScatterGatherBuffer[ScatterEntry].SegmentPointer);
CopyThis = MIN(Copy, ADDR_TO_U32(ScatterGatherBuffer[ScatterEntry].Segment));
AdaptecLog("Adaptec: S/G Address=0x%04X, Copy=%u", Address, CopyThis);
IoCopyWorker(Adaptec, Address, SegmentBuffer, CopyThis, &Skip);
Copied += CopyThis;
Copy -= CopyThis;
}
ScatterGatherAddrCurrent += ScatterGatherRead * sizeof(SGE);
} while (ScatterGatherLeft > 0 && Copy > 0);
}
else if (AdaptecRequests->CmdBlock.Opcode == SCSI_INITIATOR_COMMAND ||
AdaptecRequests->CmdBlock.Opcode == SCSI_INITIATOR_COMMAND_RES)
{
uint32_t Address = DataPointer;
AdaptecLog("Adaptec: Non-scattered buffer\n");
pclog("Data Pointer=%#x\n", DataPointer);
pclog("Data Length=%u\n", DataLength);
AdaptecLog("Pointer Address=%#x\n", Address);
IoCopyWorker(Adaptec, Address, SegmentBuffer, MIN(DataLength, Copy), &Skip);
Copied += MIN(DataLength, Copy);
}
}
return Copied - MIN(Skip, Copied);
}
static int AdaptecCopySegmentBufferToGuest(Adaptec_t *Adaptec, AdaptecRequests_t *AdaptecRequests,
SGBUF *SegmentBuffer, uint32_t Skip, uint32_t Copy)
{
return AdaptecScatterGatherBufferWalker(Adaptec, AdaptecRequests, AdaptecCopyBufferToGuestWorker, SegmentBuffer, Skip, Copy);
}
static int AdaptecCopySegmentBufferFromGuest(Adaptec_t *Adaptec, AdaptecRequests_t *AdaptecRequests,
SGBUF *SegmentBuffer, uint32_t Skip, uint32_t Copy)
{
return AdaptecScatterGatherBufferWalker(Adaptec, AdaptecRequests, AdaptecCopyBufferFromGuestWorker, SegmentBuffer, Skip, Copy);
}
uint8_t AdaptecRead(uint16_t Port, void *p)
{
Adaptec_t *Adaptec = &AdaptecLUN;
uint8_t Temp;
switch (Port & 3)
{
case 0:
Temp = Adaptec->Status;
if (Adaptec->Status & STAT_STST)
{
Adaptec->Status &= ~STAT_STST;
Adaptec->Status |= STAT_IDLE;
Temp = Adaptec->Status;
}
break;
case 1:
Temp = Adaptec->DataBuf[Adaptec->DataReply];
if (Adaptec->DataReplyLeft)
{
Adaptec->DataReply++;
Adaptec->DataReplyLeft--;
if (!Adaptec->DataReplyLeft)
{
AdaptecCommandComplete(Adaptec);
}
}
break;
case 2:
Temp = Adaptec->Interrupt;
if (Adaptec->ScsiBios)
{
Adaptec->Interrupt = INTR_MBIF | INTR_ANY;
picint(1 << Adaptec->Irq);
}
break;
case 3:
Temp = Adaptec->Geometry;
break;
}
AdaptecLog("Adaptec: Read Port 0x%02X, Returned Value %02X\n", Port, Temp);
return Temp;
}
void AdaptecWrite(uint16_t Port, uint8_t Val, void *p)
{
Adaptec_t *Adaptec = &AdaptecLUN;
AdaptecLog("Adaptec: Write Port 0x%02X, Value %02X\n", Port, Val);
switch (Port & 3)
{
case 0:
if ((Val & CTRL_HRST) || (Val & CTRL_SRST))
{
int HardReset = !!(Val & CTRL_HRST);
AdaptecLog("Adaptec: %s reset\n", HardReset ? "hard" : "soft");
AdaptecInitReset(Adaptec, HardReset);
break;
}
if (Val & CTRL_IRST)
AdaptecClearInterrupt(Adaptec);
break;
case 1:
if ((Val == 0x02) && (Adaptec->Command == 0xFF))
{
if (Adaptec->MailboxCount)
{
ScsiCallback[scsi_cdrom_id] = 1;
}
break;
}
if (Adaptec->Command == 0xFF)
{
Adaptec->Command = Val;
Adaptec->CmdParam = 0;
Adaptec->Status &= ~(STAT_INVCMD | STAT_IDLE);
AdaptecLog("Adaptec: Operation Code 0x%02X\n", Val);
switch (Adaptec->Command)
{
case 0x03:
case 0x00:
case 0x04:
case 0x84:
case 0x85:
case 0x0A:
case 0x0B:
Adaptec->CmdParamLeft = 0;
break;
case 0x07:
case 0x08:
case 0x09:
case 0x0D:
case 0x1F:
Adaptec->CmdParamLeft = 1;
break;
case 0x06:
Adaptec->CmdParamLeft = 4;
break;
case 0x01:
Adaptec->CmdParamLeft = sizeof(MailboxInit_t);
break;
case 0x28:
case 0x29:
Adaptec->CmdParamLeft = 0;
break;
}
}
else
{
Adaptec->CmdBuf[Adaptec->CmdParam] = Val;
Adaptec->CmdParam++;
Adaptec->CmdParamLeft--;
}
if (!Adaptec->CmdParamLeft)
{
switch (Adaptec->Command)
{
case 0x00:
Adaptec->DataReplyLeft = 0;
break;
case 0x01:
{
MailboxInit_t *MailboxInit = (MailboxInit_t *)Adaptec->CmdBuf;
Adaptec->MailboxCount = MailboxInit->Count;
Adaptec->MailboxOutAddr = ADDR_TO_U32(MailboxInit->Address);
Adaptec->MailboxInAddr = Adaptec->MailboxOutAddr + (Adaptec->MailboxCount * sizeof(Mailbox_t));
AdaptecLog("Adaptec Initialize Mailbox Command\n");
AdaptecLog("Mailbox Out Address=0x%08X\n", Adaptec->MailboxOutAddr);
AdaptecLog("Mailbox In Address=0x%08X\n", Adaptec->MailboxInAddr);
AdaptecLog("Initialized Mailbox, %d entries at 0x%08X\n", MailboxInit->Count, ADDR_TO_U32(MailboxInit->Address));
Adaptec->Status &= ~STAT_INIT;
Adaptec->DataReplyLeft = 0;
}
break;
case 0x03:
break;
case 0x04:
Adaptec->DataBuf[0] = 'A';
Adaptec->DataBuf[1] = '0';
Adaptec->DataBuf[2] = '3';
Adaptec->DataBuf[3] = '1';
Adaptec->DataReplyLeft = 4;
break;
case 0x84:
Adaptec->DataBuf[0] = '0';
Adaptec->DataReplyLeft = 1;
break;
case 0x85:
Adaptec->DataBuf[0] = 'A';
Adaptec->DataReplyLeft = 1;
break;
case 0x06:
Adaptec->DataReplyLeft = 0;
break;
case 0x07:
Adaptec->BusOn = Adaptec->CmdBuf[0];
Adaptec->DataReplyLeft = 0;
break;
case 0x08:
Adaptec->BusOff = Adaptec->CmdBuf[0];
Adaptec->DataReplyLeft = 0;
break;
case 0x09:
Adaptec->DmaSpeed = Adaptec->CmdBuf[0];
Adaptec->DataReplyLeft = 0;
break;
case 0x0A:
if (ScsiDrives[scsi_cdrom_id].LunType == SCSI_CDROM)
Adaptec->DataBuf[scsi_cdrom_id] = 1;
Adaptec->DataBuf[7] = 0;
Adaptec->DataReplyLeft = 8;
break;
case 0x0B:
Adaptec->DataBuf[0] = (1 << Adaptec->DmaChannel);
if (Adaptec->DmaChannel >= 0)
AdaptecSetDMAChannel(Adaptec->DmaPort1, Adaptec->DmaData1,
Adaptec->DmaPort2, Adaptec->DmaData2);
Adaptec->DataBuf[1] = (1 << (Adaptec->Irq - 9));
Adaptec->DataBuf[2] = 7;
Adaptec->DataReplyLeft = 3;
break;
case 0x0D:
Adaptec->DataReplyLeft = Adaptec->CmdBuf[0];
Adaptec->DataBuf[1] = Adaptec->DmaSpeed;
Adaptec->DataBuf[2] = Adaptec->BusOn;
Adaptec->DataBuf[3] = Adaptec->BusOff;
Adaptec->DataBuf[4] = Adaptec->MailboxCount;
Adaptec->DataBuf[5] = Adaptec->MailboxOutAddr&0xFF;
Adaptec->DataBuf[6] = (Adaptec->MailboxOutAddr>>8);
Adaptec->DataBuf[7] = (Adaptec->MailboxOutAddr>>16);
Adaptec->DataBuf[8+scsi_cdrom_id] = 1;
break;
case 0x1F:
Adaptec->DataBuf[0] = Adaptec->CmdBuf[0];
Adaptec->DataReplyLeft = 1;
break;
case 0x28:
case 0x29:
Adaptec->DataReplyLeft = 0;
Adaptec->Status |= STAT_INVCMD;
break;
}
}
if (Adaptec->DataReplyLeft)
Adaptec->Status |= STAT_DFULL;
else if (!Adaptec->CmdParamLeft)
AdaptecCommandComplete(Adaptec);
break;
case 2:
break;
case 3:
break;
}
}
static uint8_t AdaptecConvertSenseLength(uint8_t RequestSenseLength)
{
if (RequestSenseLength == 0)
RequestSenseLength = 14;
else if (RequestSenseLength == 1)
RequestSenseLength = 0;
return RequestSenseLength;
}
static void AdaptecSenseBufferAllocate(AdaptecRequests_t *AdaptecRequests)
{
uint8_t SenseLength = AdaptecConvertSenseLength(AdaptecRequests->CmdBlock.RequestSenseLength);
AdaptecRequests->RequestSenseBuffer = malloc(SenseLength);
}
static void AdaptecSenseBufferFree(AdaptecRequests_t *AdaptecRequests, int Copy)
{
uint8_t SenseLength = AdaptecConvertSenseLength(AdaptecRequests->CmdBlock.RequestSenseLength);
if (Copy && SenseLength)
{
uint32_t SenseBufferAddress;
SenseBufferAddress = AdaptecRequests->CCBPointer;
SenseBufferAddress += AdaptecRequests->CmdBlock.CdbLength + offsetof(CCB, Cdb);
uint32_t SenseSize = SenseLength;
const void *Data = (const void *)AdaptecRequests->RequestSenseBuffer;
uint32_t l = PageLengthReadWrite(SenseBufferAddress, SenseSize);
//AdaptecBufferWrite(AdaptecRequests, SenseBufferAddress, AdaptecRequests->RequestSenseBuffer, SenseLength);
memcpy(&ram[SenseBufferAddress], Data, l);
SenseBufferAddress += l;
Data -= l;
SenseSize += l;
}
free(AdaptecRequests->RequestSenseBuffer);
}
static void AdaptecRequestComplete(SCSI *Scsi, Adaptec_t *Adaptec,
AdaptecRequests_t *AdaptecRequests)
{
if (AdaptecRequests->RequestSenseBuffer)
AdaptecSenseBufferFree(AdaptecRequests, (ScsiStatus != SCSI_STATUS_OK));
if (AdaptecRequests->CmdBlock.Opcode == SCSI_INITIATOR_COMMAND_RES ||
AdaptecRequests->CmdBlock.Opcode == SCATTER_GATHER_COMMAND_RES)
{
uint32_t Residual = 0;
SCSIQueryResidual(Scsi, &Residual);
U32_TO_ADDR(AdaptecRequests->CmdBlock.DataLength, Residual);
}
uint8_t Status = ScsiStatus;
uint32_t CCBPointer = AdaptecRequests->CCBPointer;
CCB CmdBlock;
memcpy(&CmdBlock, &AdaptecRequests->CmdBlock, sizeof(CCB));
if (Status == SCSI_STATUS_OK)
AdaptecMailboxIn(Adaptec, CCBPointer, &CmdBlock, CCB_COMPLETE, SCSI_STATUS_OK,
MBI_SUCCESS);
else if (Status == SCSI_STATUS_CHECK_CONDITION)
AdaptecMailboxIn(Adaptec, CCBPointer, &CmdBlock, CCB_COMPLETE, SCSI_STATUS_CHECK_CONDITION,
MBI_ERROR);
}
uint32_t AdaptecIoRequestCopyFromBuffer(uint32_t OffDst, SGBUF *SegmentBuffer, uint32_t Copy)
{
Adaptec_t *Adaptec = &AdaptecLUN;
uint32_t Copied = 0;
Copied = AdaptecCopySegmentBufferToGuest(Adaptec, &Adaptec->AdaptecRequests, SegmentBuffer, OffDst, Copy);
return Copied;
}
uint32_t AdaptecIoRequestCopyToBuffer(uint32_t OffSrc, SGBUF *SegmentBuffer, uint32_t Copy)
{
Adaptec_t *Adaptec = &AdaptecLUN;
uint32_t Copied = 0;
Copied = AdaptecCopySegmentBufferFromGuest(Adaptec, &Adaptec->AdaptecRequests, SegmentBuffer, OffSrc, Copy);
return Copied;
}
static void AdaptecSCSIRequestSetup(Adaptec_t *Adaptec, uint32_t CCBPointer)
{
AdaptecRequests_t *AdaptecRequests = &Adaptec->AdaptecRequests;
CCB CmdBlock;
uint32_t CCBUSize = sizeof(CCB);
void *Data = (void *)&CmdBlock;
uint32_t l = PageLengthReadWrite(CCBPointer, CCBUSize);
//AdaptecBufferRead(&Adaptec->AdaptecRequests, CCBPointer, &CmdBlock, sizeof(CCBU));
memcpy(Data, &ram[CCBPointer], l);
CCBPointer += l;
Data -= l;
CCBUSize += l;
pclog("Scanning SCSI Target ID %d\n", CmdBlock.Id);
if (CmdBlock.Id == scsi_cdrom_id && CmdBlock.Lun == 0)
{
pclog("SCSI Target ID %d detected and working\n", CmdBlock.Id);
SCSI *Scsi = &ScsiDrives[CmdBlock.Id];
AdaptecRequests->CCBPointer = CCBPointer - 30;
memcpy(&AdaptecRequests->CmdBlock, &CmdBlock, sizeof(CmdBlock));
AdaptecSenseBufferAllocate(AdaptecRequests);
uint32_t BufferSize = 0;
AdaptecQueryDataBufferSize(Adaptec, &CmdBlock, &BufferSize);
uint8_t SenseLength = AdaptecConvertSenseLength(CmdBlock.RequestSenseLength);
pclog("Control Byte Transfer Direction %02X\n", CmdBlock.ControlByte);
if (CmdBlock.ControlByte == CCB_DATA_XFER_OUT)
{
pclog("Adaptec Write Transfer\n");
SCSIWriteTransfer(Scsi, CmdBlock.Id);
}
else if (CmdBlock.ControlByte == CCB_DATA_XFER_IN)
{
pclog("Adaptec Read Transfer\n");
SCSIReadTransfer(Scsi, CmdBlock.Id);
}
else
{
AdaptecMailboxIn(Adaptec, AdaptecRequests->CCBPointer, &CmdBlock, CCB_INVALID_DIRECTION, SCSI_STATUS_OK,
MBI_ERROR);
}
SCSISendCommand(Scsi, CmdBlock.Id, CmdBlock.Cdb, CmdBlock.CdbLength, BufferSize, AdaptecRequests->RequestSenseBuffer, SenseLength);
AdaptecRequestComplete(Scsi, Adaptec, AdaptecRequests);
}
else
{
CmdBlock.Id++;
}
}
static uint32_t AdaptecMailboxOut(Adaptec_t *Adaptec, Mailbox32_t *Mailbox32)
{
Mailbox_t MailboxOut;
uint32_t Outgoing;
Outgoing = Adaptec->MailboxOutAddr + (Adaptec->MailboxOutPosCur * sizeof(Mailbox_t));
uint32_t MailboxSize = sizeof(Mailbox_t);
void *Data = (void *)&MailboxOut;
uint32_t l = PageLengthReadWrite(Outgoing, MailboxSize);
//AdaptecBufferRead(&Adaptec->AdaptecRequests, Outgoing, &MailboxOut, sizeof(Mailbox_t));
memcpy(Data, &ram[Outgoing], l);
Outgoing += l;
Data -= l;
MailboxSize += l;
Mailbox32->CCBPointer = ADDR_TO_U32(MailboxOut.CCBPointer);
Mailbox32->CmdStatus = MailboxOut.CmdStatus;
return Outgoing;
}
static void AdaptecStartMailbox(Adaptec_t *Adaptec)
{
Mailbox32_t Mailbox32;
Mailbox_t MailboxOut;
uint32_t Outgoing;
uint8_t MailboxPosCur = Adaptec->MailboxOutPosCur;
do
{
Outgoing = AdaptecMailboxOut(Adaptec, &Mailbox32);
Adaptec->MailboxOutPosCur = (Adaptec->MailboxOutPosCur + 1) % Adaptec->MailboxCount;
} while ((MailboxPosCur != Adaptec->MailboxOutPosCur) && (Mailbox32.CmdStatus == MBO_FREE));
AdaptecLog("Adaptec: Got loaded mailbox at slot %u, CCB phys 0x%08X\n", Adaptec->MailboxOutPosCur, Mailbox32.CCBPointer);
uint8_t CmdStatus = MBO_FREE;
unsigned CodeOffset = offsetof(Mailbox_t, CmdStatus);
uint32_t MailboxOffset = Outgoing + CodeOffset;
uint32_t CmdStatusSize = sizeof(CmdStatus);
const void *Data = (const void *)&CmdStatus;
uint32_t l = PageLengthReadWrite(MailboxOffset, CmdStatusSize);
//AdaptecBufferRead(&Adaptec->AdaptecRequests, Outgoing, &MailboxOut, sizeof(Mailbox_t));
memcpy(&ram[MailboxOffset], Data, l);
MailboxOffset += l;
Data -= l;
CmdStatusSize += l;
Mailbox32.CmdStatus = MBO_START;
AdaptecSCSIRequestSetup(Adaptec, Mailbox32.CCBPointer);
}
void AdaptecCallback(void *p)
{
Adaptec_t *Adaptec = &AdaptecLUN;
ScsiCallback[scsi_cdrom_id] = 0;
if (Adaptec->MailboxCount)
{
AdaptecStartMailbox(Adaptec);
}
}
void AdaptecInit(uint8_t Id)
{
AdaptecLUN.Irq = 11;
AdaptecLUN.DmaChannel = 6;
AdaptecLUN.DmaPort1 = 0xD6;
AdaptecLUN.DmaData1 = 0xD4;
AdaptecLUN.DmaPort2 = 0xC2;
AdaptecLUN.DmaData2 = 0x02;
pfnIoRequestCopyFromBuffer = AdaptecIoRequestCopyFromBuffer;
pfnIoRequestCopyToBuffer = AdaptecIoRequestCopyToBuffer;
io_sethandler(0x0334, 0x0004, AdaptecRead, NULL, NULL, AdaptecWrite, NULL, NULL, NULL);
timer_add(AdaptecCallback, &ScsiCallback[Id], &ScsiCallback[Id], NULL);
AdaptecReset(&AdaptecLUN);
}