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