xref: /openbmc/phosphor-modbus/tests/modbus_server_tester.cpp (revision a32d241b08af236b5ea1d4e18a390bdc561b435b)
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