diff --git a/.gitignore b/.gitignore index e907672..2d195a8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,7 @@ third-party/concurrentqueue/tests *.bak .vs .cache -*.wav \ No newline at end of file +*.wav +*.orig +examples/csharp/bin +examples/csharp/obj \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 812e942..45ac0a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -245,6 +245,7 @@ set(SRC_FILES communication/packet/wivicommandpacket.cpp communication/packet/i2cpacket.cpp communication/packet/linpacket.cpp + communication/packet/mdiopacket.cpp communication/packet/scriptstatuspacket.cpp communication/packet/componentversionpacket.cpp communication/packet/supportedfeaturespacket.cpp @@ -469,6 +470,7 @@ if(LIBICSNEO_BUILD_TESTS) test/i2cencoderdecodertest.cpp test/linencoderdecodertest.cpp test/a2bencoderdecodertest.cpp + test/mdioencoderdecodertest.cpp ) target_link_libraries(libicsneo-tests gtest gtest_main) diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 2df65e3..ecb6392 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -12,6 +12,7 @@ #include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h" #include "icsneo/communication/message/i2cmessage.h" #include "icsneo/communication/message/linmessage.h" +#include "icsneo/communication/message/mdiomessage.h" #include "icsneo/communication/command.h" #include "icsneo/device/device.h" #include "icsneo/communication/packet/canpacket.h" @@ -28,6 +29,7 @@ #include "icsneo/communication/packet/linpacket.h" #include "icsneo/communication/packet/componentversionpacket.h" #include "icsneo/communication/packet/supportedfeaturespacket.h" +#include "icsneo/communication/packet/mdiopacket.h" #include using namespace icsneo; @@ -166,6 +168,18 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrnetwork; return true; } + case Network::Type::MDIO: { + result = HardwareMDIOPacket::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 + } + + MDIOMessage& msg = *static_cast(result.get()); + msg.network = packet->network; + return true; + } case Network::Type::Internal: { switch(packet->network.getNetID()) { case Network::NetID::Reset_Status: { @@ -252,7 +266,7 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata); - return true; + return true; case ExtendedCommand::GetSupportedFeatures: result = SupportedFeaturesPacket::DecodeToMessage(packet->data); return true; @@ -262,7 +276,7 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptr(*packet); diff --git a/communication/encoder.cpp b/communication/encoder.cpp index 598cc85..2a4d514 100644 --- a/communication/encoder.cpp +++ b/communication/encoder.cpp @@ -10,6 +10,7 @@ #include "icsneo/communication/message/i2cmessage.h" #include "icsneo/communication/packet/a2bpacket.h" #include "icsneo/communication/packet/linpacket.h" +#include "icsneo/communication/packet/mdiopacket.h" using namespace icsneo; @@ -108,6 +109,18 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector& result, } break; } // End of Network::Type::LIN + case Network::Type::MDIO: { + auto mdiomsg = std::dynamic_pointer_cast(message); + if(!mdiomsg) { + report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error); + return false; + } + buffer = &result; + if(!HardwareMDIOPacket::EncodeFromMessage(*mdiomsg, result, report)) { + return false; + } + break; + } // End of Network::Type::MDIO default: report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error); return false; diff --git a/communication/packet/mdiopacket.cpp b/communication/packet/mdiopacket.cpp new file mode 100644 index 0000000..fbec6c6 --- /dev/null +++ b/communication/packet/mdiopacket.cpp @@ -0,0 +1,79 @@ +#include "icsneo/communication/packet/mdiopacket.h" + +namespace icsneo +{ + +const size_t HardwareMDIOPacket::mdioDataSize = 2; + +std::shared_ptr HardwareMDIOPacket::DecodeToMessage(const std::vector& bytestream) +{ + auto msg = std::make_shared(); + const HardwareMDIOPacket* packet = reinterpret_cast(bytestream.data()); + if((sizeof(HardwareMDIOPacket) != (bytestream.size())) || (packet->length != 0)) + { + return nullptr; + } + + msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast(packet->networkID)); + msg->clause = static_cast(packet->header.ST); + msg->direction = static_cast((packet->header.OP & 0x2) ? 1 : 0); + msg->isTXMsg = static_cast(packet->header.TRANSMIT & 0x01u); + msg->phyAddress = (packet->header.PHY_ADDR & 0x1Fu); + if (msg->clause == MDIOMessage::Clause::Clause45) + { // 16-bit register address + msg->devAddress = (packet->header.C45_DEVTYPE & 0x1Fu); + msg->regAddress = (packet->header.REG_ADDR & 0xFFFFu); + } + else + { // 5-bit register address + msg->devAddress = 0; + msg->regAddress = (packet->header.REG_ADDR & 0x1Fu); + } + + msg->isTXMsg = static_cast(packet->header.TRANSMIT & 0x01u); + msg->txTimeout = static_cast(packet->header.ERR_TIMEOUT & 0x01u); + msg->txAborted = static_cast(packet->header.ERR_JOB_CANCELLED & 0x01u); + msg->txInvalidBus = static_cast(packet->header.ERR_INVALID_BUS & 0x01u); + msg->txInvalidPhyAddr = static_cast(packet->header.ERR_INVALID_PHYADDR & 0x01u); + msg->txInvalidRegAddr = static_cast(packet->header.ERR_INVALID_REGADDR & 0x01u); + msg->txInvalidClause = static_cast(packet->header.ERR_UNSUPPORTED_CLAUSE & 0x01u); + msg->txInvalidOpcode = static_cast(packet->header.ERR_UNSUPPORTED_OPCODE & 0x01u); + //We don't care about 0xTRB0Dx in this case... + //copy 0xTRB0STAT even though we likely won't use it either + msg->description = packet->stats; + msg->timestamp = (packet->timestamp & (0x7FFFFFFFFFFFFFFFull)); + + std::copy(packet->data, packet->data + mdioDataSize, std::back_inserter(msg->data)); + + return msg; +} + +bool HardwareMDIOPacket::EncodeFromMessage(const MDIOMessage& message, std::vector& bytestream, const device_eventhandler_t& report) +{ + const size_t numDataBytes = message.data.size(); + if(mdioDataSize < numDataBytes) + { + report(APIEvent::Type::MDIOMessageExceedsMaxLength, APIEvent::Severity::Error); + return false; + } + + uint8_t st = (message.clause == MDIOMessage::Clause::Clause45) ? 0x0 : 0x1; + uint8_t op = (message.direction == MDIOMessage::Direction::Read) ? 0x2 : 0x1; + uint8_t phyAddr = message.phyAddress & 0x1F; + uint16_t regAddr = (message.clause == MDIOMessage::Clause::Clause45) ? message.regAddress : message.regAddress & 0x1F; + uint8_t c45DevType = (message.clause == MDIOMessage::Clause::Clause45) ? message.devAddress & 0x1F : 0; + + bytestream.push_back(static_cast((message.description >> 8) & 0xFF)); // MSB first + bytestream.push_back(static_cast(message.description & 0xFF)); // LSB + bytestream.push_back(op); // opcode + bytestream.push_back(st); // st (clause) + bytestream.push_back(phyAddr); // st (clause) + bytestream.push_back(c45DevType); // clause 45 device type + bytestream.push_back(regAddr & 0xFF); // reg addr LSB + bytestream.push_back((regAddr >> 8) & 0xFF); // reg addr MSB + + std::copy(message.data.begin(), message.data.end(), std::back_inserter(bytestream)); + return true; +} + +} \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index de44741..7329681 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -6,6 +6,7 @@ option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interacti option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON) option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON) option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON) +option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON) # Disabled until we properly build these in-tree # option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF) @@ -43,6 +44,10 @@ if(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE) add_subdirectory(cpp/coremini) endif() +if(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE) + add_subdirectory(cpp/mdio) +endif() + # if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE) # add_subdirectory(csharp) # endif() diff --git a/examples/cpp/mdio/CMakeLists.txt b/examples/cpp/mdio/CMakeLists.txt new file mode 100644 index 0000000..c3b0815 --- /dev/null +++ b/examples/cpp/mdio/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.2) +project(libicsneocpp-mdio VERSION 0.1.0) + +set(CMAKE_CXX_STANDARD_REQUIRED 11) + +include(GNUInstallDirs) + +# Add an include directory like so if desired +#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + +# Enable Warnings +if(MSVC) + # Force to always compile with W4 + if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() +else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas") +endif() + +# Add libicsneo, usually a git submodule within your project works well +#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo) + +add_executable(libicsneocpp-mdio src/MDIOExample.cpp) +target_link_libraries(libicsneocpp-mdio icsneocpp) \ No newline at end of file diff --git a/examples/cpp/mdio/src/MDIOExample.cpp b/examples/cpp/mdio/src/MDIOExample.cpp new file mode 100644 index 0000000..5d64a98 --- /dev/null +++ b/examples/cpp/mdio/src/MDIOExample.cpp @@ -0,0 +1,221 @@ +#include +#include +#include +#include + +#include "icsneo/icsneocpp.h" +#include "icsneo/communication/message/mdiomessage.h" + +/* Note: This example requires MDIO 1 channels to be connected on the device, and uses RADGalaxy or RADStar2 */ + +char getCharInput(std::vector allowed) +{ + bool found = false; + std::string input; + + while (!found) + { + std::cin >> input; + + if (input.length() == 1) + { + for (char compare : allowed) + { + if (compare == input.c_str()[0]) + { + found = true; + break; + } + } + } + + if (!found) + { + std::cout << "Input did not match expected options. Please try again." << std::endl; + std::cout << "" << std::endl; + } + } + + return input.c_str()[0]; +} + +int main() +{ + // Print version + std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl; + std::cout << "\nFinding devices... " << std::flush; + auto devices = icsneo::FindAllDevices(); // This is type std::vector> + // You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective + std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; + + // List off the devices + for (auto &device : devices) + std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl; + std::cout << std::endl; + + for (auto &device : devices) + { + std::cout << "Connecting to " << device->describe() << "... "; + bool ret = device->open(); + if (!ret) + { // Failed to open + std::cout << "FAIL" << std::endl; + std::cout << icsneo::GetLastError() << std::endl + << std::endl; + continue; + } + std::cout << "OK" << std::endl; + + // The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us + std::cout << "\tGoing online... "; + ret = device->goOnline(); + if (!ret) + { + std::cout << "FAIL" << std::endl; + device->close(); + continue; + } + std::cout << "OK" << std::endl; + + // A real application would just check the result of icsneo_goOnline() rather than calling this + // This function is intended to be called later on if needed + std::cout << "\tChecking online status... "; + ret = device->isOnline(); + if (!ret) + { + std::cout << "FAIL\n" + << std::endl; + device->close(); + continue; + } + std::cout << "OK" << std::endl; + + auto handler = device->addMessageCallback(std::make_shared([&](std::shared_ptr message) + { + if(icsneo::Message::Type::Frame == message->type) { + auto frame = std::static_pointer_cast(message); + if(icsneo::Network::Type::MDIO == frame->network.getType()) { + auto msg = std::static_pointer_cast(message); + std::cout << msg->network << " " << ((msg->isTXMsg)? "TX" : "RX") << " frame\n"; + std::cout << "Clause: " << ((msg->clause == icsneo::MDIOMessage::Clause::Clause22) ? "22" : "45") << "\n"; + std::cout << "Direction: " << ((msg->direction == icsneo::MDIOMessage::Direction::Read) ? "Read" : "Write") << "\n"; + std::cout << std::hex << "PHY Address: 0x" << static_cast(msg->phyAddress) << "\n"; + if(msg->clause == icsneo::MDIOMessage::Clause::Clause45) + std::cout << std::hex << "Dev Address: 0x" << static_cast(msg->devAddress) << "\n"; + std::cout << std::hex << "Reg Address: 0x" << static_cast(msg->regAddress) << "\n"; + std::cout << "Data: \n"; + for(uint8_t& each : msg->data) { + std::cout << std::hex << "0x" << static_cast(each) << " "; + } + std::cout << "\n"; + } + } })); + + /* + * BCM89810 on RADGalaxy/RADGigastar + */ + // We can transmit messages to read the PHY ID of BCM89810 PHY + std::cout << "\tTransmitting a MDIO request to read ID on BCM89810...\n"; + auto mdio_r = std::make_shared(); + mdio_r->network = icsneo::Network::NetID::MDIO1; + mdio_r->phyAddress = 0x00u; + mdio_r->regAddress = 0x02u; + mdio_r->direction = icsneo::MDIOMessage::Direction::Read; + mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22; + ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO + std::cout << (ret ? "OK" : "FAIL") << std::endl; + + // We can transmit messages to write to arbitrary register + std::cout << "\tTransmitting a MDIO request to write register on BCM89810...\n"; + mdio_r = std::make_shared(); + mdio_r->network = icsneo::Network::NetID::MDIO1; + mdio_r->phyAddress = 0x00u; + mdio_r->regAddress = 0x1Bu; + mdio_r->data = {0xAA, 0xAF}; + mdio_r->direction = icsneo::MDIOMessage::Direction::Write; + mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22; + ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO + std::cout << (ret ? "OK" : "FAIL") << std::endl; + + // We can transmit messages to read back to arbitrary register + std::cout << "\tTransmitting a MDIO request to read register on BCM89810...\n"; + mdio_r = std::make_shared(); + mdio_r->network = icsneo::Network::NetID::MDIO1; + mdio_r->phyAddress = 0x00u; + mdio_r->regAddress = 0x1Bu; + mdio_r->direction = icsneo::MDIOMessage::Direction::Read; + mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22; + ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO + std::cout << (ret ? "OK" : "FAIL") << std::endl; + + /* + * 88Q2112 on RADGigastar, RADSupermoon, RADMoon2 + */ + // We can transmit messages to read the PHY ID of BCM89810 PHY + std::cout << "\tTransmitting a MDIO request to read ID on 88Q2112...\n"; + mdio_r = std::make_shared(); + mdio_r->network = icsneo::Network::NetID::MDIO1; + mdio_r->phyAddress = 0x06u; + mdio_r->devAddress = 0x01u; + mdio_r->regAddress = 0x0002u; + mdio_r->direction = icsneo::MDIOMessage::Direction::Read; + mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45; + ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO + std::cout << (ret ? "OK" : "FAIL") << std::endl; + + // We can transmit messages to write to arbitrary register + std::cout << "\tTransmitting a MDIO request to write register on 88Q2112...\n"; + mdio_r = std::make_shared(); + mdio_r->network = icsneo::Network::NetID::MDIO1; + mdio_r->phyAddress = 0x06u; + mdio_r->devAddress = 0x01u; + mdio_r->regAddress = 0x0902u; + mdio_r->data = {0xA3, 0x02}; + mdio_r->direction = icsneo::MDIOMessage::Direction::Write; + mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45; + ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO + std::cout << (ret ? "OK" : "FAIL") << std::endl; + + // We can transmit messages to read back to arbitrary register + std::cout << "\tTransmitting a MDIO request to read register on 88Q2112...\n"; + mdio_r = std::make_shared(); + mdio_r->network = icsneo::Network::NetID::MDIO1; + mdio_r->phyAddress = 0x06u; + mdio_r->devAddress = 0x01u; + mdio_r->regAddress = 0x0902u; + mdio_r->direction = icsneo::MDIOMessage::Direction::Read; + mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45; + ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO + std::cout << (ret ? "OK" : "FAIL") << std::endl; + + std::cout << "\n\n"; + + // Go offline, stop sending and receiving traffic + auto shutdown = [&]() + { + device->removeMessageCallback(handler); + std::cout << "\tGoing offline... "; + ret = device->goOffline(); + std::cout << (ret ? "OK" : "FAIL") << std::endl; + std::cout << "\tDisconnecting... "; + ret = device->close(); + std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl; + }; + + while (true) + { + char input = getCharInput(std::vector{'X', 'x'}); + switch (input) + { + case 'X': + case 'x': + shutdown(); + printf("Exiting program\n"); + return 0; + default: + break; + } + } + } + return 0; +} \ No newline at end of file diff --git a/include/icsneo/api/event.h b/include/icsneo/api/event.h index 4fcc282..3b9140b 100644 --- a/include/icsneo/api/event.h +++ b/include/icsneo/api/event.h @@ -111,6 +111,7 @@ public: ErrorSettingSocketOption = 0x3107, GetIfAddrsError = 0x3108, SendToError = 0x3109, + MDIOMessageExceedsMaxLength = 0x3110, // FTD3XX FTOK = 0x4000, // placeholder @@ -214,4 +215,4 @@ public: #endif // __cplusplus -#endif \ No newline at end of file +#endif diff --git a/include/icsneo/communication/message/mdiomessage.h b/include/icsneo/communication/message/mdiomessage.h new file mode 100644 index 0000000..92960bc --- /dev/null +++ b/include/icsneo/communication/message/mdiomessage.h @@ -0,0 +1,41 @@ +#ifndef __MDIOMESSAGE_H_ +#define __MDIOMESSAGE_H_ + +#ifdef __cplusplus + +#include "icsneo/communication/message/message.h" + +namespace icsneo { + +class MDIOMessage : public Frame { +public: + enum class Clause : uint8_t { + Clause45 = 0, + Clause22 = 1 + }; + + enum class Direction : uint8_t { + Write = 0, + Read = 1 + }; + + bool isTXMsg = false; + bool txTimeout = false; + bool txAborted = false; + bool txInvalidBus = false; + bool txInvalidPhyAddr = false; + bool txInvalidRegAddr = false; + bool txInvalidClause = false; + bool txInvalidOpcode = false; + uint8_t phyAddress; + uint8_t devAddress; + uint16_t regAddress; + Direction direction; + Clause clause; +}; + +} + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/communication/network.h b/include/icsneo/communication/network.h index a69a33a..1e38eba 100644 --- a/include/icsneo/communication/network.h +++ b/include/icsneo/communication/network.h @@ -91,6 +91,7 @@ public: Ethernet_DAQ = 69, Data_To_Host = 70, TextAPI_To_Host = 71, + SPI1 = 72, OP_Ethernet6 = 73, Red_VBat = 74, OP_Ethernet7 = 75, @@ -146,7 +147,15 @@ public: DWCAN16 = 541, LIN7 = 542, LIN8 = 543, - RED_SET_RTC = 544, + SPI2 = 544, + MDIO1 = 545, + MDIO2 = 546, + MDIO3 = 547, + MDIO4 = 548, + MDIO5 = 549, + MDIO6 = 550, + MDIO7 = 551, + MDIO8 = 552, Any = 0xfffe, // Never actually set as type, but used as flag for filtering Invalid = 0xffff }; @@ -163,6 +172,8 @@ public: ISO9141 = 9, I2C = 10, A2B = 11, + SPI = 12, + MDIO = 13, Any = 0xFE, // Never actually set as type, but used as flag for filtering Other = 0xFF }; @@ -239,6 +250,16 @@ public: DWCAN16 = 69, LIN7 = 70, LIN8 = 71, + SPI1 = 72, + SPI2 = 73, + MDIO1 = 75, + MDIO2 = 76, + MDIO3 = 77, + MDIO4 = 78, + MDIO5 = 79, + MDIO6 = 80, + MDIO7 = 81, + MDIO8 = 82, }; static const char* GetTypeString(Type type) { switch(type) { @@ -266,6 +287,10 @@ public: return "I²C"; case Type::A2B: return "A2B"; + case Type::SPI: + return "SPI"; + case Type::MDIO: + return "MDIO"; case Type::Invalid: default: return "Invalid Type"; @@ -327,7 +352,6 @@ public: case NetID::NeoMemorySDRead: case NetID::NeoMemoryWriteDone: case NetID::RED_GET_RTC: - case NetID::RED_SET_RTC: return Type::Internal; case NetID::Invalid: case NetID::Any: @@ -368,6 +392,18 @@ public: case NetID::A2B1: case NetID::A2B2: return Type::A2B; + case NetID::SPI1: + case NetID::SPI2: + return Type::SPI; + case NetID::MDIO1: + case NetID::MDIO2: + case NetID::MDIO3: + case NetID::MDIO4: + case NetID::MDIO5: + case NetID::MDIO6: + case NetID::MDIO7: + case NetID::MDIO8: + return Type::MDIO; default: return Type::Other; } @@ -456,8 +492,6 @@ public: return "RED_HARDWARE_EXCEP"; case NetID::RED_GET_RTC: return "RED_GET_RTC"; - case NetID::RED_SET_RTC: - return "RED_SET_RTC"; case NetID::ISO9141_3: return "ISO 9141 3"; case NetID::HSCAN2: @@ -626,6 +660,26 @@ public: return "LIN 08"; case NetID::WBMS: return "WBMS"; + case NetID::SPI1: + return "SPI 1"; + case NetID::SPI2: + return "SPI 2"; + case NetID::MDIO1: + return "MDIO 1"; + case NetID::MDIO2: + return "MDIO 2"; + case NetID::MDIO3: + return "MDIO 3"; + case NetID::MDIO4: + return "MDIO 4"; + case NetID::MDIO5: + return "MDIO 5"; + case NetID::MDIO6: + return "MDIO 6"; + case NetID::MDIO7: + return "MDIO 7"; + case NetID::MDIO8: + return "MDIO 8"; case NetID::Any: case NetID::Invalid: break; @@ -778,6 +832,26 @@ public: return CoreMini::LIN7; case NetID::LIN8: return CoreMini::LIN8; + case NetID::SPI1: + return CoreMini::SPI1; + case NetID::SPI2: + return CoreMini::SPI2; + case NetID::MDIO1: + return CoreMini::MDIO1; + case NetID::MDIO2: + return CoreMini::MDIO2; + case NetID::MDIO3: + return CoreMini::MDIO3; + case NetID::MDIO4: + return CoreMini::MDIO4; + case NetID::MDIO5: + return CoreMini::MDIO5; + case NetID::MDIO6: + return CoreMini::MDIO6; + case NetID::MDIO7: + return CoreMini::MDIO7; + case NetID::MDIO8: + return CoreMini::MDIO8; default: return std::nullopt; } @@ -928,6 +1002,27 @@ public: return NetID::LIN7; case CoreMini::LIN8: return NetID::LIN8; + case CoreMini::SPI1: + return NetID::SPI1; + case CoreMini::SPI2: + return NetID::SPI2; + case CoreMini::MDIO1: + return NetID::MDIO1; + case CoreMini::MDIO2: + return NetID::MDIO2; + case CoreMini::MDIO3: + return NetID::MDIO3; + case CoreMini::MDIO4: + return NetID::MDIO4; + case CoreMini::MDIO5: + return NetID::MDIO5; + case CoreMini::MDIO6: + return NetID::MDIO6; + case CoreMini::MDIO7: + return NetID::MDIO7; + case CoreMini::MDIO8: + return NetID::MDIO8; + } return NetID::Invalid; // Should be unreachable, the compiler should warn about new CoreMini IDs } @@ -1095,4 +1190,4 @@ private: #define ICSNEO_NETWORK_TYPE_OTHER 0xFF #endif -#endif \ No newline at end of file +#endif diff --git a/include/icsneo/communication/packet/mdiopacket.h b/include/icsneo/communication/packet/mdiopacket.h new file mode 100644 index 0000000..2a73446 --- /dev/null +++ b/include/icsneo/communication/packet/mdiopacket.h @@ -0,0 +1,61 @@ +#ifndef __MDIOPACKET_H__ +#define __MDIOPACKET_H__ + +#ifdef __cplusplus + +#include "icsneo/communication/message/mdiomessage.h" +#include "icsneo/api/eventmanager.h" +#include "icsneo/communication/packetizer.h" +#include "icsneo/communication/network.h" +#include +#include + +namespace icsneo { + +#pragma pack(push, 2) + +struct HardwareMDIOPacket { + + static std::shared_ptr DecodeToMessage(const std::vector& bytestream); + static bool EncodeFromMessage(const MDIOMessage& message, std::vector& bytestream, const device_eventhandler_t& report); + + struct { + // C1xMDIO + uint16_t PHY_ADDR : 5; // mdio phy address, 5-bit + uint16_t ST : 2; // Start of Frame (Clause 22/45) + uint16_t OP : 2; // OP Code (Read/Write) + uint16_t ADVANCED_MODE : 1; // future? + uint16_t TRANSMIT : 1; // This is a TX message + uint16_t C45_DEVTYPE : 5; + + // C2xMDIO + uint16_t ERR_JOB_CANCELLED : 1; // error flag + uint16_t ERR_TIMEOUT : 1; // error flag + uint16_t ERR_INVALID_BUS : 1; // error flag + uint16_t ERR_INVALID_PHYADDR : 1; // error flag + uint16_t ERR_INVALID_REGADDR : 1; // error flag + uint16_t ERR_UNSUPPORTED_CLAUSE : 1; // error flag + uint16_t ERR_UNSUPPORTED_OPCODE : 1; // error flag + uint16_t ERR_OVERFLOW : 1; // error flag + uint16_t : 8; + + // C3xMDIO + uint16_t REG_ADDR; + } header; + + uint8_t data[8]; + uint16_t stats; + uint64_t timestamp; + uint16_t networkID; + uint16_t length; + + static const size_t mdioDataSize; +}; + +#pragma pack(pop) + +} + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/device/tree/rada2b/rada2b.h b/include/icsneo/device/tree/rada2b/rada2b.h index 862e0f3..8a39ac0 100644 --- a/include/icsneo/device/tree/rada2b/rada2b.h +++ b/include/icsneo/device/tree/rada2b/rada2b.h @@ -32,7 +32,9 @@ public: Network::NetID::A2B2, Network::NetID::I2C, - Network::NetID::I2C2 + Network::NetID::I2C2, + + Network::NetID::MDIO1, }; return supportedNetworks; } diff --git a/include/icsneo/device/tree/radgalaxy/radgalaxy.h b/include/icsneo/device/tree/radgalaxy/radgalaxy.h index da7eeab..5de4c02 100644 --- a/include/icsneo/device/tree/radgalaxy/radgalaxy.h +++ b/include/icsneo/device/tree/radgalaxy/radgalaxy.h @@ -47,7 +47,13 @@ public: Network::NetID::OP_Ethernet9, Network::NetID::OP_Ethernet10, Network::NetID::OP_Ethernet11, - Network::NetID::OP_Ethernet12 + Network::NetID::OP_Ethernet12, + + Network::NetID::MDIO1, + Network::NetID::MDIO2, + Network::NetID::MDIO3, + Network::NetID::MDIO4, + Network::NetID::MDIO5, }; return supportedNetworks; } diff --git a/include/icsneo/device/tree/radgigastar/radgigastar.h b/include/icsneo/device/tree/radgigastar/radgigastar.h index 6a7e625..6be691b 100644 --- a/include/icsneo/device/tree/radgigastar/radgigastar.h +++ b/include/icsneo/device/tree/radgigastar/radgigastar.h @@ -65,6 +65,9 @@ protected: Network::NetID::I2C, Network::NetID::I2C2, Network::NetID::I2C3, + + Network::NetID::MDIO1, + Network::NetID::MDIO2, }; rxNetworks.insert(rxNetworks.end(), supportedRxNetworks.begin(), supportedRxNetworks.end()); } @@ -91,6 +94,9 @@ protected: Network::NetID::I2C, Network::NetID::I2C2, Network::NetID::I2C3, + + Network::NetID::MDIO1, + Network::NetID::MDIO2, }; txNetworks.insert(txNetworks.end(), supportedTxNetworks.begin(), supportedTxNetworks.end()); } diff --git a/include/icsneo/device/tree/radmars/radmars.h b/include/icsneo/device/tree/radmars/radmars.h index 9004e74..7e3ed19 100644 --- a/include/icsneo/device/tree/radmars/radmars.h +++ b/include/icsneo/device/tree/radmars/radmars.h @@ -60,6 +60,9 @@ protected: Network::NetID::FlexRay1b, Network::NetID::I2C, + + Network::NetID::MDIO1, + Network::NetID::MDIO2, }; rxNetworks.insert(rxNetworks.end(), supportedRxNetworks.begin(), supportedRxNetworks.end()); } @@ -84,6 +87,9 @@ protected: // FlexRay is Receive Only Network::NetID::I2C, + + Network::NetID::MDIO1, + Network::NetID::MDIO2, }; txNetworks.insert(txNetworks.end(), supportedTxNetworks.begin(), supportedTxNetworks.end()); } diff --git a/include/icsneo/device/tree/radmoon2/radmoon2base.h b/include/icsneo/device/tree/radmoon2/radmoon2base.h index 30eb928..6488700 100644 --- a/include/icsneo/device/tree/radmoon2/radmoon2base.h +++ b/include/icsneo/device/tree/radmoon2/radmoon2base.h @@ -16,6 +16,14 @@ public: APM1000E_CLK, // Clock Option and Keysight Branding }; + static const std::vector& GetSupportedNetworks() { + static std::vector supportedNetworks = { + Network::NetID::MDIO1, + Network::NetID::MDIO2, + }; + return supportedNetworks; + } + SKU getSKU() const { switch(getSerial().back()) { case 'A': @@ -66,6 +74,14 @@ protected: bool requiresVehiclePower() const override { return false; } + void setupSupportedRXNetworks(std::vector& rxNetworks) override { + for(auto& netid : GetSupportedNetworks()) + rxNetworks.emplace_back(netid); + } + + // The supported TX networks are the same as the supported RX networks for this device + void setupSupportedTXNetworks(std::vector& txNetworks) override { setupSupportedRXNetworks(txNetworks); } + std::optional getCoreminiStartAddressFlash() const override { return std::nullopt; } diff --git a/include/icsneo/device/tree/radstar2/radstar2.h b/include/icsneo/device/tree/radstar2/radstar2.h index 7e9b596..3081fd3 100644 --- a/include/icsneo/device/tree/radstar2/radstar2.h +++ b/include/icsneo/device/tree/radstar2/radstar2.h @@ -24,7 +24,10 @@ public: Network::NetID::Ethernet, Network::NetID::OP_Ethernet1, - Network::NetID::OP_Ethernet2 + Network::NetID::OP_Ethernet2, + + Network::NetID::MDIO1, + Network::NetID::MDIO2, }; return supportedNetworks; } diff --git a/include/icsneo/device/tree/radsupermoon/radsupermoon.h b/include/icsneo/device/tree/radsupermoon/radsupermoon.h index e7b00c2..cb03134 100644 --- a/include/icsneo/device/tree/radsupermoon/radsupermoon.h +++ b/include/icsneo/device/tree/radsupermoon/radsupermoon.h @@ -24,6 +24,8 @@ public: static std::vector supportedNetworks = { Network::NetID::Ethernet, Network::NetID::OP_Ethernet1, + Network::NetID::MDIO1, + Network::NetID::MDIO2, }; return supportedNetworks; } diff --git a/include/icsneo/icsneocpp.h b/include/icsneo/icsneocpp.h index 584260c..62134fc 100644 --- a/include/icsneo/icsneocpp.h +++ b/include/icsneo/icsneocpp.h @@ -19,6 +19,7 @@ #include "icsneo/communication/message/i2cmessage.h" #include "icsneo/communication/message/a2bmessage.h" #include "icsneo/communication/message/linmessage.h" +#include "icsneo/communication/message/mdiomessage.h" #include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h" diff --git a/test/mdioencoderdecodertest.cpp b/test/mdioencoderdecodertest.cpp new file mode 100644 index 0000000..532c6af --- /dev/null +++ b/test/mdioencoderdecodertest.cpp @@ -0,0 +1,227 @@ +#include "icsneo/icsneocpp.h" +#include "icsneo/communication/encoder.h" +#include "icsneo/communication/packet/mdiopacket.h" +#include "icsneo/communication/message/mdiomessage.h" +#include "icsneo/communication/packetizer.h" +#include "icsneo/api/eventmanager.h" +#include "gtest/gtest.h" +#include + +using namespace icsneo; + +class MDIOEncoderDecoderTest : public ::testing::Test { +protected: + void SetUp() override { + report = [](APIEvent::Type, APIEvent::Severity) { + // Unless caught by the test, the packetizer should not throw errors + EXPECT_TRUE(false); + }; + packetizer.emplace([this](APIEvent::Type t, APIEvent::Severity s) { + report(t, s); + }); + packetEncoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) { + report(t, s); + }); + packetDecoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) { + report(t, s); + }); + } + device_eventhandler_t report; + std::optional packetEncoder; + std::optional packetizer; + std::optional packetDecoder; + + std::vector testBytesClause22 = + {0xAA, 0x0C, 0x11, 0x00, 0x21, 0x02, 0xAB, 0xCD, + 0x01, 0x01, 0x18, 0x00, 0x14, 0x00, 0x56, 0x78}; + + std::vector testBytesClause45 = + {0xAA, 0x0C, 0x11, 0x00, 0x21, 0x02, 0xAB, 0xCD, + 0x02, 0x00, 0x06, 0x14, 0x34, 0x12, 0x56, 0x78}; + + std::vector testBytesClause22Mask = + {0xAA, 0x0C, 0x11, 0x00, 0x21, 0x02, 0xFF, 0xFF, + 0x01, 0x01, 0x1F, 0x00, 0x1F, 0x00, 0xFF, 0xFF}; + + std::vector testBytesClause45Mask = + {0xAA, 0x0C, 0x11, 0x00, 0x21, 0x02, 0xFF, 0xFF, + 0x02, 0x00, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF}; + + std::vector recvBytesClause22 = + { + 0xaa, 0x0c, // header + 0x22, 0x00, // length + 0x21, 0x02, // hw netid + 0x26, 0x0D, // word1 + 0x00, 0x00, // word2 + 0x14, 0x00, // word3 + 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data[8] + 0xCD, 0xAB, // stats + 0x97, 0x29, 0xe6, 0xfb, 0xc1, 0xfc, 0xb0, 0x80, // timestamp + 0x4A, 0x00, // netid + 0x00, 0x00, // length + }; + std::vector recvBytesClause45 = + { + 0xaa, 0x0c, // header + 0x22, 0x00, // length + 0x21, 0x02, // hw netid + 0x92, 0x1C, // word1 + 0xFF, 0x00, // word2 + 0x56, 0x14, // word3 + 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data[8] + 0xCD, 0xAB, // stats + 0x97, 0x29, 0xe6, 0xfb, 0xc1, 0xfc, 0xb0, 0x80, // timestamp + 0x4A, 0x00, // netid + 0x00, 0x00, // length + }; +}; + +TEST_F(MDIOEncoderDecoderTest, PacketEncoderClause22Test) { + std::vector bytestream; + auto message = std::make_shared(); + message->network = icsneo::Network::NetID::MDIO1; + message->description = 0xABCD; + message->phyAddress = 0x18u; + message->devAddress = 0x13u; + message->regAddress = 0x14u; + message->data = {0x56u, 0x78u}; + message->direction = MDIOMessage::Direction::Write; + message->clause = MDIOMessage::Clause::Clause22; + message->isTXMsg = true; + packetEncoder->encode(*packetizer, bytestream, message); + EXPECT_EQ(bytestream, testBytesClause22); +} + +TEST_F(MDIOEncoderDecoderTest, PacketEncoderClause45Test) { + std::vector bytestream; + auto message = std::make_shared(); + message->network = icsneo::Network::NetID::MDIO1; + message->description = 0xABCD; + message->phyAddress = 0x06u; + message->devAddress = 0x14u; + message->regAddress = 0x1234u; + message->data = {0x56u, 0x78u}; + message->direction = MDIOMessage::Direction::Read; + message->clause = MDIOMessage::Clause::Clause45; + message->isTXMsg = true; + packetEncoder->encode(*packetizer, bytestream, message); + EXPECT_EQ(bytestream, testBytesClause45); +} + +TEST_F(MDIOEncoderDecoderTest, PacketEncoderClause22MaskTest) { + std::vector bytestream; + auto message = std::make_shared(); + message->network = icsneo::Network::NetID::MDIO1; + message->description = 0xFFFFu; + message->phyAddress = 0xFFu; + message->devAddress = 0xFFu; + message->regAddress = 0xFFFFu; + message->data = {0xFFu, 0xFFu}; + message->direction = MDIOMessage::Direction::Write; + message->clause = MDIOMessage::Clause::Clause22; + message->isTXMsg = true; + packetEncoder->encode(*packetizer, bytestream, message); + EXPECT_EQ(bytestream, testBytesClause22Mask); +} + +TEST_F(MDIOEncoderDecoderTest, PacketEncoderClause45MaskTest) { + std::vector bytestream; + auto message = std::make_shared(); + message->network = icsneo::Network::NetID::MDIO1; + message->description = 0xFFFFu; + message->phyAddress = 0xFFu; + message->devAddress = 0xFFu; + message->regAddress = 0xFFFFu; + message->data = {0xFFu, 0xFFu}; + message->direction = MDIOMessage::Direction::Read; + message->clause = MDIOMessage::Clause::Clause45; + message->isTXMsg = true; + packetEncoder->encode(*packetizer, bytestream, message); + EXPECT_EQ(bytestream, testBytesClause45Mask); +} + +TEST_F(MDIOEncoderDecoderTest, PacketDecoderClause22Test) { + std::shared_ptr decodeMsg; + std::shared_ptr message = std::make_shared(); + + message->network = icsneo::Network::NetID::MDIO1; + message->description = 0xABCD; + message->phyAddress = 0x06u; + message->devAddress = 0x00u; + message->regAddress = 0x14u; + message->data = {0x56u, 0x78u}; + message->direction = MDIOMessage::Direction::Read; + message->clause = MDIOMessage::Clause::Clause22; + message->isTXMsg = true; + message->timestamp = static_cast(0xB0FCC1FBE62997); + + EXPECT_TRUE(packetizer->input(recvBytesClause22)); + auto packets = packetizer->output(); + EXPECT_FALSE(packets.empty()); + EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back())); + auto testMessage = std::dynamic_pointer_cast(decodeMsg); + EXPECT_EQ(message->network, testMessage->network); + EXPECT_EQ(message->description, testMessage->description); + EXPECT_EQ(message->phyAddress, testMessage->phyAddress); + EXPECT_EQ(message->devAddress, testMessage->devAddress); + EXPECT_EQ(message->regAddress, testMessage->regAddress); + EXPECT_EQ(message->data, testMessage->data); + EXPECT_EQ(message->direction, testMessage->direction); + EXPECT_EQ(message->clause, testMessage->clause); + EXPECT_EQ(message->isTXMsg, testMessage->isTXMsg); + EXPECT_EQ(message->txTimeout, testMessage->txTimeout); + EXPECT_EQ(message->txAborted, testMessage->txAborted); + EXPECT_EQ(message->txInvalidBus, testMessage->txInvalidBus); + EXPECT_EQ(message->txInvalidPhyAddr, testMessage->txInvalidPhyAddr); + EXPECT_EQ(message->txInvalidRegAddr, testMessage->txInvalidRegAddr); + EXPECT_EQ(message->txInvalidClause, testMessage->txInvalidClause); + EXPECT_EQ(message->txInvalidOpcode, testMessage->txInvalidOpcode); + EXPECT_EQ(message->timestamp, testMessage->timestamp); +} + +TEST_F(MDIOEncoderDecoderTest, PacketDecoderClause45Test) { + std::shared_ptr decodeMsg; + std::shared_ptr message = std::make_shared(); + + message->network = icsneo::Network::NetID::MDIO1; + message->description = 0xABCD; + message->phyAddress = 0x12u; + message->devAddress = 0x03u; + message->regAddress = 0x1456u; + message->data = {0x56u, 0x78u}; + message->direction = MDIOMessage::Direction::Write; + message->clause = MDIOMessage::Clause::Clause45; + message->isTXMsg = true; + message->txTimeout = true; + message->txAborted = true; + message->txInvalidBus = true; + message->txInvalidPhyAddr = true; + message->txInvalidRegAddr = true; + message->txInvalidClause = true; + message->txInvalidOpcode = true; + message->timestamp = static_cast(0xB0FCC1FBE62997); + + EXPECT_TRUE(packetizer->input(recvBytesClause45)); + auto packets = packetizer->output(); + EXPECT_FALSE(packets.empty()); + EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back())); + auto testMessage = std::dynamic_pointer_cast(decodeMsg); + EXPECT_EQ(message->network, testMessage->network); + EXPECT_EQ(message->description, testMessage->description); + EXPECT_EQ(message->phyAddress, testMessage->phyAddress); + EXPECT_EQ(message->devAddress, testMessage->devAddress); + EXPECT_EQ(message->regAddress, testMessage->regAddress); + EXPECT_EQ(message->data, testMessage->data); + EXPECT_EQ(message->direction, testMessage->direction); + EXPECT_EQ(message->clause, testMessage->clause); + EXPECT_EQ(message->isTXMsg, testMessage->isTXMsg); + EXPECT_EQ(message->txTimeout, testMessage->txTimeout); + EXPECT_EQ(message->txAborted, testMessage->txAborted); + EXPECT_EQ(message->txInvalidBus, testMessage->txInvalidBus); + EXPECT_EQ(message->txInvalidPhyAddr, testMessage->txInvalidPhyAddr); + EXPECT_EQ(message->txInvalidRegAddr, testMessage->txInvalidRegAddr); + EXPECT_EQ(message->txInvalidClause, testMessage->txInvalidClause); + EXPECT_EQ(message->txInvalidOpcode, testMessage->txInvalidOpcode); + EXPECT_EQ(message->timestamp, testMessage->timestamp); +}