Fixes for the (S)VGA common DAC and some card-specific DAT's (ATi 68860, BT48x family, and the Cirrus Logic DAC), fixes Star Control II among other things.
This commit is contained in:
395
backup code/scsi/scsi_null.c
Normal file
395
backup code/scsi/scsi_null.c
Normal file
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Emulation of SCSI null device, used for invalid LUN's where
|
||||
* LUN 0 is valid.
|
||||
*
|
||||
* Version: @(#)scsi_null.c 1.0.0 2018/06/12
|
||||
*
|
||||
* Author: Miran Grca, <mgrca8@gmail.com>
|
||||
*
|
||||
* Copyright 2017,2018 Miran Grca.
|
||||
*/
|
||||
#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 "../timer.h"
|
||||
#include "../device.h"
|
||||
#include "../nvr.h"
|
||||
#include "../disk/hdd.h"
|
||||
#include "../disk/hdc.h"
|
||||
#include "../disk/hdc_ide.h"
|
||||
#include "../plat.h"
|
||||
#include "../ui.h"
|
||||
#include "scsi.h"
|
||||
#include "../cdrom/cdrom.h"
|
||||
#include "scsi_device.h"
|
||||
|
||||
|
||||
/* Bits of 'status' */
|
||||
#define ERR_STAT 0x01
|
||||
#define DRQ_STAT 0x08 /* Data request */
|
||||
#define DSC_STAT 0x10
|
||||
#define SERVICE_STAT 0x10
|
||||
#define READY_STAT 0x40
|
||||
#define BUSY_STAT 0x80
|
||||
|
||||
/* Bits of 'error' */
|
||||
#define ABRT_ERR 0x04 /* Command aborted */
|
||||
#define MCR_ERR 0x08 /* Media change request */
|
||||
|
||||
#define MAX_BLOCKS_AT_ONCE 340
|
||||
|
||||
|
||||
#define scsi_null_sense_key scsi_null_device_sense[2]
|
||||
#define scsi_null_asc scsi_null_device_sense[12]
|
||||
#define scsi_null_ascq scsi_null_device_sense[13]
|
||||
|
||||
|
||||
static uint8_t status, phase, packet_status, error, command, packet_len, sense_desc;
|
||||
static int64_t callback;
|
||||
static uint8_t null_id, null_lun;
|
||||
static uint8_t *temp_buffer;
|
||||
|
||||
|
||||
|
||||
#ifdef ENABLE_SCSI_NULL_LOG
|
||||
int scsi_null_do_log = ENABLE_SCSI_NULL_LOG;
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_log(const char *fmt, ...)
|
||||
{
|
||||
#ifdef ENABLE_SCSI_NULL_LOG
|
||||
va_list ap;
|
||||
|
||||
if (scsi_null_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Translates ATAPI status (ERR_STAT flag) to SCSI status. */
|
||||
int
|
||||
scsi_null_err_stat_to_scsi(void)
|
||||
{
|
||||
if (status & ERR_STAT)
|
||||
return SCSI_STATUS_CHECK_CONDITION;
|
||||
else
|
||||
return SCSI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_command_common(void)
|
||||
{
|
||||
status = BUSY_STAT;
|
||||
phase = 1;
|
||||
if (packet_status == CDROM_PHASE_COMPLETE) {
|
||||
scsi_null_callback();
|
||||
callback = 0LL;
|
||||
} else
|
||||
callback = -1LL; /* Speed depends on SCSI controller */
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_command_complete(void)
|
||||
{
|
||||
packet_status = CDROM_PHASE_COMPLETE;
|
||||
scsi_null_command_common();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_command_read_dma(void)
|
||||
{
|
||||
packet_status = CDROM_PHASE_DATA_IN_DMA;
|
||||
scsi_null_command_common();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_data_command_finish(int len, int block_len, int alloc_len, int direction)
|
||||
{
|
||||
if (alloc_len >= 0) {
|
||||
if (alloc_len < len)
|
||||
len = alloc_len;
|
||||
}
|
||||
if (len == 0)
|
||||
scsi_null_command_complete();
|
||||
else {
|
||||
if (direction == 0)
|
||||
scsi_null_command_read_dma();
|
||||
else
|
||||
fatal("SCSI NULL device write command\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_set_phase(uint8_t phase)
|
||||
{
|
||||
SCSIDevices[null_id][null_lun].Phase = phase;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_cmd_error(void)
|
||||
{
|
||||
scsi_null_set_phase(SCSI_PHASE_STATUS);
|
||||
error = ((scsi_null_sense_key & 0xf) << 4) | ABRT_ERR;
|
||||
status = READY_STAT | ERR_STAT;
|
||||
phase = 3;
|
||||
packet_status = 0x80;
|
||||
callback = 50 * SCSI_TIME;
|
||||
scsi_null_log("SCSI NULL: ERROR: %02X/%02X/%02X\n", scsi_null_sense_key, scsi_null_asc, scsi_null_ascq);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_invalid_lun(void)
|
||||
{
|
||||
scsi_null_sense_key = SENSE_ILLEGAL_REQUEST;
|
||||
scsi_null_asc = ASC_INV_LUN;
|
||||
scsi_null_ascq = 0;
|
||||
scsi_null_set_phase(SCSI_PHASE_STATUS);
|
||||
scsi_null_cmd_error();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_invalid_field(void)
|
||||
{
|
||||
scsi_null_sense_key = SENSE_ILLEGAL_REQUEST;
|
||||
scsi_null_asc = ASC_INV_FIELD_IN_CMD_PACKET;
|
||||
scsi_null_ascq = 0;
|
||||
scsi_null_cmd_error();
|
||||
status = 0x53;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
scsi_null_request_sense(uint8_t *buffer, uint8_t alloc_length, int desc)
|
||||
{
|
||||
/*Will return 18 bytes of 0*/
|
||||
if (alloc_length != 0) {
|
||||
memset(buffer, 0, alloc_length);
|
||||
if (!desc)
|
||||
if (alloc_length <= 18)
|
||||
memcpy(buffer, scsi_null_device_sense, alloc_length);
|
||||
else {
|
||||
memset(buffer, 0x00, alloc_length);
|
||||
memcpy(buffer, scsi_null_device_sense, 18);
|
||||
}
|
||||
else {
|
||||
buffer[1] = scsi_null_sense_key;
|
||||
buffer[2] = scsi_null_asc;
|
||||
buffer[3] = scsi_null_ascq;
|
||||
}
|
||||
} else
|
||||
return;
|
||||
|
||||
buffer[0] = 0x70;
|
||||
|
||||
scsi_null_log("SCSI NULL: Reporting sense: %02X %02X %02X\n", buffer[2], buffer[12], buffer[13]);
|
||||
|
||||
/* Clear the sense stuff as per the spec. */
|
||||
scsi_null_sense_key = scsi_null_asc = scsi_null_ascq = 0x00;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
scsi_null_request_sense_for_scsi(uint8_t *buffer, uint8_t alloc_length)
|
||||
{
|
||||
scsi_null_request_sense(buffer, alloc_length, 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
scsi_null_command(uint8_t *cdb)
|
||||
{
|
||||
int32_t *BufLen;
|
||||
uint32_t len;
|
||||
int max_len;
|
||||
unsigned idx = 0;
|
||||
unsigned size_idx, preamble_len;
|
||||
|
||||
BufLen = &SCSIDevices[null_id][null_lun].BufferLength;
|
||||
|
||||
status &= ~ERR_STAT;
|
||||
packet_len = 0;
|
||||
|
||||
scsi_null_set_phase(SCSI_PHASE_STATUS);
|
||||
|
||||
command = cdb[0];
|
||||
switch (cdb[0]) {
|
||||
case GPCMD_REQUEST_SENSE:
|
||||
/* If there's a unit attention condition and there's a buffered not ready, a standalone REQUEST SENSE
|
||||
should forget about the not ready, and report unit attention straight away. */
|
||||
sense_desc = cdb [1];
|
||||
|
||||
if ((*BufLen == -1) || (cdb[4] < *BufLen))
|
||||
*BufLen = cdb[4];
|
||||
|
||||
if (*BufLen < cdb[4])
|
||||
cdb[4] = *BufLen;
|
||||
|
||||
len = (cdb[1] & 1) ? 8 : 18;
|
||||
|
||||
scsi_null_set_phase(SCSI_PHASE_DATA_IN);
|
||||
scsi_null_data_command_finish(len, len, cdb[4], 0);
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case GPCMD_INQUIRY:
|
||||
max_len = cdb[3];
|
||||
max_len <<= 8;
|
||||
max_len |= cdb[4];
|
||||
|
||||
if ((!max_len) || (*BufLen == 0)) {
|
||||
scsi_null_set_phase(SCSI_PHASE_STATUS);
|
||||
packet_status = CDROM_PHASE_COMPLETE;
|
||||
callback = 20 * SCSI_TIME;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cdb[1] & 1) {
|
||||
scsi_null_invalid_field();
|
||||
return;
|
||||
} else {
|
||||
temp_buffer = malloc(1024);
|
||||
|
||||
preamble_len = 5;
|
||||
size_idx = 4;
|
||||
|
||||
memset(temp_buffer, 0, 8);
|
||||
temp_buffer[0] = (3 << 5); /*Invalid*/
|
||||
temp_buffer[1] = 0; /*Fixed*/
|
||||
temp_buffer[2] = 0x02; /*SCSI-2 compliant*/
|
||||
temp_buffer[3] = 0x02;
|
||||
temp_buffer[4] = 31;
|
||||
temp_buffer[6] = 1; /* 16-bit transfers supported */
|
||||
temp_buffer[7] = 0x20; /* Wide bus supported */
|
||||
|
||||
ide_padstr8(temp_buffer + 8, 8, EMU_NAME); /* Vendor */
|
||||
ide_padstr8(temp_buffer + 16, 16, "INVALID"); /* Product */
|
||||
ide_padstr8(temp_buffer + 32, 4, EMU_VERSION); /* Revision */
|
||||
idx = 36;
|
||||
|
||||
if (max_len == 96) {
|
||||
temp_buffer[4] = 91;
|
||||
idx = 96;
|
||||
}
|
||||
}
|
||||
|
||||
temp_buffer[size_idx] = idx - preamble_len;
|
||||
len=idx;
|
||||
|
||||
if (len > max_len)
|
||||
len = max_len;
|
||||
|
||||
if ((*BufLen == -1) || (len < *BufLen))
|
||||
*BufLen = len;
|
||||
|
||||
if (len > *BufLen)
|
||||
len = *BufLen;
|
||||
|
||||
scsi_null_set_phase(SCSI_PHASE_DATA_IN);
|
||||
scsi_null_data_command_finish(len, len, max_len, 0);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
scsi_null_invalid_lun();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_null_phase_data_in(void)
|
||||
{
|
||||
uint8_t *hdbufferb = SCSIDevices[null_id][null_lun].CmdBuffer;
|
||||
int32_t *BufLen = &SCSIDevices[null_id][null_lun].BufferLength;
|
||||
|
||||
if (!*BufLen) {
|
||||
scsi_null_log("scsi_null_phase_data_in(): Buffer length is 0\n");
|
||||
scsi_null_set_phase(SCSI_PHASE_STATUS);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case GPCMD_REQUEST_SENSE:
|
||||
scsi_null_log("SCSI NULL: %08X, %08X\n", hdbufferb, *BufLen);
|
||||
scsi_null_request_sense(hdbufferb, *BufLen, sense_desc & 1);
|
||||
break;
|
||||
#if 0
|
||||
case GPCMD_INQUIRY:
|
||||
memcpy(hdbufferb, temp_buffer, *BufLen);
|
||||
free(temp_buffer);
|
||||
temp_buffer = NULL;
|
||||
scsi_null_log("%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
||||
hdbufferb[0], hdbufferb[1], hdbufferb[2], hdbufferb[3], hdbufferb[4], hdbufferb[5], hdbufferb[6], hdbufferb[7],
|
||||
hdbufferb[8], hdbufferb[9], hdbufferb[10], hdbufferb[11], hdbufferb[12], hdbufferb[13], hdbufferb[14], hdbufferb[15]);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
fatal("SCSI NULL: Bad Command for phase 2 (%02X)\n", command);
|
||||
break;
|
||||
}
|
||||
|
||||
scsi_null_set_phase(SCSI_PHASE_STATUS);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
scsi_null_callback(void)
|
||||
{
|
||||
switch(packet_status) {
|
||||
case CDROM_PHASE_IDLE:
|
||||
scsi_null_log("SCSI NULL: PHASE_IDLE\n");
|
||||
phase = 1;
|
||||
status = READY_STAT | DRQ_STAT | (status & ERR_STAT);
|
||||
return;
|
||||
case CDROM_PHASE_COMPLETE:
|
||||
scsi_null_log("SCSI NULL: PHASE_COMPLETE\n");
|
||||
status = READY_STAT;
|
||||
phase = 3;
|
||||
packet_status = 0xFF;
|
||||
return;
|
||||
case CDROM_PHASE_DATA_IN_DMA:
|
||||
scsi_null_log("SCSI NULL: PHASE_DATA_IN_DMA\n");
|
||||
scsi_null_phase_data_in();
|
||||
packet_status = CDROM_PHASE_COMPLETE;
|
||||
status = READY_STAT;
|
||||
phase = 3;
|
||||
return;
|
||||
case CDROM_PHASE_ERROR:
|
||||
scsi_null_log("SCSI NULL: PHASE_ERROR\n");
|
||||
status = READY_STAT | ERR_STAT;
|
||||
phase = 3;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
scsi_null_set_location(uint8_t id, uint8_t lun)
|
||||
{
|
||||
null_id = id;
|
||||
null_lun = lun;
|
||||
}
|
||||
Reference in New Issue
Block a user