Skip to content

Commit

Permalink
Merge pull request #36 from OrangeFox86/31-fault-while-using-retro-fi…
Browse files Browse the repository at this point in the history
…ghters-controller

31 addressed faults with retro fighters controller
  • Loading branch information
Tails86 authored Dec 18, 2022
2 parents 45331bf + 2d4ac27 commit 383b9a9
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 55 deletions.
12 changes: 10 additions & 2 deletions src/coreLib/DreamcastMainNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ DreamcastMainNode::DreamcastMainNode(MapleBusInterface& bus,
playerData),
mSubNodes(),
mTransmissionTimeliner(bus, prioritizedTxScheduler),
mScheduleId(-1)
mScheduleId(-1),
mCommFailCount(0)
{
addInfoRequestToSchedule();
mSubNodes.reserve(DreamcastPeripheral::MAX_SUB_PERIPHERALS);
Expand Down Expand Up @@ -57,6 +58,9 @@ void DreamcastMainNode::txComplete(std::shared_ptr<const MaplePacket> packet,
DEBUG_PRINT("P%lu connected (", mPlayerData.playerIndex + 1);
debugPrintPeripherals();
DEBUG_PRINT(")\n");

// Reset failure count
mCommFailCount = 0;
}

if (mask > 0)
Expand Down Expand Up @@ -93,6 +97,9 @@ void DreamcastMainNode::readTask(uint64_t currentTimeUs)
// See if there is anything to receive
if (readStatus.busPhase == MapleBusInterface::Phase::READ_COMPLETE)
{
// Reset failure count
mCommFailCount = 0;

// Check addresses to determine what sub nodes are attached
uint8_t sendAddr = readStatus.received->getFrameSenderAddr();
uint8_t recAddr = readStatus.received->getFrameRecipientAddr();
Expand Down Expand Up @@ -137,7 +144,7 @@ void DreamcastMainNode::readTask(uint64_t currentTimeUs)
|| readStatus.busPhase == MapleBusInterface::Phase::WRITE_FAILED)
{
uint8_t recipientAddr = readStatus.transmission->packet->getFrameRecipientAddr();
if (recipientAddr & mAddr)
if ((recipientAddr & mAddr) && ++mCommFailCount >= MAX_FAILURE_DISCONNECT_COUNT)
{
// A transmission failure on a main node must cause peripheral disconnect
if (mPeripherals.size() > 0)
Expand All @@ -147,6 +154,7 @@ void DreamcastMainNode::readTask(uint64_t currentTimeUs)

disconnectMainPeripheral(currentTimeUs);
}
mCommFailCount = 0;
}
else
{
Expand Down
4 changes: 4 additions & 0 deletions src/coreLib/DreamcastMainNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class DreamcastMainNode : public DreamcastNode
public:
//! Number of microseconds in between each info request when no peripheral is detected
static const uint32_t US_PER_CHECK = 16000;
//! Number of communication failures before main peripheral is disconnected
static const uint32_t MAX_FAILURE_DISCONNECT_COUNT = 3;

protected:
//! The sub nodes under this node
Expand All @@ -73,4 +75,6 @@ class DreamcastMainNode : public DreamcastNode
TransmissionTimeliner mTransmissionTimeliner;
//! ID of the device info request auto reload transmission this object added to the schedule
int64_t mScheduleId;
//! Current count of number of communication failures
uint32_t mCommFailCount;
};
55 changes: 41 additions & 14 deletions src/coreLib/PrioritizedTxScheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ PrioritizedTxScheduler::PrioritizedTxScheduler() :
mSchedule.resize(PRIORITY_COUNT);
}

PrioritizedTxScheduler::PrioritizedTxScheduler(uint32_t max) :
mNextId(1),
mSchedule()
{
mSchedule.resize(max + 1);
}

PrioritizedTxScheduler::~PrioritizedTxScheduler() {}

uint32_t PrioritizedTxScheduler::add(std::shared_ptr<Transmission> tx)
Expand Down Expand Up @@ -88,9 +95,9 @@ uint64_t PrioritizedTxScheduler::computeNextTimeCadence(uint64_t currentTime,
}
}

std::shared_ptr<const Transmission> PrioritizedTxScheduler::popNext(uint64_t time)
PrioritizedTxScheduler::ScheduleItem PrioritizedTxScheduler::peekNext(uint64_t time)
{
std::shared_ptr<Transmission> item = nullptr;
ScheduleItem scheduleItem;

// Find a priority list with item ready to be popped
std::vector<std::list<std::shared_ptr<Transmission>>>::iterator scheduleIter = mSchedule.begin();
Expand Down Expand Up @@ -148,18 +155,38 @@ std::shared_ptr<const Transmission> PrioritizedTxScheduler::popNext(uint64_t tim

if (found)
{
// Pop it!
item = (*itemIter);
scheduleIter->erase(itemIter);

// Reschedule this if auto repeat settings are valid
if (item != nullptr
&& item->autoRepeatUs > 0
&& (item->autoRepeatEndTimeUs == 0 || time <= item->autoRepeatEndTimeUs))
{
item->nextTxTimeUs = computeNextTimeCadence(time, item->autoRepeatUs, item->nextTxTimeUs);
add(item);
}
scheduleItem.mScheduleIter = scheduleIter;
scheduleItem.mItemIter = itemIter;
scheduleItem.mTime = time;
scheduleItem.mIsValid = true;
}
}

return scheduleItem;
}

