xref: /openbmc/phosphor-modbus/tests/modbus_server_tester.cpp (revision e92aba4516471f5a01d4ab1f93eb9919ec05c21f)
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 
ServerTester(sdbusplus::async::context & ctx,int fd)23a32d241bSJagpal Singh Gill ServerTester::ServerTester(sdbusplus::async::context& ctx, int fd) :
24cad9ecf6SJagpal Singh Gill     fd(fd), fdioInstance(ctx, fd), mutex("TestMutex")
25a32d241bSJagpal Singh Gill {}
26a32d241bSJagpal Singh Gill 
processRequests()27a32d241bSJagpal Singh Gill auto ServerTester::processRequests() -> sdbusplus::async::task<void>
28a32d241bSJagpal Singh Gill {
29cad9ecf6SJagpal Singh Gill     // Acquire lock to guard against concurrent access to fdioInstance
30cad9ecf6SJagpal Singh Gill     sdbusplus::async::lock_guard lg{mutex};
31cad9ecf6SJagpal Singh Gill     co_await lg.lock();
32cad9ecf6SJagpal 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 
processMessage(MessageIntf & request,size_t requestSize,MessageIntf & response,bool & segmentedResponse)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 
checkRequestSize(size_t requestSize,size_t expectedSize)101cad9ecf6SJagpal Singh Gill static inline void checkRequestSize(size_t requestSize, size_t expectedSize)
102cad9ecf6SJagpal Singh Gill {
103cad9ecf6SJagpal Singh Gill     EXPECT_EQ(requestSize, expectedSize) << "Invalid request size";
104cad9ecf6SJagpal Singh Gill }
105cad9ecf6SJagpal Singh Gill 
processReadHoldingRegisters(MessageIntf & request,size_t requestSize,MessageIntf & response,bool & segmentedResponse)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 
125*e92aba45SJagpal Singh Gill     if (registerOffset == testFailureReadHoldingRegister)
126a32d241bSJagpal Singh Gill     {
127a32d241bSJagpal Singh Gill         response << request.raw[0]
128a32d241bSJagpal Singh Gill                  << (uint8_t)readHoldingRegistersErrorFunctionCode
129a32d241bSJagpal Singh Gill                  << uint8_t(RTUIntf::ModbusExceptionCode::illegalFunctionCode);
130a32d241bSJagpal Singh Gill         response.appendCRC();
131a32d241bSJagpal Singh Gill     }
132a32d241bSJagpal Singh Gill     else
133a32d241bSJagpal Singh Gill     {
134*e92aba45SJagpal Singh Gill         auto expectedResponseIter =
135*e92aba45SJagpal Singh Gill             testReadHoldingRegisterMap.find(registerOffset);
136*e92aba45SJagpal Singh Gill         if (expectedResponseIter == testReadHoldingRegisterMap.end())
137*e92aba45SJagpal Singh Gill         {
138a32d241bSJagpal Singh Gill             FAIL() << "Invalid register offset:" << registerOffset;
139*e92aba45SJagpal Singh Gill             return;
140*e92aba45SJagpal Singh Gill         }
141*e92aba45SJagpal Singh Gill 
142*e92aba45SJagpal Singh Gill         checkRequestSize(registerCount,
143*e92aba45SJagpal Singh Gill                          std::get<0>(expectedResponseIter->second));
144*e92aba45SJagpal Singh Gill 
145*e92aba45SJagpal Singh Gill         auto& expectedResponse = std::get<1>(expectedResponseIter->second);
146*e92aba45SJagpal Singh Gill 
147*e92aba45SJagpal Singh Gill         response << request.raw[0] << request.raw[1]
148*e92aba45SJagpal Singh Gill                  << uint8_t(2 * registerCount);
149*e92aba45SJagpal Singh Gill         for (size_t i = 0; i < registerCount; i++)
150*e92aba45SJagpal Singh Gill         {
151*e92aba45SJagpal Singh Gill             response << uint16_t(expectedResponse[i]);
152*e92aba45SJagpal Singh Gill         }
153*e92aba45SJagpal Singh Gill         response.appendCRC();
154*e92aba45SJagpal Singh Gill 
155*e92aba45SJagpal Singh Gill         segmentedResponse =
156*e92aba45SJagpal Singh Gill             (registerOffset == testSuccessReadHoldingRegisterSegmentedOffset);
157a32d241bSJagpal Singh Gill     }
158a32d241bSJagpal Singh Gill }
159a32d241bSJagpal Singh Gill 
160a32d241bSJagpal Singh Gill } // namespace phosphor::modbus::test
161