mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-01-28 12:49:08 +00:00
Implemented AVAILABLE, ERASE, DETERMINE ACCESS PATHS commands. Tweaks to interrupt queueing (request_INTR now blocks until
the interrupt actually gets signaled on the unibus). OpenVMS 7.3 now boots on the VAX.
This commit is contained in:
@@ -92,7 +92,8 @@ irq_request_c::irq_request_c(
|
||||
unsigned level,
|
||||
unsigned vector) :
|
||||
_level(level),
|
||||
_vector(vector)
|
||||
_vector(vector),
|
||||
_isComplete(false)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -174,13 +175,13 @@ void unibusadapter_c::worker_init_event() {
|
||||
device->on_init_changed();
|
||||
}
|
||||
|
||||
INFO("clearing due to INIT empty %d", _irqRequests.empty());
|
||||
|
||||
// Clear bus request queues
|
||||
/*
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_dmaRequests.clear();
|
||||
_irqRequests.clear();
|
||||
while (!_dmaRequests.empty()) _dmaRequests.pop();
|
||||
while (!_irqRequests.empty()) _irqRequests.pop();
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
*/
|
||||
}
|
||||
|
||||
void unibusadapter_c::worker_power_event() {
|
||||
@@ -194,13 +195,13 @@ void unibusadapter_c::worker_power_event() {
|
||||
device->on_power_changed();
|
||||
}
|
||||
|
||||
INFO("clearing due to power empty %d", _irqRequests.empty());
|
||||
|
||||
// Clear bus request queues
|
||||
/*
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_dmaRequests.clear();
|
||||
_irqRequests.clear();
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
*/
|
||||
while (!_dmaRequests.empty()) _dmaRequests.pop();
|
||||
while (!_irqRequests.empty()) _irqRequests.pop();
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
}
|
||||
|
||||
// process DATI/DATO access to active device registers
|
||||
@@ -587,11 +588,11 @@ bool unibusadapter_c::request_DMA(
|
||||
|
||||
void unibusadapter_c::dma_worker()
|
||||
{
|
||||
|
||||
//worker_init_realtime_priority(rt_device);
|
||||
while(true)
|
||||
{
|
||||
dma_request_c* dmaReq = nullptr;
|
||||
irq_request_c irqReq(0,0);
|
||||
irq_request_c* irqReq = nullptr;
|
||||
|
||||
//
|
||||
// Wait for the next request.
|
||||
@@ -610,8 +611,7 @@ void unibusadapter_c::dma_worker()
|
||||
//
|
||||
if (!_irqRequests.empty())
|
||||
{
|
||||
irq_request_c const& req = _irqRequests.front();
|
||||
irqReq = req;
|
||||
irqReq = _irqRequests.front();
|
||||
_irqRequests.pop();
|
||||
}
|
||||
else
|
||||
@@ -622,16 +622,18 @@ void unibusadapter_c::dma_worker()
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
|
||||
// Sanity check: Should be no active DMA requests on the PRU.
|
||||
assert (!request_DMA_active(nullptr));
|
||||
// Sanity check: Should be no active DMA or interrupt requests on the PRU.
|
||||
assert (!request_DMA_active(nullptr) && !request_INTR_active(nullptr));
|
||||
|
||||
/*
|
||||
// If there's an IRQ still active, wait for it to finish.
|
||||
// TODO: find a way to avoid having to do this.
|
||||
timeout_c timer;
|
||||
while (request_INTR_active(nullptr))
|
||||
{
|
||||
INFO("intr active");
|
||||
timer.wait_us(50);
|
||||
}
|
||||
} */
|
||||
|
||||
if (dmaReq)
|
||||
{
|
||||
@@ -670,7 +672,7 @@ void unibusadapter_c::dma_worker()
|
||||
// Wait for the transfer to complete.
|
||||
// TODO: we're polling the mailbox; is there a more efficient way to do this?
|
||||
timeout_c timeout;
|
||||
while (request_DMA_active(NULL))
|
||||
while (request_DMA_active(nullptr))
|
||||
{
|
||||
timeout.wait_us(50);
|
||||
}
|
||||
@@ -691,8 +693,14 @@ void unibusadapter_c::dma_worker()
|
||||
dmaReq->SetUnibusEndAddr(mailbox->dma.cur_addr);
|
||||
dmaReq->SetSuccess(mailbox->dma.cur_status == DMA_STATE_READY);
|
||||
|
||||
assert(dmaReq->GetUnibusAddr() + dmaReq->GetWordCount() * 2 == mailbox->dma.cur_addr + 2);
|
||||
if(dmaReq->GetUnibusAddr() + dmaReq->GetWordCount() * 2 != mailbox->dma.cur_addr + 2)
|
||||
|
||||
{
|
||||
FATAL("PRU end addr 0x%x, expected 0x%x",
|
||||
mailbox->dma.cur_addr + 2,
|
||||
dmaReq->GetUnibusAddr() + dmaReq->GetWordCount() * 2);
|
||||
}
|
||||
|
||||
//
|
||||
// Signal that the request is complete.
|
||||
//
|
||||
@@ -704,7 +712,7 @@ void unibusadapter_c::dma_worker()
|
||||
else
|
||||
{
|
||||
// Handle interrupt request
|
||||
switch(irqReq.GetInterruptLevel())
|
||||
switch(irqReq->GetInterruptLevel())
|
||||
{
|
||||
case 4:
|
||||
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4;
|
||||
@@ -723,15 +731,30 @@ void unibusadapter_c::dma_worker()
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR("Request_INTR(): Illegal priority %u, aborting", irqReq.GetInterruptLevel());
|
||||
ERROR("Request_INTR(): Illegal priority %u, aborting", irqReq->GetInterruptLevel());
|
||||
return;
|
||||
}
|
||||
|
||||
mailbox->intr.vector = irqReq.GetVector();
|
||||
|
||||
mailbox->intr.vector = irqReq->GetVector();
|
||||
|
||||
// start!
|
||||
mailbox->arm2pru_req = ARM2PRU_INTR;
|
||||
// PRU now changes state
|
||||
|
||||
// Signal that the request has been raised.
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
irqReq->SetComplete();
|
||||
pthread_cond_signal(&_requestFinished_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
// Wait for the transfer to complete.
|
||||
// TODO: we're polling the mailbox; is there a more efficient way to
|
||||
// do this? (as w/dma)
|
||||
timeout_c timeout;
|
||||
while(request_INTR_active(nullptr))
|
||||
{
|
||||
timeout.wait_us(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -746,10 +769,20 @@ void unibusadapter_c::request_INTR(uint32_t level, uint32_t vector) {
|
||||
vector);
|
||||
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_irqRequests.push(request);
|
||||
_irqRequests.push(&request);
|
||||
pthread_cond_signal(&_busWakeup_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
//
|
||||
// Wait for request to finish.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while (!request.IsComplete())
|
||||
{
|
||||
pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
//
|
||||
// And we're done.
|
||||
//
|
||||
|
||||
@@ -80,10 +80,14 @@ public:
|
||||
|
||||
uint32_t GetInterruptLevel() { return _level; }
|
||||
uint32_t GetVector() { return _vector; }
|
||||
bool IsComplete() { return _isComplete; }
|
||||
|
||||
void SetComplete() { _isComplete = true; }
|
||||
|
||||
private:
|
||||
uint32_t _level;
|
||||
uint32_t _vector;
|
||||
bool _isComplete;
|
||||
};
|
||||
|
||||
|
||||
@@ -126,7 +130,7 @@ public:
|
||||
private:
|
||||
|
||||
std::queue<dma_request_c*> _dmaRequests;
|
||||
std::queue<irq_request_c> _irqRequests;
|
||||
std::queue<irq_request_c*> _irqRequests;
|
||||
pthread_t _busWorker_pthread;
|
||||
pthread_cond_t _busWakeup_cond;
|
||||
pthread_cond_t _requestFinished_cond;
|
||||
|
||||
@@ -156,7 +156,7 @@ mscp_server::Poll(void)
|
||||
ControlMessageHeader* header =
|
||||
reinterpret_cast<ControlMessageHeader*>(message->Message);
|
||||
|
||||
DEBUG("Message size 0x%x opcode 0x%x rsvd 0x%x mod 0x%x unit %d, ursvd 0x%x, ref 0x%x",
|
||||
INFO("Message size 0x%x opcode 0x%x rsvd 0x%x mod 0x%x unit %d, ursvd 0x%x, ref 0x%x",
|
||||
message->MessageLength,
|
||||
header->Word3.Command.Opcode,
|
||||
header->Word3.Command.Reserved,
|
||||
@@ -169,6 +169,18 @@ mscp_server::Poll(void)
|
||||
|
||||
switch (header->Word3.Command.Opcode)
|
||||
{
|
||||
case Opcodes::AVAILABLE:
|
||||
cmdStatus = Available(message, header->UnitNumber, header->Word3.Command.Modifiers);
|
||||
break;
|
||||
|
||||
case Opcodes::DETERMINE_ACCESS_PATHS:
|
||||
cmdStatus = DetermineAccessPaths(message, header->UnitNumber);
|
||||
break;
|
||||
|
||||
case Opcodes::ERASE:
|
||||
cmdStatus = Erase(message, header->UnitNumber, header->Word3.Command.Modifiers);
|
||||
break;
|
||||
|
||||
case Opcodes::GET_UNIT_STATUS:
|
||||
cmdStatus = GetUnitStatus(message, header->UnitNumber, header->Word3.Command.Modifiers);
|
||||
break;
|
||||
@@ -198,7 +210,7 @@ mscp_server::Poll(void)
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG("cmd 0x%x st 0x%x fl 0x%x", cmdStatus, GET_STATUS(cmdStatus), GET_FLAGS(cmdStatus));
|
||||
INFO("cmd 0x%x st 0x%x fl 0x%x", cmdStatus, GET_STATUS(cmdStatus), GET_FLAGS(cmdStatus));
|
||||
|
||||
//
|
||||
// Set the endcode and status bits
|
||||
@@ -241,18 +253,15 @@ mscp_server::Poll(void)
|
||||
message->Word1.Info.Credits = 0;
|
||||
}
|
||||
|
||||
//timer.wait_us(250);
|
||||
|
||||
//
|
||||
// Post the response to the port's response ring.
|
||||
//
|
||||
if(!_port->PostResponse(message.get()))
|
||||
{
|
||||
FATAL("no room at the inn.");
|
||||
}
|
||||
if(!_port->PostResponse(message.get()))
|
||||
{
|
||||
FATAL("no room at the inn.");
|
||||
}
|
||||
|
||||
// Hack: give interrupts time to settle before doing another transfer.
|
||||
//timer.wait_us(2500);
|
||||
|
||||
//
|
||||
// Go around and pick up the next one.
|
||||
@@ -282,6 +291,116 @@ mscp_server::Poll(void)
|
||||
DEBUG("MSCP Polling thread exiting.");
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mscp_server::Available(
|
||||
shared_ptr<Message> message,
|
||||
uint16_t unitNumber,
|
||||
uint16_t modifiers)
|
||||
{
|
||||
// Message has no message-specific data.
|
||||
// We don't do much with this now...
|
||||
// Just set the specified drive as Available if appropriate.
|
||||
// We do nothing with the spin-down modifier.
|
||||
|
||||
INFO("MSCP AVAILABLE");
|
||||
|
||||
mscp_drive_c* drive = GetDrive(unitNumber);
|
||||
|
||||
if (nullptr == drive ||
|
||||
!drive->IsAvailable())
|
||||
{
|
||||
return STATUS(Status::UNIT_OFFLINE, 0x3, 0);
|
||||
}
|
||||
|
||||
drive->SetOffline();
|
||||
|
||||
return STATUS(Status::SUCCESS, 0x40, 0); // still connected
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mscp_server::DetermineAccessPaths(
|
||||
shared_ptr<Message> message,
|
||||
uint16_t unitNumber)
|
||||
{
|
||||
INFO("MSCP DETERMINE ACCESS PATHS drive %d", unitNumber);
|
||||
|
||||
// "This command must be treated as a no-op that always succeeds
|
||||
// if the unit is incapable of being connected to more than one
|
||||
// controller." That's us!
|
||||
|
||||
return STATUS(Status::SUCCESS, 0, 0);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mscp_server::Erase(
|
||||
shared_ptr<Message> message,
|
||||
uint16_t unitNumber,
|
||||
uint16_t modifiers)
|
||||
{
|
||||
#pragma pack(push,1)
|
||||
struct EraseParameters
|
||||
{
|
||||
uint32_t ByteCount;
|
||||
uint32_t Unused0;
|
||||
uint32_t Unused1;
|
||||
uint32_t Unused2;
|
||||
uint32_t LBN;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
// TODO: Factor this code out (shared w/Read and Write)
|
||||
EraseParameters* params =
|
||||
reinterpret_cast<EraseParameters*>(GetParameterPointer(message));
|
||||
|
||||
INFO ("MSCP ERASE unit %d chan count %d lbn %d",
|
||||
unitNumber,
|
||||
params->ByteCount,
|
||||
params->LBN);
|
||||
|
||||
// Adjust message length for response
|
||||
message->MessageLength = sizeof(EraseParameters) +
|
||||
HEADER_SIZE;
|
||||
|
||||
mscp_drive_c* drive = GetDrive(unitNumber);
|
||||
|
||||
// Check unit
|
||||
if (nullptr == drive ||
|
||||
!drive->IsAvailable())
|
||||
{
|
||||
return STATUS(Status::UNIT_OFFLINE, 0x3, 0);
|
||||
}
|
||||
|
||||
// Check LBN
|
||||
if (params->LBN > drive->GetBlockCount() + 1) // + 1 for RCT
|
||||
{
|
||||
return STATUS(Status::INVALID_COMMAND + (0x1c << 8), 0, 0); // TODO: set sub-code
|
||||
}
|
||||
|
||||
// Check byte count
|
||||
if (params->ByteCount > ((drive->GetBlockCount() + 1) - params->LBN) * drive->GetBlockSize())
|
||||
{
|
||||
return STATUS(Status::INVALID_COMMAND + (0x0c << 8), 0, 0); // TODO: as above
|
||||
}
|
||||
|
||||
//
|
||||
// OK: do the transfer from a zero'd out buffer to disk
|
||||
//
|
||||
unique_ptr<uint8_t> memBuffer(new uint8_t[params->ByteCount]);
|
||||
memset(reinterpret_cast<void*>(memBuffer.get()), 0, params->ByteCount);
|
||||
|
||||
drive->Write(params->LBN,
|
||||
params->ByteCount,
|
||||
memBuffer.get());
|
||||
|
||||
// Set parameters for response.
|
||||
// We leave ByteCount as is (for now anyway)
|
||||
// And set First Bad Block to 0. (This is unnecessary since we're
|
||||
// not reporting a bad block, but we're doing it for completeness.)
|
||||
params->LBN = 0;
|
||||
|
||||
return STATUS(Status::SUCCESS, 0, 0);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mscp_server::GetUnitStatus(
|
||||
shared_ptr<Message> message,
|
||||
@@ -311,26 +430,55 @@ mscp_server::GetUnitStatus(
|
||||
// Adjust message length for response
|
||||
message->MessageLength = sizeof(GetUnitStatusResponseParameters) +
|
||||
HEADER_SIZE;
|
||||
|
||||
|
||||
|
||||
ControlMessageHeader* header =
|
||||
reinterpret_cast<ControlMessageHeader*>(message->Message);
|
||||
|
||||
if (modifiers & 0x1)
|
||||
{
|
||||
// Next Unit modifier: return the next known unit >= unitNumber.
|
||||
// Unless unitNumber is greater than the number of drives we support
|
||||
// we just return the unit specified by unitNumber.
|
||||
if (unitNumber >= _port->GetDriveCount())
|
||||
{
|
||||
// In this case we act as if drive 0 was queried.
|
||||
unitNumber = 0;
|
||||
header->UnitNumber = 0;
|
||||
}
|
||||
}
|
||||
|
||||
mscp_drive_c* drive = GetDrive(unitNumber);
|
||||
|
||||
if (nullptr == drive ||
|
||||
!drive->IsAvailable())
|
||||
{
|
||||
INFO("Returning UNIT OFFLINE");
|
||||
return STATUS(Status::UNIT_OFFLINE, 3); // unknown -- todo move to enum
|
||||
}
|
||||
|
||||
GetUnitStatusResponseParameters* params =
|
||||
reinterpret_cast<GetUnitStatusResponseParameters*>(
|
||||
GetParameterPointer(message));
|
||||
|
||||
if (nullptr == drive)
|
||||
{
|
||||
// No such drive
|
||||
params->UnitIdentifier = 0;
|
||||
params->ShadowUnit = 0;
|
||||
return STATUS(Status::UNIT_OFFLINE, 0x3, 0); // offline; unknown unit.
|
||||
}
|
||||
|
||||
if(!drive->IsAvailable())
|
||||
{
|
||||
// Known drive, but offline.
|
||||
params->UnitIdentifier = 0;
|
||||
params->ShadowUnit = 0;
|
||||
|
||||
return STATUS(Status::UNIT_OFFLINE, 0x23, 0); // offline; no volume available
|
||||
}
|
||||
|
||||
params->Reserved0 = 0;
|
||||
params->Reserved1 = 0;
|
||||
params->Reserved2 = 0;
|
||||
params->UnitFlags = 0; // TODO: 0 for now, which is sane.
|
||||
params->MultiUnitCode = 0; // Controller dependent, we don't support multi-unit drives.
|
||||
params->UnitIdentifier = drive->GetUnitID();
|
||||
params->MediaTypeIdentifier = drive->GetMediaID();
|
||||
params->ShadowUnit = unitNumber; // Always equal to unit number
|
||||
params->ShadowUnit = 0; // Always equal to unit number
|
||||
|
||||
//
|
||||
// For group, and cylinder size we return 0 -- this is appropriate for the
|
||||
@@ -351,11 +499,11 @@ mscp_server::GetUnitStatus(
|
||||
|
||||
if (drive->IsOnline())
|
||||
{
|
||||
return STATUS(Status::SUCCESS, 0);
|
||||
return STATUS(Status::SUCCESS, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return STATUS(Status::UNIT_AVAILABLE, 0);
|
||||
return STATUS(Status::UNIT_AVAILABLE, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +564,7 @@ mscp_server::Online(
|
||||
!drive->IsAvailable())
|
||||
{
|
||||
INFO("Returning UNIT OFFLINE");
|
||||
return STATUS(Status::UNIT_OFFLINE, 3); // unknown -- todo move to enum
|
||||
return STATUS(Status::UNIT_OFFLINE, 0x3, 0); // unknown -- todo move to enum
|
||||
}
|
||||
|
||||
bool alreadyOnline = drive->IsOnline();
|
||||
@@ -437,7 +585,7 @@ mscp_server::Online(
|
||||
params->Reserved1 = 0;
|
||||
|
||||
return STATUS(Status::SUCCESS |
|
||||
(alreadyOnline ? SuccessSubcodes::ALREADY_ONLINE : SuccessSubcodes::NORMAL), 0);
|
||||
(alreadyOnline ? SuccessSubcodes::ALREADY_ONLINE : SuccessSubcodes::NORMAL), 0, 0);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
@@ -470,7 +618,7 @@ mscp_server::SetControllerCharacteristics(
|
||||
//
|
||||
if (params->MSCPVersion != 0)
|
||||
{
|
||||
return STATUS(Status::INVALID_COMMAND, 0); // TODO: set sub-status
|
||||
return STATUS(Status::INVALID_COMMAND, 0, 0); // TODO: set sub-status
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -486,7 +634,7 @@ mscp_server::SetControllerCharacteristics(
|
||||
params->HostTimeout = 0xff; // Controller timeout: return the max value.
|
||||
params->TimeAndDate = _port->GetControllerIdentifier(); // Controller ID
|
||||
|
||||
return STATUS(Status::SUCCESS, 0);
|
||||
return STATUS(Status::SUCCESS, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -540,7 +688,7 @@ mscp_server::SetUnitCharacteristics(
|
||||
!drive->IsAvailable())
|
||||
{
|
||||
INFO("Returning UNIT OFFLINE");
|
||||
return STATUS(Status::UNIT_OFFLINE, 3);
|
||||
return STATUS(Status::UNIT_OFFLINE, 0x3, 0);
|
||||
}
|
||||
|
||||
SetUnitCharacteristicsResponseParameters* params =
|
||||
@@ -554,7 +702,7 @@ mscp_server::SetUnitCharacteristics(
|
||||
params->UnitSize = drive->GetBlockCount();
|
||||
params->VolumeSerialNumber = 0; // We report no serial
|
||||
|
||||
return STATUS(Status::SUCCESS, 0);
|
||||
return STATUS(Status::SUCCESS, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -578,7 +726,7 @@ mscp_server::Read(
|
||||
ReadParameters* params =
|
||||
reinterpret_cast<ReadParameters*>(GetParameterPointer(message));
|
||||
|
||||
DEBUG("MSCP READ unit %d chan o%o pa o%o count %d lbn %d",
|
||||
INFO ("MSCP READ unit %d chan o%o pa o%o count %d lbn %d",
|
||||
unitNumber,
|
||||
params->BufferPhysicalAddress >> 24,
|
||||
params->BufferPhysicalAddress & 0x00ffffff,
|
||||
@@ -595,7 +743,7 @@ mscp_server::Read(
|
||||
if (nullptr == drive ||
|
||||
!drive->IsAvailable())
|
||||
{
|
||||
return STATUS(Status::UNIT_OFFLINE, 3);
|
||||
return STATUS(Status::UNIT_OFFLINE, 0x3, 0);
|
||||
}
|
||||
|
||||
// TODO: Need to rectify reads/writes to RCT area more cleanly
|
||||
@@ -604,12 +752,12 @@ mscp_server::Read(
|
||||
// Check LBN and byte count
|
||||
if (params->LBN >= drive->GetBlockCount() + 1) // + 1 for RCT write protect flag
|
||||
{
|
||||
return STATUS(Status::INVALID_COMMAND + (0x1c << 8), 0); // TODO: set sub-code
|
||||
return STATUS(Status::INVALID_COMMAND + (0x1c << 8), 0, 0); // TODO: set sub-code
|
||||
}
|
||||
|
||||
if (params->ByteCount > ((drive->GetBlockCount() + 1) - params->LBN) * drive->GetBlockSize())
|
||||
{
|
||||
return STATUS(Status::INVALID_COMMAND + (0xc << 8), 0); // TODO: as above
|
||||
return STATUS(Status::INVALID_COMMAND + (0xc << 8), 0, 0); // TODO: as above
|
||||
}
|
||||
|
||||
//
|
||||
@@ -628,7 +776,7 @@ mscp_server::Read(
|
||||
// not reporting a bad block, but we're doing it for completeness.)
|
||||
params->LBN = 0;
|
||||
|
||||
return STATUS(Status::SUCCESS,0);
|
||||
return STATUS(Status::SUCCESS, 0, 0);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
@@ -652,7 +800,7 @@ mscp_server::Write(
|
||||
WriteParameters* params =
|
||||
reinterpret_cast<WriteParameters*>(GetParameterPointer(message));
|
||||
|
||||
DEBUG("MSCP WRITE unit %d chan o%o pa o%o count %d lbn %d",
|
||||
INFO ("MSCP WRITE unit %d chan o%o pa o%o count %d lbn %d",
|
||||
unitNumber,
|
||||
params->BufferPhysicalAddress >> 24,
|
||||
params->BufferPhysicalAddress & 0x00ffffff,
|
||||
@@ -669,19 +817,19 @@ mscp_server::Write(
|
||||
if (nullptr == drive ||
|
||||
!drive->IsAvailable())
|
||||
{
|
||||
return STATUS(Status::UNIT_OFFLINE, 3);
|
||||
return STATUS(Status::UNIT_OFFLINE, 0x3, 0);
|
||||
}
|
||||
|
||||
// Check LBN
|
||||
if (params->LBN > drive->GetBlockCount() + 1) // + 1 for RCT
|
||||
{
|
||||
return STATUS(Status::INVALID_COMMAND + (0x1c << 8), 0); // TODO: set sub-code
|
||||
return STATUS(Status::INVALID_COMMAND + (0x1c << 8), 0, 0); // TODO: set sub-code
|
||||
}
|
||||
|
||||
// Check byte count
|
||||
if (params->ByteCount > ((drive->GetBlockCount() + 1) - params->LBN) * drive->GetBlockSize())
|
||||
{
|
||||
return STATUS(Status::INVALID_COMMAND + (0x0c << 8), 0); // TODO: as above
|
||||
return STATUS(Status::INVALID_COMMAND + (0x0c << 8), 0, 0); // TODO: as above
|
||||
}
|
||||
|
||||
//
|
||||
@@ -702,7 +850,7 @@ mscp_server::Write(
|
||||
// not reporting a bad block, but we're doing it for completeness.)
|
||||
params->LBN = 0;
|
||||
|
||||
return STATUS(Status::SUCCESS,0);
|
||||
return STATUS(Status::SUCCESS, 0, 0);
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
|
||||
@@ -9,7 +9,7 @@ class mscp_drive_c;
|
||||
|
||||
// Builds a uint32_t containing the status, flags, and endcode for a response message,
|
||||
// used to simplify returning the appropriate status bits from command functions.
|
||||
#define STATUS(status, flags) ((flags) << 8) | ((status) << 16)
|
||||
#define STATUS(status, modifier, flags) ((flags) << 8) | (((status) | ((modifier) * 0x20)) << 16)
|
||||
|
||||
#define GET_STATUS(status) (((status) >> 16) & 0xffff)
|
||||
#define GET_FLAGS(status) (((status) >> 8) & 0xff)
|
||||
@@ -134,6 +134,9 @@ public:
|
||||
bool on_param_changed(parameter_c *param) override { return true; }
|
||||
|
||||
private:
|
||||
uint32_t Available(std::shared_ptr<Message> message, uint16_t unitNumber, uint16_t modifiers);
|
||||
uint32_t DetermineAccessPaths(std::shared_ptr<Message> message, uint16_t unitNumber);
|
||||
uint32_t Erase(std::shared_ptr<Message> message, uint16_t unitNumber, uint16_t modifiers);
|
||||
uint32_t GetUnitStatus(std::shared_ptr<Message> message, uint16_t unitNumber, uint16_t modifiers);
|
||||
uint32_t Online(std::shared_ptr<Message> message, uint16_t unitNumber, uint16_t modifiers);
|
||||
uint32_t SetControllerCharacteristics(std::shared_ptr<Message> message);
|
||||
|
||||
@@ -148,7 +148,7 @@ void uda_c::worker(void)
|
||||
|
||||
case InitializationStep::Step1:
|
||||
// Wait 100uS, set SA.
|
||||
timeout.wait_us(100);
|
||||
timeout.wait_ms(100);
|
||||
|
||||
INFO("Transition to Init state S1.");
|
||||
//
|
||||
@@ -162,10 +162,8 @@ void uda_c::worker(void)
|
||||
break;
|
||||
|
||||
case InitializationStep::Step2:
|
||||
// Wait 100uS, set SA.
|
||||
timeout.wait_ms(100);
|
||||
|
||||
INFO("Transition to Init state S2.");
|
||||
timeout.wait_ms(1000);
|
||||
// update the SA read value for step 2:
|
||||
// S2 is set, unibus port type (0), SA bits 15-8 written
|
||||
// by the host in step 1.
|
||||
@@ -176,7 +174,7 @@ void uda_c::worker(void)
|
||||
|
||||
case InitializationStep::Step3:
|
||||
// Wait 100uS, set SA.
|
||||
timeout.wait_ms(100);
|
||||
timeout.wait_ms(1000);
|
||||
|
||||
INFO("Transition to Init state S3.");
|
||||
// Update the SA read value for step 3:
|
||||
@@ -187,6 +185,7 @@ void uda_c::worker(void)
|
||||
break;
|
||||
|
||||
case InitializationStep::Step4:
|
||||
timeout.wait_ms(100);
|
||||
|
||||
// Clear communications area, set SA
|
||||
INFO("Clearing comm area at 0x%x.", _ringBase);
|
||||
@@ -195,10 +194,10 @@ void uda_c::worker(void)
|
||||
// TODO: -6 and -8 are described; do these always get cleared or only
|
||||
// on VAXen? ZUDJ diag only expects -2 and -4 to be cleared...
|
||||
for(uint32_t i = 0;
|
||||
i < (_responseRingLength + _commandRingLength) * sizeof(Descriptor) + 4;
|
||||
i < (_responseRingLength + _commandRingLength) * sizeof(Descriptor) + 6;
|
||||
i += 2)
|
||||
{
|
||||
DMAWriteWord(_ringBase + i - 4, 0x0);
|
||||
DMAWriteWord(_ringBase + i - 6, 0x0);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -225,7 +224,7 @@ void uda_c::worker(void)
|
||||
// _sa = 0x4063; //UDA50
|
||||
_sa = 0x4042;
|
||||
update_SA();
|
||||
Interrupt();
|
||||
Interrupt();
|
||||
break;
|
||||
|
||||
case InitializationStep::Complete:
|
||||
@@ -317,7 +316,8 @@ uda_c::on_after_register_access(
|
||||
// be generated during normal operation and, if IE=1,
|
||||
// during initialization.
|
||||
_step1Value = value;
|
||||
intr_vector.value = _interruptVector = (value & 0x7f) << 2;
|
||||
|
||||
intr_vector.value = _interruptVector = ((value & 0x7f) << 2);
|
||||
_interruptEnable = !!(value & 0x80);
|
||||
_responseRingLength = (1 << ((value & 0x700) >> 8));
|
||||
_commandRingLength = (1 << ((value & 0x3800) >> 11));
|
||||
@@ -325,6 +325,8 @@ uda_c::on_after_register_access(
|
||||
INFO("Step1: 0x%x", value);
|
||||
INFO("resp ring 0x%x", _responseRingLength);
|
||||
INFO("cmd ring 0x%x", _commandRingLength);
|
||||
INFO("vector 0x%x", _interruptVector);
|
||||
INFO("ie %d", _interruptEnable);
|
||||
|
||||
// Move to step 2.
|
||||
StateTransition(InitializationStep::Step2);
|
||||
@@ -344,7 +346,7 @@ uda_c::on_after_register_access(
|
||||
_ringBase = value & 0xfffe;
|
||||
_purgeInterruptEnable = !!(value & 0x1);
|
||||
|
||||
INFO("Step2: 0x%x", value);
|
||||
INFO("Step2: rb 0x%x pi %d", _ringBase, _purgeInterruptEnable);
|
||||
// Move to step 3 and interrupt as necessary.
|
||||
StateTransition(InitializationStep::Step3);
|
||||
break;
|
||||
@@ -363,7 +365,7 @@ uda_c::on_after_register_access(
|
||||
// [ringbase+0].
|
||||
_ringBase |= ((value & 0x7fff) << 16);
|
||||
|
||||
INFO("Step3: 0x%x", value);
|
||||
INFO("Step3: ringbase 0x%x", _ringBase);
|
||||
// Move to step 4 and interrupt as necessary.
|
||||
StateTransition(InitializationStep::Step4);
|
||||
break;
|
||||
@@ -740,6 +742,7 @@ uda_c::Interrupt(void)
|
||||
{
|
||||
if (_interruptEnable && _interruptVector != 0)
|
||||
{
|
||||
INFO("interrupt");
|
||||
interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ void menus_c::menu_devices(void) {
|
||||
// cpu_c cpu;
|
||||
|
||||
// create RL11 + drives
|
||||
RL11_c RL11; // instantiates also 4 RL01/02 drives
|
||||
// RL11_c RL11; // instantiates also 4 RL01/02 drives
|
||||
cur_device = NULL;
|
||||
|
||||
paneldriver->reset(); // reset I2C, restart worker()
|
||||
@@ -95,9 +95,9 @@ void menus_c::menu_devices(void) {
|
||||
//demo_regs.install();
|
||||
//demo_regs.worker_start();
|
||||
|
||||
RL11.install();
|
||||
RL11.connect_to_panel();
|
||||
RL11.worker_start();
|
||||
//RL11.install();
|
||||
//RL11.connect_to_panel();
|
||||
//RL11.worker_start();
|
||||
|
||||
RK05.install();
|
||||
RK05.worker_start();
|
||||
@@ -344,11 +344,11 @@ void menus_c::menu_devices(void) {
|
||||
//RL11.disconnect_from_panel();
|
||||
//RL11.uninstall();
|
||||
|
||||
RK05.worker_stop();
|
||||
RK05.uninstall();
|
||||
// RK05.worker_stop();
|
||||
// RK05.uninstall();
|
||||
|
||||
// UDA50.worker_stop();
|
||||
// UDA50.uninstall();
|
||||
UDA50.worker_stop();
|
||||
UDA50.uninstall();
|
||||
|
||||
//demo_regs.worker_stop();
|
||||
//demo_regs.uninstall();
|
||||
|
||||
Reference in New Issue
Block a user