std::shared_ptr<Transmission> PrioritizedTxScheduler::popItem(ScheduleItem& scheduleItem)
{
std::shared_ptr<Transmission> item = nullptr;

if (scheduleItem.mIsValid)
{
// Save the transmission
item = scheduleItem.getTx();

// Pop it!
scheduleItem.mScheduleIter->erase(scheduleItem.mItemIter);
scheduleItem.mIsValid = false;

// Reschedule this if auto repeat settings are valid
if (item != nullptr
&& item->autoRepeatUs > 0
&& (item->autoRepeatEndTimeUs == 0 || scheduleItem.mTime <= item->autoRepeatEndTimeUs))
{
item->nextTxTimeUs = computeNextTimeCadence(scheduleItem.mTime,
item->autoRepeatUs,
item->nextTxTimeUs);
add(item);
}
}

Expand Down
38 changes: 35 additions & 3 deletions src/coreLib/PrioritizedTxScheduler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,38 @@ class PrioritizedTxScheduler
PRIORITY_COUNT
};

//! Points to a schedule item within the current schedule
class ScheduleItem
{
//! Only the PrioritizedTxScheduler may modify private elements here
friend PrioritizedTxScheduler;

public:
//! Constructor
ScheduleItem() : mIsValid(false), mTime(0) {}

//! @returns the transmission for this schedule item
std::shared_ptr<Transmission> getTx() {return mIsValid ? *mItemIter : nullptr;}

private:
//! Set to true iff iterators are valid
bool mIsValid;
//! The schedule group
std::vector<std::list<std::shared_ptr<Transmission>>>::iterator mScheduleIter;
//! The item within the schedule group
std::list<std::shared_ptr<Transmission>>::iterator mItemIter;
//! The time at which this item was peeked
uint64_t mTime;
};

public:
//! Default constructor
PrioritizedTxScheduler();

//! Constructor with custom priority list
//! @param[in] max The maximum accepted priority
PrioritizedTxScheduler(uint32_t max);

//! Virtual destructor
virtual ~PrioritizedTxScheduler();

Expand All @@ -49,11 +77,15 @@ class PrioritizedTxScheduler
uint32_t autoRepeatUs=0,
uint64_t autoRepeatEndTimeUs=0);

//! Pops the next scheduled packet, given the current time
//! Peeks the next scheduled packet, given the current time
//! @param[in] time The current time
//! @returns nullptr if no scheduled packet is available for the given time
//! @returns the next sceduled packet for the given current time
std::shared_ptr<const Transmission> popNext(uint64_t time);
//! @returns the next scheduled item for the given current time
ScheduleItem peekNext(uint64_t time);

//! Pops a schedule item that was retrieved using peekNext
//! @param[in,out] scheduleItem The schedule item to pop and invalidate
std::shared_ptr<Transmission> popItem(ScheduleItem& scheduleItem);

//! Cancels scheduled transmission by transmission ID
//! @param[in] transmissionId The transmission ID of the transmissions to cancel
Expand Down
28 changes: 15 additions & 13 deletions src/coreLib/TransmissionTimeliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <assert.h>

TransmissionTimeliner::TransmissionTimeliner(MapleBusInterface& bus, std::shared_ptr<PrioritizedTxScheduler> schedule):
mBus(bus), mSchedule(schedule), mCurrentTx(nullptr), mNextTx(nullptr)
mBus(bus), mSchedule(schedule), mCurrentTx(nullptr)
{}

