xref: /openbmc/phosphor-modbus/mocked_test_device/mock_modbus_server.cpp (revision 2168bbd050af3c68d820f96054a939dda02688c6)
1*2168bbd0SJagpal Singh Gill #include "modbus/modbus.hpp"
2*2168bbd0SJagpal Singh Gill #include "modbus/modbus_commands.hpp"
3*2168bbd0SJagpal Singh Gill #include "modbus/modbus_message.hpp"
4*2168bbd0SJagpal Singh Gill 
5*2168bbd0SJagpal Singh Gill #include <fcntl.h>
6*2168bbd0SJagpal Singh Gill 
7*2168bbd0SJagpal Singh Gill #include <sdbusplus/async.hpp>
8*2168bbd0SJagpal Singh Gill 
9*2168bbd0SJagpal Singh Gill #include <iostream>
10*2168bbd0SJagpal Singh Gill #include <string>
11*2168bbd0SJagpal Singh Gill 
12*2168bbd0SJagpal Singh Gill namespace phosphor::modbus::test
13*2168bbd0SJagpal Singh Gill {
14*2168bbd0SJagpal Singh Gill 
15*2168bbd0SJagpal Singh Gill class MessageIntf : public phosphor::modbus::rtu::Message
16*2168bbd0SJagpal Singh Gill {
17*2168bbd0SJagpal Singh Gill     friend class TestServer;
18*2168bbd0SJagpal Singh Gill };
19*2168bbd0SJagpal Singh Gill 
20*2168bbd0SJagpal Singh Gill class TestServer
21*2168bbd0SJagpal Singh Gill {
22*2168bbd0SJagpal Singh Gill   public:
23*2168bbd0SJagpal Singh Gill     explicit TestServer(sdbusplus::async::context& ctx, int fd);
24*2168bbd0SJagpal Singh Gill 
25*2168bbd0SJagpal Singh Gill   private:
26*2168bbd0SJagpal Singh Gill     auto processRequests() -> sdbusplus::async::task<void>;
27*2168bbd0SJagpal Singh Gill     void processMessage(MessageIntf& request, size_t requestSize,
28*2168bbd0SJagpal Singh Gill                         MessageIntf& response);
29*2168bbd0SJagpal Singh Gill 
30*2168bbd0SJagpal Singh Gill     void processReadHoldingRegisters(MessageIntf& request, size_t requestSize,
31*2168bbd0SJagpal Singh Gill                                      MessageIntf& response);
32*2168bbd0SJagpal Singh Gill 
33*2168bbd0SJagpal Singh Gill     sdbusplus::async::context& ctx;
34*2168bbd0SJagpal Singh Gill     int fd;
35*2168bbd0SJagpal Singh Gill     sdbusplus::async::fdio fdioInstance;
36*2168bbd0SJagpal Singh Gill };
37*2168bbd0SJagpal Singh Gill 
TestServer(sdbusplus::async::context & ctx,int fd)38*2168bbd0SJagpal Singh Gill TestServer::TestServer(sdbusplus::async::context& ctx, int fd) :
39*2168bbd0SJagpal Singh Gill     ctx(ctx), fd(fd), fdioInstance(ctx, fd)
40*2168bbd0SJagpal Singh Gill {
41*2168bbd0SJagpal Singh Gill     ctx.spawn(processRequests());
42*2168bbd0SJagpal Singh Gill }
43*2168bbd0SJagpal Singh Gill 
processRequests()44*2168bbd0SJagpal Singh Gill auto TestServer::processRequests() -> sdbusplus::async::task<void>
45*2168bbd0SJagpal Singh Gill {
46*2168bbd0SJagpal Singh Gill     while (!ctx.stop_requested())
47*2168bbd0SJagpal Singh Gill     {
48*2168bbd0SJagpal Singh Gill         MessageIntf request;
49*2168bbd0SJagpal Singh Gill         co_await fdioInstance.next();
50*2168bbd0SJagpal Singh Gill         auto ret = read(fd, request.raw.data(), request.raw.size());
51*2168bbd0SJagpal Singh Gill         // Request message need to be at least 4 bytes long - address(1),
52*2168bbd0SJagpal Singh Gill         // function code(1), ..., CRC(2)
53*2168bbd0SJagpal Singh Gill         if (ret < 4)
54*2168bbd0SJagpal Singh Gill         {
55*2168bbd0SJagpal Singh Gill             std::cerr << "Invalid Server message size:" << ret << ", drop it"
56*2168bbd0SJagpal Singh Gill                       << std::endl;
57*2168bbd0SJagpal Singh Gill             continue;
58*2168bbd0SJagpal Singh Gill         }
59*2168bbd0SJagpal Singh Gill 
60*2168bbd0SJagpal Singh Gill         MessageIntf response;
61*2168bbd0SJagpal Singh Gill         processMessage(request, ret, response);
62*2168bbd0SJagpal Singh Gill 
63*2168bbd0SJagpal Singh Gill         ret = write(fd, response.raw.data(), response.len);
64*2168bbd0SJagpal Singh Gill         if (ret < 0)
65*2168bbd0SJagpal Singh Gill         {
66*2168bbd0SJagpal Singh Gill             std::cerr << "Failed to send response" << std::endl;
67*2168bbd0SJagpal Singh Gill         }
68*2168bbd0SJagpal Singh Gill     }
69*2168bbd0SJagpal Singh Gill }
70*2168bbd0SJagpal Singh Gill 
processMessage(MessageIntf & request,size_t requestSize,MessageIntf & response)71*2168bbd0SJagpal Singh Gill void TestServer::processMessage(MessageIntf& request, size_t requestSize,
72*2168bbd0SJagpal Singh Gill                                 MessageIntf& response)
73*2168bbd0SJagpal Singh Gill {
74*2168bbd0SJagpal Singh Gill     constexpr uint8_t readHoldingRegistersFunctionCode = 0x3;
75*2168bbd0SJagpal Singh Gill 
76*2168bbd0SJagpal Singh Gill     switch (request.functionCode)
77*2168bbd0SJagpal Singh Gill     {
78*2168bbd0SJagpal Singh Gill         case readHoldingRegistersFunctionCode:
79*2168bbd0SJagpal Singh Gill             processReadHoldingRegisters(request, requestSize, response);
80*2168bbd0SJagpal Singh Gill             break;
81*2168bbd0SJagpal Singh Gill         default:
82*2168bbd0SJagpal Singh Gill             std::cerr << "Server received unknown request" << std::endl;
83*2168bbd0SJagpal Singh Gill             break;
84*2168bbd0SJagpal Singh Gill     }
85*2168bbd0SJagpal Singh Gill }
86*2168bbd0SJagpal Singh Gill 
processReadHoldingRegisters(MessageIntf & request,size_t requestSize,MessageIntf & response)87*2168bbd0SJagpal Singh Gill void TestServer::processReadHoldingRegisters(
88*2168bbd0SJagpal Singh Gill     MessageIntf& request, size_t requestSize, MessageIntf& response)
89*2168bbd0SJagpal Singh Gill {
90*2168bbd0SJagpal Singh Gill     uint16_t registerOffset = request.raw[2] << 8 | request.raw[3];
91*2168bbd0SJagpal Singh Gill     uint16_t registerCount = request.raw[4] << 8 | request.raw[5];
92*2168bbd0SJagpal Singh Gill 
93*2168bbd0SJagpal Singh Gill     std::cout << "Received readHoldingRegisters request with size:"
94*2168bbd0SJagpal Singh Gill               << requestSize << ", registerOffset:" << registerOffset
95*2168bbd0SJagpal Singh Gill               << ", registerCount:" << registerCount << std::endl;
96*2168bbd0SJagpal Singh Gill 
97*2168bbd0SJagpal Singh Gill     response << request.raw[0] << request.raw[1] << uint8_t(2 * registerCount);
98*2168bbd0SJagpal Singh Gill     for (int i = 0; i < registerCount; i++)
99*2168bbd0SJagpal Singh Gill     {
100*2168bbd0SJagpal Singh Gill         constexpr uint16_t readHoldingRegisterResponse = 0x4142;
101*2168bbd0SJagpal Singh Gill         response << uint16_t(readHoldingRegisterResponse);
102*2168bbd0SJagpal Singh Gill     }
103*2168bbd0SJagpal Singh Gill     response.appendCRC();
104*2168bbd0SJagpal Singh Gill }
105*2168bbd0SJagpal Singh Gill 
106*2168bbd0SJagpal Singh Gill } // namespace phosphor::modbus::test
107*2168bbd0SJagpal Singh Gill 
main(int argc,char * argv[])108*2168bbd0SJagpal Singh Gill int main(int argc, char* argv[])
109*2168bbd0SJagpal Singh Gill {
110*2168bbd0SJagpal Singh Gill     if (argc < 2)
111*2168bbd0SJagpal Singh Gill     {
112*2168bbd0SJagpal Singh Gill         std::cerr << "Usage: " << argv[0] << " <integer_value>" << std::endl;
113*2168bbd0SJagpal Singh Gill         return 1;
114*2168bbd0SJagpal Singh Gill     }
115*2168bbd0SJagpal Singh Gill 
116*2168bbd0SJagpal Singh Gill     int interfaceValue = 1;
117*2168bbd0SJagpal Singh Gill     try
118*2168bbd0SJagpal Singh Gill     {
119*2168bbd0SJagpal Singh Gill         interfaceValue = std::stoi(argv[1]);
120*2168bbd0SJagpal Singh Gill     }
121*2168bbd0SJagpal Singh Gill     catch (const std::invalid_argument& e)
122*2168bbd0SJagpal Singh Gill     {
123*2168bbd0SJagpal Singh Gill         std::cerr << "Invalid argument: " << e.what() << std::endl;
124*2168bbd0SJagpal Singh Gill         return 1;
125*2168bbd0SJagpal Singh Gill     }
126*2168bbd0SJagpal Singh Gill     catch (const std::out_of_range& e)
127*2168bbd0SJagpal Singh Gill     {
128*2168bbd0SJagpal Singh Gill         std::cerr << "Argument out of range: " << e.what() << std::endl;
129*2168bbd0SJagpal Singh Gill         return 1;
130*2168bbd0SJagpal Singh Gill     }
131*2168bbd0SJagpal Singh Gill 
132*2168bbd0SJagpal Singh Gill     using TestServerIntf = phosphor::modbus::test::TestServer;
133*2168bbd0SJagpal Singh Gill     std::string devicePathStr =
134*2168bbd0SJagpal Singh Gill         std::string("/dev/ttyV") + std::to_string(interfaceValue);
135*2168bbd0SJagpal Singh Gill     const char* serverDevicePath = devicePathStr.c_str();
136*2168bbd0SJagpal Singh Gill     std::cout << "Starting at device path" << serverDevicePath << std::endl;
137*2168bbd0SJagpal Singh Gill     constexpr auto path = "/xyz/openbmc_project";
138*2168bbd0SJagpal Singh Gill     constexpr auto serviceName = "xyz.openbmc_project.ModbusRTUTestServer";
139*2168bbd0SJagpal Singh Gill     sdbusplus::async::context ctx;
140*2168bbd0SJagpal Singh Gill 
141*2168bbd0SJagpal Singh Gill     auto fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
142*2168bbd0SJagpal Singh Gill     if (fdServer == -1)
143*2168bbd0SJagpal Singh Gill     {
144*2168bbd0SJagpal Singh Gill         std::cerr << "Failed to open serial port " << serverDevicePath
145*2168bbd0SJagpal Singh Gill                   << " with error: " << strerror(errno) << std::endl;
146*2168bbd0SJagpal Singh Gill         return 1;
147*2168bbd0SJagpal Singh Gill     }
148*2168bbd0SJagpal Singh Gill 
149*2168bbd0SJagpal Singh Gill     std::cout << "Creating Modbus RTU test server at " << path << std::endl;
150*2168bbd0SJagpal Singh Gill     TestServerIntf server{ctx, fdServer};
151*2168bbd0SJagpal Singh Gill 
152*2168bbd0SJagpal Singh Gill     ctx.request_name(serviceName);
153*2168bbd0SJagpal Singh Gill 
154*2168bbd0SJagpal Singh Gill     ctx.run();
155*2168bbd0SJagpal Singh Gill     return 0;
156*2168bbd0SJagpal Singh Gill }
157