1a32d241bSJagpal Singh Gill #include "modbus_server_tester.hpp" 2a32d241bSJagpal Singh Gill 3a32d241bSJagpal Singh Gill #include "modbus/modbus.hpp" 4a32d241bSJagpal Singh Gill #include "modbus/modbus_commands.hpp" 5a32d241bSJagpal Singh Gill #include "modbus/modbus_exception.hpp" 6a32d241bSJagpal Singh Gill 7a32d241bSJagpal Singh Gill #include <phosphor-logging/lg2.hpp> 8a32d241bSJagpal Singh Gill #include <sdbusplus/async.hpp> 9a32d241bSJagpal Singh Gill 10a32d241bSJagpal Singh Gill #include <gtest/gtest.h> 11a32d241bSJagpal Singh Gill 12a32d241bSJagpal Singh Gill namespace phosphor::modbus::test 13a32d241bSJagpal Singh Gill { 14a32d241bSJagpal Singh Gill 15a32d241bSJagpal Singh Gill PHOSPHOR_LOG2_USING; 16a32d241bSJagpal Singh Gill 17a32d241bSJagpal Singh Gill namespace RTUIntf = phosphor::modbus::rtu; 18a32d241bSJagpal Singh Gill using namespace std::literals; 19a32d241bSJagpal Singh Gill 20a32d241bSJagpal Singh Gill constexpr uint8_t readHoldingRegistersFunctionCode = 0x3; 21a32d241bSJagpal Singh Gill constexpr uint8_t readHoldingRegistersErrorFunctionCode = 0x83; 22a32d241bSJagpal Singh Gill 23a32d241bSJagpal Singh Gill ServerTester::ServerTester(sdbusplus::async::context& ctx, int fd) : 24*cad9ecf6SJagpal Singh Gill fd(fd), fdioInstance(ctx, fd), mutex("TestMutex") 25a32d241bSJagpal Singh Gill {} 26a32d241bSJagpal Singh Gill 27a32d241bSJagpal Singh Gill auto ServerTester::processRequests() -> sdbusplus::async::task<void> 28a32d241bSJagpal Singh Gill { 29*cad9ecf6SJagpal Singh Gill // Acquire lock to guard against concurrent access to fdioInstance 30*cad9ecf6SJagpal Singh Gill sdbusplus::async::lock_guard lg{mutex}; 31*cad9ecf6SJagpal Singh Gill co_await lg.lock(); 32*cad9ecf6SJagpal Singh Gill 33a32d241bSJagpal Singh Gill MessageIntf request; 34a32d241bSJagpal Singh Gill co_await fdioInstance.next(); 35a32d241bSJagpal Singh Gill auto ret = read(fd, request.raw.data(), request.raw.size()); 36a32d241bSJagpal Singh Gill // Request message need to be at least 4 bytes long - address(1), 37a32d241bSJagpal Singh Gill // function code(1), ..., CRC(2) 38a32d241bSJagpal Singh Gill if (ret < 4) 39a32d241bSJagpal Singh Gill { 40a32d241bSJagpal Singh Gill error("Invalid Server message size {SIZE}, drop it", "SIZE", ret); 41a32d241bSJagpal Singh Gill co_return; 42a32d241bSJagpal Singh Gill } 43a32d241bSJagpal Singh Gill 44a32d241bSJagpal Singh Gill MessageIntf response; 45a32d241bSJagpal Singh Gill bool segmentedResponse = false; 46a32d241bSJagpal Singh Gill processMessage(request, ret, response, segmentedResponse); 47a32d241bSJagpal Singh Gill 48a32d241bSJagpal Singh Gill if (!segmentedResponse) 49a32d241bSJagpal Singh Gill { 50a32d241bSJagpal Singh Gill ret = write(fd, response.raw.data(), response.len); 51a32d241bSJagpal Singh Gill if (ret < 0) 52a32d241bSJagpal Singh Gill { 53a32d241bSJagpal Singh Gill error("Failed to send response {ERROR}", "ERROR", strerror(errno)); 54a32d241bSJagpal Singh Gill } 55a32d241bSJagpal Singh Gill co_return; 56a32d241bSJagpal Singh Gill } 57a32d241bSJagpal Singh Gill 58a32d241bSJagpal Singh Gill // Segmented response 59a32d241bSJagpal Singh Gill ret = write(fd, response.raw.data(), response.len - 2); 60a32d241bSJagpal Singh Gill if (ret < 0) 61a32d241bSJagpal Singh Gill { 62a32d241bSJagpal Singh Gill error("Failed to send 1st segment response {ERROR}", "ERROR", 63a32d241bSJagpal Singh Gill strerror(errno)); 64a32d241bSJagpal Singh Gill co_return; 65a32d241bSJagpal Singh Gill } 66a32d241bSJagpal Singh Gill 67a32d241bSJagpal Singh Gill debug("First segment sent successfully"); 68a32d241bSJagpal Singh Gill 69a32d241bSJagpal Singh Gill ret = write(fd, response.raw.data() + response.len - 2, 2); 70a32d241bSJagpal Singh Gill if (ret < 0) 71a32d241bSJagpal Singh Gill { 72a32d241bSJagpal Singh Gill error("Failed to send 2nd segment response {ERROR}", "ERROR", 73a32d241bSJagpal Singh Gill strerror(errno)); 74a32d241bSJagpal Singh Gill co_return; 75a32d241bSJagpal Singh Gill } 76a32d241bSJagpal Singh Gill 77a32d241bSJagpal Singh Gill debug("Second segment sent successfully"); 78a32d241bSJagpal Singh Gill 79a32d241bSJagpal Singh Gill co_return; 80a32d241bSJagpal Singh Gill } 81a32d241bSJagpal Singh Gill 82a32d241bSJagpal Singh Gill void ServerTester::processMessage(MessageIntf& request, size_t requestSize, 83a32d241bSJagpal Singh Gill MessageIntf& response, 84a32d241bSJagpal Singh Gill bool& segmentedResponse) 85a32d241bSJagpal Singh Gill { 86a32d241bSJagpal Singh Gill EXPECT_EQ(request.address, testDeviceAddress) << "Invalid device address"; 87a32d241bSJagpal Singh Gill 88a32d241bSJagpal Singh Gill switch (request.functionCode) 89a32d241bSJagpal Singh Gill { 90a32d241bSJagpal Singh Gill case readHoldingRegistersFunctionCode: 91a32d241bSJagpal Singh Gill processReadHoldingRegisters(request, requestSize, response, 92a32d241bSJagpal Singh Gill segmentedResponse); 93a32d241bSJagpal Singh Gill break; 94a32d241bSJagpal Singh Gill default: 95a32d241bSJagpal Singh Gill FAIL() << "Server received unknown request function code " 96a32d241bSJagpal Singh Gill << request.functionCode; 97a32d241bSJagpal Singh Gill break; 98a32d241bSJagpal Singh Gill } 99a32d241bSJagpal Singh Gill } 100a32d241bSJagpal Singh Gill 101*cad9ecf6SJagpal Singh Gill static inline void checkRequestSize(size_t requestSize, size_t expectedSize) 102*cad9ecf6SJagpal Singh Gill { 103*cad9ecf6SJagpal Singh Gill EXPECT_EQ(requestSize, expectedSize) << "Invalid request size"; 104*cad9ecf6SJagpal Singh Gill } 105*cad9ecf6SJagpal Singh Gill 106a32d241bSJagpal Singh Gill void ServerTester::processReadHoldingRegisters( 107a32d241bSJagpal Singh Gill MessageIntf& request, size_t requestSize, MessageIntf& response, 108a32d241bSJagpal Singh Gill bool& segmentedResponse) 109a32d241bSJagpal Singh Gill { 110a32d241bSJagpal Singh Gill constexpr size_t expectedRequestSize = 8; 111a32d241bSJagpal Singh Gill 112a32d241bSJagpal Singh Gill if (requestSize != expectedRequestSize) 113a32d241bSJagpal Singh Gill { 114a32d241bSJagpal Singh Gill FAIL() << "Invalid readHoldingRegisters request size:" << requestSize 115a32d241bSJagpal Singh Gill << ", drop it"; 116a32d241bSJagpal Singh Gill return; 117a32d241bSJagpal Singh Gill } 118a32d241bSJagpal Singh Gill 119a32d241bSJagpal Singh Gill // NOTE: This code deliberately avoids using any packing helpers from 120a32d241bSJagpal Singh Gill // message.hpp. This ensures that message APIs are tested as intended on the 121a32d241bSJagpal Singh Gill // client side. 122a32d241bSJagpal Singh Gill uint16_t registerOffset = request.raw[2] << 8 | request.raw[3]; 123a32d241bSJagpal Singh Gill uint16_t registerCount = request.raw[4] << 8 | request.raw[5]; 124a32d241bSJagpal Singh Gill 125a32d241bSJagpal Singh Gill if (registerOffset == testSuccessReadHoldingRegisterOffset || 126a32d241bSJagpal Singh Gill registerOffset == testSuccessReadHoldingRegisterSegmentedOffset) 127a32d241bSJagpal Singh Gill { 128*cad9ecf6SJagpal Singh Gill checkRequestSize(registerCount, testSuccessReadHoldingRegisterCount); 129*cad9ecf6SJagpal Singh Gill 130a32d241bSJagpal Singh Gill response << request.raw[0] << request.raw[1] 131a32d241bSJagpal Singh Gill << uint8_t(2 * registerCount) 132a32d241bSJagpal Singh Gill << uint16_t(testSuccessReadHoldingRegisterResponse[0]) 133a32d241bSJagpal Singh Gill << uint16_t(testSuccessReadHoldingRegisterResponse[1]); 134a32d241bSJagpal Singh Gill response.appendCRC(); 135a32d241bSJagpal Singh Gill segmentedResponse = 136a32d241bSJagpal Singh Gill (registerOffset == testSuccessReadHoldingRegisterSegmentedOffset); 137a32d241bSJagpal Singh Gill } 138*cad9ecf6SJagpal Singh Gill else if (registerOffset == testReadHoldingRegisterModelOffset) 139*cad9ecf6SJagpal Singh Gill { 140*cad9ecf6SJagpal Singh Gill checkRequestSize(registerCount, testReadHoldingRegisterModelCount); 141*cad9ecf6SJagpal Singh Gill 142*cad9ecf6SJagpal Singh Gill response << request.raw[0] << request.raw[1] 143*cad9ecf6SJagpal Singh Gill << uint8_t(2 * testReadHoldingRegisterModelCount); 144*cad9ecf6SJagpal Singh Gill for (size_t i = 0; i < testReadHoldingRegisterModelCount; i++) 145*cad9ecf6SJagpal Singh Gill { 146*cad9ecf6SJagpal Singh Gill response << uint16_t(testReadHoldingRegisterModel[i]); 147*cad9ecf6SJagpal Singh Gill } 148*cad9ecf6SJagpal Singh Gill response.appendCRC(); 149*cad9ecf6SJagpal Singh Gill } 150a32d241bSJagpal Singh Gill else if (registerOffset == testFailureReadHoldingRegister) 151a32d241bSJagpal Singh Gill { 152*cad9ecf6SJagpal Singh Gill checkRequestSize(registerCount, testSuccessReadHoldingRegisterCount); 153*cad9ecf6SJagpal Singh Gill 154a32d241bSJagpal Singh Gill response << request.raw[0] 155a32d241bSJagpal Singh Gill << (uint8_t)readHoldingRegistersErrorFunctionCode 156a32d241bSJagpal Singh Gill << uint8_t(RTUIntf::ModbusExceptionCode::illegalFunctionCode); 157a32d241bSJagpal Singh Gill response.appendCRC(); 158a32d241bSJagpal Singh Gill } 159a32d241bSJagpal Singh Gill else 160a32d241bSJagpal Singh Gill { 161a32d241bSJagpal Singh Gill FAIL() << "Invalid register offset:" << registerOffset; 162a32d241bSJagpal Singh Gill } 163a32d241bSJagpal Singh Gill } 164a32d241bSJagpal Singh Gill 165a32d241bSJagpal Singh Gill } // namespace phosphor::modbus::test 166