TransmissionTimeliner::ReadStatus TransmissionTimeliner::readTask(uint64_t currentTimeUs)
Expand Down Expand Up @@ -34,20 +34,22 @@ std::shared_ptr<const Transmission> TransmissionTimeliner::writeTask(uint64_t cu
{
std::shared_ptr<const Transmission> txSent = nullptr;

// Get next transmission
if (mNextTx == nullptr && !mBus.isBusy())
if (!mBus.isBusy())
{
mNextTx = mSchedule->popNext(currentTimeUs);
}

// Transmit
if (mNextTx != nullptr)
{
assert(mNextTx->packet->isValid());
if (mBus.write(*mNextTx->packet, mNextTx->expectResponse))
PrioritizedTxScheduler::ScheduleItem item = mSchedule->peekNext(currentTimeUs);
txSent = item.getTx();
if (txSent != nullptr)
{
mCurrentTx = txSent = mNextTx;
mNextTx = nullptr;
assert(txSent->packet->isValid());
if (mBus.write(*txSent->packet, txSent->expectResponse))
{
mCurrentTx = txSent;
mSchedule->popItem(item);
}
else
{
txSent = nullptr;
}
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/coreLib/TransmissionTimeliner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,4 @@ class TransmissionTimeliner
std::shared_ptr<PrioritizedTxScheduler> mSchedule;
//! The currently sending transmission
std::shared_ptr<const Transmission> mCurrentTx;
//! Recently popped transmission that is waiting to be sent
std::shared_ptr<const Transmission> mNextTx;
};
14 changes: 7 additions & 7 deletions src/coreLib/parsers/MaplePassthroughCommandParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@ class EchoTransmitter : public Transmitter
{
if (writeFailed)
{
printf("%lu: failed write\n", tx->transmissionId);
printf("%lu: failed write\n", (long unsigned int)tx->transmissionId);
}
else
{
printf("%lu: failed read\n", tx->transmissionId);
printf("%lu: failed read\n", (long unsigned int)tx->transmissionId);
}
}

virtual void txComplete(std::shared_ptr<const MaplePacket> packet,
std::shared_ptr<const Transmission> tx) final
{
printf("%lu: complete {", tx->transmissionId);
printf("%08lX", packet->frameWord);
printf("%lu: complete {", (long unsigned int)tx->transmissionId);
printf("%08lX", (long unsigned int)packet->frameWord);
for (std::vector<uint32_t>::const_iterator iter = packet->payload.begin();
iter != packet->payload.end();
++iter)
{
printf(" %08lX", *iter);
printf(" %08lX", (long unsigned int)*iter);
}
printf("}\n");
}
Expand Down Expand Up @@ -126,10 +126,10 @@ void MaplePassthroughCommandParser::submit(const char* chars, uint32_t len)
packet,
true);
std::vector<uint32_t>::iterator iter = words.begin();
printf("%lu: added {%08lX", id, *iter++);
printf("%lu: added {%08lX", (long unsigned int)id, (long unsigned int)*iter++);
for(; iter < words.end(); ++iter)
{
printf(" %08lX", *iter);
printf(" %08lX", (long unsigned int)*iter);
}
printf("}\n");
}
Expand Down
24 changes: 20 additions & 4 deletions src/test/MainNodeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class MainNodeTest : public ::testing::Test
mScreenData(mMutex),
mPlayerData{0, mDreamcastControllerObserver, mScreenData, mClock, mUsbFileSystem},
mMapleBus(),
mPrioritizedTxScheduler(std::make_shared<PrioritizedTxScheduler>(DreamcastMainNode::MAX_PRIORITY)),
mPrioritizedTxScheduler(std::make_shared<PrioritizedTxScheduler>(PrioritizedTxScheduler::PRIORITY_COUNT - 1)),
mDreamcastMainNode(mMapleBus, mPlayerData, mPrioritizedTxScheduler)
{}

Expand Down Expand Up @@ -265,9 +265,9 @@ TEST_F(MainNodeTest, peripheralDisconnect)
// The task will process events, and it will return read failure
MapleBusInterface::Status status;
status.phase = MapleBusInterface::Phase::READ_FAILED;
EXPECT_CALL(mMapleBus, processEvents(1000000))
.Times(1)
.WillOnce(Return(status));
EXPECT_CALL(mMapleBus, processEvents(_))
.Times(3)
.WillRepeatedly(Return(status));
// All sub node's task functions will be called with the current time
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[0], mainPeripheralDisconnected()).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[1], mainPeripheralDisconnected()).Times(1);
Expand All @@ -280,9 +280,25 @@ TEST_F(MainNodeTest, peripheralDisconnect)
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[2], task(1000000)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[3], task(1000000)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[4], task(1000000)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[0], task(1000001)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[1], task(1000001)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[2], task(1000001)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[3], task(1000001)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[4], task(1000001)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[0], task(1000002)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[1], task(1000002)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[2], task(1000002)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[3], task(1000002)).Times(1);
EXPECT_CALL(*mDreamcastMainNode.mMockedSubNodes[4], task(1000002)).Times(1);
// The peripheral will get to run task twice before it disconnects on the 3rd failure
EXPECT_CALL(*mockedDreamcastPeripheral, task(1000000)).Times(1);
EXPECT_CALL(*mockedDreamcastPeripheral, task(1000001)).Times(1);

// --- TEST EXECUTION ---
mDreamcastMainNode.task(1000000);
mDreamcastMainNode.task(1000001);
// Disconnection happens on 3rd failure
mDreamcastMainNode.task(1000002);

// --- EXPECTATIONS ---
// All peripherals removed
Expand Down
Loading

0 comments on commit 383b9a9

Please sign in to comment.