diff --git a/CMakeLists.txt b/CMakeLists.txt index c207b37..b69a0c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,6 +177,7 @@ set(SRC_FILES communication/packet/iso9141packet.cpp communication/packet/ethphyregpacket.cpp communication/packet/logicaldiskinfopacket.cpp + communication/packet/wivicommandpacket.cpp communication/decoder.cpp communication/encoder.cpp communication/ethernetpacketizer.cpp diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 060d2bd..91f2d6f 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -6,6 +6,7 @@ #include "icsneo/communication/message/canerrorcountmessage.h" #include "icsneo/communication/message/neoreadmemorysdmessage.h" #include "icsneo/communication/message/extendedresponsemessage.h" +#include "icsneo/communication/message/wiviresponsemessage.h" #include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h" #include "icsneo/communication/command.h" #include "icsneo/device/device.h" @@ -16,6 +17,7 @@ #include "icsneo/communication/packet/versionpacket.h" #include "icsneo/communication/packet/ethphyregpacket.h" #include "icsneo/communication/packet/logicaldiskinfopacket.h" +#include "icsneo/communication/packet/wivicommandpacket.h" #include using namespace icsneo; @@ -299,6 +301,14 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata); + if(!result) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning); + return false; + } + return true; + } case Network::NetID::EthPHYControl: { result = HardwareEthernetPhyRegisterPacket::DecodeToMessage(packet->data, report); if(!result) { diff --git a/communication/packet/wivicommandpacket.cpp b/communication/packet/wivicommandpacket.cpp new file mode 100644 index 0000000..efe23d3 --- /dev/null +++ b/communication/packet/wivicommandpacket.cpp @@ -0,0 +1,87 @@ +#include "icsneo/communication/packet/wivicommandpacket.h" +#include "icsneo/communication/message/wiviresponsemessage.h" + +using namespace icsneo; + +std::shared_ptr WiVI::CommandPacket::DecodeToMessage(const std::vector& bytestream) { + if(bytestream.size() < sizeof(WiVI::CommandPacket::Header)) + return {}; + + auto msg = std::make_shared(); + const auto& header = *reinterpret_cast(bytestream.data()); + switch(header.cmd) { + case WiVI::Command::Result: { + if(bytestream.size() < sizeof(WiVI::CommandPacket::Result)) + return {}; + + if(bytestream.size() != sizeof(WiVI::CommandPacket::Header) + header.length) + return {}; + + const auto& decoded = *reinterpret_cast(bytestream.data()); + msg->responseTo = decoded.responseTo; + msg->success = decoded.result != 0; + break; + } + case WiVI::Command::GetSignal: { + // Use the SetSignal structure since it matches the response + if(bytestream.size() < sizeof(WiVI::CommandPacket::SetSignal)) + return {}; + + if(bytestream.size() != sizeof(WiVI::CommandPacket::SetSignal) + header.length) + return {}; + + const auto& setSignal = *reinterpret_cast(bytestream.data()); + msg->responseTo = WiVI::Command::GetSignal; + msg->value = setSignal.value.ValueInt32; + break; + } + case WiVI::Command::GetAll: { + if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll)) + return {}; + + if(bytestream.size() != sizeof(WiVI::CommandPacket::GetAll) + header.length) + return {}; + + const auto& getAll = *reinterpret_cast(bytestream.data()); + msg->responseTo = WiVI::Command::GetAll; + msg->info.emplace(); + msg->info->sleepRequest = getAll.sleepRequest; + msg->info->connectionTimeoutMinutes = getAll.connectionTimeoutMinutes; + + // Check that we have enough data for the capture infos + if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll) + (sizeof(WiVI::CaptureInfo) * getAll.numCaptureInfos)) + return {}; + + msg->info->captures.resize(getAll.numCaptureInfos); + for(uint16_t i = 0; i < getAll.numCaptureInfos; i++) + msg->info->captures[i] = getAll.captureInfos[i]; + break; + } + default: // Unknown command response + return {}; + } + return msg; +} + +std::vector WiVI::CommandPacket::GetSignal::Encode(WiVI::SignalType type) { + std::vector ret(sizeof(WiVI::CommandPacket::GetSignal)); + auto& frame = *reinterpret_cast(ret.data()); + + frame.header.cmd = WiVI::Command::GetSignal; + frame.header.length = sizeof(frame) - sizeof(frame.header); + frame.type = type; + + return ret; +} + +std::vector WiVI::CommandPacket::SetSignal::Encode(WiVI::SignalType type, CoreMiniFixedPointValue value) { + std::vector ret(sizeof(WiVI::CommandPacket::SetSignal)); + auto& frame = *reinterpret_cast(ret.data()); + + frame.header.cmd = WiVI::Command::SetSignal; + frame.header.length = sizeof(frame) - sizeof(frame.header); + frame.type = type; + frame.value = value; + + return ret; +} diff --git a/device/device.cpp b/device/device.cpp index 9500f8c..247103a 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -5,6 +5,8 @@ #include "icsneo/device/extensions/deviceextension.h" #include "icsneo/platform/optional.h" #include "icsneo/disk/fat.h" +#include "icsneo/communication/packet/wivicommandpacket.h" +#include "icsneo/communication/message/wiviresponsemessage.h" #include #include #include @@ -787,6 +789,38 @@ optional Device::getAnalogIO(IO type, size_t number /* = 1 */) { return nullopt; } +bool Device::allowSleep(bool remoteWakeup) { + if(!isOpen()) { + report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error); + return false; + } + + static std::shared_ptr filter = std::make_shared(Message::Type::WiVICommandResponse); + const auto generic = com->waitForMessageSync([this, remoteWakeup]() { + // VSSAL sets bit0 to indicate that it's waiting to sleep, then + // it waits for Wireless neoVI to acknowledge by clearing it. + // If we set bit1 at the same time we clear bit0, remote wakeup + // will be suppressed (assuming the device supported it in the + // first place) + return com->sendCommand(Command::WiVICommand, WiVI::CommandPacket::SetSignal::Encode( + WiVI::SignalType::SleepRequest, remoteWakeup ? 0 : 2 + )); + }, filter); + + if(!generic || generic->type != Message::Type::WiVICommandResponse) { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return false; + } + + const auto resp = std::static_pointer_cast(generic); + if(!resp->success || !resp->value.has_value()) { + report(APIEvent::Type::ValueNotYetPresent, APIEvent::Severity::Error); + return false; + } + + return *resp->value; +} + Lifetime Device::suppressDisconnects() { std::lock_guard lk(heartbeatMutex); heartbeatSuppressedByUser++; diff --git a/include/icsneo/communication/command.h b/include/icsneo/communication/command.h index 975f67e..568db33 100644 --- a/include/icsneo/communication/command.h +++ b/include/icsneo/communication/command.h @@ -23,6 +23,7 @@ enum class Command : uint8_t { ReadSettings = 0xC7, // Previously known as 3G_READ_SETTINGS_EX SetVBattMonitor = 0xDB, // Previously known as RED_CMD_CM_VBATT_MONITOR 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 MiscControl = 0xE7, Extended = 0xF0, diff --git a/include/icsneo/communication/message/message.h b/include/icsneo/communication/message/message.h index 3710b44..13ac397 100644 --- a/include/icsneo/communication/message/message.h +++ b/include/icsneo/communication/message/message.h @@ -29,6 +29,7 @@ public: EthernetPhyRegister = 0x8007, LogicalDiskInfo = 0x8008, ExtendedResponse = 0x8009, + WiVICommandResponse = 0x800a, }; Message(Type t) : type(t) {} diff --git a/include/icsneo/communication/message/wiviresponsemessage.h b/include/icsneo/communication/message/wiviresponsemessage.h new file mode 100644 index 0000000..0e0c1bb --- /dev/null +++ b/include/icsneo/communication/message/wiviresponsemessage.h @@ -0,0 +1,38 @@ +#ifndef __WIVIRESPONSEMESSAGE_H_ +#define __WIVIRESPONSEMESSAGE_H_ + +#ifdef __cplusplus + +#include "icsneo/communication/message/message.h" +#include "icsneo/communication/command.h" +#include "icsneo/platform/optional.h" +#include "icsneo/communication/packet/wivicommandpacket.h" +#include + +namespace icsneo { + +namespace WiVI { + +struct Info { + uint8_t sleepRequest; + uint16_t connectionTimeoutMinutes; + std::vector captures; +}; + +// The response for Command::WiVICommand +class ResponseMessage : public Message { +public: + ResponseMessage() : Message(Message::Type::WiVICommandResponse) {} + bool success = true; + optional responseTo; + optional value; + optional info; +}; + +} // namespace WiVI + +} // namespace icsneo + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/communication/network.h b/include/icsneo/communication/network.h index 57f999d..71f99b8 100644 --- a/include/icsneo/communication/network.h +++ b/include/icsneo/communication/network.h @@ -118,6 +118,7 @@ public: LIN6 = 98, LSFTCAN2 = 99, LogicalDiskInfo = 187, + WiVICommand = 221, EthPHYControl = 239, ExtendedCommand = 240, FlexRayControl = 243, @@ -274,6 +275,7 @@ public: case NetID::Main51: case NetID::ReadSettings: case NetID::LogicalDiskInfo: + case NetID::WiVICommand: case NetID::EthPHYControl: case NetID::ExtendedCommand: case NetID::NeoMemorySDRead: @@ -514,6 +516,8 @@ public: return "LSFTCAN 2"; case NetID::LogicalDiskInfo: return "Logical Disk Information"; + case NetID::WiVICommand: + return "WiVI Command"; case NetID::EthPHYControl: return "Ethernet PHY Register Control"; case NetID::ExtendedCommand: @@ -917,6 +921,7 @@ private: #define ICSNEO_NETID_LIN6 98 #define ICSNEO_NETID_LSFTCAN2 99 #define ICSNEO_NETID_LOGICAL_DISK_INFO 187 +#define ICSNEO_NETID_WIVI_COMMAND 221 #define ICSNEO_NETID_ETH_PHY_CONTROL 239 #define ICSNEO_NETID_EXTENDED_COMMAND 240 #define ICSNEO_NETID_FLEXRAY_CONTROL 243 diff --git a/include/icsneo/communication/packet/wivicommandpacket.h b/include/icsneo/communication/packet/wivicommandpacket.h new file mode 100644 index 0000000..ff2fd1f --- /dev/null +++ b/include/icsneo/communication/packet/wivicommandpacket.h @@ -0,0 +1,131 @@ +#ifndef __WIVICOMMANDPACKET_H_ +#define __WIVICOMMANDPACKET_H_ + +#ifdef __cplusplus + +#include "icsneo/communication/packet.h" +#include +#include + +#pragma pack(push,2) + +namespace icsneo { + +union CoreMiniFixedPointValue { + CoreMiniFixedPointValue() {} + CoreMiniFixedPointValue(int32_t integralValue) { ValueInt32 = integralValue; } + + struct { + union { + uint32_t ValueFractionPart; + struct { + int16_t ValueInt16FractionLSB; + int16_t ValueInt16FractionMSB; + }; + }; + union { + int32_t ValueInt32; + struct { + int16_t ValueInt16; + int16_t ValueInt16PartMSB; + }; + struct{ + uint8_t ValueInt8; + uint8_t ValueInt8PartMSB; + int16_t ValueInt8Part16MSB; + }; + }; + }; + int64_t ValueLarge; +}; + +namespace WiVI { + +class ResponseMessage; // Forward declaration to avoid cyclic includes + +enum class Command : uint16_t { + GetAll = 0x0010, + ClearUploads = 0x0011, + SetSignal = 0x0012, + GetSignal = 0x0013, + Result = 0x0014, + GetPhysicalSignal = 0x0015, +}; + +enum class SignalType : uint16_t { // enumCoreMiniValueMiscValueType + WokeUpOnSMS = 0x0067, + AvailableDiskSpaceKB = 0x0069, + ManualTrigger = 0x006c, + SleepRequest = 0x006d, + ConnectionTimeout = 0x006e, + TimeSinceLastMessageMs = 0x006f, + UploadsPending = 0x0077, +}; + +struct Upload { + uint32_t startSector; + uint32_t endSector; + struct { + uint16_t pending : 1; + uint16_t started : 1; + uint16_t reserved : 14; + } flags; +}; + +struct CaptureInfo { + uint16_t captureBlockIndex; // What capture block is this for + struct { + uint16_t isPrePost : 1; + uint16_t isPreTime : 1; // Only valid if the capture block's bPrePostExtract is set + uint16_t uploadOverCellular : 1; + uint16_t uploadOverWiFi : 1; + uint16_t uploadStackSize : 3; // 0 => size of 1 + uint16_t uploadOverflow : 1; + uint16_t uploadPriority : 4; + uint16_t reserved : 4; + } flags; + // Only valid if the capture block's bPrePostExtract is set + uint32_t preTriggerSize; + Upload uploadStage; + Upload uploadStack[2]; +}; + +struct CommandPacket { + static std::shared_ptr DecodeToMessage(const std::vector& bytestream); + + struct Header { + WiVI::Command cmd; + uint16_t length; + }; + + struct Result { + Header header; + WiVI::Command responseTo; + uint16_t result; + }; + + struct GetSignal { + static std::vector Encode(WiVI::SignalType type); + + Header header; + WiVI::SignalType type; + }; + + struct SetSignal { + static std::vector Encode(WiVI::SignalType type, CoreMiniFixedPointValue value); + + Header header; + WiVI::SignalType type; + CoreMiniFixedPointValue value; + }; +}; + +} // namespace WiVI + +} // namespace icsneo + +#pragma pack(pop) + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index 2e439a8..4fcdab1 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -289,6 +289,8 @@ public: */ optional getAnalogIO(IO type, size_t number = 1); + bool allowSleep(bool remoteWakeup = false); + virtual std::vector> getFlexRayControllers() const { return {}; } void addExtension(std::shared_ptr&& extension);