2020-03-24 01:00:28 +01:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Version: @(#)scsi_spock.c 1.0.1 2020/03/23
|
|
|
|
|
*
|
|
|
|
|
* Authors: Sarah Walker, <http://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.h"
|
|
|
|
|
#include "../io.h"
|
|
|
|
|
#include "../timer.h"
|
|
|
|
|
#include "../dma.h"
|
|
|
|
|
#include "../pic.h"
|
|
|
|
|
#include "../mca.h"
|
|
|
|
|
#include "../mem.h"
|
|
|
|
|
#include "../rom.h"
|
|
|
|
|
#include "../device.h"
|
|
|
|
|
#include "../nvr.h"
|
|
|
|
|
#include "../plat.h"
|
|
|
|
|
#include "scsi.h"
|
|
|
|
|
#include "scsi_device.h"
|
|
|
|
|
#include "scsi_spock.h"
|
|
|
|
|
|
|
|
|
|
#define SPOCK_U68_1990_ROM L"roms/scsi/ibm/64f4376.bin"
|
|
|
|
|
#define SPOCK_U69_1990_ROM L"roms/scsi/ibm/64f4377.bin"
|
|
|
|
|
|
|
|
|
|
#define SPOCK_U68_1991_ROM L"roms/scsi/ibm/92F2244.U68"
|
|
|
|
|
#define SPOCK_U69_1991_ROM L"roms/scsi/ibm/92F2245.U69"
|
|
|
|
|
|
|
|
|
|
#define SPOCK_TIME (20)
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
SCSI_STATE_IDLE,
|
|
|
|
|
SCSI_STATE_SELECT,
|
|
|
|
|
SCSI_STATE_SELECT_FAILED,
|
|
|
|
|
SCSI_STATE_SEND_COMMAND,
|
|
|
|
|
SCSI_STATE_END_PHASE
|
|
|
|
|
} scsi_state_t;
|
|
|
|
|
|
|
|
|
|
#pragma pack(push,1)
|
|
|
|
|
typedef struct {
|
|
|
|
|
uint16_t pos;
|
|
|
|
|
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 cmd_status;
|
|
|
|
|
int cir_status;
|
|
|
|
|
|
|
|
|
|
uint8_t pacing;
|
|
|
|
|
|
|
|
|
|
uint8_t buf[0x600];
|
|
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
int phys_id;
|
|
|
|
|
int lun_id;
|
|
|
|
|
} dev_id[SCSI_ID_MAX];
|
|
|
|
|
|
|
|
|
|
uint8_t last_status;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
if (!scsi->irq_status) {
|
|
|
|
|
for (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 *p)
|
|
|
|
|
{
|
|
|
|
|
spock_t *scsi = (spock_t *)p;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
spock_writew(uint16_t port, uint16_t val, void *p)
|
|
|
|
|
{
|
|
|
|
|
spock_t *scsi = (spock_t *)p;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spock_log("spock_writew: port=%04x val=%04x\n", port, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
|
spock_read(uint16_t port, void *p)
|
|
|
|
|
{
|
|
|
|
|
spock_t *scsi = (spock_t *)p;
|
|
|
|
|
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;
|
|
|
|
|
if (scsi->cir_status == 0) {
|
|
|
|
|
spock_log("Status Cmd Empty\n");
|
|
|
|
|
temp |= STATUS_CMD_EMPTY;
|
|
|
|
|
}
|
|
|
|
|
if (scsi->cir_status == 3) {
|
|
|
|
|
spock_log("Status Cmd Full\n");
|
|
|
|
|
temp |= STATUS_CMD_FULL;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spock_log("spock_read: port=%04x val=%02x %04x(%05x):%04x %02x\n", port, temp, CS, cs, cpu_state.pc, BH);
|
|
|
|
|
return temp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
|
spock_readw(uint16_t port, void *p)
|
|
|
|
|
{
|
|
|
|
|
spock_t *scsi = (spock_t *)p;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
DMAPageRead(Address, (uint8_t *)SG, sizeof(SGE));
|
|
|
|
|
spock_add_to_period(scsi, sizeof(SGE));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
spock_get_len(spock_t *scsi, scb_t *scb)
|
|
|
|
|
{
|
|
|
|
|
uint32_t DataToTransfer = 0, i = 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 (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 adapter_id, phys_id, 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("Physical Device Number -1\n");
|
|
|
|
|
scsi->dev_id[adapter_id].phys_id = -1;
|
|
|
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
|
|
|
} else if (phys_id == 7) { /*Can not assign adapter*/
|
|
|
|
|
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_FAIL);
|
|
|
|
|
} else {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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\n");
|
|
|
|
|
if ((scsi->attention & 15) == 15) { /*Adapter reset*/
|
|
|
|
|
for (i = 0; i < 7; i++)
|
|
|
|
|
scsi_device_reset(&scsi_devices[i]);
|
|
|
|
|
spock_log("Adapter Reset\n");
|
|
|
|
|
|
|
|
|
|
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 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);
|
|
|
|
|
} else
|
|
|
|
|
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;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
DMAPageWrite(scb->term_status_block_addr + 0x7*2, (uint8_t *)&term_stat_block_addr7, 2);
|
|
|
|
|
DMAPageWrite(scb->term_status_block_addr + 0x8*2, (uint8_t *)&term_stat_block_addr8, 2);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DMAPageRead(scsi->scb_addr, (uint8_t *)&scb->command, 2);
|
|
|
|
|
DMAPageRead(scsi->scb_addr + 2, (uint8_t *)&scb->enable, 2);
|
|
|
|
|
DMAPageRead(scsi->scb_addr + 4, (uint8_t *)&scb->lba_addr, 4);
|
|
|
|
|
DMAPageRead(scsi->scb_addr + 8, (uint8_t *)&scb->sge.sys_buf_addr, 4);
|
|
|
|
|
DMAPageRead(scsi->scb_addr + 12, (uint8_t *)&scb->sge.sys_buf_byte_count, 4);
|
|
|
|
|
DMAPageRead(scsi->scb_addr + 16, (uint8_t *)&scb->term_status_block_addr, 4);
|
|
|
|
|
DMAPageRead(scsi->scb_addr + 20, (uint8_t *)&scb->scb_chain_addr, 4);
|
|
|
|
|
DMAPageRead(scsi->scb_addr + 24, (uint8_t *)&scb->block_count, 2);
|
|
|
|
|
DMAPageRead(scsi->scb_addr + 26, (uint8_t *)&scb->block_length, 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->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);
|
|
|
|
|
|
|
|
|
|
if (scb->command == 0x245f) { /*Issued by NT October 1991 and December 1991 (Undocumented version of Send Other SCSI?)*/
|
|
|
|
|
spock_log("Send Other SCSI (BIOS?)\n");
|
|
|
|
|
scb->command = CMD_SEND_OTHER_SCSI;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr, (uint8_t *)&get_complete_stat->scb_status, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 2, (uint8_t *)&get_complete_stat->retry_count, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 4, (uint8_t *)&get_complete_stat->residual_byte_count, 4);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 8, (uint8_t *)&get_complete_stat->sg_list_element_addr, 4);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 12, (uint8_t *)&get_complete_stat->device_dep_status_len, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 14, (uint8_t *)&get_complete_stat->cmd_status, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 16, (uint8_t *)&get_complete_stat->error, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 18, (uint8_t *)&get_complete_stat->reserved, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 20, (uint8_t *)&get_complete_stat->cache_info_status, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 22, (uint8_t *)&get_complete_stat->scb_addr, 4);
|
|
|
|
|
scsi->scb_state = 3;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CMD_UNKNOWN_1C10:
|
|
|
|
|
spock_log("Unknown 1C10\n");
|
|
|
|
|
DMAPageRead(scb->sge.sys_buf_addr, scsi->buf, scb->sge.sys_buf_byte_count);
|
|
|
|
|
scsi->scb_state = 3;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CMD_UNKNOWN_1C11:
|
|
|
|
|
spock_log("Unknown 1C11\n");
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr, scsi->buf, scb->sge.sys_buf_byte_count);
|
|
|
|
|
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 = 0x8eff;
|
|
|
|
|
get_pos_info->pos2 = scsi->pos_regs[3] | (scsi->pos_regs[2] << 8);
|
|
|
|
|
get_pos_info->pos3 = 0x0e | (scsi->pos_regs[4] << 8);
|
|
|
|
|
get_pos_info->pos4 = 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;
|
|
|
|
|
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr, (uint8_t *)&get_pos_info->pos, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 2, (uint8_t *)&get_pos_info->pos2, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 4, (uint8_t *)&get_pos_info->pos3, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 6, (uint8_t *)&get_pos_info->pos4, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 8, (uint8_t *)&get_pos_info->pos5, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 10, (uint8_t *)&get_pos_info->pos6, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 12, (uint8_t *)&get_pos_info->pos7, 2);
|
|
|
|
|
DMAPageWrite(scb->sge.sys_buf_addr + 14, (uint8_t *)&get_pos_info->pos8, 2);
|
|
|
|
|
scsi->scb_state = 3;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CMD_DEVICE_INQUIRY:
|
|
|
|
|
spock_log("Device Inquiry\n");
|
|
|
|
|
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->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
|
|
|
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:
|
|
|
|
|
spock_log("Send Other SCSI\n");
|
|
|
|
|
DMAPageRead(scsi->scb_addr + 0x18, scsi->cdb, 12);
|
|
|
|
|
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->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
|
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
|
|
|
scsi->scb_state = 2;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case CMD_READ_DEVICE_CAPACITY:
|
|
|
|
|
spock_log("Device Capacity\n");
|
|
|
|
|
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->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
|
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
|
|
|
scsi->scb_state = 2;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case CMD_READ_DATA:
|
|
|
|
|
spock_log("Device Read Data\n");
|
|
|
|
|
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->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
|
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
|
|
|
scsi->scb_state = 2;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case CMD_WRITE_DATA:
|
|
|
|
|
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->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
|
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
|
|
|
scsi->scb_state = 2;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case CMD_VERIFY:
|
|
|
|
|
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->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
|
|
|
scsi->data_len = 0;
|
|
|
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
|
|
|
scsi->scb_state = 2;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case CMD_REQUEST_SENSE:
|
|
|
|
|
spock_log("Device Request Sense\n");
|
|
|
|
|
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->cdb_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
|
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
|
|
|
scsi->scb_state = 2;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2: /* Wait */
|
|
|
|
|
if (scsi->scsi_state == SCSI_STATE_IDLE) {
|
|
|
|
|
if (scsi->last_status == SCSI_STATUS_OK) {
|
|
|
|
|
scsi->scb_state = 3;
|
|
|
|
|
spock_log("Status is Good on device ID %d\n", 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\n", scsi->cdb_id);
|
|
|
|
|
DMAPageWrite(scb->term_status_block_addr + 0x7*2, (uint8_t *)&term_stat_block_addr7, 2);
|
|
|
|
|
DMAPageWrite(scb->term_status_block_addr + 0x8*2, (uint8_t *)&term_stat_block_addr8, 2);
|
|
|
|
|
DMAPageWrite(scb->term_status_block_addr + 0xb*2, (uint8_t *)&term_stat_block_addrb, 2);
|
|
|
|
|
DMAPageWrite(scb->term_status_block_addr + 0xc*2, (uint8_t *)&term_stat_block_addrc, 2);
|
|
|
|
|
}
|
|
|
|
|
} else if (scsi->scsi_state == SCSI_STATE_SELECT_FAILED) {
|
|
|
|
|
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;
|
|
|
|
|
DMAPageWrite(scb->term_status_block_addr + 0x7*2, (uint8_t *)&term_stat_block_addr7, 2);
|
|
|
|
|
DMAPageWrite(scb->term_status_block_addr + 0x8*2, (uint8_t *)&term_stat_block_addr8, 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\n");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (scsi->scb_state != old_scb_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
spock_process_scsi(spock_t *scsi, scb_t *scb)
|
|
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
double p;
|
|
|
|
|
scsi_device_t *sd;
|
|
|
|
|
|
|
|
|
|
switch (scsi->scsi_state) {
|
|
|
|
|
case SCSI_STATE_IDLE:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCSI_STATE_SELECT:
|
|
|
|
|
if ((scsi->cdb_id != (uint8_t)-1) && scsi_device_present(&scsi_devices[scsi->cdb_id])) {
|
|
|
|
|
scsi->scsi_state = SCSI_STATE_SEND_COMMAND;
|
|
|
|
|
spock_log("Device selected at ID %i\n", scsi->cdb_id);
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
spock_log("Device selection failed at ID %i\n", scsi->cdb_id);
|
|
|
|
|
scsi->scsi_state = SCSI_STATE_SELECT_FAILED;
|
|
|
|
|
if (!scsi->cmd_timer) {
|
|
|
|
|
spock_log("Callback to reset\n");
|
|
|
|
|
scsi->cmd_timer = 1;
|
|
|
|
|
}
|
|
|
|
|
spock_add_to_period(scsi, 1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCSI_STATE_SELECT_FAILED:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCSI_STATE_SEND_COMMAND:
|
|
|
|
|
sd = &scsi_devices[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;
|
|
|
|
|
|
|
|
|
|
sd->buffer_length = spock_get_len(scsi, scb);
|
|
|
|
|
|
|
|
|
|
scsi_device_command_phase0(sd, scsi->temp_cdb);
|
|
|
|
|
spock_log("SCSI ID %i: Command %02X: CDB1 = %02x, LUN = %i, Buffer Length %i, SCSI Phase %02X\n", scsi->cdb_id, scsi->temp_cdb[0], scsi->temp_cdb[1], scsi->dev_id[scsi->scb_id].lun_id, sd->buffer_length, 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 (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);
|
|
|
|
|
DMAPageWrite(Address, &sd->sc->temp_buffer[sg_pos], DataTx);
|
|
|
|
|
} else if ((sd->phase == SCSI_PHASE_DATA_OUT) && DataTx) {
|
|
|
|
|
spock_log("Reading S/G segment %i: length %i, pointer %08X\n", c, DataTx, Address);
|
|
|
|
|
DMAPageRead(Address, &sd->sc->temp_buffer[sg_pos], DataTx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sg_pos += scb->sge.sys_buf_byte_count;
|
|
|
|
|
buflen -= scb->sge.sys_buf_byte_count;
|
|
|
|
|
|
|
|
|
|
if (buflen < 0)
|
|
|
|
|
buflen = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (sd->phase == SCSI_PHASE_DATA_IN) {
|
|
|
|
|
DMAPageWrite(scsi->data_ptr, sd->sc->temp_buffer, MIN(sd->buffer_length, (int)scsi->data_len));
|
|
|
|
|
} else if (sd->phase == SCSI_PHASE_DATA_OUT)
|
|
|
|
|
DMAPageRead(scsi->data_ptr, sd->sc->temp_buffer, MIN(sd->buffer_length, (int)scsi->data_len));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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\n");
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
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 %08x\n", scsi->scb_addr);
|
|
|
|
|
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 & 0xf);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spock_process_scsi(scsi, scb);
|
|
|
|
|
|
|
|
|
|
period = 0.2 * ((double) scsi->temp_period);
|
|
|
|
|
timer_on(&scsi->callback_timer, (scsi->media_period + period + 10.0), 0);
|
|
|
|
|
spock_log("Temporary period: %lf us (%" PRIi64 " periods)\n", scsi->callback_timer.period, scsi->temp_period);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
spock_mca_write(int port, uint8_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
spock_t *scsi = (spock_t *)priv;
|
|
|
|
|
|
|
|
|
|
if (port < 0x102)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
spock_log("spock_mca_write: port=%04x val=%02x %04x:%04x\n", port, val, CS, cpu_state.pc);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
spock_log("SCSI pos reg write POS2 = %02x, POS3 = %02x, POS4 = %02x\n", scsi->pos_regs[2], scsi->pos_regs[3], scsi->pos_regs[4]);
|
|
|
|
|
|
|
|
|
|
if (scsi->pos_regs[2] & 1) {
|
|
|
|
|
spock_log("spock scsi io = %04x\n", (((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540);
|
|
|
|
|
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[2] >> 4) == 0x0f)
|
|
|
|
|
mem_mapping_disable(&scsi->bios_rom.mapping);
|
|
|
|
|
else {
|
|
|
|
|
spock_log("Spock BIOS segment select (hex val) = %02x, enabled bios area is %s\n", scsi->pos_regs[2] >> 4, (scsi->pos_regs[4] & 0x80) ? "32KB" : "16KB");
|
|
|
|
|
mem_mapping_set_addr(&scsi->bios_rom.mapping, ((scsi->pos_regs[2] >> 4) * 0x2000) + 0xc0000, 0x8000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
|
spock_mca_read(int port, void *priv)
|
|
|
|
|
{
|
|
|
|
|
spock_t *scsi = (spock_t *)priv;
|
|
|
|
|
|
|
|
|
|
spock_log("spock_mca_read: port=%04x %02x %04x:%04x\n", port, scsi->pos_regs[port & 7], CS,cpu_state.pc);
|
|
|
|
|
|
|
|
|
|
return scsi->pos_regs[port & 7];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
|
spock_mca_feedb(void *priv)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
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 (i = 0; i < 7; i++)
|
|
|
|
|
scsi_device_reset(&scsi_devices[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
|
spock_init(const device_t *info)
|
|
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
spock_t *scsi = malloc(sizeof(spock_t));
|
|
|
|
|
memset(scsi, 0x00, sizeof(spock_t));
|
|
|
|
|
|
|
|
|
|
scsi->irq = 14;
|
|
|
|
|
|
|
|
|
|
scsi->bios_ver = device_get_config_int("bios_ver");
|
|
|
|
|
|
|
|
|
|
if (scsi->bios_ver)
|
|
|
|
|
rom_init_interleaved(&scsi->bios_rom, SPOCK_U68_1991_ROM, SPOCK_U69_1991_ROM,
|
|
|
|
|
0xc8000, 0x8000, 0x7fff, 0x4000, MEM_MAPPING_EXTERNAL);
|
|
|
|
|
else
|
|
|
|
|
rom_init_interleaved(&scsi->bios_rom, SPOCK_U68_1990_ROM, SPOCK_U69_1990_ROM,
|
|
|
|
|
0xc8000, 0x8000, 0x7fff, 0x4000, MEM_MAPPING_EXTERNAL);
|
|
|
|
|
|
|
|
|
|
mem_mapping_disable(&scsi->bios_rom.mapping);
|
|
|
|
|
|
|
|
|
|
scsi->pos_regs[0] = 0xff;
|
|
|
|
|
scsi->pos_regs[1] = 0x8e;
|
2020-03-24 02:34:20 +01:00
|
|
|
// mca_add(spock_mca_read, spock_mca_write, spock_mca_feedb, spock_mca_reset, scsi);
|
|
|
|
|
mca_add(spock_mca_read, spock_mca_write, spock_mca_feedb, scsi);
|
2020-03-24 01:00:28 +01:00
|
|
|
|
|
|
|
|
scsi->in_reset = 2;
|
|
|
|
|
scsi->cmd_timer = SPOCK_TIME * 50;
|
|
|
|
|
scsi->status = STATUS_BUSY;
|
|
|
|
|
|
|
|
|
|
for (c = 0; c < (SCSI_ID_MAX-1); c++)
|
|
|
|
|
scsi->dev_id[c].phys_id = -1;
|
|
|
|
|
|
|
|
|
|
scsi->dev_id[SCSI_ID_MAX-1].phys_id = 7;
|
|
|
|
|
|
|
|
|
|
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)));
|
|
|
|
|
|
|
|
|
|
return scsi;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
spock_close(void *p)
|
|
|
|
|
{
|
|
|
|
|
spock_t *scsi = (spock_t *)p;
|
|
|
|
|
|
|
|
|
|
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[] = {
|
|
|
|
|
{
|
|
|
|
|
"bios_ver", "BIOS Version", CONFIG_SELECTION, "", 1,
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
"1991 BIOS (>1GB)", 1
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"1990 BIOS", 0
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
""
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"", "", -1
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const device_t spock_device =
|
|
|
|
|
{
|
|
|
|
|
"IBM PS/2 SCSI Adapter (Spock)",
|
|
|
|
|
DEVICE_MCA,
|
|
|
|
|
0,
|
|
|
|
|
spock_init, spock_close, NULL,
|
|
|
|
|
spock_available,
|
|
|
|
|
NULL, NULL,
|
|
|
|
|
spock_rom_config
|
|
|
|
|
};
|