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