1. The SCSI ID is now more correct about about the difference between physical unit number and actual SCSI ID. This should fix boot freezes on NT 1991 builds for x86 using said card, as well as keeping compatibility the same as before.
1279 lines
47 KiB
C
1279 lines
47 KiB
C
/*
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
* running old operating systems and software designed for IBM
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
* system designs based on the PCI bus.
|
|
*
|
|
* This file is part of the 86Box distribution.
|
|
*
|
|
* Implementation of the IBM PS/2 SCSI controller with
|
|
* cache for MCA only.
|
|
*
|
|
*
|
|
*
|
|
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
|
|
* TheCollector1995, <mariogplayer@gmail.com>
|
|
*
|
|
* Copyright 2020 Sarah Walker.
|
|
* Copyright 2020 TheCollector1995.
|
|
*/
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <wchar.h>
|
|
#define HAVE_STDARG_H
|
|
#include <86box/86box.h>
|
|
#include <86box/io.h>
|
|
#include <86box/timer.h>
|
|
#include <86box/dma.h>
|
|
#include <86box/pic.h>
|
|
#include <86box/mca.h>
|
|
#include <86box/mem.h>
|
|
#include <86box/rom.h>
|
|
#include <86box/device.h>
|
|
#include <86box/nvr.h>
|
|
#include <86box/plat.h>
|
|
#include <86box/scsi.h>
|
|
#include <86box/scsi_device.h>
|
|
#include <86box/scsi_spock.h>
|
|
|
|
#define SPOCK_U68_1990_ROM "roms/scsi/ibm/64f4376.bin"
|
|
#define SPOCK_U69_1990_ROM "roms/scsi/ibm/64f4377.bin"
|
|
|
|
#define SPOCK_U68_1991_ROM "roms/scsi/ibm/92F2244.U68"
|
|
#define SPOCK_U69_1991_ROM "roms/scsi/ibm/92F2245.U69"
|
|
|
|
#define SPOCK_TIME (20)
|
|
|
|
typedef enum {
|
|
SCSI_STATE_IDLE,
|
|
SCSI_STATE_SELECT,
|
|
SCSI_STATE_SEND_COMMAND,
|
|
SCSI_STATE_END_PHASE
|
|
} scsi_state_t;
|
|
|
|
#pragma pack(push, 1)
|
|
typedef struct {
|
|
uint16_t pos;
|
|
uint16_t pos1;
|
|
uint16_t pos2;
|
|
uint16_t pos3;
|
|
uint16_t pos4;
|
|
uint16_t pos5;
|
|
uint16_t pos6;
|
|
uint16_t pos7;
|
|
uint16_t pos8;
|
|
} get_pos_info_t;
|
|
|
|
typedef struct {
|
|
uint16_t scb_status;
|
|
uint16_t retry_count;
|
|
uint32_t residual_byte_count;
|
|
uint32_t sg_list_element_addr;
|
|
uint16_t device_dep_status_len;
|
|
uint16_t cmd_status;
|
|
uint16_t error;
|
|
uint16_t reserved;
|
|
uint16_t cache_info_status;
|
|
uint32_t scb_addr;
|
|
} get_complete_stat_t;
|
|
|
|
typedef struct {
|
|
uint32_t sys_buf_addr;
|
|
uint32_t sys_buf_byte_count;
|
|
} SGE;
|
|
|
|
typedef struct {
|
|
uint16_t command;
|
|
uint16_t enable;
|
|
uint32_t lba_addr;
|
|
SGE sge;
|
|
uint32_t term_status_block_addr;
|
|
uint32_t scb_chain_addr;
|
|
uint16_t block_count;
|
|
uint16_t block_length;
|
|
} scb_t;
|
|
#pragma pack(pop)
|
|
|
|
typedef struct {
|
|
rom_t bios_rom;
|
|
|
|
int bios_ver;
|
|
int irq, irq_inactive;
|
|
|
|
uint8_t pos_regs[8];
|
|
|
|
uint8_t basic_ctrl;
|
|
uint32_t command;
|
|
|
|
uint8_t attention,
|
|
attention_pending;
|
|
int attention_wait;
|
|
|
|
uint8_t cir[4],
|
|
cir_pending[4];
|
|
|
|
uint8_t irq_status;
|
|
|
|
uint32_t scb_addr;
|
|
|
|
uint8_t status;
|
|
|
|
get_complete_stat_t get_complete_stat;
|
|
get_pos_info_t get_pos_info;
|
|
|
|
scb_t scb;
|
|
int scb_id;
|
|
int adapter_id;
|
|
int assign;
|
|
int present[8];
|
|
|
|
int cmd_status;
|
|
int cir_status;
|
|
|
|
uint8_t pacing;
|
|
uint8_t irq_state;
|
|
uint8_t buf[0x600];
|
|
|
|
struct {
|
|
int phys_id;
|
|
int lun_id;
|
|
} dev_id[SCSI_ID_MAX];
|
|
|
|
uint8_t last_status, bus;
|
|
uint8_t cdb[12];
|
|
int cdb_len;
|
|
int cdb_id;
|
|
uint32_t data_ptr, data_len;
|
|
uint8_t temp_cdb[12];
|
|
|
|
int irq_requests[SCSI_ID_MAX];
|
|
|
|
pc_timer_t callback_timer;
|
|
|
|
int cmd_timer;
|
|
|
|
int scb_state;
|
|
int in_reset;
|
|
int in_invalid;
|
|
int spock_16bit;
|
|
|
|
uint64_t temp_period;
|
|
double media_period;
|
|
|
|
scsi_state_t scsi_state;
|
|
} spock_t;
|
|
|
|
#define CTRL_RESET (1 << 7)
|
|
#define CTRL_DMA_ENA (1 << 1)
|
|
#define CTRL_IRQ_ENA (1 << 0)
|
|
|
|
#define STATUS_CMD_FULL (1 << 3)
|
|
#define STATUS_CMD_EMPTY (1 << 2)
|
|
#define STATUS_IRQ (1 << 1)
|
|
#define STATUS_BUSY (1 << 0)
|
|
|
|
#define ENABLE_PT (1 << 12)
|
|
|
|
#define CMD_MASK 0xff3f
|
|
#define CMD_ASSIGN 0x040e
|
|
#define CMD_DEVICE_INQUIRY 0x1c0b
|
|
#define CMD_DMA_PACING_CONTROL 0x040d
|
|
#define CMD_FEATURE_CONTROL 0x040c
|
|
#define CMD_GET_POS_INFO 0x1c0a
|
|
#define CMD_INVALID_412 0x0412
|
|
#define CMD_GET_COMPLETE_STATUS 0x1c07
|
|
#define CMD_FORMAT_UNIT 0x1c16
|
|
#define CMD_READ_DATA 0x1c01
|
|
#define CMD_READ_DEVICE_CAPACITY 0x1c09
|
|
#define CMD_REQUEST_SENSE 0x1c08
|
|
#define CMD_RESET 0x0400
|
|
#define CMD_SEND_OTHER_SCSI 0x241f
|
|
#define CMD_UNKNOWN_1C10 0x1c10
|
|
#define CMD_UNKNOWN_1C11 0x1c11
|
|
#define CMD_WRITE_DATA 0x1c02
|
|
#define CMD_VERIFY 0x1c03
|
|
|
|
#define IRQ_TYPE_NONE 0x0
|
|
#define IRQ_TYPE_SCB_COMPLETE 0x1
|
|
#define IRQ_TYPE_SCB_COMPLETE_RETRY 0x5
|
|
#define IRQ_TYPE_ADAPTER_HW_FAILURE 0x7
|
|
#define IRQ_TYPE_IMM_CMD_COMPLETE 0xa
|
|
#define IRQ_TYPE_COMMAND_FAIL 0xc
|
|
#define IRQ_TYPE_COMMAND_ERROR 0xe
|
|
#define IRQ_TYPE_SW_SEQ_ERROR 0xf
|
|
#define IRQ_TYPE_RESET_COMPLETE 0x10
|
|
|
|
#ifdef ENABLE_SPOCK_LOG
|
|
int spock_do_log = ENABLE_SPOCK_LOG;
|
|
|
|
static void
|
|
spock_log(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (spock_do_log) {
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
# define spock_log(fmt, ...)
|
|
#endif
|
|
|
|
static void
|
|
spock_rethink_irqs(spock_t *scsi)
|
|
{
|
|
int irq_pending = 0;
|
|
|
|
if (!scsi->irq_status) {
|
|
for (uint8_t c = 0; c < SCSI_ID_MAX; c++) {
|
|
if (scsi->irq_requests[c] != IRQ_TYPE_NONE) {
|
|
/* Found IRQ */
|
|
scsi->irq_status = c | (scsi->irq_requests[c] << 4);
|
|
spock_log("Found IRQ: status = %02x\n", scsi->irq_status);
|
|
scsi->status |= STATUS_IRQ;
|
|
irq_pending = 1;
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
irq_pending = 1;
|
|
|
|
if (scsi->basic_ctrl & CTRL_IRQ_ENA) {
|
|
if (irq_pending) {
|
|
spock_log("IRQ issued\n");
|
|
scsi->irq_inactive = 0;
|
|
picint(1 << scsi->irq);
|
|
} else {
|
|
/* No IRQs pending, clear IRQ state */
|
|
spock_log("IRQ cleared\n");
|
|
scsi->irq_status = 0;
|
|
scsi->irq_inactive = 1;
|
|
scsi->status &= ~STATUS_IRQ;
|
|
picintc(1 << scsi->irq);
|
|
}
|
|
} else {
|
|
spock_log("IRQ disabled\n");
|
|
picintc(1 << scsi->irq);
|
|
}
|
|
}
|
|
|
|
static __inline void
|
|
spock_set_irq(spock_t *scsi, int id, int type)
|
|
{
|
|
spock_log("spock_set_irq id=%i type=%x %02x\n", id, type, scsi->irq_status);
|
|
scsi->irq_requests[id] = type;
|
|
if (!scsi->irq_status) /* Don't change IRQ status if one is currently being processed */
|
|
spock_rethink_irqs(scsi);
|
|
}
|
|
|
|
static __inline void
|
|
spock_clear_irq(spock_t *scsi, int id)
|
|
{
|
|
spock_log("spock_clear_irq id=%i\n", id);
|
|
scsi->irq_requests[id] = IRQ_TYPE_NONE;
|
|
spock_rethink_irqs(scsi);
|
|
}
|
|
|
|
static void
|
|
spock_add_to_period(spock_t *scsi, int TransferLength)
|
|
{
|
|
scsi->temp_period += (uint64_t) TransferLength;
|
|
}
|
|
|
|
static void
|
|
spock_write(uint16_t port, uint8_t val, void *priv)
|
|
{
|
|
spock_t *scsi = (spock_t *) priv;
|
|
|
|
spock_log("spock_write: port=%04x val=%02x %04x:%04x\n", port, val, CS, cpu_state.pc);
|
|
|
|
switch (port & 7) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3: /*Command Interface Register*/
|
|
scsi->cir_pending[port & 3] = val;
|
|
if (port & 2)
|
|
scsi->cir_status |= 2;
|
|
else
|
|
scsi->cir_status |= 1;
|
|
break;
|
|
|
|
case 4: /*Attention Register*/
|
|
scsi->attention_pending = val;
|
|
scsi->attention_wait = 2;
|
|
scsi->status |= STATUS_BUSY;
|
|
break;
|
|
|
|
case 5: /*Basic Control Register*/
|
|
if ((scsi->basic_ctrl & CTRL_RESET) && !(val & CTRL_RESET)) {
|
|
spock_log("Spock: SCSI reset and busy\n");
|
|
scsi->in_reset = 1;
|
|
scsi->cmd_timer = SPOCK_TIME * 2;
|
|
scsi->status |= STATUS_BUSY;
|
|
}
|
|
scsi->basic_ctrl = val;
|
|
spock_rethink_irqs(scsi);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
spock_writew(uint16_t port, uint16_t val, void *priv)
|
|
{
|
|
spock_t *scsi = (spock_t *) priv;
|
|
|
|
switch (port & 7) {
|
|
case 0: /*Command Interface Register*/
|
|
scsi->cir_pending[0] = val & 0xff;
|
|
scsi->cir_pending[1] = val >> 8;
|
|
scsi->cir_status |= 1;
|
|
break;
|
|
case 2: /*Command Interface Register*/
|
|
scsi->cir_pending[2] = val & 0xff;
|
|
scsi->cir_pending[3] = val >> 8;
|
|
scsi->cir_status |= 2;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
spock_log("spock_writew: port=%04x val=%04x\n", port, val);
|
|
}
|
|
|
|
static uint8_t
|
|
spock_read(uint16_t port, void *priv)
|
|
{
|
|
const spock_t *scsi = (spock_t *) priv;
|
|
uint8_t temp = 0xff;
|
|
|
|
switch (port & 7) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3: /*Command Interface Register*/
|
|
temp = scsi->cir_pending[port & 3];
|
|
break;
|
|
|
|
case 4: /*Attention Register*/
|
|
temp = scsi->attention_pending;
|
|
break;
|
|
case 5: /*Basic Control Register*/
|
|
temp = scsi->basic_ctrl;
|
|
break;
|
|
case 6: /*IRQ status*/
|
|
temp = scsi->irq_status;
|
|
break;
|
|
case 7: /*Basic Status Register*/
|
|
temp = scsi->status;
|
|
spock_log("Cir Status=%d\n", scsi->cir_status);
|
|
if (scsi->cir_status == 0) {
|
|
spock_log("Status Cmd Empty\n");
|
|
temp |= STATUS_CMD_EMPTY;
|
|
} else if (scsi->cir_status == 3) {
|
|
spock_log("Status Cmd Full\n");
|
|
temp |= STATUS_CMD_FULL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
spock_log("spock_read: port=%04x val=%02x %04x(%05x):%04x.\n", port, temp, CS, cs, cpu_state.pc);
|
|
return temp;
|
|
}
|
|
|
|
static uint16_t
|
|
spock_readw(uint16_t port, void *priv)
|
|
{
|
|
const spock_t *scsi = (spock_t *) priv;
|
|
uint16_t temp = 0xffff;
|
|
|
|
switch (port & 7) {
|
|
case 0: /*Command Interface Register*/
|
|
temp = scsi->cir_pending[0] | (scsi->cir_pending[1] << 8);
|
|
break;
|
|
case 2: /*Command Interface Register*/
|
|
temp = scsi->cir_pending[2] | (scsi->cir_pending[3] << 8);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
spock_log("spock_readw: port=%04x val=%04x\n", port, temp);
|
|
return temp;
|
|
}
|
|
|
|
static void
|
|
spock_rd_sge(spock_t *scsi, uint32_t Address, SGE *SG)
|
|
{
|
|
dma_bm_read(Address, (uint8_t *) SG, sizeof(SGE), 2);
|
|
spock_add_to_period(scsi, sizeof(SGE));
|
|
}
|
|
|
|
static int
|
|
spock_get_len(spock_t *scsi, scb_t *scb)
|
|
{
|
|
uint32_t DataToTransfer = 0;
|
|
|
|
spock_log("Data Buffer write: length %d, pointer 0x%04X\n",
|
|
scsi->data_len, scsi->data_ptr);
|
|
|
|
if (!scsi->data_len)
|
|
return 0;
|
|
|
|
if (scb->enable & ENABLE_PT) {
|
|
for (uint32_t i = 0; i < scsi->data_len; i += 8) {
|
|
spock_rd_sge(scsi, scsi->data_ptr + i, &scb->sge);
|
|
|
|
DataToTransfer += scb->sge.sys_buf_byte_count;
|
|
}
|
|
return DataToTransfer;
|
|
} else {
|
|
return (scsi->data_len);
|
|
}
|
|
}
|
|
|
|
static void
|
|
spock_process_imm_cmd(spock_t *scsi)
|
|
{
|
|
int i;
|
|
int j = 0;
|
|
int adapter_id;
|
|
int phys_id;
|
|
int lun_id;
|
|
|
|
switch (scsi->command & CMD_MASK) {
|
|
case CMD_ASSIGN:
|
|
adapter_id = (scsi->command >> 16) & 15;
|
|
phys_id = (scsi->command >> 20) & 7;
|
|
lun_id = (scsi->command >> 24) & 7;
|
|
if (adapter_id == 15) {
|
|
if (phys_id == 7) /*Device 15 always adapter*/
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
else /*Can not re-assign device 15 (always adapter)*/
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_FAIL);
|
|
} else {
|
|
if (scsi->command & (1 << 23)) {
|
|
spock_log("Assign: adapter id=%d\n", adapter_id);
|
|
scsi->dev_id[adapter_id].phys_id = -1;
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
} else {
|
|
if (phys_id != scsi->adapter_id) {
|
|
scsi->dev_id[adapter_id].phys_id = phys_id;
|
|
scsi->dev_id[adapter_id].lun_id = lun_id;
|
|
spock_log("Assign: adapter dev=%x scsi ID=%i LUN=%i.\n", adapter_id, scsi->dev_id[adapter_id].phys_id, scsi->dev_id[adapter_id].lun_id);
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
} else { /*Can not assign adapter*/
|
|
spock_log("Assign: PUN=%d, cannot assign adapter.\n", phys_id);
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_FAIL);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CMD_DMA_PACING_CONTROL:
|
|
scsi->pacing = scsi->cir[2];
|
|
spock_log("Pacing control: %i\n", scsi->pacing);
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
break;
|
|
case CMD_FEATURE_CONTROL:
|
|
spock_log("Feature control: timeout=%is d-rate=%i\n", (scsi->command >> 16) & 0x1fff, scsi->command >> 29);
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
break;
|
|
case CMD_INVALID_412:
|
|
spock_log("Invalid 412\n");
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
break;
|
|
case CMD_RESET:
|
|
spock_log("Reset Command, attention = %d.\n", scsi->attention & 0x0f);
|
|
if ((scsi->attention & 0x0f) == 0x0f) { /*Adapter reset*/
|
|
for (i = 0; i < 8; i++) {
|
|
scsi_device_reset(&scsi_devices[scsi->bus][i]);
|
|
}
|
|
|
|
for (i = 6; i > -1; i--) {
|
|
if (scsi_device_present(&scsi_devices[scsi->bus][i])) {
|
|
spock_log("Adapter Reset, SCSI reset present devices=%d, phys ID=%d, type=%04x.\n", j, scsi->dev_id[i].phys_id, scsi_devices[scsi->bus][i].type);
|
|
scsi->present[j] = i;
|
|
j++;
|
|
} else {
|
|
scsi->present[j] = 0xff;
|
|
spock_log("Adapter Reset, SCSI reset not present devices=%d, phys ID=%d, type=%04x.\n", j, scsi->dev_id[i].phys_id, scsi_devices[scsi->bus][i].type);
|
|
}
|
|
}
|
|
|
|
scsi->scb_state = 0;
|
|
}
|
|
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
break;
|
|
|
|
default:
|
|
fatal("scsi_callback: Bad command %02x\n", scsi->command);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
spock_execute_cmd(spock_t *scsi, scb_t *scb)
|
|
{
|
|
int c;
|
|
int j = 0;
|
|
int old_scb_state;
|
|
|
|
if (scsi->in_reset) {
|
|
spock_log("Reset type = %d\n", scsi->in_reset);
|
|
|
|
scsi->status &= ~STATUS_BUSY;
|
|
scsi->irq_status = 0;
|
|
|
|
for (c = 0; c < SCSI_ID_MAX; c++)
|
|
spock_clear_irq(scsi, c);
|
|
|
|
if (scsi->in_reset == 1) {
|
|
scsi->basic_ctrl |= CTRL_IRQ_ENA;
|
|
}
|
|
|
|
spock_set_irq(scsi, 0x0f, IRQ_TYPE_RESET_COMPLETE);
|
|
|
|
/*Reset device mappings*/
|
|
for (c = 0; c < 7; c++) {
|
|
scsi->dev_id[c].phys_id = c;
|
|
scsi->dev_id[c].lun_id = 0;
|
|
}
|
|
for (; c < (SCSI_ID_MAX - 1); c++)
|
|
scsi->dev_id[c].phys_id = -1;
|
|
|
|
scsi->in_reset = 0;
|
|
|
|
for (c = 6; c > -1; c--) {
|
|
if (scsi_device_present(&scsi_devices[scsi->bus][c])) {
|
|
spock_log("Reset, SCSI reset present devices=%d, phys ID=%d, type=%04x.\n", j, scsi->dev_id[c].phys_id, scsi_devices[scsi->bus][c].type);
|
|
scsi->present[j] = c;
|
|
j++;
|
|
} else {
|
|
scsi->present[j] = 0xff;
|
|
spock_log("Reset, SCSI reset not present devices=%d, phys ID=%d, type=%04x.\n", j, scsi->dev_id[c].phys_id, scsi_devices[scsi->bus][c].type);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (scsi->in_invalid) {
|
|
spock_log("Invalid command\n");
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_ERROR);
|
|
scsi->in_invalid = 0;
|
|
return;
|
|
}
|
|
|
|
spock_log("SCB State = %d\n", scsi->scb_state);
|
|
|
|
do {
|
|
old_scb_state = scsi->scb_state;
|
|
|
|
switch (scsi->scb_state) {
|
|
case 0: /* Idle */
|
|
break;
|
|
|
|
case 1: /* Select */
|
|
if (scsi->dev_id[scsi->scb_id].phys_id == -1) {
|
|
uint16_t term_stat_block_addr7 = (0xe << 8) | 0;
|
|
uint16_t term_stat_block_addr8 = (0xa << 8) | 0;
|
|
|
|
spock_log("Start failed, SCB ID = %d\n", scsi->scb_id);
|
|
spock_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL);
|
|
scsi->scb_state = 0;
|
|
dma_bm_write(scb->term_status_block_addr + 0x7 * 2, (uint8_t *) &term_stat_block_addr7, 2, 2);
|
|
dma_bm_write(scb->term_status_block_addr + 0x8 * 2, (uint8_t *) &term_stat_block_addr8, 2, 2);
|
|
break;
|
|
}
|
|
|
|
dma_bm_read(scsi->scb_addr, (uint8_t *) &scb->command, 2, 2);
|
|
dma_bm_read(scsi->scb_addr + 2, (uint8_t *) &scb->enable, 2, 2);
|
|
dma_bm_read(scsi->scb_addr + 4, (uint8_t *) &scb->lba_addr, 4, 2);
|
|
dma_bm_read(scsi->scb_addr + 8, (uint8_t *) &scb->sge.sys_buf_addr, 4, 2);
|
|
dma_bm_read(scsi->scb_addr + 12, (uint8_t *) &scb->sge.sys_buf_byte_count, 4, 2);
|
|
dma_bm_read(scsi->scb_addr + 16, (uint8_t *) &scb->term_status_block_addr, 4, 2);
|
|
dma_bm_read(scsi->scb_addr + 20, (uint8_t *) &scb->scb_chain_addr, 4, 2);
|
|
dma_bm_read(scsi->scb_addr + 24, (uint8_t *) &scb->block_count, 2, 2);
|
|
dma_bm_read(scsi->scb_addr + 26, (uint8_t *) &scb->block_length, 2, 2);
|
|
|
|
spock_log("SCB : \n"
|
|
" Command = %04x\n"
|
|
" Enable = %04x\n"
|
|
" LBA addr = %08x\n"
|
|
" System buffer addr = %08x\n"
|
|
" System buffer byte count = %08x\n"
|
|
" Terminate status block addr = %08x\n"
|
|
" SCB chain address = %08x\n"
|
|
" Block count = %04x\n"
|
|
" Block length = %04x\n"
|
|
" SCB id = %d, Phys id = %d, Spock CMD = %08x, CMD Mask = %02x.\n",
|
|
scb->command, scb->enable, scb->lba_addr,
|
|
scb->sge.sys_buf_addr, scb->sge.sys_buf_byte_count,
|
|
scb->term_status_block_addr, scb->scb_chain_addr,
|
|
scb->block_count, scb->block_length, scsi->scb_id, scsi->dev_id[scsi->scb_id].phys_id, scsi->command, scb->command & 0xc0);
|
|
|
|
switch (scb->command & CMD_MASK) {
|
|
case CMD_GET_COMPLETE_STATUS:
|
|
{
|
|
spock_log("Get Complete Status\n");
|
|
get_complete_stat_t *get_complete_stat = &scsi->get_complete_stat;
|
|
|
|
get_complete_stat->scb_status = 0x201;
|
|
get_complete_stat->retry_count = 0;
|
|
get_complete_stat->residual_byte_count = 0;
|
|
get_complete_stat->sg_list_element_addr = 0;
|
|
get_complete_stat->device_dep_status_len = 0x0c;
|
|
get_complete_stat->cmd_status = scsi->cmd_status << 8;
|
|
get_complete_stat->error = 0;
|
|
get_complete_stat->reserved = 0;
|
|
get_complete_stat->cache_info_status = 0;
|
|
get_complete_stat->scb_addr = scsi->scb_addr;
|
|
|
|
dma_bm_write(scb->sge.sys_buf_addr, (uint8_t *) &get_complete_stat->scb_status, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 2, (uint8_t *) &get_complete_stat->retry_count, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 4, (uint8_t *) &get_complete_stat->residual_byte_count, 4, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 8, (uint8_t *) &get_complete_stat->sg_list_element_addr, 4, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 12, (uint8_t *) &get_complete_stat->device_dep_status_len, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 14, (uint8_t *) &get_complete_stat->cmd_status, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 16, (uint8_t *) &get_complete_stat->error, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 18, (uint8_t *) &get_complete_stat->reserved, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 20, (uint8_t *) &get_complete_stat->cache_info_status, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 22, (uint8_t *) &get_complete_stat->scb_addr, 4, 2);
|
|
scsi->scb_state = 3;
|
|
}
|
|
break;
|
|
|
|
case CMD_UNKNOWN_1C10:
|
|
spock_log("Unknown 1C10\n");
|
|
dma_bm_read(scb->sge.sys_buf_addr, scsi->buf, scb->sge.sys_buf_byte_count, 2);
|
|
scsi->scb_state = 3;
|
|
break;
|
|
|
|
case CMD_UNKNOWN_1C11:
|
|
spock_log("Unknown 1C11\n");
|
|
dma_bm_write(scb->sge.sys_buf_addr, scsi->buf, scb->sge.sys_buf_byte_count, 2);
|
|
scsi->scb_state = 3;
|
|
break;
|
|
|
|
case CMD_GET_POS_INFO:
|
|
{
|
|
spock_log("Get POS Info\n");
|
|
get_pos_info_t *get_pos_info = &scsi->get_pos_info;
|
|
|
|
get_pos_info->pos = scsi->spock_16bit ? 0x8efe : 0x8eff;
|
|
get_pos_info->pos1 = scsi->pos_regs[3] | (scsi->pos_regs[2] << 8);
|
|
get_pos_info->pos2 = scsi->irq | (scsi->pos_regs[4] << 8);
|
|
get_pos_info->pos3 = 1 << 12;
|
|
get_pos_info->pos4 = (7 << 8) | 8;
|
|
get_pos_info->pos5 = (16 << 8) | scsi->pacing;
|
|
get_pos_info->pos6 = (30 << 8) | 1;
|
|
get_pos_info->pos7 = 0;
|
|
get_pos_info->pos8 = 0;
|
|
|
|
dma_bm_write(scb->sge.sys_buf_addr, (uint8_t *) &get_pos_info->pos, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 2, (uint8_t *) &get_pos_info->pos1, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 4, (uint8_t *) &get_pos_info->pos2, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 6, (uint8_t *) &get_pos_info->pos3, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 8, (uint8_t *) &get_pos_info->pos4, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 10, (uint8_t *) &get_pos_info->pos5, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 12, (uint8_t *) &get_pos_info->pos6, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 14, (uint8_t *) &get_pos_info->pos7, 2, 2);
|
|
dma_bm_write(scb->sge.sys_buf_addr + 16, (uint8_t *) &get_pos_info->pos8, 2, 2);
|
|
scsi->scb_state = 3;
|
|
}
|
|
break;
|
|
|
|
case CMD_DEVICE_INQUIRY:
|
|
if (scsi->present[scsi->scb_id] != 0xff)
|
|
scsi->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
else
|
|
scsi->cdb_id = 0xff;
|
|
|
|
spock_log("Device Inquiry, ID=%d\n", scsi->cdb_id);
|
|
scsi->cdb[0] = GPCMD_INQUIRY;
|
|
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb[2] = 0; /*Page code*/
|
|
scsi->cdb[3] = 0;
|
|
scsi->cdb[4] = scb->sge.sys_buf_byte_count; /*Allocation length*/
|
|
scsi->cdb[5] = 0; /*Control*/
|
|
scsi->cdb_len = 6;
|
|
scsi->data_ptr = scb->sge.sys_buf_addr;
|
|
scsi->data_len = scb->sge.sys_buf_byte_count;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = 2;
|
|
return;
|
|
|
|
case CMD_SEND_OTHER_SCSI:
|
|
if (scsi->present[scsi->scb_id] != 0xff)
|
|
scsi->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
else
|
|
scsi->cdb_id = 0xff;
|
|
|
|
dma_bm_read(scsi->scb_addr + 0x18, scsi->cdb, 12, 2);
|
|
spock_log("Send Other SCSI, SCB ID=%d, PHYS ID=%d, CDB[0]=%02x, CDB_ID=%d, ID Present=%d.\n", scsi->scb_id, scsi->dev_id[scsi->scb_id].phys_id, scsi->cdb[0], scsi->cdb_id, scsi->present[scsi->scb_id]);
|
|
scsi->cdb[1] = (scsi->cdb[1] & 0x1f) | (scsi->dev_id[scsi->scb_id].lun_id << 5); /*Patch correct LUN into command*/
|
|
scsi->cdb_len = (scb->lba_addr & 0xff) ? (scb->lba_addr & 0xff) : 6;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = 2;
|
|
return;
|
|
|
|
case CMD_READ_DEVICE_CAPACITY:
|
|
if (scsi->present[scsi->scb_id] != 0xff)
|
|
scsi->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
else
|
|
scsi->cdb_id = 0xff;
|
|
|
|
spock_log("Device Capacity, SCB ID=%d, PHYS ID=%d\n", scsi->scb_id, scsi->dev_id[scsi->scb_id].phys_id);
|
|
scsi->cdb[0] = GPCMD_READ_CDROM_CAPACITY;
|
|
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb[2] = 0; /*LBA*/
|
|
scsi->cdb[3] = 0;
|
|
scsi->cdb[4] = 0;
|
|
scsi->cdb[5] = 0;
|
|
scsi->cdb[6] = 0; /*Reserved*/
|
|
scsi->cdb[7] = 0;
|
|
scsi->cdb[8] = 0;
|
|
scsi->cdb[9] = 0; /*Control*/
|
|
scsi->cdb_len = 10;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = 2;
|
|
return;
|
|
|
|
case CMD_READ_DATA:
|
|
if (scsi->present[scsi->scb_id] != 0xff)
|
|
scsi->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
else
|
|
scsi->cdb_id = 0xff;
|
|
|
|
spock_log("Device Read Data, SCB ID=%d, PHYS ID=%d\n", scsi->scb_id, scsi->dev_id[scsi->scb_id].phys_id);
|
|
scsi->cdb[0] = GPCMD_READ_10;
|
|
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/
|
|
scsi->cdb[3] = (scb->lba_addr >> 16) & 0xff;
|
|
scsi->cdb[4] = (scb->lba_addr >> 8) & 0xff;
|
|
scsi->cdb[5] = scb->lba_addr & 0xff;
|
|
scsi->cdb[6] = 0; /*Reserved*/
|
|
scsi->cdb[7] = (scb->block_count >> 8) & 0xff;
|
|
scsi->cdb[8] = scb->block_count & 0xff;
|
|
scsi->cdb[9] = 0; /*Control*/
|
|
scsi->cdb_len = 10;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = 2;
|
|
return;
|
|
|
|
case CMD_WRITE_DATA:
|
|
if (scsi->present[scsi->scb_id] != 0xff)
|
|
scsi->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
else
|
|
scsi->cdb_id = 0xff;
|
|
|
|
spock_log("Device Write Data\n");
|
|
scsi->cdb[0] = GPCMD_WRITE_10;
|
|
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/
|
|
scsi->cdb[3] = (scb->lba_addr >> 16) & 0xff;
|
|
scsi->cdb[4] = (scb->lba_addr >> 8) & 0xff;
|
|
scsi->cdb[5] = scb->lba_addr & 0xff;
|
|
scsi->cdb[6] = 0; /*Reserved*/
|
|
scsi->cdb[7] = (scb->block_count >> 8) & 0xff;
|
|
scsi->cdb[8] = scb->block_count & 0xff;
|
|
scsi->cdb[9] = 0; /*Control*/
|
|
scsi->cdb_len = 10;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = 2;
|
|
return;
|
|
|
|
case CMD_VERIFY:
|
|
if (scsi->present[scsi->scb_id] != 0xff)
|
|
scsi->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
else
|
|
scsi->cdb_id = 0xff;
|
|
|
|
spock_log("Device Verify\n");
|
|
scsi->cdb[0] = GPCMD_VERIFY_10;
|
|
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/
|
|
scsi->cdb[3] = (scb->lba_addr >> 16) & 0xff;
|
|
scsi->cdb[4] = (scb->lba_addr >> 8) & 0xff;
|
|
scsi->cdb[5] = scb->lba_addr & 0xff;
|
|
scsi->cdb[6] = 0; /*Reserved*/
|
|
scsi->cdb[7] = (scb->block_count >> 8) & 0xff;
|
|
scsi->cdb[8] = scb->block_count & 0xff;
|
|
scsi->cdb[9] = 0; /*Control*/
|
|
scsi->cdb_len = 10;
|
|
scsi->data_len = 0;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = 2;
|
|
return;
|
|
|
|
case CMD_REQUEST_SENSE:
|
|
if (scsi->present[scsi->scb_id] != 0xff)
|
|
scsi->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
else
|
|
scsi->cdb_id = 0xff;
|
|
|
|
spock_log("Device Request Sense, ID=%d\n", scsi->cdb_id);
|
|
scsi->cdb[0] = GPCMD_REQUEST_SENSE;
|
|
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb[2] = 0;
|
|
scsi->cdb[3] = 0;
|
|
scsi->cdb[4] = scb->sge.sys_buf_byte_count; /*Allocation length*/
|
|
scsi->cdb[5] = 0;
|
|
scsi->cdb_len = 6;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = 2;
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 2: /* Wait */
|
|
if (scsi->scsi_state == SCSI_STATE_IDLE) {
|
|
if (scsi_device_present(&scsi_devices[scsi->bus][scsi->cdb_id]) && (scsi->cdb_id != 0xff)) {
|
|
if (scsi->last_status == SCSI_STATUS_OK) {
|
|
scsi->scb_state = 3;
|
|
spock_log("Status is Good on device ID %d, cdb id = %d.\n", scsi->scb_id, scsi->cdb_id);
|
|
} else if (scsi->last_status == SCSI_STATUS_CHECK_CONDITION) {
|
|
uint16_t term_stat_block_addr7 = (0xc << 8) | 2;
|
|
uint16_t term_stat_block_addr8 = 0x20;
|
|
uint16_t term_stat_block_addrb = scsi->scb_addr & 0xffff;
|
|
uint16_t term_stat_block_addrc = scsi->scb_addr >> 16;
|
|
|
|
spock_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL);
|
|
scsi->scb_state = 0;
|
|
spock_log("Status Check Condition on device ID %d, cdb id = %d.\n", scsi->attention & 0x0f, scsi->cdb_id);
|
|
dma_bm_write(scb->term_status_block_addr + 0x7 * 2, (uint8_t *) &term_stat_block_addr7, 2, 2);
|
|
dma_bm_write(scb->term_status_block_addr + 0x8 * 2, (uint8_t *) &term_stat_block_addr8, 2, 2);
|
|
dma_bm_write(scb->term_status_block_addr + 0xb * 2, (uint8_t *) &term_stat_block_addrb, 2, 2);
|
|
dma_bm_write(scb->term_status_block_addr + 0xc * 2, (uint8_t *) &term_stat_block_addrc, 2, 2);
|
|
}
|
|
} else {
|
|
uint16_t term_stat_block_addr7 = (0xc << 8) | 2;
|
|
uint16_t term_stat_block_addr8 = 0x10;
|
|
spock_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL);
|
|
scsi->scb_state = 0;
|
|
spock_log("Status Check Condition on device ID %d on no device\n", scsi->scb_id);
|
|
dma_bm_write(scb->term_status_block_addr + 0x7 * 2, (uint8_t *) &term_stat_block_addr7, 2, 2);
|
|
dma_bm_write(scb->term_status_block_addr + 0x8 * 2, (uint8_t *) &term_stat_block_addr8, 2, 2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3: /* Complete */
|
|
if (scb->enable & 1) {
|
|
scsi->scb_state = 1;
|
|
scsi->scb_addr = scb->scb_chain_addr;
|
|
spock_log("Next SCB - %08x\n", scsi->scb_addr);
|
|
} else {
|
|
spock_set_irq(scsi, scsi->scb_id, IRQ_TYPE_SCB_COMPLETE);
|
|
scsi->scb_state = 0;
|
|
spock_log("Complete SCB ID = %d.\n", scsi->attention & 0x0f);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
} while (scsi->scb_state != old_scb_state);
|
|
}
|
|
|
|
static void
|
|
spock_process_scsi(spock_t *scsi, scb_t *scb)
|
|
{
|
|
double p;
|
|
scsi_device_t *sd;
|
|
|
|
switch (scsi->scsi_state) {
|
|
case SCSI_STATE_IDLE:
|
|
break;
|
|
|
|
case SCSI_STATE_SELECT:
|
|
spock_log("Selecting ID %d, SCB ID %d, LUN %d, adapter id = %d.\n", scsi->cdb_id, scsi->scb_id, scsi->dev_id[scsi->scb_id].lun_id, scsi->attention);
|
|
if ((scsi->cdb_id != 0xff) && scsi_device_present(&scsi_devices[scsi->bus][scsi->cdb_id])) {
|
|
scsi->scsi_state = SCSI_STATE_SEND_COMMAND;
|
|
spock_log("Device selected at ID %i.\n", scsi->cdb_id);
|
|
} else {
|
|
spock_log("Device selection failed at ID %i.\n", scsi->cdb_id);
|
|
scsi->scsi_state = SCSI_STATE_IDLE;
|
|
if (!scsi->cmd_timer) {
|
|
spock_log("Callback to reset\n");
|
|
scsi->cmd_timer = 1;
|
|
}
|
|
spock_add_to_period(scsi, 1);
|
|
}
|
|
break;
|
|
|
|
case SCSI_STATE_SEND_COMMAND:
|
|
spock_log("Send Command ID=%d.\n", scsi->cdb_id);
|
|
sd = &scsi_devices[scsi->bus][scsi->cdb_id];
|
|
memset(scsi->temp_cdb, 0x00, 12);
|
|
|
|
if (scsi->cdb_len < 12) {
|
|
memcpy(scsi->temp_cdb, scsi->cdb,
|
|
scsi->cdb_len);
|
|
spock_add_to_period(scsi, scsi->cdb_len);
|
|
} else {
|
|
memcpy(scsi->temp_cdb, scsi->cdb,
|
|
12);
|
|
spock_add_to_period(scsi, 12);
|
|
}
|
|
|
|
scsi->data_ptr = scb->sge.sys_buf_addr;
|
|
scsi->data_len = scb->sge.sys_buf_byte_count;
|
|
|
|
if (scb->enable & 0x400)
|
|
sd->buffer_length = -1;
|
|
else
|
|
sd->buffer_length = spock_get_len(scsi, scb);
|
|
|
|
scsi_device_command_phase0(sd, scsi->temp_cdb);
|
|
spock_log("SCSI ID %i: Current CDB[0] = %02x, LUN = %i, data len = %i, max len = %i, phase val = %02x\n", scsi->cdb_id, scsi->temp_cdb[0], scsi->temp_cdb[1] >> 5, sd->buffer_length, spock_get_len(scsi, scb), sd->phase);
|
|
|
|
if ((sd->phase != SCSI_PHASE_STATUS) && (sd->buffer_length > 0)) {
|
|
p = scsi_device_get_callback(sd);
|
|
if (p <= 0.0)
|
|
spock_add_to_period(scsi, sd->buffer_length);
|
|
else
|
|
scsi->media_period += p;
|
|
|
|
if (scb->enable & ENABLE_PT) {
|
|
int32_t buflen = sd->buffer_length;
|
|
int sg_pos = 0;
|
|
uint32_t DataTx = 0;
|
|
uint32_t Address;
|
|
|
|
if (scb->sge.sys_buf_byte_count > 0) {
|
|
for (uint32_t c = 0; c < scsi->data_len; c += 8) {
|
|
spock_rd_sge(scsi, scsi->data_ptr + c, &scb->sge);
|
|
|
|
Address = scb->sge.sys_buf_addr;
|
|
DataTx = MIN((int) scb->sge.sys_buf_byte_count, buflen);
|
|
|
|
if ((sd->phase == SCSI_PHASE_DATA_IN) && DataTx) {
|
|
spock_log("Writing S/G segment %i: length %i, pointer %08X\n", c, DataTx, Address);
|
|
dma_bm_write(Address, &sd->sc->temp_buffer[sg_pos], DataTx, 2);
|
|
} else if ((sd->phase == SCSI_PHASE_DATA_OUT) && DataTx) {
|
|
spock_log("Reading S/G segment %i: length %i, pointer %08X\n", c, DataTx, Address);
|
|
dma_bm_read(Address, &sd->sc->temp_buffer[sg_pos], DataTx, 2);
|
|
}
|
|
|
|
sg_pos += scb->sge.sys_buf_byte_count;
|
|
buflen -= scb->sge.sys_buf_byte_count;
|
|
|
|
if (buflen < 0)
|
|
buflen = 0;
|
|
}
|
|
}
|
|
} else {
|
|
spock_log("Normal Transfer\n");
|
|
if (sd->phase == SCSI_PHASE_DATA_IN)
|
|
dma_bm_write(scsi->data_ptr, sd->sc->temp_buffer, MIN(sd->buffer_length, (int) scsi->data_len), 2);
|
|
else if (sd->phase == SCSI_PHASE_DATA_OUT)
|
|
dma_bm_read(scsi->data_ptr, sd->sc->temp_buffer, MIN(sd->buffer_length, (int) scsi->data_len), 2);
|
|
}
|
|
|
|
scsi_device_command_phase1(sd);
|
|
}
|
|
scsi->last_status = sd->status;
|
|
scsi->scsi_state = SCSI_STATE_END_PHASE;
|
|
break;
|
|
|
|
case SCSI_STATE_END_PHASE:
|
|
scsi->scsi_state = SCSI_STATE_IDLE;
|
|
|
|
spock_log("State to idle, cmd timer %d\n", scsi->cmd_timer);
|
|
if (!scsi->cmd_timer)
|
|
scsi->cmd_timer = 1;
|
|
|
|
spock_add_to_period(scsi, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
spock_callback(void *priv)
|
|
{
|
|
double period;
|
|
spock_t *scsi = (spock_t *) priv;
|
|
scb_t *scb = &scsi->scb;
|
|
|
|
scsi->temp_period = 0;
|
|
scsi->media_period = 0.0;
|
|
|
|
if (scsi->cmd_timer) {
|
|
scsi->cmd_timer--;
|
|
if (!scsi->cmd_timer) {
|
|
spock_execute_cmd(scsi, scb);
|
|
}
|
|
}
|
|
|
|
if (scsi->attention_wait && (scsi->scb_state == 0 || (scsi->attention_pending & 0xf0) == 0xe0)) {
|
|
scsi->attention_wait--;
|
|
if (!scsi->attention_wait) {
|
|
scsi->attention = scsi->attention_pending;
|
|
scsi->status &= ~STATUS_BUSY;
|
|
scsi->cir[0] = scsi->cir_pending[0];
|
|
scsi->cir[1] = scsi->cir_pending[1];
|
|
scsi->cir[2] = scsi->cir_pending[2];
|
|
scsi->cir[3] = scsi->cir_pending[3];
|
|
scsi->cir_status = 0;
|
|
|
|
spock_log("SCSI attention = %02x.\n", scsi->attention_pending);
|
|
switch (scsi->attention >> 4) {
|
|
case 1: /*Immediate command*/
|
|
scsi->cmd_status = 0x0a;
|
|
scsi->command = scsi->cir[0] | (scsi->cir[1] << 8) | (scsi->cir[2] << 16) | (scsi->cir[3] << 24);
|
|
switch (scsi->command & CMD_MASK) {
|
|
case CMD_ASSIGN:
|
|
case CMD_DMA_PACING_CONTROL:
|
|
case CMD_FEATURE_CONTROL:
|
|
case CMD_INVALID_412:
|
|
case CMD_RESET:
|
|
spock_process_imm_cmd(scsi);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
case 4:
|
|
case 0x0f: /*Start SCB*/
|
|
scsi->cmd_status = 1;
|
|
scsi->scb_addr = scsi->cir[0] | (scsi->cir[1] << 8) | (scsi->cir[2] << 16) | (scsi->cir[3] << 24);
|
|
scsi->scb_id = scsi->attention & 0x0f;
|
|
scsi->cmd_timer = SPOCK_TIME * 2;
|
|
spock_log("Start SCB at ID = %d, attention = %02x\n", scsi->scb_id, scsi->attention >> 4);
|
|
scsi->scb_state = 1;
|
|
break;
|
|
|
|
case 5: /*Invalid*/
|
|
case 0x0a: /*Invalid*/
|
|
scsi->in_invalid = 1;
|
|
scsi->cmd_timer = SPOCK_TIME * 2;
|
|
break;
|
|
|
|
case 0x0e: /*EOI*/
|
|
scsi->irq_status = 0;
|
|
spock_clear_irq(scsi, scsi->attention & 0x0f);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
spock_process_scsi(scsi, scb);
|
|
|
|
period = 0.2 * ((double) scsi->temp_period);
|
|
timer_on_auto(&scsi->callback_timer, (scsi->media_period + period + 10.0));
|
|
spock_log("Temporary period: %lf us (%" PRIi64 " periods), media period = %lf\n", scsi->callback_timer.period, scsi->temp_period, scsi->media_period);
|
|
}
|
|
|
|
static void
|
|
spock_mca_write(int port, uint8_t val, void *priv)
|
|
{
|
|
spock_t *scsi = (spock_t *) priv;
|
|
|
|
if (port < 0x102)
|
|
return;
|
|
|
|
io_removehandler((((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540, 0x0008, spock_read, spock_readw, NULL, spock_write, spock_writew, NULL, scsi);
|
|
mem_mapping_disable(&scsi->bios_rom.mapping);
|
|
|
|
scsi->pos_regs[port & 7] = val;
|
|
|
|
scsi->adapter_id = (scsi->pos_regs[3] & 0xe0) >> 5;
|
|
|
|
if (scsi->pos_regs[2] & 1) {
|
|
io_sethandler((((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540, 0x0008, spock_read, spock_readw, NULL, spock_write, spock_writew, NULL, scsi);
|
|
if (scsi->pos_regs[4] & 2) {
|
|
if (((scsi->pos_regs[2] >> 4) & 0x0f) != 0x0f) {
|
|
mem_mapping_set_addr(&scsi->bios_rom.mapping, ((scsi->pos_regs[2] >> 4) * 0x2000) + 0xc0000, 0x8000);
|
|
mem_mapping_enable(&scsi->bios_rom.mapping);
|
|
}
|
|
}
|
|
}
|
|
spock_log("[%04X:%08X]: POS Write Port = %x, val = %02x, rom addr = %05x\n", CS, cpu_state.pc, port & 7, val, ((scsi->pos_regs[2] >> 4) * 0x2000) + 0xc0000);
|
|
}
|
|
|
|
static uint8_t
|
|
spock_mca_read(int port, void *priv)
|
|
{
|
|
const spock_t *scsi = (spock_t *) priv;
|
|
|
|
spock_log("[%04X:%08X]: POS Read Port = %x, val = %02x\n", CS, cpu_state.pc,
|
|
port & 7, scsi->pos_regs[port & 7]);
|
|
return scsi->pos_regs[port & 7];
|
|
}
|
|
|
|
static uint8_t
|
|
spock_mca_feedb(void *priv)
|
|
{
|
|
const spock_t *scsi = (spock_t *) priv;
|
|
|
|
return (scsi->pos_regs[2] & 0x01);
|
|
}
|
|
|
|
static void
|
|
spock_mca_reset(void *priv)
|
|
{
|
|
spock_t *scsi = (spock_t *) priv;
|
|
|
|
scsi->in_reset = 2;
|
|
scsi->cmd_timer = SPOCK_TIME * 50;
|
|
scsi->status = STATUS_BUSY;
|
|
scsi->scsi_state = SCSI_STATE_IDLE;
|
|
scsi->scb_state = 0;
|
|
scsi->in_invalid = 0;
|
|
scsi->attention_wait = 0;
|
|
scsi->basic_ctrl = 0;
|
|
|
|
/* Reset all devices on controller reset. */
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
scsi_device_reset(&scsi_devices[scsi->bus][i]);
|
|
scsi->present[i] = 0xff;
|
|
}
|
|
|
|
spock_log("Reset.\n");
|
|
mem_mapping_disable(&scsi->bios_rom.mapping);
|
|
scsi->pos_regs[4] = 0x02;
|
|
spock_mca_write(0x102, 0, scsi);
|
|
}
|
|
|
|
static void *
|
|
spock_init(const device_t *info)
|
|
{
|
|
spock_t *scsi = malloc(sizeof(spock_t));
|
|
memset(scsi, 0x00, sizeof(spock_t));
|
|
|
|
scsi->bus = scsi_get_bus();
|
|
|
|
scsi->irq = 14;
|
|
|
|
scsi->bios_ver = device_get_config_int("bios_ver");
|
|
scsi->spock_16bit = info->local & 0xff;
|
|
|
|
switch (scsi->bios_ver) {
|
|
case 0:
|
|
rom_init_interleaved(&scsi->bios_rom, SPOCK_U68_1990_ROM, SPOCK_U69_1990_ROM,
|
|
0xc8000, 0x8000, 0x7fff, 0x4000, MEM_MAPPING_EXTERNAL);
|
|
break;
|
|
case 1:
|
|
rom_init_interleaved(&scsi->bios_rom, SPOCK_U68_1991_ROM, SPOCK_U69_1991_ROM,
|
|
0xc8000, 0x8000, 0x7fff, 0x4000, MEM_MAPPING_EXTERNAL);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
mem_mapping_disable(&scsi->bios_rom.mapping);
|
|
|
|
scsi->pos_regs[0] = scsi->spock_16bit ? 0xfe : 0xff;
|
|
scsi->pos_regs[1] = 0x8e;
|
|
scsi->pos_regs[4] = 0x02;
|
|
mca_add(spock_mca_read, spock_mca_write, spock_mca_feedb, spock_mca_reset, scsi);
|
|
|
|
scsi->in_reset = 2;
|
|
scsi->cmd_timer = SPOCK_TIME * 50;
|
|
scsi->status = STATUS_BUSY;
|
|
|
|
for (uint8_t c = 0; c < (SCSI_ID_MAX - 1); c++)
|
|
scsi->dev_id[c].phys_id = -1;
|
|
|
|
scsi->dev_id[SCSI_ID_MAX - 1].phys_id = scsi->adapter_id;
|
|
|
|
timer_add(&scsi->callback_timer, spock_callback, scsi, 1);
|
|
scsi->callback_timer.period = 10.0;
|
|
|
|
timer_set_delay_u64(&scsi->callback_timer,
|
|
(uint64_t) (scsi->callback_timer.period * ((double) TIMER_USEC)));
|
|
|
|
scsi_bus_set_speed(scsi->bus, 5000000.0);
|
|
|
|
return scsi;
|
|
}
|
|
|
|
static void
|
|
spock_close(void *priv)
|
|
{
|
|
spock_t *scsi = (spock_t *) priv;
|
|
|
|
if (scsi) {
|
|
free(scsi);
|
|
scsi = NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
spock_available(void)
|
|
{
|
|
return rom_present(SPOCK_U68_1991_ROM) && rom_present(SPOCK_U69_1991_ROM) && rom_present(SPOCK_U68_1990_ROM) && rom_present(SPOCK_U69_1990_ROM);
|
|
}
|
|
|
|
static const device_config_t spock_rom_config[] = {
|
|
// clang-format off
|
|
{
|
|
.name = "bios_ver",
|
|
.description = "BIOS Revision",
|
|
.type = CONFIG_SELECTION,
|
|
.default_string = "",
|
|
.default_int = 1,
|
|
.file_filter = "",
|
|
.spinner = { 0 },
|
|
.selection = {
|
|
{ .description = "1991 BIOS (>1GB)", .value = 1 },
|
|
{ .description = "1990 BIOS", .value = 0 },
|
|
{ .description = "" }
|
|
},
|
|
},
|
|
{ .name = "", .description = "", .type = CONFIG_END }
|
|
// clang-format on
|
|
};
|
|
|
|
const device_t spock_device = {
|
|
.name = "IBM PS/2 SCSI Adapter (Spock)",
|
|
.internal_name = "spock",
|
|
.flags = DEVICE_MCA,
|
|
.local = 0,
|
|
.init = spock_init,
|
|
.close = spock_close,
|
|
.reset = NULL,
|
|
{ .available = spock_available },
|
|
.speed_changed = NULL,
|
|
.force_redraw = NULL,
|
|
.config = spock_rom_config
|
|
};
|
|
|
|
const device_t tribble_device = {
|
|
.name = "IBM PS/2 SCSI Adapter (Tribble)",
|
|
.internal_name = "tribble",
|
|
.flags = DEVICE_MCA,
|
|
.local = 1,
|
|
.init = spock_init,
|
|
.close = spock_close,
|
|
.reset = NULL,
|
|
{ .available = spock_available },
|
|
.speed_changed = NULL,
|
|
.force_redraw = NULL,
|
|
.config = spock_rom_config
|
|
};
|