mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-01-28 12:49:08 +00:00
- Removed delay in mscp server polling loop, which as I suspected was papering over an issue. We now pull all messages from the command ring at once and save them locally. When processing completes, the polling loop goes back to sleep. This jibes with host code expectations of the port. No more delay necessary to avoid race conditions. - Cleaned up RCT/RBN information so that this can be dynamically configured in the future, should the need arise
285 lines
5.8 KiB
C++
285 lines
5.8 KiB
C++
/*
|
|
mscp_drive.cpp: Implementation of MSCP disks.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <memory>
|
|
|
|
using namespace std;
|
|
|
|
#include "logger.hpp"
|
|
#include "utils.hpp"
|
|
#include "mscp_drive.hpp"
|
|
#include "mscp_server.hpp"
|
|
|
|
mscp_drive_c::mscp_drive_c(
|
|
storagecontroller_c *controller,
|
|
uint32_t driveNumber) :
|
|
storagedrive_c(controller),
|
|
_useImageSize(false)
|
|
{
|
|
log_label = "MSCPD";
|
|
SetDriveType("RA81");
|
|
SetOffline();
|
|
|
|
// Calculate the unit's ID:
|
|
// drive number in upper 32 bits, class/model in lower.
|
|
_unitID = (static_cast<uint64_t>(driveNumber + 1) << 32) | 0xffffffff;
|
|
|
|
// Initialize the RCT area
|
|
_rctData.reset(new uint8_t[GetBlockSize()]);
|
|
memset(reinterpret_cast<void *>(_rctData.get()), 0, GetBlockSize());
|
|
}
|
|
|
|
mscp_drive_c::~mscp_drive_c()
|
|
{
|
|
if (file_is_open())
|
|
{
|
|
file_close();
|
|
}
|
|
}
|
|
|
|
uint32_t mscp_drive_c::GetBlockSize()
|
|
{
|
|
//
|
|
// For the time being this is always 512 bytes.
|
|
//
|
|
return 512;
|
|
}
|
|
|
|
uint32_t mscp_drive_c::GetBlockCount()
|
|
{
|
|
if (_useImageSize)
|
|
{
|
|
// Return the image size / Block size (rounding down).
|
|
return file_size() / GetBlockSize();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use the size defined by the drive type.
|
|
//
|
|
return _driveInfo.BlockCount;
|
|
}
|
|
}
|
|
|
|
uint32_t mscp_drive_c::GetRCTBlockCount()
|
|
{
|
|
//
|
|
// We provide only a single RCT block. Per the latest MSCP spec no RCT is required,
|
|
// however several operating systems appear to expect at least one block be present
|
|
// for volume write-protect flags.
|
|
//
|
|
return 1;
|
|
}
|
|
|
|
uint32_t mscp_drive_c::GetMediaID()
|
|
{
|
|
return _driveInfo.MediaID;
|
|
}
|
|
|
|
uint64_t mscp_drive_c::GetUnitID()
|
|
{
|
|
return _unitID;
|
|
}
|
|
|
|
uint16_t mscp_drive_c::GetRCTSize()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
uint8_t mscp_drive_c::GetRBNs()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint8_t mscp_drive_c::GetRCTCopies()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
bool mscp_drive_c::IsAvailable()
|
|
{
|
|
return file_is_open();
|
|
}
|
|
|
|
bool mscp_drive_c::IsOnline()
|
|
{
|
|
return _online;
|
|
}
|
|
|
|
void mscp_drive_c::SetOnline()
|
|
{
|
|
_online = true;
|
|
|
|
//
|
|
// Once online, the drive's type and image cannot be changed until
|
|
// the drive is offline.
|
|
//
|
|
type_name.readonly = true;
|
|
image_filepath.readonly = true;
|
|
}
|
|
|
|
void mscp_drive_c::SetOffline()
|
|
{
|
|
_online = false;
|
|
type_name.readonly = false;
|
|
image_filepath.readonly = false;
|
|
}
|
|
|
|
//
|
|
// Writes the specified number of bytes from the provided buffer,
|
|
// starting at the specified logical block.
|
|
//
|
|
void mscp_drive_c::Write(
|
|
uint32_t blockNumber,
|
|
size_t lengthInBytes,
|
|
uint8_t* buffer)
|
|
{
|
|
file_write(
|
|
buffer,
|
|
blockNumber * GetBlockSize(),
|
|
lengthInBytes);
|
|
}
|
|
|
|
//
|
|
// Reads the specifed number of bytes starting at the specified logical
|
|
// block. Returns a pointer to a buffer containing the data read.
|
|
// Caller is responsible for freeing this buffer.
|
|
//
|
|
uint8_t* mscp_drive_c::Read(
|
|
uint32_t blockNumber,
|
|
size_t lengthInBytes)
|
|
{
|
|
uint8_t* buffer = new uint8_t[lengthInBytes];
|
|
|
|
assert(nullptr != buffer);
|
|
|
|
file_read(
|
|
buffer,
|
|
blockNumber * GetBlockSize(),
|
|
lengthInBytes);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
//
|
|
// Writes a single block's worth of data from the provided buffer into the
|
|
// RCT area at the specified RCT block. Buffer must be at least as large
|
|
// as the disk's block size.
|
|
//
|
|
void mscp_drive_c::WriteRCTBlock(
|
|
uint32_t rctBlockNumber,
|
|
uint8_t* buffer)
|
|
{
|
|
assert (rctBlockNumber < GetRCTBlockCount());
|
|
|
|
memcpy(
|
|
reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
|
|
reinterpret_cast<void *>(buffer),
|
|
GetBlockSize());
|
|
}
|
|
|
|
//
|
|
// Reads a single block's worth of data from the RCT area (at the specified
|
|
// block offset). Returns a pointer to a buffer containing the data read.
|
|
// Caller is responsible for freeing this buffer.
|
|
//
|
|
uint8_t* mscp_drive_c::ReadRCTBlock(
|
|
uint32_t rctBlockNumber)
|
|
{
|
|
assert (rctBlockNumber < GetRCTBlockCount());
|
|
|
|
uint8_t* buffer = new uint8_t[GetBlockSize()];
|
|
assert (nullptr != buffer);
|
|
|
|
memcpy(
|
|
reinterpret_cast<void *>(buffer),
|
|
reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
|
|
GetBlockSize());
|
|
|
|
return buffer;
|
|
}
|
|
|
|
void mscp_drive_c::UpdateCapacity()
|
|
{
|
|
capacity.value =
|
|
GetBlockCount() * GetBlockSize();
|
|
}
|
|
|
|
bool mscp_drive_c::on_param_changed(
|
|
parameter_c *param)
|
|
{
|
|
if (&type_name == param)
|
|
{
|
|
return SetDriveType(type_name.new_value.c_str());
|
|
}
|
|
else if (&image_filepath == param)
|
|
{
|
|
//
|
|
// Try to open the image file.
|
|
//
|
|
if (file_open(image_filepath.new_value, true))
|
|
{
|
|
image_filepath.value = image_filepath.new_value;
|
|
UpdateCapacity();
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// TODO: if file is a nonstandard size?
|
|
}
|
|
else if (&use_image_size == param)
|
|
{
|
|
_useImageSize = use_image_size.new_value;
|
|
use_image_size.value = use_image_size.new_value;
|
|
UpdateCapacity();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mscp_drive_c::SetDriveType(const char* typeName)
|
|
{
|
|
//
|
|
// Search through drive data table for name,
|
|
// and if valid, set the type appropriately.
|
|
//
|
|
int index = 0;
|
|
while (g_driveTable[index].BlockCount != 0)
|
|
{
|
|
if (!strcasecmp(typeName, g_driveTable[index].TypeName))
|
|
{
|
|
_driveInfo = g_driveTable[index];
|
|
type_name.value = _driveInfo.TypeName;
|
|
UpdateCapacity();
|
|
return true;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
// Not found
|
|
return false;
|
|
}
|
|
|
|
void mscp_drive_c::worker(void)
|
|
{
|
|
// Nothing to do here at the moment.
|
|
}
|
|
|
|
void mscp_drive_c::on_power_changed(void)
|
|
{
|
|
// Take the drive offline due to power change
|
|
SetOffline();
|
|
}
|
|
|
|
void mscp_drive_c::on_init_changed(void)
|
|
{
|
|
// Take the drive offline due to reset
|
|
SetOffline();
|
|
}
|
|
|
|
|