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