mirror of
https://github.com/akuker/RASCSI.git
synced 2026-10-26 04:02:17 +00:00
Restore the SCBR device as an X68000 Host Bridge, including the Human68k-compatible host file-system service and the bridge-specific SCSI message handlers. Back its Ethernet path with the TAP driver, derive a stable locally administered MAC address, preserve the driver minimum-frame/FCS expectations, and restrict received traffic to its own unicast frames and broadcasts. Match the RASCTL/RASETHER/RASDRV bridge protocol that originated in the parent RaSCSI project: dispatch its non-standard message format on READ(10)/WRITE(10) (0x28/0x2a), with type, function, phase, and a three-byte byte count carried in the CDB. Advertise the legacy control channel required by RASCTL, translate its list and device-management commands to PiSCSI operations, and defer stop or system-shutdown requests until after the response is sent, while gating this insecure interface behind a new '-c disabled|media|full' runtime option. Preserve the type-1 Ethernet exchange used by RASETHER and the type-2, phased filesystem exchange used by RASDRV. Register the device in the factory, protobuf API, controller transfer path, command-line tooling, device listings, and web UI. Share the explicit bridge and proxy-ARP topology workflow with DaynaPort so both adapters validate and use the same mode/interface pair. Add build support for iconv where needed, extend unit coverage for device creation, RAS* protocol handling, CLI parsing, web attachment, and topology selection, and update the man pages and network-profile documentation for Host Bridge operation and its networking limits.
1239 lines
31 KiB
C++
1239 lines
31 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// SCSI Target Emulator PiSCSI
|
|
// for Raspberry Pi
|
|
//
|
|
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
|
// Copyright (C) 2014-2020 GIMONS
|
|
// Copyright (C) akuker
|
|
//
|
|
// Licensed under the BSD 3-Clause License.
|
|
// See LICENSE file in the project root folder.
|
|
//
|
|
// [ SCSI Host Bridge for the Sharp X68000 ]
|
|
//
|
|
// Note: This requires a special driver on the host system and will only
|
|
// work with the Sharp X68000 operating system.
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "shared/piscsi_exceptions.h"
|
|
#include "scsi_command_util.h"
|
|
#include "scsi_host_bridge.h"
|
|
#include <arpa/inet.h>
|
|
#include <algorithm>
|
|
|
|
using namespace std;
|
|
using namespace scsi_defs;
|
|
using namespace scsi_command_util;
|
|
|
|
SCSIBR::SCSIBR(int lun) : PrimaryDevice(SCBR, lun)
|
|
{
|
|
SupportsParams(true);
|
|
}
|
|
|
|
bool SCSIBR::Init(const param_map& params)
|
|
{
|
|
PrimaryDevice::Init(params);
|
|
|
|
// Create host file system
|
|
fs.Reset();
|
|
|
|
AddCommand(scsi_command::eCmdTestUnitReady, [this] { TestUnitReady(); });
|
|
// The X68000 bridge protocol overloads READ(10) and WRITE(10), using
|
|
// bytes 2, 3 and 9 as type, function and phase and bytes 6--8 as a
|
|
// three-byte byte count. RASCTL, RASDRV and RASETHER all use these opcodes.
|
|
AddCommand(scsi_command::eCmdRead10, [this] { GetMessage10(); });
|
|
AddCommand(scsi_command::eCmdWrite10, [this] { SendMessage10(); });
|
|
|
|
#ifdef __linux__
|
|
tap_enabled = tap.Init(GetParams());
|
|
if (!tap_enabled){
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
// Generate MAC Address
|
|
if (tap_enabled) {
|
|
array<uint8_t, 6> uplink_mac = {};
|
|
tap.GetUplinkMacAddr(uplink_mac.data());
|
|
mac_addr = DeriveMac(uplink_mac);
|
|
}
|
|
|
|
// Packet reception flag OFF
|
|
packet_enable = false;
|
|
|
|
SetReady(tap_enabled);
|
|
|
|
// Not terminating on regular Linux PCs is helpful for testing
|
|
#if defined(__x86_64__) || defined(__X86__)
|
|
return true;
|
|
#else
|
|
return tap_enabled;
|
|
#endif
|
|
}
|
|
|
|
array<uint8_t, 6> SCSIBR::DeriveMac(span<const uint8_t> uplink_mac)
|
|
{
|
|
if (uplink_mac.size() != 6) {
|
|
return {};
|
|
}
|
|
|
|
// The X68000 driver treats this as its sole mutable hardware address. Use
|
|
// a deterministic locally administered unicast address, distinct from the
|
|
// physical uplink, bridge, and kernel TAP MAC addresses.
|
|
uint64_t suffix = 14695981039346656037ULL;
|
|
for (const uint8_t byte : uplink_mac) {
|
|
suffix = (suffix ^ byte) * 1099511628211ULL;
|
|
}
|
|
return { 0x02, static_cast<uint8_t>(suffix >> 32), static_cast<uint8_t>(suffix >> 24),
|
|
static_cast<uint8_t>(suffix >> 16), static_cast<uint8_t>(suffix >> 8), static_cast<uint8_t>(suffix) };
|
|
}
|
|
|
|
void SCSIBR::CleanUp()
|
|
{
|
|
tap.CleanUp();
|
|
}
|
|
|
|
vector<uint8_t> SCSIBR::InquiryInternal() const
|
|
{
|
|
vector<uint8_t> buf = HandleInquiry(device_type::communications, scsi_level::scsi_2, false);
|
|
|
|
// The bridge returns more additional bytes than the other devices
|
|
buf.resize(0x1F + 8 + 5);
|
|
|
|
buf[4] = 0x1F + 8;
|
|
|
|
// RASCTL uses this flag to discover a bridge with its type-0 control channel.
|
|
// The control channel is disabled by default because it has no authentication.
|
|
if (IsRasctlControlEnabled()) {
|
|
buf[36] = '1';
|
|
}
|
|
|
|
// TAP Enable
|
|
if (tap_enabled) {
|
|
buf[37] = '1';
|
|
}
|
|
|
|
// CFileSys Enable
|
|
buf[38] = '1';
|
|
|
|
return buf;
|
|
}
|
|
|
|
void SCSIBR::TestUnitReady()
|
|
{
|
|
// Always successful
|
|
EnterStatusPhase();
|
|
}
|
|
|
|
int SCSIBR::GetMessage10(cdb_t cdb, vector<uint8_t>& buf)
|
|
{
|
|
// Type
|
|
const int type = cdb[2];
|
|
|
|
// Function number
|
|
const int func = cdb[3];
|
|
|
|
// Phase
|
|
const int phase = cdb[9];
|
|
const int len = GetInt24(cdb, 6);
|
|
|
|
switch (type) {
|
|
case 0: { // RASCTL compatibility control channel
|
|
if (!IsRasctlControlEnabled()) {
|
|
return 0;
|
|
}
|
|
|
|
const size_t response_length = min(static_cast<size_t>(len), buf.size());
|
|
fill_n(buf.begin(), response_length, 0);
|
|
memcpy(buf.data(), rasctl_control_response.data(), min(response_length, rasctl_control_response.size()));
|
|
rasctl_shutdown_ready = rasctl_shutdown != rasctl_shutdown_mode::none;
|
|
return len;
|
|
}
|
|
|
|
case 1: // Ethernet
|
|
// Do not process if TAP is invalid
|
|
if (!tap_enabled) {
|
|
return 0;
|
|
}
|
|
|
|
switch (func) {
|
|
case 0: // Get MAC address
|
|
return GetMacAddr(buf);
|
|
|
|
case 1: // Received packet acquisition (size/buffer)
|
|
if (phase == 0) {
|
|
// Get packet size
|
|
ReceivePacket();
|
|
SetInt16(buf, 0, packet_len);
|
|
return 2;
|
|
} else {
|
|
// Get package data
|
|
GetPacketBuf(buf, 0);
|
|
return packet_len;
|
|
}
|
|
|
|
case 2: // Received packet acquisition (size + buffer simultaneously)
|
|
ReceivePacket();
|
|
SetInt16(buf, 0, packet_len);
|
|
GetPacketBuf(buf, 2);
|
|
return packet_len + 2;
|
|
|
|
case 3: {
|
|
// Simultaneous acquisition of multiple packets (size + buffer simultaneously)
|
|
// Currently the maximum number of packets is 10
|
|
// Isn't it too fast if I increase more?
|
|
int total_len = 0;
|
|
for (int i = 0; i < 10; i++) {
|
|
ReceivePacket();
|
|
SetInt16(buf, total_len, packet_len);
|
|
total_len += 2;
|
|
if (packet_len == 0)
|
|
break;
|
|
GetPacketBuf(buf, 0);
|
|
total_len += packet_len;
|
|
}
|
|
return total_len;
|
|
}
|
|
|
|
default:
|
|
assert(false);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case 2: // Host Drive
|
|
switch (phase) {
|
|
case 0: // Get result code
|
|
return ReadFsResult(buf);
|
|
|
|
case 1: // Return data acquisition
|
|
return ReadFsOut(buf);
|
|
|
|
case 2: // Return additional data acquisition
|
|
return ReadFsOpt(buf);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Error
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
|
|
bool SCSIBR::ReadWrite(cdb_t cdb, vector<uint8_t>& buf)
|
|
{
|
|
// Type
|
|
const int type = cdb[2];
|
|
|
|
// Function number
|
|
const int func = cdb[3];
|
|
|
|
// Phase
|
|
const int phase = cdb[9];
|
|
|
|
// Get the number of lights
|
|
const int len = GetInt24(cdb, 6);
|
|
|
|
switch (type) {
|
|
case 0: { // RASCTL compatibility control channel
|
|
if (!IsRasctlControlEnabled()) {
|
|
return false;
|
|
}
|
|
|
|
const size_t command_length = min(static_cast<size_t>(len), buf.size());
|
|
const char *command = reinterpret_cast<const char *>(buf.data());
|
|
rasctl_control_request = string(command, strnlen(command, command_length));
|
|
rasctl_control_response.clear();
|
|
rasctl_shutdown = rasctl_shutdown_mode::none;
|
|
rasctl_shutdown_ready = false;
|
|
return true;
|
|
}
|
|
|
|
case 1: // Ethernet
|
|
// Do not process if TAP is invalid
|
|
if (!tap_enabled) {
|
|
return false;
|
|
}
|
|
|
|
switch (func) {
|
|
case 0:
|
|
SetMacAddr(buf);
|
|
return true;
|
|
|
|
case 1:
|
|
SendPacket(buf, len);
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 2: // Host drive
|
|
switch (phase) {
|
|
case 0: // issue command
|
|
WriteFs(func, buf);
|
|
return true;
|
|
|
|
case 1: // additional data writing
|
|
WriteFsOpt(buf, len);
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
optional<string> SCSIBR::TakeRasctlControlRequest()
|
|
{
|
|
return exchange(rasctl_control_request, nullopt);
|
|
}
|
|
|
|
void SCSIBR::SetRasctlControlResponse(string response, rasctl_shutdown_mode shutdown_mode)
|
|
{
|
|
rasctl_control_response = std::move(response);
|
|
rasctl_shutdown = shutdown_mode;
|
|
}
|
|
|
|
SCSIBR::rasctl_shutdown_mode SCSIBR::TakeRasctlShutdownMode()
|
|
{
|
|
if (!rasctl_shutdown_ready) {
|
|
return rasctl_shutdown_mode::none;
|
|
}
|
|
|
|
rasctl_shutdown_ready = false;
|
|
return exchange(rasctl_shutdown, rasctl_shutdown_mode::none);
|
|
}
|
|
|
|
void SCSIBR::GetMessage10()
|
|
{
|
|
// Ensure a sufficient buffer size (because it is not a transfer for each block)
|
|
GetController()->AllocateBuffer(0x1000000);
|
|
|
|
GetController()->SetLength(GetMessage10(GetController()->GetCmd(), GetController()->GetBuffer()));
|
|
if (GetController()->GetLength() <= 0) {
|
|
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
|
}
|
|
|
|
// Set next block
|
|
GetController()->SetBlocks(1);
|
|
GetController()->SetNext(1);
|
|
|
|
EnterDataInPhase();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// SEND MESSAGE(10)
|
|
//
|
|
// This Send Message command is used by the X68000 host driver
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::SendMessage10() const
|
|
{
|
|
GetController()->SetLength(GetInt24(GetController()->GetCmd(), 6));
|
|
if (GetController()->GetLength() <= 0) {
|
|
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
|
}
|
|
|
|
// Ensure a sufficient buffer size (because it is not a transfer for each block)
|
|
GetController()->AllocateBuffer(0x1000000);
|
|
|
|
// Set next block
|
|
GetController()->SetBlocks(1);
|
|
GetController()->SetNext(1);
|
|
|
|
EnterDataOutPhase();
|
|
}
|
|
|
|
int SCSIBR::GetMacAddr(vector<uint8_t>& mac) const
|
|
{
|
|
memcpy(mac.data(), mac_addr.data(), mac_addr.size());
|
|
return static_cast<int>(mac_addr.size());
|
|
}
|
|
|
|
void SCSIBR::SetMacAddr(span<const uint8_t> mac)
|
|
{
|
|
memcpy(mac_addr.data(), mac.data(), mac_addr.size());
|
|
}
|
|
|
|
void SCSIBR::ReceivePacket()
|
|
{
|
|
// previous packet has not been received
|
|
if (packet_enable) {
|
|
return;
|
|
}
|
|
|
|
// Receive packet
|
|
packet_len = tap.Receive(packet_buf.data());
|
|
|
|
// Check if received packet
|
|
if (memcmp(packet_buf.data(), mac_addr.data(), mac_addr.size()) != 0
|
|
&& memcmp(packet_buf.data(), bcast_addr.data(), bcast_addr.size()) != 0) {
|
|
packet_len = 0;
|
|
}
|
|
|
|
// RASETHER.SYS is based on a physical Ethernet adapter driver and expects
|
|
// a 60-byte Ethernet frame before the four-byte FCS. TAP can provide short
|
|
// local frames such as ARP without that wire-level padding.
|
|
if (packet_len >= ETH_FCS_LEN && packet_len < MINIMUM_FRAME_SIZE + ETH_FCS_LEN) {
|
|
fill(packet_buf.begin() + packet_len - ETH_FCS_LEN, packet_buf.begin() + MINIMUM_FRAME_SIZE, 0);
|
|
const uint32_t crc = CTapDriver::Crc32(span(packet_buf.data(), MINIMUM_FRAME_SIZE));
|
|
packet_buf[MINIMUM_FRAME_SIZE + 0] = static_cast<uint8_t>(crc >> 0);
|
|
packet_buf[MINIMUM_FRAME_SIZE + 1] = static_cast<uint8_t>(crc >> 8);
|
|
packet_buf[MINIMUM_FRAME_SIZE + 2] = static_cast<uint8_t>(crc >> 16);
|
|
packet_buf[MINIMUM_FRAME_SIZE + 3] = static_cast<uint8_t>(crc >> 24);
|
|
packet_len = MINIMUM_FRAME_SIZE + ETH_FCS_LEN;
|
|
}
|
|
|
|
// Discard if it exceeds the buffer size
|
|
if (packet_len > 2048) {
|
|
packet_len = 0;
|
|
}
|
|
|
|
// Store in receive buffer
|
|
if (packet_len > 0) {
|
|
packet_enable = true;
|
|
}
|
|
}
|
|
|
|
void SCSIBR::GetPacketBuf(vector<uint8_t>& buf, int index)
|
|
{
|
|
// Size limit
|
|
int len = packet_len;
|
|
if (len > 2048) {
|
|
len = 2048;
|
|
}
|
|
|
|
// Copy
|
|
memcpy(buf.data() + index, packet_buf.data(), len);
|
|
|
|
// Received
|
|
packet_enable = false;
|
|
}
|
|
|
|
void SCSIBR::SendPacket(span<const uint8_t> buf, int len) const
|
|
{
|
|
tap.Send(buf.data(), len);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $40 - Device Boot
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_InitDevice(vector<uint8_t>& buf)
|
|
{
|
|
fs.Reset();
|
|
fsresult = fs.InitDevice((Human68k::argument_t*)buf.data());
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $41 - Directory Check
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_CheckDir(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
const int i = sizeof(uint32_t);
|
|
|
|
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
|
|
|
fsresult = fs.CheckDir(nUnit, pNamests);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $42 - Create Directory
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_MakeDir(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
const int i = sizeof(uint32_t);
|
|
|
|
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
|
|
|
fsresult = fs.MakeDir(nUnit, pNamests);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $43 - Remove Directory
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_RemoveDir(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
const int i = sizeof(uint32_t);
|
|
|
|
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
|
|
|
fsresult = fs.RemoveDir(nUnit, pNamests);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $44 - Rename
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Rename(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::namests_t);
|
|
|
|
const Human68k::namests_t *pNamestsNew = (Human68k::namests_t*)&(buf.data()[i]);
|
|
|
|
fsresult = fs.Rename(nUnit, pNamests, pNamestsNew);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $45 - Delete File
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Delete(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
const int i = sizeof(uint32_t);
|
|
|
|
const auto *pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
|
|
|
fsresult = fs.Delete(nUnit, pNamests);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $46 - Get / Set file attributes
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Attribute(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::namests_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
uint32_t nHumanAttribute = ntohl(*dp);
|
|
|
|
fsresult = fs.Attribute(nUnit, pNamests, nHumanAttribute);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $47 - File Search
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Files(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nKey = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::namests_t);
|
|
|
|
auto files = (Human68k::files_t*)&(buf.data()[i]);
|
|
|
|
files->sector = ntohl(files->sector);
|
|
files->offset = ntohs(files->offset);
|
|
files->time = ntohs(files->time);
|
|
files->date = ntohs(files->date);
|
|
files->size = ntohl(files->size);
|
|
|
|
fsresult = fs.Files(nUnit, nKey, pNamests, files);
|
|
|
|
files->sector = htonl(files->sector);
|
|
files->offset = htons(files->offset);
|
|
files->time = htons(files->time);
|
|
files->date = htons(files->date);
|
|
files->size = htonl(files->size);
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], files, sizeof(Human68k::files_t));
|
|
i += sizeof(Human68k::files_t);
|
|
|
|
fsoutlen = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $48 - File next search
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_NFiles(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nKey = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
auto files = (Human68k::files_t*)&(buf.data()[i]);
|
|
|
|
files->sector = ntohl(files->sector);
|
|
files->offset = ntohs(files->offset);
|
|
files->time = ntohs(files->time);
|
|
files->date = ntohs(files->date);
|
|
files->size = ntohl(files->size);
|
|
|
|
fsresult = fs.NFiles(nUnit, nKey, files);
|
|
|
|
files->sector = htonl(files->sector);
|
|
files->offset = htons(files->offset);
|
|
files->time = htons(files->time);
|
|
files->date = htons(files->date);
|
|
files->size = htonl(files->size);
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], files, sizeof(Human68k::files_t));
|
|
i += sizeof(Human68k::files_t);
|
|
|
|
fsoutlen = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $49 - File Creation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Create(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nKey = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::namests_t);
|
|
|
|
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nAttribute = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
auto bp = (int*)&(buf.data()[i]);
|
|
const uint32_t bForce = ntohl(*bp);
|
|
|
|
pFcb->fileptr = ntohl(pFcb->fileptr);
|
|
pFcb->mode = ntohs(pFcb->mode);
|
|
pFcb->time = ntohs(pFcb->time);
|
|
pFcb->date = ntohs(pFcb->date);
|
|
pFcb->size = ntohl(pFcb->size);
|
|
|
|
fsresult = fs.Create(nUnit, nKey, pNamests, pFcb, nAttribute, bForce);
|
|
|
|
pFcb->fileptr = htonl(pFcb->fileptr);
|
|
pFcb->mode = htons(pFcb->mode);
|
|
pFcb->time = htons(pFcb->time);
|
|
pFcb->date = htons(pFcb->date);
|
|
pFcb->size = htonl(pFcb->size);
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t));
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
fsoutlen = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $4A - Open File
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Open(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nKey = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::namests_t);
|
|
|
|
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
|
|
|
pFcb->fileptr = ntohl(pFcb->fileptr);
|
|
pFcb->mode = ntohs(pFcb->mode);
|
|
pFcb->time = ntohs(pFcb->time);
|
|
pFcb->date = ntohs(pFcb->date);
|
|
pFcb->size = ntohl(pFcb->size);
|
|
|
|
fsresult = fs.Open(nUnit, nKey, pNamests, pFcb);
|
|
|
|
pFcb->fileptr = htonl(pFcb->fileptr);
|
|
pFcb->mode = htons(pFcb->mode);
|
|
pFcb->time = htons(pFcb->time);
|
|
pFcb->date = htons(pFcb->date);
|
|
pFcb->size = htonl(pFcb->size);
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t));
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
fsoutlen = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $4B - Close File
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Close(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nKey = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
|
|
|
pFcb->fileptr = ntohl(pFcb->fileptr);
|
|
pFcb->mode = ntohs(pFcb->mode);
|
|
pFcb->time = ntohs(pFcb->time);
|
|
pFcb->date = ntohs(pFcb->date);
|
|
pFcb->size = ntohl(pFcb->size);
|
|
|
|
fsresult = fs.Close(nUnit, nKey, pFcb);
|
|
|
|
pFcb->fileptr = htonl(pFcb->fileptr);
|
|
pFcb->mode = htons(pFcb->mode);
|
|
pFcb->time = htons(pFcb->time);
|
|
pFcb->date = htons(pFcb->date);
|
|
pFcb->size = htonl(pFcb->size);
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t));
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
fsoutlen = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $4C - Read File
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Read(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nKey = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nSize = ntohl(*dp);
|
|
|
|
pFcb->fileptr = ntohl(pFcb->fileptr);
|
|
pFcb->mode = ntohs(pFcb->mode);
|
|
pFcb->time = ntohs(pFcb->time);
|
|
pFcb->date = ntohs(pFcb->date);
|
|
pFcb->size = ntohl(pFcb->size);
|
|
|
|
fsresult = fs.Read(nKey, pFcb, fsopt.data(), nSize);
|
|
|
|
pFcb->fileptr = htonl(pFcb->fileptr);
|
|
pFcb->mode = htons(pFcb->mode);
|
|
pFcb->time = htons(pFcb->time);
|
|
pFcb->date = htons(pFcb->date);
|
|
pFcb->size = htonl(pFcb->size);
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t));
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
fsoutlen = i;
|
|
|
|
fsoptlen = fsresult;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $4D - Write file
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Write(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nKey = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nSize = ntohl(*dp);
|
|
|
|
pFcb->fileptr = ntohl(pFcb->fileptr);
|
|
pFcb->mode = ntohs(pFcb->mode);
|
|
pFcb->time = ntohs(pFcb->time);
|
|
pFcb->date = ntohs(pFcb->date);
|
|
pFcb->size = ntohl(pFcb->size);
|
|
|
|
fsresult = fs.Write(nKey, pFcb, fsopt.data(), nSize);
|
|
|
|
pFcb->fileptr = htonl(pFcb->fileptr);
|
|
pFcb->mode = htons(pFcb->mode);
|
|
pFcb->time = htons(pFcb->time);
|
|
pFcb->date = htons(pFcb->date);
|
|
pFcb->size = htonl(pFcb->size);
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t));
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
fsoutlen = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $4E - Seek file
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Seek(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nKey = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nMode = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
auto ip = (const int*)&(buf.data()[i]);
|
|
int nOffset = ntohl(*ip);
|
|
|
|
pFcb->fileptr = ntohl(pFcb->fileptr);
|
|
pFcb->mode = ntohs(pFcb->mode);
|
|
pFcb->time = ntohs(pFcb->time);
|
|
pFcb->date = ntohs(pFcb->date);
|
|
pFcb->size = ntohl(pFcb->size);
|
|
|
|
fsresult = fs.Seek(nKey, pFcb, nMode, nOffset);
|
|
|
|
pFcb->fileptr = htonl(pFcb->fileptr);
|
|
pFcb->mode = htons(pFcb->mode);
|
|
pFcb->time = htons(pFcb->time);
|
|
pFcb->date = htons(pFcb->date);
|
|
pFcb->size = htonl(pFcb->size);
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t));
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
fsoutlen = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $4F - File Timestamp Get / Set
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_TimeStamp(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nKey = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
uint32_t nHumanTime = ntohl(*dp);
|
|
|
|
pFcb->fileptr = ntohl(pFcb->fileptr);
|
|
pFcb->mode = ntohs(pFcb->mode);
|
|
pFcb->time = ntohs(pFcb->time);
|
|
pFcb->date = ntohs(pFcb->date);
|
|
pFcb->size = ntohl(pFcb->size);
|
|
|
|
fsresult = fs.TimeStamp(nUnit, nKey, pFcb, nHumanTime);
|
|
|
|
pFcb->fileptr = htonl(pFcb->fileptr);
|
|
pFcb->mode = htons(pFcb->mode);
|
|
pFcb->time = htons(pFcb->time);
|
|
pFcb->date = htons(pFcb->date);
|
|
pFcb->size = htonl(pFcb->size);
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t));
|
|
i += sizeof(Human68k::fcb_t);
|
|
|
|
fsoutlen = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $50 - Get Capacity
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_GetCapacity(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
|
|
Human68k::capacity_t cap = {};
|
|
fsresult = fs.GetCapacity(nUnit, &cap);
|
|
|
|
cap.freearea = htons(cap.freearea);
|
|
cap.clusters = htons(cap.clusters);
|
|
cap.sectors = htons(cap.sectors);
|
|
cap.bytes = htons(cap.bytes);
|
|
|
|
memcpy(fsout.data(), &cap, sizeof(Human68k::capacity_t));
|
|
fsoutlen = sizeof(Human68k::capacity_t);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $51 - Drive status inspection/control
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_CtrlDrive(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
const int i = sizeof(uint32_t);
|
|
|
|
auto pCtrlDrive = (Human68k::ctrldrive_t*)&(buf.data()[i]);
|
|
|
|
fsresult = fs.CtrlDrive(nUnit, pCtrlDrive);
|
|
|
|
memcpy(fsout.data(), pCtrlDrive, sizeof(Human68k::ctrldrive_t));
|
|
fsoutlen = sizeof(Human68k::ctrldrive_t);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $52 - Get DPB
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_GetDPB(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
|
|
Human68k::dpb_t dpb = {};
|
|
fsresult = fs.GetDPB(nUnit, &dpb);
|
|
|
|
dpb.sector_size = htons(dpb.sector_size);
|
|
dpb.fat_sector = htons(dpb.fat_sector);
|
|
dpb.file_max = htons(dpb.file_max);
|
|
dpb.data_sector = htons(dpb.data_sector);
|
|
dpb.cluster_max = htons(dpb.cluster_max);
|
|
dpb.root_sector = htons(dpb.root_sector);
|
|
|
|
memcpy(fsout.data(), &dpb, sizeof(Human68k::dpb_t));
|
|
fsoutlen = sizeof(Human68k::dpb_t);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $53 - Read Sector
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_DiskRead(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nSector = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
uint32_t nSize = ntohl(*dp);
|
|
|
|
fsresult = fs.DiskRead(nUnit, fsout.data(), nSector, nSize);
|
|
fsoutlen = 0x200;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $54 - Write Sector
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_DiskWrite(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
|
|
fsresult = fs.DiskWrite(nUnit);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $55 - IOCTRL
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Ioctrl(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
int i = sizeof(uint32_t);
|
|
|
|
dp = (uint32_t*)&(buf.data()[i]);
|
|
const uint32_t nFunction = ntohl(*dp);
|
|
i += sizeof(uint32_t);
|
|
|
|
auto pIoctrl = (Human68k::ioctrl_t*)&(buf.data()[i]);
|
|
|
|
switch (nFunction) {
|
|
case 2:
|
|
case (uint32_t)-2:
|
|
pIoctrl->param = htonl(pIoctrl->param);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
fsresult = fs.Ioctrl(nUnit, nFunction, pIoctrl);
|
|
|
|
switch (nFunction) {
|
|
case 0:
|
|
pIoctrl->media = htons(pIoctrl->media);
|
|
break;
|
|
case 1:
|
|
case (uint32_t)-3:
|
|
pIoctrl->param = htonl(pIoctrl->param);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
i = 0;
|
|
memcpy(&fsout[i], pIoctrl, sizeof(Human68k::ioctrl_t));
|
|
i += sizeof(Human68k::ioctrl_t);
|
|
fsoutlen = i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $56 - Flush
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Flush(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
|
|
fsresult = fs.Flush(nUnit);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $57 - Check Media
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_CheckMedia(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
|
|
fsresult = fs.CheckMedia(nUnit);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// $58 - Lock
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::FS_Lock(vector<uint8_t>& buf)
|
|
{
|
|
auto dp = (uint32_t*)buf.data();
|
|
const uint32_t nUnit = ntohl(*dp);
|
|
|
|
fsresult = fs.Lock(nUnit);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Read Filesystem (result code)
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
int SCSIBR::ReadFsResult(vector<uint8_t>& buf) const
|
|
{
|
|
auto dp = (uint32_t *)buf.data();
|
|
*dp = htonl(fsresult);
|
|
return sizeof(uint32_t);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Read Filesystem (return data)
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
int SCSIBR::ReadFsOut(vector<uint8_t>& buf) const
|
|
{
|
|
copy_n(fsout.begin(), fsoutlen, buf.begin());
|
|
return fsoutlen;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Read file system (return option data)
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
int SCSIBR::ReadFsOpt(vector<uint8_t>& buf) const
|
|
{
|
|
copy_n(fsopt.begin(), fsoptlen, buf.begin());
|
|
return fsoptlen;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Write Filesystem
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::WriteFs(int func, vector<uint8_t>& buf)
|
|
{
|
|
fsresult = FS_FATAL_INVALIDCOMMAND;
|
|
fsoutlen = 0;
|
|
fsoptlen = 0;
|
|
|
|
func &= 0x1f;
|
|
switch (func) {
|
|
case 0x00:
|
|
FS_InitDevice(buf); // $40 - start device
|
|
break;
|
|
case 0x01:
|
|
FS_CheckDir(buf); // $41 - directory check
|
|
break;
|
|
case 0x02:
|
|
FS_MakeDir(buf); // $42 - create directory
|
|
break;
|
|
case 0x03:
|
|
FS_RemoveDir(buf); // $43 - remove directory
|
|
break;
|
|
case 0x04:
|
|
FS_Rename(buf); // $44 - change file name
|
|
break;
|
|
case 0x05:
|
|
FS_Delete(buf); // $45 - delete file
|
|
break;
|
|
case 0x06:
|
|
FS_Attribute(buf); // $46 - Get/set file attribute
|
|
break;
|
|
case 0x07:
|
|
FS_Files(buf); // $47 - file search
|
|
break;
|
|
case 0x08:
|
|
FS_NFiles(buf); // $48 - next file search
|
|
break;
|
|
case 0x09:
|
|
FS_Create(buf); // $49 - create file
|
|
break;
|
|
case 0x0A:
|
|
FS_Open(buf); // $4A - File open
|
|
break;
|
|
case 0x0B:
|
|
FS_Close(buf); // $4B - File close
|
|
break;
|
|
case 0x0C:
|
|
FS_Read(buf); // $4C - read file
|
|
break;
|
|
case 0x0D:
|
|
FS_Write(buf); // $4D - write file
|
|
break;
|
|
case 0x0E:
|
|
FS_Seek(buf); // $4E - File seek
|
|
break;
|
|
case 0x0F:
|
|
FS_TimeStamp(buf); // $4F - Get/set file modification time
|
|
break;
|
|
case 0x10:
|
|
FS_GetCapacity(buf); // $50 - get capacity
|
|
break;
|
|
case 0x11:
|
|
FS_CtrlDrive(buf); // $51 - Drive control/state check
|
|
break;
|
|
case 0x12:
|
|
FS_GetDPB(buf); // $52 - Get DPB
|
|
break;
|
|
case 0x13:
|
|
FS_DiskRead(buf); // $53 - read sector
|
|
break;
|
|
case 0x14:
|
|
FS_DiskWrite(buf); // $54 - write sector
|
|
break;
|
|
case 0x15:
|
|
FS_Ioctrl(buf); // $55 - IOCTRL
|
|
break;
|
|
case 0x16:
|
|
FS_Flush(buf); // $56 - flush
|
|
break;
|
|
case 0x17:
|
|
FS_CheckMedia(buf); // $57 - check media exchange
|
|
break;
|
|
case 0x18:
|
|
FS_Lock(buf); // $58 - exclusive control
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// File system write (input option data)
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void SCSIBR::WriteFsOpt(const vector<uint8_t>& buf, int num)
|
|
{
|
|
copy_n(buf.begin(), num, fsopt.begin());
|
|
}
|