From 6bcd8e5637d219b3830d2ec8a2406ad3beafe973 Mon Sep 17 00:00:00 2001 From: Paul Hollinsky Date: Mon, 28 Feb 2022 03:40:44 -0500 Subject: [PATCH] Disk: Read driver for Plasion --- CMakeLists.txt | 1 + communication/multichannelcommunication.cpp | 7 ++ disk/plasiondiskreaddriver.cpp | 74 +++++++++++++++++++ .../communication/multichannelcommunication.h | 14 ++-- include/icsneo/device/tree/plasion/neoviion.h | 3 +- include/icsneo/disk/plasiondiskreaddriver.h | 43 +++++++++++ 6 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 disk/plasiondiskreaddriver.cpp create mode 100644 include/icsneo/disk/plasiondiskreaddriver.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f552fcc..d81b45b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,6 +153,7 @@ set(SRC_FILES disk/diskwritedriver.cpp disk/nulldiskdriver.cpp disk/neomemorydiskreaddriver.cpp + disk/plasiondiskreaddriver.cpp ${PLATFORM_SRC} ) diff --git a/communication/multichannelcommunication.cpp b/communication/multichannelcommunication.cpp index 4445a93..70a6f91 100644 --- a/communication/multichannelcommunication.cpp +++ b/communication/multichannelcommunication.cpp @@ -2,6 +2,7 @@ #include "icsneo/communication/command.h" #include "icsneo/communication/decoder.h" #include "icsneo/communication/packetizer.h" +#include "icsneo/communication/message/neoreadmemorysdmessage.h" using namespace icsneo; @@ -134,6 +135,12 @@ void MultiChannelCommunication::hidReadTask() { if(numVnets >= 3) currentQueue = &vnetQueues[2]; break; + case CommandType::SDCC1_to_HostPC: { + auto msg = std::make_shared(); + std::swap(msg->data, payloadBytes); + dispatchMessage(msg); + break; + } } if(currentQueue == nullptr) { diff --git a/disk/plasiondiskreaddriver.cpp b/disk/plasiondiskreaddriver.cpp new file mode 100644 index 0000000..2fe171f --- /dev/null +++ b/disk/plasiondiskreaddriver.cpp @@ -0,0 +1,74 @@ +#include "icsneo/disk/plasiondiskreaddriver.h" +#include "icsneo/communication/message/neoreadmemorysdmessage.h" +#include "icsneo/communication/multichannelcommunication.h" +#include + +using namespace icsneo; +using namespace icsneo::Disk; + +optional PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report, + uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) { + static std::shared_ptr NeoMemorySDRead = std::make_shared(Network::NetID::NeoMemorySDRead); + + if(amount > getBlockSizeBounds().second) + return nullopt; + + if(amount % getBlockSizeBounds().first != 0) + return nullopt; + + if(pos % getBlockSizeBounds().first != 0) + return nullopt; + + if(cachePos != pos || std::chrono::steady_clock::now() > cachedAt + CacheTime) { + // The cache does not have this data, go get it + std::mutex m; + std::condition_variable cv; + uint32_t copied = 0; + bool error = false; + std::unique_lock lk(m); + auto cb = com.addMessageCallback(MessageCallback([&](std::shared_ptr msg) { + std::unique_lock lk(m); + + const auto sdmsg = std::dynamic_pointer_cast(msg); + if(!sdmsg || cache.size() < copied + sdmsg->data.size()) { + error = true; + lk.unlock(); + cv.notify_all(); + return; + } + + // Invalidate the cache here in case we fail half-way through + cachedAt = std::chrono::steady_clock::time_point(); + + memcpy(cache.data() + copied, sdmsg->data.data(), sdmsg->data.size()); + copied += sdmsg->data.size(); + if(copied == amount) { + lk.unlock(); + cv.notify_all(); + } + }, NeoMemorySDRead)); + + uint32_t sector = pos / SectorSize; + com.rawWrite({ + uint8_t(MultiChannelCommunication::CommandType::HostPC_from_SDCC1), + uint8_t(sector & 0xFF), + uint8_t((sector >> 8) & 0xFF), + uint8_t((sector >> 16) & 0xFF), + uint8_t((sector >> 24) & 0xFF), + uint8_t(amount & 0xFF), + uint8_t((amount >> 8) & 0xFF), + }); + + bool hitTimeout = !cv.wait_for(lk, timeout, [&copied, &error, &amount] { return error || copied == amount; }); + com.removeMessageCallback(cb); + + if(hitTimeout) + return nullopt; + + cachedAt = std::chrono::steady_clock::now(); + cachePos = pos; + } + + memcpy(into, cache.data(), amount); + return amount; +} \ No newline at end of file diff --git a/include/icsneo/communication/multichannelcommunication.h b/include/icsneo/communication/multichannelcommunication.h index 8428573..ffdc49f 100644 --- a/include/icsneo/communication/multichannelcommunication.h +++ b/include/icsneo/communication/multichannelcommunication.h @@ -24,10 +24,6 @@ public: void joinThreads() override; bool sendPacket(std::vector& bytes) override; -protected: - bool preprocessPacket(std::deque& usbReadFifo); - -private: enum class CommandType : uint8_t { PlasmaReadRequest = 0x10, // Status read request to HSC PlasmaStatusResponse = 0x11, // Status response by HSC @@ -49,6 +45,10 @@ private: Microblaze_to_HostPC = 0x81 // Microblaze processor data to host PC }; +protected: + bool preprocessPacket(std::deque& usbReadFifo); + +private: static bool CommandTypeIsValid(CommandType cmd) { switch(cmd) { case CommandType::PlasmaReadRequest: @@ -77,8 +77,10 @@ private: static bool CommandTypeHasAddress(CommandType cmd) { // Check CommandTypeIsValid before this, you will get false on an invalid command switch(cmd) { - case CommandType::SDCC1_to_HostPC: - case CommandType::SDCC2_to_HostPC: + case CommandType::HostPC_to_SDCC1: + case CommandType::HostPC_from_SDCC1: + case CommandType::HostPC_to_SDCC2: + case CommandType::HostPC_from_SDCC2: return true; default: return false; diff --git a/include/icsneo/device/tree/plasion/neoviion.h b/include/icsneo/device/tree/plasion/neoviion.h index d03d9e0..fbc8d55 100644 --- a/include/icsneo/device/tree/plasion/neoviion.h +++ b/include/icsneo/device/tree/plasion/neoviion.h @@ -6,6 +6,7 @@ #include "icsneo/device/tree/plasion/plasion.h" #include "icsneo/device/devicetype.h" #include "icsneo/platform/ftdi.h" +#include "icsneo/disk/plasiondiskreaddriver.h" namespace icsneo { @@ -24,7 +25,7 @@ public: private: NeoVIION(neodevice_t neodevice) : Plasion(neodevice) { - initialize(); + initialize(); getWritableNeoDevice().type = DEVICE_TYPE; productId = PRODUCT_ID; } diff --git a/include/icsneo/disk/plasiondiskreaddriver.h b/include/icsneo/disk/plasiondiskreaddriver.h new file mode 100644 index 0000000..1bc0b7e --- /dev/null +++ b/include/icsneo/disk/plasiondiskreaddriver.h @@ -0,0 +1,43 @@ +#ifndef __PLASIONDISKREADDRIVER_H__ +#define __PLASIONDISKREADDRIVER_H__ + +#ifdef __cplusplus + +#include "icsneo/disk/diskreaddriver.h" +#include +#include + +namespace icsneo { + +namespace Disk { + +/** + * A disk read driver which uses the PLASION HID command set to read from the disk + */ +class PlasionDiskReadDriver : public ReadDriver { +public: + Access getAccess() const override { return Access::EntireCard; } + std::pair getBlockSizeBounds() const override { + static_assert(SectorSize <= std::numeric_limits::max(), "Incorrect sector size"); + static_assert(SectorSize >= std::numeric_limits::min(), "Incorrect sector size"); + return { static_cast(SectorSize), static_cast(MaxSize) }; + } + +private: + static constexpr const uint32_t MaxSize = 65024; + static constexpr const std::chrono::duration CacheTime = std::chrono::seconds(1); + + std::array cache; + uint64_t cachePos = 0; + std::chrono::time_point cachedAt; + + optional readLogicalDiskAligned(Communication& com, device_eventhandler_t report, + uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) override; +}; + +} // namespace Disk + +} // namespace icsneo + +#endif // __cplusplus +#endif // __NEOMEMORYDISKREADDRIVER_H__ \ No newline at end of file