diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fa56a2..3ad118a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,12 +175,14 @@ set(SRC_FILES communication/message/ethphymessage.cpp communication/packet/flexraypacket.cpp communication/packet/canpacket.cpp + communication/packet/a2bpacket.cpp communication/packet/ethernetpacket.cpp communication/packet/versionpacket.cpp communication/packet/iso9141packet.cpp communication/packet/ethphyregpacket.cpp communication/packet/logicaldiskinfopacket.cpp communication/packet/wivicommandpacket.cpp + communication/packet/scriptstatuspacket.cpp communication/decoder.cpp communication/encoder.cpp communication/ethernetpacketizer.cpp diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 91f2d6f..ba2bfef 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -7,10 +7,13 @@ #include "icsneo/communication/message/neoreadmemorysdmessage.h" #include "icsneo/communication/message/extendedresponsemessage.h" #include "icsneo/communication/message/wiviresponsemessage.h" +#include "icsneo/communication/message/scriptstatusmessage.h" +#include "icsneo/communication/message/a2bmessage.h" #include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h" #include "icsneo/communication/command.h" #include "icsneo/device/device.h" #include "icsneo/communication/packet/canpacket.h" +#include "icsneo/communication/packet/a2bpacket.h" #include "icsneo/communication/packet/ethernetpacket.h" #include "icsneo/communication/packet/flexraypacket.h" #include "icsneo/communication/packet/iso9141packet.h" @@ -18,6 +21,7 @@ #include "icsneo/communication/packet/ethphyregpacket.h" #include "icsneo/communication/packet/logicaldiskinfopacket.h" #include "icsneo/communication/packet/wivicommandpacket.h" +#include "icsneo/communication/packet/scriptstatuspacket.h" #include using namespace icsneo; @@ -317,10 +321,30 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata); + if(!result) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning); + return false; + } + return true; + } default: break; } } + case Network::Type::A2B: { + result = HardwareA2BPacket::DecodeToMessage(packet->data); + + if(!result) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error); + return false; // A nullptr was returned, the packet was not long enough to decode + } + + A2BMessage& msg = *static_cast(result.get()); + msg.network = packet->network; + return true; + } } // For the moment other types of messages will automatically be decoded as raw messages diff --git a/communication/packet/a2bpacket.cpp b/communication/packet/a2bpacket.cpp new file mode 100644 index 0000000..916724c --- /dev/null +++ b/communication/packet/a2bpacket.cpp @@ -0,0 +1,63 @@ +#include "icsneo/communication/packet/a2bpacket.h" +#include "icsneo/communication/message/a2bmessage.h" +#include + +using namespace icsneo; + +std::shared_ptr HardwareA2BPacket::DecodeToMessage(const std::vector &bytestream) +{ + + constexpr uint8_t coreMiniMessageHeaderSize = 28; + + if(bytestream.size() < coreMiniMessageHeaderSize) + { + return nullptr; + } + + auto getSampleFromBytes = [](uint8_t bytesPerSample, const uint8_t *bytes) { + A2BPCMSample result = 0; + + for(auto i = 0; i < bytesPerSample; i++) + { + result |= bytes[i] << (i * 8); + } + + return result; + }; + + + const HardwareA2BPacket *data = (const HardwareA2BPacket*)bytestream.data(); + + uint32_t totalPackedLength = static_cast(bytestream.size()) - static_cast(coreMiniMessageHeaderSize); // First 28 bytes are message header. + + uint8_t bytesPerChannel = data->header.channelSize16 ? 2 : 4; + uint8_t numChannels = data->header.channelNum; + uint8_t bitDepth = data->header.channelSize16 ? A2BPCM_L16 : A2BPCM_L24; + bool monitor = data->header.monitor; + + std::shared_ptr msg = std::make_shared(bitDepth, bytesPerChannel, numChannels, monitor); + + const uint8_t *bytes = bytestream.data(); + bytes+=coreMiniMessageHeaderSize; + + uint8_t channel = 0; + + for(uint32_t i = 0; i < totalPackedLength; i += 2 * static_cast(bytesPerChannel), bytes += 2 * bytesPerChannel, channel = (channel + 1) % numChannels) + { + + msg->addSample( + getSampleFromBytes(bytesPerChannel, bytes), + A2BMessage::A2BDirection::DownStream, + channel + ); + + msg->addSample( + getSampleFromBytes(bytesPerChannel, bytes + bytesPerChannel), + A2BMessage::A2BDirection::UpStream, + channel + ); + + } + + return msg; +} diff --git a/communication/packet/scriptstatuspacket.cpp b/communication/packet/scriptstatuspacket.cpp new file mode 100644 index 0000000..e94f7f7 --- /dev/null +++ b/communication/packet/scriptstatuspacket.cpp @@ -0,0 +1,30 @@ +#include +#include "icsneo/communication/packet/scriptstatuspacket.h" +#include "icsneo/communication/message/scriptstatusmessage.h" + +using namespace icsneo; + +std::shared_ptr ScriptStatus::DecodeToMessage(const std::vector& bytestream){ + if(bytestream.size() != sizeof(ScriptStatus)) + return {}; + + auto msg = std::make_shared(); + const auto& decoded = *reinterpret_cast(bytestream.data()); + msg->isCoreminiRunning = decoded.status.isRunning; + msg->sectorOverflows = decoded.sectorOverflows; + msg->numRemainingSectorBuffers = decoded.numRemainingSectorBuffers; + msg->lastSector = decoded.lastSector; + msg->readBinSize = decoded.readBinSize; + msg->minSector = decoded.minSector; + msg->maxSector = decoded.maxSector; + msg->currentSector = decoded.currentSector; + msg->coreminiCreateTime = ((uint64_t)decoded.coreminiCreateTimeMsb << 32) | decoded.coreminiCreateTimeLsb; + msg->fileChecksum = decoded.fileChecksum; + msg->coreminiVersion = decoded.coreminiVersion; + msg->coreminiHeaderSize = decoded.coreminiHeaderSize; + msg->diagnosticErrorCode = decoded.diagErrCode; + msg->diagnosticErrorCodeCount = decoded.diagErrCodeCount; + msg->maxCoreminiSizeKB = decoded.maxCoreminiSizeKB; + + return msg; +} \ No newline at end of file diff --git a/device/device.cpp b/device/device.cpp index 092f221..e2683b9 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -6,6 +6,7 @@ #include "icsneo/disk/fat.h" #include "icsneo/communication/packet/wivicommandpacket.h" #include "icsneo/communication/message/wiviresponsemessage.h" +#include "icsneo/communication/message/scriptstatusmessage.h" #include #include #include @@ -410,6 +411,83 @@ bool Device::goOffline() { return true; } +int8_t Device::prepareScriptLoad() { + if(!isOpen()) { + report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error); + return false; + } + + static std::shared_ptr filter = std::make_shared(Network::NetID::CoreMiniPreLoad); + + if(!com->sendCommand(Command::CoreMiniPreload)) + return false; + + int8_t retVal = 0; + while(retVal == 0) + { + auto generic = com->waitForMessageSync(filter, std::chrono::milliseconds(1000)); + + if(!generic) { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return false; + } + + const auto resp = std::static_pointer_cast(generic); + retVal = (int8_t)resp->data[0]; + } + + return retVal; +} + +bool Device::startScript() +{ + if(!isOpen()) { + report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error); + return false; + } + + uint8_t LocationSdCard = 1; //Only support starting a coremini in an SDCard + auto generic = com->sendCommand(Command::LoadCoreMini, LocationSdCard); + + if(!generic) + { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return false; + } + + return true; +} + +bool Device::stopScript() +{ + if(!isOpen()) { + report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error); + return false; + } + + auto generic = com->sendCommand(Command::ClearCoreMini); + + if(!generic) + { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return false; + } + + return true; +} + +bool Device::clearScript() +{ + if(!stopScript()) + return false; + + std::vector clearData(512, 0xCD); + uint64_t ScriptLocation = 0; //We only support a coremini in an SDCard, which is at the very beginning of the card + auto written = writeLogicalDisk(ScriptLocation, clearData.data(), clearData.size()); + + return written > 0; +} + bool Device::transmit(std::shared_ptr frame) { if(!isOpen()) { report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error); @@ -1076,6 +1154,246 @@ bool Device::allowSleep(bool remoteWakeup) { return true; } +void Device::scriptStatusThreadBody() +{ + std::unique_lock lk(scriptStatusMutex); + + EventManager::GetInstance().downgradeErrorsOnCurrentThread(); + + bool first = true; + while(!stopScriptStatusThread) + { + if(first) // Skip the first wait + first = false; + else + stopScriptStatusCv.wait_for(lk, std::chrono::seconds(10)); + + const auto resp = getScriptStatus(); + + //If value changed/was inserted, notify callback + if(updateScriptStatusValue(ScriptStatus::CoreMiniRunning, resp->isCoreminiRunning)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::CoreMiniRunning, resp->isCoreminiRunning); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::SectorOverflow, resp->sectorOverflows)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::SectorOverflow, resp->sectorOverflows); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::RemainingSectors, resp->numRemainingSectorBuffers)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::RemainingSectors, resp->numRemainingSectorBuffers); + lk.lock(); + } + + bool logging = false; + if(updateScriptStatusValue(ScriptStatus::LastSector, resp->lastSector)) + { + logging = true; + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::LastSector, resp->lastSector); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::Logging, logging)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::Logging, logging); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::ReadBinSize, resp->readBinSize)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::ReadBinSize, resp->readBinSize); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::MinSector, resp->minSector)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::MinSector, resp->minSector); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::MaxSector, resp->maxSector)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::MaxSector, resp->maxSector); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::CurrentSector, resp->currentSector)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::CurrentSector, resp->currentSector); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::CoreMiniCreateTime, resp->coreminiCreateTime)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::CoreMiniCreateTime, resp->coreminiCreateTime); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::FileChecksum, resp->fileChecksum)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::FileChecksum, resp->fileChecksum); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::CoreMiniVersion, resp->coreminiVersion)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::CoreMiniVersion, resp->coreminiVersion); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::CoreMiniHeaderSize, resp->coreminiHeaderSize)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::CoreMiniHeaderSize, resp->coreminiHeaderSize); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::DiagnosticErrorCode, resp->diagnosticErrorCode)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::DiagnosticErrorCode, resp->diagnosticErrorCode); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::DiagnosticErrorCodeCount, resp->diagnosticErrorCodeCount)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::DiagnosticErrorCodeCount, resp->diagnosticErrorCodeCount); + lk.lock(); + } + + if(updateScriptStatusValue(ScriptStatus::MaxCoreMiniSize, resp->maxCoreminiSizeKB)) + { + lk.unlock(); + notifyScriptStatusCallback(ScriptStatus::MaxCoreMiniSize, resp->maxCoreminiSizeKB); + lk.lock(); + } + } +} + +std::shared_ptr Device::getScriptStatus() const +{ + static std::shared_ptr filter = std::make_shared(Message::Type::ScriptStatus); + + const auto generic = com->waitForMessageSync([this]() { + return com->sendCommand(Command::ScriptStatus); + }, filter, std::chrono::milliseconds(3000)); + + if(!generic || generic->type != Message::Type::ScriptStatus) { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return nullptr; + } + + return std::static_pointer_cast(generic); +} + +bool Device::updateScriptStatusValue(ScriptStatus key, uint64_t value) +{ + auto pair = scriptStatusValues.find(key); + if(pair != scriptStatusValues.end()) + { + if(pair->second != value) + { + //Value changed + scriptStatusValues[key] = value; + return true; + } + //Value didn't change + return false; + } + + //Value was inserted + scriptStatusValues.insert(std::make_pair(key, value)); + return true; +} + +void Device::notifyScriptStatusCallback(ScriptStatus key, uint64_t value) +{ + auto callbackList = scriptStatusCallbacks.find(key); + if(callbackList != scriptStatusCallbacks.end()) + { + for(const auto& callback : callbackList->second) + { + if(callback) { + try { + callback(value); + } catch(...) { + report(APIEvent::Type::Unknown, APIEvent::Severity::Error); + } + } + } + } +} + +Lifetime Device::addScriptStatusCallback(ScriptStatus key, ScriptStatusCallback cb) +{ + if(!isOpen()) { + report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error); + return {}; + } + + std::lock_guard lk(scriptStatusMutex); + if(!scriptStatusThread.joinable()) { + // Start the thread + stopScriptStatusThread = false; + scriptStatusThread = std::thread([this]() { scriptStatusThreadBody(); }); + } + + size_t idx = 0; + std::vector callbackList; + auto callbackPair = scriptStatusCallbacks.find(key); + if(callbackPair != scriptStatusCallbacks.end()) + callbackList = callbackPair->second; + + if(idx == callbackList.size()) + callbackList.push_back(std::move(cb)); + else callbackList[idx] = std::move(cb); + + scriptStatusCallbacks.insert_or_assign(key, callbackList); + + return Lifetime([this, key, idx](){ + std::unique_lock lk2(scriptStatusMutex); + auto callbackList = scriptStatusCallbacks.find(key); + if(callbackList != scriptStatusCallbacks.end()) + callbackList->second[idx] = ScriptStatusCallback(); + stopScriptStatusThreadIfNecessary(std::move(lk2)); + }); +} + +void Device::stopScriptStatusThreadIfNecessary(std::unique_lock lk) +{ + for(const auto& callbackList : scriptStatusCallbacks) + { + for(const auto& callback : callbackList.second) + { + if(callback) + return; + } + } + + stopScriptStatusThread = true; + lk.unlock(); + stopScriptStatusCv.notify_all(); + scriptStatusThread.join(); + scriptStatusThread = std::thread(); +} + Lifetime Device::suppressDisconnects() { std::lock_guard lk(heartbeatMutex); heartbeatSuppressedByUser++; diff --git a/include/icsneo/communication/command.h b/include/icsneo/communication/command.h index 568db33..b671b93 100644 --- a/include/icsneo/communication/command.h +++ b/include/icsneo/communication/command.h @@ -10,6 +10,8 @@ enum class Command : uint8_t { EnableNetworkCommunicationEx = 0x08, NeoReadMemory = 0x40, NeoWriteMemory = 0x41, + ClearCoreMini = 0x42, + LoadCoreMini = 0x43, RequestSerialNumber = 0xA1, GetMainVersion = 0xA3, // Previously known as RED_CMD_APP_VERSION_REQ SetSettings = 0xA4, // Previously known as RED_CMD_SET_BAUD_REQ, follow up with SaveSettings to write to EEPROM @@ -25,9 +27,11 @@ enum class Command : uint8_t { RequestBitSmash = 0xDC, // Previously known as RED_CMD_CM_BITSMASH WiVICommand = 0xDD, // Previously known as RED_CMD_WIVI_COMM GetVBattReq = 0xDF, // Previously known as RED_CMD_VBATT_REQUEST + ScriptStatus = 0xE0, // Previously known as RED_CMD_SCRIPT_STATUS MiscControl = 0xE7, Extended = 0xF0, FlexRayControl = 0xF3, + CoreMiniPreload = 0xF4, // Previously known as RED_CMD_COREMINI_PRELOAD PHYControlRegisters = 0xEF }; diff --git a/include/icsneo/communication/message/a2bmessage.h b/include/icsneo/communication/message/a2bmessage.h new file mode 100644 index 0000000..5082990 --- /dev/null +++ b/include/icsneo/communication/message/a2bmessage.h @@ -0,0 +1,140 @@ +#ifndef __A2BMESSAGE_H_ +#define __A2BMESSAGE_H_ + +#ifdef __cplusplus + +#include "icsneo/communication/message/message.h" + +#define A2BMESSAGE_UPSTREAM 1 +#define A2BMESSAGE_DOWNSTREAM 0 + +#define A2BPCM_L16 16 +#define A2BPCM_L24 24 + +#define A2BPCM_SAMPLERATE_44100 44100 +#define A2BPCM_SAMPLERATE_48000 48000 + +namespace icsneo +{ + +typedef uint32_t A2BPCMSample; +typedef std::vector ChannelBuffer; + +class A2BMessage : public Frame +{ +public: + enum class A2BDirection : uint8_t + { + DownStream = 0, + UpStream = 1 + }; + + A2BMessage() = delete; + + A2BMessage(uint8_t bitDepth, uint8_t bytesPerSample, uint8_t numChannels, bool monitor) : + mBitDepth(bitDepth), + mBytesPerSample(bytesPerSample), + mMonitor(monitor) + { + downstream.resize(numChannels); + upstream.resize(numChannels); + } + + void addSample(A2BPCMSample &&sample, A2BDirection dir, uint8_t channel) + { + if(dir == A2BDirection::DownStream) + { + downstream[channel].push_back(std::move(sample)); + } + else + { + upstream[channel].push_back(std::move(sample)); + } + totalSamples++; + } + + const A2BPCMSample *getSamples(A2BDirection dir, uint8_t channel) const + { + if(channel >= getNumChannels()) + { + return nullptr; + } + + if(dir == A2BDirection::DownStream) + { + return downstream[channel].data(); + } + return upstream[channel].data(); + } + + std::optional getSample(A2BDirection dir, uint8_t channel, uint32_t sampleIndex) const + { + const A2BPCMSample *samples = getSamples(dir, channel); + auto numSamplesInChannel = getNumSamplesInChannel(dir, channel); + + if( + samples == nullptr || + sampleIndex >= numSamplesInChannel.value_or(0) + ) + { + return std::nullopt; + } + + return samples[sampleIndex]; + } + + std::optional getNumSamplesInChannel(A2BDirection dir, uint8_t channel) const + { + if(channel >= getNumChannels()) + { + return std::nullopt; + } + + if(dir == A2BDirection::DownStream) + { + return downstream[channel].size(); + } + + return upstream[channel].size(); + } + + size_t getNumSamples() const + { + return totalSamples; + } + + uint8_t getNumChannels() const + { + return static_cast(downstream.size()); + } + + uint8_t getBitDepth() const + { + return mBitDepth; + } + + uint8_t getBytesPerSample() const + { + return mBytesPerSample; + } + + bool isMonitor() const + { + return mMonitor; + } + +private: + std::vector downstream; + std::vector upstream; + size_t totalSamples = 0; + + uint8_t mBitDepth; + uint8_t mBytesPerSample; + bool mMonitor; +}; + +} + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/communication/message/message.h b/include/icsneo/communication/message/message.h index 13ac397..592025c 100644 --- a/include/icsneo/communication/message/message.h +++ b/include/icsneo/communication/message/message.h @@ -30,6 +30,7 @@ public: LogicalDiskInfo = 0x8008, ExtendedResponse = 0x8009, WiVICommandResponse = 0x800a, + ScriptStatus = 0x800b, }; Message(Type t) : type(t) {} diff --git a/include/icsneo/communication/message/scriptstatusmessage.h b/include/icsneo/communication/message/scriptstatusmessage.h new file mode 100644 index 0000000..17e072f --- /dev/null +++ b/include/icsneo/communication/message/scriptstatusmessage.h @@ -0,0 +1,36 @@ +#ifndef __SCRIPTSTATUSMESSAGE_H +#define __SCRIPTSTATUSMESSAGE_H + +#ifdef __cplusplus + +#include "icsneo/communication/message/message.h" +#include "icsneo/communication/packet/scriptstatuspacket.h" + +namespace icsneo +{ +//Response to Command::ScriptStatus +class ScriptStatusMessage : public Message { +public: + ScriptStatusMessage() : Message( Message::Type::ScriptStatus ) {} + + bool isCoreminiRunning = false; + uint32_t sectorOverflows = 0; + uint32_t numRemainingSectorBuffers = 0; + int32_t lastSector = 0; + int32_t readBinSize = 0; + int32_t minSector = 0; + int32_t maxSector = 0; + int32_t currentSector = 0; + uint64_t coreminiCreateTime = 0; + uint16_t fileChecksum = 0; + uint16_t coreminiVersion = 0; + uint16_t coreminiHeaderSize = 0; + uint8_t diagnosticErrorCode = 0; + uint8_t diagnosticErrorCodeCount = 0; + uint16_t maxCoreminiSizeKB = 0; +}; +} + +#endif // __cplusplus + +#endif //__SCRIPTSTATUSMESSAGE_H diff --git a/include/icsneo/communication/network.h b/include/icsneo/communication/network.h index b6eb1a8..655b00c 100644 --- a/include/icsneo/communication/network.h +++ b/include/icsneo/communication/network.h @@ -119,9 +119,11 @@ public: LSFTCAN2 = 99, LogicalDiskInfo = 187, WiVICommand = 221, + ScriptStatus = 224, EthPHYControl = 239, ExtendedCommand = 240, FlexRayControl = 243, + CoreMiniPreLoad = 244, HW_COM_Latency_Test = 512, DeviceStatus = 513, UDP = 514, @@ -147,6 +149,7 @@ public: SWCAN = 8, ISO9141 = 9, I2C = 10, + A2B = 11, Any = 0xFE, // Never actually set as type, but used as flag for filtering Other = 0xFF }; @@ -234,6 +237,8 @@ public: return "Single Wire CAN"; case Type::I2C: return "I²C"; + case Type::A2B: + return "A2B"; case Type::Invalid: default: return "Invalid Type"; @@ -278,7 +283,9 @@ public: case NetID::ReadSettings: case NetID::LogicalDiskInfo: case NetID::WiVICommand: + case NetID::ScriptStatus: case NetID::EthPHYControl: + case NetID::CoreMiniPreLoad: case NetID::ExtendedCommand: case NetID::NeoMemorySDRead: case NetID::NeoMemoryWriteDone: @@ -318,6 +325,9 @@ public: case NetID::I2C3: case NetID::I2C4: return Type::I2C; + case NetID::A2B1: + case NetID::A2B2: + return Type::A2B; default: return Type::Other; } @@ -520,6 +530,10 @@ public: return "Logical Disk Information"; case NetID::WiVICommand: return "WiVI Command"; + case NetID::ScriptStatus: + return "Script Status"; + case NetID::CoreMiniPreLoad: + return "CoreMini PreLoad"; case NetID::EthPHYControl: return "Ethernet PHY Register Control"; case NetID::ExtendedCommand: @@ -928,9 +942,11 @@ private: #define ICSNEO_NETID_LSFTCAN2 99 #define ICSNEO_NETID_LOGICAL_DISK_INFO 187 #define ICSNEO_NETID_WIVI_COMMAND 221 +#define ICSNEO_NETID_SCRIPT_STATUS 224 #define ICSNEO_NETID_ETH_PHY_CONTROL 239 #define ICSNEO_NETID_EXTENDED_COMMAND 240 #define ICSNEO_NETID_FLEXRAY_CONTROL 243 +#define ICSNEO_NETID_COREMINI_PRELOAD 244 #define ICSNEO_NETID_HW_COM_LATENCY_TEST 512 #define ICSNEO_NETID_DEVICE_STATUS 513 #define ICSNEO_NETID_UDP 514 diff --git a/include/icsneo/communication/packet/a2bpacket.h b/include/icsneo/communication/packet/a2bpacket.h new file mode 100644 index 0000000..da90bb3 --- /dev/null +++ b/include/icsneo/communication/packet/a2bpacket.h @@ -0,0 +1,44 @@ +#ifndef __A2BPACKET_H__ +#define __A2BPACKET_H__ + +#ifdef __cplusplus + +#include "icsneo/api/eventmanager.h" +#include +#include +#include +#include "icsneo/communication/message/message.h" + + + +namespace icsneo { + +typedef uint16_t icscm_bitfield; + +struct HardwareA2BPacket { + + static std::shared_ptr DecodeToMessage(const std::vector& bytestream); + + struct + { + // CxA2B + icscm_bitfield channelNum : 8; + icscm_bitfield channelSize16 : 1; + icscm_bitfield : 7; + + // CxA2B2 + icscm_bitfield monitor : 1; + icscm_bitfield txmsg : 1; + icscm_bitfield errIndicator : 1; + icscm_bitfield syncFrame : 1; + icscm_bitfield upstream : 1; + icscm_bitfield : 11; + icscm_bitfield rfu2; + } header; +}; + +} + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/communication/packet/scriptstatuspacket.h b/include/icsneo/communication/packet/scriptstatuspacket.h new file mode 100644 index 0000000..921f50f --- /dev/null +++ b/include/icsneo/communication/packet/scriptstatuspacket.h @@ -0,0 +1,86 @@ +#ifndef __SCRIPTSTATUSPACKET_H +#define __SCRIPTSTATUSPACKET_H + +#ifdef __cplusplus + +#include "icsneo/communication/packet.h" +#include +#include + +#pragma pack(push,2) +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union +#pragma warning(disable: 4200) // nonstandard extension used: zero-sized array in struct/union +#endif // _MSC_VER + +namespace icsneo +{ + +class ScriptStatusMessage; + +struct CoreMiniStatus +{ + uint32_t justReset : 1; + uint32_t communicationEnabled : 1; + uint32_t isRunning : 1; + uint32_t checksumFailed : 1; + uint32_t licenseFailed : 1; + uint32_t versionMismatch : 1; + uint32_t bootOff : 1; + uint32_t hardwareFailure : 1; // To check SRAM failure (for now) + uint32_t isPassiveConnect : 1; // Always zero. Set to one when neoVI connection is passive,i.e. no async traffic + uint32_t usbCommunicationEnabled : 1; // Set to one when USB Host PC has enabled communication. + uint32_t linuxCommunicationEnabled : 1; // Set to one when Android (Linux) has enabled communication. + uint32_t tooBig : 1; + uint32_t hidUsbState : 1; + uint32_t fpgaUsbState : 1; + uint32_t filesystem : 1; + uint32_t isEncrypted : 1; + uint32_t reserved : 16; +}; + +struct ScriptStatus +{ + static std::shared_ptr DecodeToMessage(const std::vector& bytestream); + + CoreMiniStatus status; + uint32_t sectorOverflows; + uint32_t numRemainingSectorBuffers; + int32_t lastSector; + int32_t readBinSize; + int32_t minSector; + int32_t maxSector; + int32_t currentSector; + uint32_t coreminiCreateTimeMsb; + uint32_t coreminiCreateTimeLsb; + uint16_t zero2; + uint16_t zero3; + uint16_t fileChecksum; + uint16_t coreminiVersion; + uint16_t coreminiHeaderSize; + uint16_t coreminiEngineMinVersion; // This firmware min version + uint16_t coreminiScriptMinEngineVers; // Current loaded script's min engine requirements. + uint8_t eolResults1; + uint8_t eolResults2; + uint8_t eolResults3; + uint8_t zero6; + uint8_t diagErrCode; + uint8_t diagErrCodeCount; + /* FPGA version Fpga reports to HID over SPI */ + uint8_t spiFpgaVers1; + uint8_t spiFpgaVers2; + uint16_t maxCoreminiSizeKB; + uint16_t maxCoreminiFlashSizeKB; +}; + +} // namespace icsneo + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER +#pragma pack(pop) + +#endif // __cplusplus + +#endif //__SCRIPTSTATUSPACKET_H diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index 7eebf50..c7f8c98 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "icsneo/api/eventmanager.h" #include "icsneo/api/lifetime.h" #include "icsneo/device/neodevice.h" @@ -29,6 +30,7 @@ #include "icsneo/communication/io.h" #include "icsneo/communication/message/resetstatusmessage.h" #include "icsneo/communication/message/wiviresponsemessage.h" +#include "icsneo/communication/message/scriptstatusmessage.h" #include "icsneo/device/extensions/flexray/controller.h" #include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h" #include "icsneo/communication/message/ethphymessage.h" @@ -125,6 +127,19 @@ public: virtual bool goOnline(); virtual bool goOffline(); + enum class PreloadReturn : uint8_t + { + Pending, + Ok, + OKEncrypted, + NoScript, + }; + + int8_t prepareScriptLoad(); + bool startScript(); + bool stopScript(); + bool clearScript(); + // Message polling related functions bool enableMessagePolling(); bool disableMessagePolling(); @@ -332,6 +347,129 @@ public: */ bool allowSleep(bool remoteWakeup = false); + enum class ScriptStatus { + CoreMiniRunning = 0, + SectorOverflow = 1, + RemainingSectors = 2, + LastSector = 3, + ReadBinSize = 4, + MinSector = 5, + MaxSector = 6, + CurrentSector = 7, + CoreMiniCreateTime = 8, + FileChecksum = 9, + CoreMiniVersion = 10, + CoreMiniHeaderSize = 11, + DiagnosticErrorCode = 12, + DiagnosticErrorCodeCount = 13, + MaxCoreMiniSize = 14, + Logging = 16, + }; + + typedef std::function< void(uint64_t value) > ScriptStatusCallback; + + /** + * Get all current script status values + */ + std::shared_ptr getScriptStatus() const; + + /** + * Add a callback to be called when VSSAL script running state changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addCoreMiniRunningCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CoreMiniRunning, std::move(cb)); } + + /** + * Add a callback to be called when the number of times a sector was dropped due to lack of space + * in firmware's filesystem buffer changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addSectorOverflowsCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::SectorOverflow, std::move(cb)); } + + /** + * Add a callback to be called when number of sectors of space left in firmware's local file system buffer changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addNumberRemainingSectorsCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::RemainingSectors, std::move(cb)); } + + /** + * Add a callback to be called when last sector that was written to changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addLastSectorCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::LastSector, std::move(cb)); } + + /** + * Add a callback to be called when the size of the ReadBin changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addReadBinSizeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::ReadBinSize, std::move(cb)); } + + /** + * Add a callback to be called when the first sector address of logged data changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addMinSectorCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::MinSector, std::move(cb)); } + + /** + * Add a callback to be called when the last sector address of logged data changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addMaxSectorCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::MaxSector, std::move(cb)); } + + /** + * Add a callback to be called when the sector that is about to be written changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addCurrentSectorCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CurrentSector, std::move(cb)); } + + /** + * Add a callback to be called when the VSSAL script create time changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addCoreMiniCreateTimeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CoreMiniCreateTime, std::move(cb)); } + + /** + * Add a callback to be called when the VSSAL script checksum changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addFileChecksumCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::FileChecksum, std::move(cb)); } + + /** + * Add a callback to be called when the VSSAL script version changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addCoreMiniVersionCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CoreMiniVersion, std::move(cb)); } + + /** + * Add a callback to be called when the VSSAL script header size changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addCoreMiniHeaderSizeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CoreMiniHeaderSize, std::move(cb)); } + + /** + * Add a callback to be called when the firmware diagnostic error code changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addDiagnosticErrorCodeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::DiagnosticErrorCode, std::move(cb)); } + + /** + * Add a callback to be called when the firmware diagnostic error code count changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addDiagnosticErrorCodeCountCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::DiagnosticErrorCodeCount, std::move(cb)); } + + /** + * Add a callback to be called when the maximum size a VSSAL script can be changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addMaxCoreMiniSizeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::MaxCoreMiniSize, std::move(cb)); } + + /** + * Add a callback to be called when the device logging state changes + */ + NODISCARD("If the Lifetime is not held, the callback will be immediately removed") + Lifetime addLoggingCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::Logging, std::move(cb)); } + virtual std::vector> getFlexRayControllers() const { return {}; } void addExtension(std::shared_ptr&& extension); @@ -441,8 +579,8 @@ protected: } template - std::unique_ptr makeSettings(std::shared_ptr com) { - return std::unique_ptr(new Settings(com)); + std::unique_ptr makeSettings(std::shared_ptr comm) { + return std::unique_ptr(new Settings(comm)); } virtual void setupSettings(IDeviceSettings&) {} @@ -529,6 +667,20 @@ private: std::vector< std::pair > sleepRequestedCallbacks; void wiviThreadBody(); void stopWiVIThreadIfNecessary(std::unique_lock lk); + + //Script status + std::atomic stopScriptStatusThread{false}; + std::condition_variable stopScriptStatusCv; + mutable std::mutex scriptStatusMutex; + std::thread scriptStatusThread; + std::unordered_map> scriptStatusCallbacks; + std::unordered_map scriptStatusValues; + Lifetime addScriptStatusCallback(ScriptStatus, ScriptStatusCallback); + bool updateScriptStatusValue(ScriptStatus, uint64_t newValue); + void notifyScriptStatusCallback(ScriptStatus, uint64_t); + void scriptStatusThreadBody(); + void stopScriptStatusThreadIfNecessary(std::unique_lock lk); + }; } diff --git a/include/icsneo/icsneocpp.h b/include/icsneo/icsneocpp.h index ed5636f..1f33c26 100644 --- a/include/icsneo/icsneocpp.h +++ b/include/icsneo/icsneocpp.h @@ -16,6 +16,7 @@ #include "icsneo/communication/message/iso9141message.h" #include "icsneo/communication/message/canerrorcountmessage.h" #include "icsneo/communication/message/ethphymessage.h" +#include "icsneo/communication/message/a2bmessage.h" namespace icsneo {