Files
RASCSI/cpp/devices/scsi_host_bridge.cpp
T
Daniel Markstedt 485298ffe5 restore X68000 Host Bridge support
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.
2026-09-01 00:52:00 +02:00

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());
}