2020-08-28 14:18:02 +00:00
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// SCSI Target Emulator RaSCSI (*^..^*)
|
|
|
|
|
// for Raspberry Pi
|
|
|
|
|
//
|
|
|
|
|
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
|
|
|
|
// Copyright (C) 2014-2020 GIMONS
|
2021-03-08 01:29:30 +00:00
|
|
|
|
// Copyright (C) akuker
|
2020-08-28 14:18:02 +00:00
|
|
|
|
//
|
2021-03-08 01:29:30 +00:00
|
|
|
|
// Licensed under the BSD 3-Clause License.
|
|
|
|
|
// See LICENSE file in the project root folder.
|
2020-08-28 14:18:02 +00:00
|
|
|
|
//
|
2021-03-08 01:29:30 +00:00
|
|
|
|
// [ SCSI hard disk ]
|
2020-08-28 14:18:02 +00:00
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
#include "scsihd.h"
|
|
|
|
|
#include "xm6.h"
|
|
|
|
|
#include "fileio.h"
|
2021-08-09 23:56:13 +00:00
|
|
|
|
#include "exceptions.h"
|
2020-08-28 14:18:02 +00:00
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// SCSI Hard Disk
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Constructor
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
2021-07-26 18:33:36 +00:00
|
|
|
|
SCSIHD::SCSIHD() : Disk("SCHD")
|
2020-08-28 14:18:02 +00:00
|
|
|
|
{
|
2021-08-08 15:08:58 +00:00
|
|
|
|
disk.protectable = true;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Reset
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
2021-07-26 18:33:36 +00:00
|
|
|
|
void SCSIHD::Reset()
|
2020-08-28 14:18:02 +00:00
|
|
|
|
{
|
|
|
|
|
// Unlock and release attention
|
2021-08-08 15:08:58 +00:00
|
|
|
|
disk.locked = FALSE;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
disk.attn = FALSE;
|
|
|
|
|
|
|
|
|
|
// No reset, clear code
|
|
|
|
|
disk.reset = FALSE;
|
|
|
|
|
disk.code = 0x00;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Open
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
2021-08-09 23:56:13 +00:00
|
|
|
|
void SCSIHD::Open(const Filepath& path, BOOL /*attn*/)
|
2020-08-28 14:18:02 +00:00
|
|
|
|
{
|
|
|
|
|
ASSERT(!disk.ready);
|
|
|
|
|
|
|
|
|
|
// read open required
|
2021-07-07 21:46:45 +00:00
|
|
|
|
Fileio fio;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
if (!fio.Open(path, Fileio::ReadOnly)) {
|
2021-08-09 23:56:13 +00:00
|
|
|
|
throw ioexception("Can't open hard disk file read-only");
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get file size
|
2021-07-07 21:46:45 +00:00
|
|
|
|
off64_t size = fio.GetFileSize();
|
2020-08-28 14:18:02 +00:00
|
|
|
|
fio.Close();
|
|
|
|
|
|
|
|
|
|
// Must be 512 bytes
|
|
|
|
|
if (size & 0x1ff) {
|
2021-08-09 23:56:13 +00:00
|
|
|
|
throw ioexception("File size must be a multiple of 512 bytes");
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2TB according to xm6i
|
|
|
|
|
// There is a similar one in wxw/wxw_cfg.cpp
|
2021-08-08 15:08:58 +00:00
|
|
|
|
// Bigger files/drives require READ/WRITE(16) to be implemented
|
2020-08-28 14:18:02 +00:00
|
|
|
|
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
|
2021-08-09 23:56:13 +00:00
|
|
|
|
throw ioexception("File size must not exceed 2 TB");
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sector size and number of blocks
|
|
|
|
|
disk.size = 9;
|
|
|
|
|
disk.blocks = (DWORD)(size >> 9);
|
|
|
|
|
|
|
|
|
|
// Call base class
|
2021-08-09 23:56:13 +00:00
|
|
|
|
Disk::Open(path);
|
2020-08-28 14:18:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// INQUIRY
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
2021-07-26 18:33:36 +00:00
|
|
|
|
int SCSIHD:: Inquiry(
|
2020-08-28 14:18:02 +00:00
|
|
|
|
const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor)
|
|
|
|
|
{
|
|
|
|
|
char vendor[32];
|
|
|
|
|
char product[32];
|
|
|
|
|
char rev[32];
|
|
|
|
|
|
|
|
|
|
ASSERT(cdb);
|
|
|
|
|
ASSERT(buf);
|
|
|
|
|
ASSERT(cdb[0] == 0x12);
|
|
|
|
|
|
|
|
|
|
// EVPD check
|
|
|
|
|
if (cdb[1] & 0x01) {
|
|
|
|
|
disk.code = DISK_INVALIDCDB;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ready check (Error if no image file)
|
|
|
|
|
if (!disk.ready) {
|
|
|
|
|
disk.code = DISK_NOTREADY;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Basic data
|
|
|
|
|
// buf[0] ... Direct Access Device
|
|
|
|
|
// buf[2] ... SCSI-2 compliant command system
|
|
|
|
|
// buf[3] ... SCSI-2 compliant Inquiry response
|
|
|
|
|
// buf[4] ... Inquiry additional data
|
|
|
|
|
memset(buf, 0, 8);
|
|
|
|
|
|
|
|
|
|
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
|
|
|
|
if (((cdb[1] >> 5) & 0x07) != disk.lun) {
|
|
|
|
|
buf[0] = 0x7f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf[2] = 0x02;
|
|
|
|
|
buf[3] = 0x02;
|
|
|
|
|
buf[4] = 122 + 3; // Value close to real HDD
|
|
|
|
|
|
|
|
|
|
// Fill with blanks
|
|
|
|
|
memset(&buf[8], 0x20, buf[4] - 3);
|
|
|
|
|
|
|
|
|
|
// Determine vendor name/product name
|
|
|
|
|
sprintf(vendor, BENDER_SIGNATURE);
|
2021-07-07 21:46:45 +00:00
|
|
|
|
int size = disk.blocks >> 11;
|
2020-08-28 14:18:02 +00:00
|
|
|
|
if (size < 300)
|
|
|
|
|
sprintf(product, "PRODRIVE LPS%dS", size);
|
|
|
|
|
else if (size < 600)
|
|
|
|
|
sprintf(product, "MAVERICK%dS", size);
|
|
|
|
|
else if (size < 800)
|
|
|
|
|
sprintf(product, "LIGHTNING%dS", size);
|
|
|
|
|
else if (size < 1000)
|
|
|
|
|
sprintf(product, "TRAILBRAZER%dS", size);
|
|
|
|
|
else if (size < 2000)
|
|
|
|
|
sprintf(product, "FIREBALL%dS", size);
|
|
|
|
|
else
|
|
|
|
|
sprintf(product, "FBSE%d.%dS", size / 1000, (size % 1000) / 100);
|
|
|
|
|
|
|
|
|
|
// Vendor name
|
|
|
|
|
memcpy(&buf[8], vendor, strlen(vendor));
|
|
|
|
|
|
|
|
|
|
// Product name
|
|
|
|
|
memcpy(&buf[16], product, strlen(product));
|
|
|
|
|
|
|
|
|
|
// Revision
|
|
|
|
|
sprintf(rev, "0%01d%01d%01d",
|
|
|
|
|
(int)major, (int)(minor >> 4), (int)(minor & 0x0f));
|
|
|
|
|
memcpy(&buf[32], rev, 4);
|
|
|
|
|
|
|
|
|
|
// Size of data that can be returned
|
|
|
|
|
size = (buf[4] + 5);
|
|
|
|
|
|
|
|
|
|
// Limit if the other buffer is small
|
|
|
|
|
if (size > (int)cdb[4]) {
|
|
|
|
|
size = (int)cdb[4];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Success
|
|
|
|
|
disk.code = DISK_NOERROR;
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// MODE SELECT
|
|
|
|
|
// *Not affected by disk.code
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
2021-07-26 18:33:36 +00:00
|
|
|
|
BOOL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
2020-08-28 14:18:02 +00:00
|
|
|
|
{
|
|
|
|
|
BYTE page;
|
|
|
|
|
int size;
|
|
|
|
|
|
|
|
|
|
ASSERT(buf);
|
|
|
|
|
ASSERT(length >= 0);
|
|
|
|
|
|
|
|
|
|
// PF
|
|
|
|
|
if (cdb[1] & 0x10) {
|
|
|
|
|
// Mode Parameter header
|
|
|
|
|
if (length >= 12) {
|
|
|
|
|
// Check the block length bytes
|
|
|
|
|
size = 1 << disk.size;
|
|
|
|
|
if (buf[9] != (BYTE)(size >> 16) ||
|
|
|
|
|
buf[10] != (BYTE)(size >> 8) ||
|
|
|
|
|
buf[11] != (BYTE)size) {
|
|
|
|
|
// currently does not allow changing sector length
|
|
|
|
|
disk.code = DISK_INVALIDPRM;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
buf += 12;
|
|
|
|
|
length -= 12;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parsing the page
|
|
|
|
|
while (length > 0) {
|
|
|
|
|
// Get page
|
|
|
|
|
page = buf[0];
|
|
|
|
|
|
|
|
|
|
switch (page) {
|
|
|
|
|
// format device
|
|
|
|
|
case 0x03:
|
|
|
|
|
// check the number of bytes in the physical sector
|
|
|
|
|
size = 1 << disk.size;
|
|
|
|
|
if (buf[0xc] != (BYTE)(size >> 8) ||
|
|
|
|
|
buf[0xd] != (BYTE)size) {
|
|
|
|
|
// currently does not allow changing sector length
|
|
|
|
|
disk.code = DISK_INVALIDPRM;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// CD-ROM Parameters
|
|
|
|
|
// According to the SONY CDU-541 manual, Page code 8 is supposed
|
|
|
|
|
// to set the Logical Block Adress Format, as well as the
|
|
|
|
|
// inactivity timer multiplier
|
|
|
|
|
case 0x08:
|
|
|
|
|
// Debug code for Issue #2:
|
|
|
|
|
// https://github.com/akuker/RASCSI/issues/2
|
2021-02-07 19:00:48 +00:00
|
|
|
|
LOGWARN("[Unhandled page code] Received mode page code 8 with total length %d\n ", length);
|
2020-08-28 14:18:02 +00:00
|
|
|
|
for (int i = 0; i<length; i++)
|
|
|
|
|
{
|
|
|
|
|
printf("%02X ", buf[i]);
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
break;
|
|
|
|
|
// Other page
|
|
|
|
|
default:
|
|
|
|
|
printf("Unknown Mode Select page code received: %02X\n",page);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Advance to the next page
|
|
|
|
|
size = buf[1] + 2;
|
|
|
|
|
length -= size;
|
|
|
|
|
buf += size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do not generate an error for the time being (MINIX)
|
|
|
|
|
disk.code = DISK_NOERROR;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|