xref: /openbmc/phosphor-modbus/rtu/modbus/modbus.cpp (revision a32d241b08af236b5ea1d4e18a390bdc561b435b)
1*a32d241bSJagpal Singh Gill #include "modbus.hpp"
2*a32d241bSJagpal Singh Gill 
3*a32d241bSJagpal Singh Gill #include "modbus_commands.hpp"
4*a32d241bSJagpal Singh Gill 
5*a32d241bSJagpal Singh Gill #include <termios.h>
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 namespace phosphor::modbus::rtu
11*a32d241bSJagpal Singh Gill {
12*a32d241bSJagpal Singh Gill 
13*a32d241bSJagpal Singh Gill PHOSPHOR_LOG2_USING;
14*a32d241bSJagpal Singh Gill 
15*a32d241bSJagpal Singh Gill const std::unordered_map<int, speed_t> baudRateMap = {
16*a32d241bSJagpal Singh Gill     {0, B0},         {50, B50},        {75, B75},       {110, B110},
17*a32d241bSJagpal Singh Gill     {134, B134},     {150, B150},      {200, B200},     {300, B300},
18*a32d241bSJagpal Singh Gill     {600, B600},     {1200, B1200},    {1800, B1800},   {2400, B2400},
19*a32d241bSJagpal Singh Gill     {4800, B4800},   {9600, B9600},    {19200, B19200}, {38400, B38400},
20*a32d241bSJagpal Singh Gill     {57600, B57600}, {115200, B115200}};
21*a32d241bSJagpal Singh Gill 
Modbus(sdbusplus::async::context & ctx,int fd,uint32_t baudRate,uint16_t rtsDelay)22*a32d241bSJagpal Singh Gill Modbus::Modbus(sdbusplus::async::context& ctx, int fd, uint32_t baudRate,
23*a32d241bSJagpal Singh Gill                uint16_t rtsDelay) :
24*a32d241bSJagpal Singh Gill     ctx(ctx), fd(fd), rtsDelay(rtsDelay), fdioInstance(ctx, fd)
25*a32d241bSJagpal Singh Gill {
26*a32d241bSJagpal Singh Gill     if (!setProperties(baudRate, Parity::even))
27*a32d241bSJagpal Singh Gill     {
28*a32d241bSJagpal Singh Gill         throw std::runtime_error("Failed to set port properties");
29*a32d241bSJagpal Singh Gill     }
30*a32d241bSJagpal Singh Gill 
31*a32d241bSJagpal Singh Gill     info("Modbus created successfully");
32*a32d241bSJagpal Singh Gill }
33*a32d241bSJagpal Singh Gill 
applyParitySettings(Parity parity,termios & tty)34*a32d241bSJagpal Singh Gill static auto applyParitySettings(Parity parity, termios& tty) -> bool
35*a32d241bSJagpal Singh Gill {
36*a32d241bSJagpal Singh Gill     switch (parity)
37*a32d241bSJagpal Singh Gill     {
38*a32d241bSJagpal Singh Gill         case Parity::none:
39*a32d241bSJagpal Singh Gill             tty.c_cflag &= ~PARENB;
40*a32d241bSJagpal Singh Gill             tty.c_iflag &= ~INPCK;
41*a32d241bSJagpal Singh Gill             break;
42*a32d241bSJagpal Singh Gill         case Parity::odd:
43*a32d241bSJagpal Singh Gill             tty.c_cflag |= PARENB;
44*a32d241bSJagpal Singh Gill             tty.c_cflag |= PARODD;
45*a32d241bSJagpal Singh Gill             tty.c_iflag |= INPCK;
46*a32d241bSJagpal Singh Gill             break;
47*a32d241bSJagpal Singh Gill         case Parity::even:
48*a32d241bSJagpal Singh Gill             tty.c_cflag |= PARENB;
49*a32d241bSJagpal Singh Gill             tty.c_cflag &= ~PARODD;
50*a32d241bSJagpal Singh Gill             tty.c_iflag |= INPCK;
51*a32d241bSJagpal Singh Gill             break;
52*a32d241bSJagpal Singh Gill         default:
53*a32d241bSJagpal Singh Gill             error("Invalid parity");
54*a32d241bSJagpal Singh Gill             return false;
55*a32d241bSJagpal Singh Gill     }
56*a32d241bSJagpal Singh Gill 
57*a32d241bSJagpal Singh Gill     return true;
58*a32d241bSJagpal Singh Gill }
59*a32d241bSJagpal Singh Gill 
setProperties(uint32_t inBaudRate,Parity inParity)60*a32d241bSJagpal Singh Gill auto Modbus::setProperties(uint32_t inBaudRate, Parity inParity) -> bool
61*a32d241bSJagpal Singh Gill {
62*a32d241bSJagpal Singh Gill     if (inBaudRate == baudRate && inParity == parity)
63*a32d241bSJagpal Singh Gill     {
64*a32d241bSJagpal Singh Gill         return true;
65*a32d241bSJagpal Singh Gill     }
66*a32d241bSJagpal Singh Gill 
67*a32d241bSJagpal Singh Gill     termios tty;
68*a32d241bSJagpal Singh Gill     if (tcgetattr(fd, &tty) != 0)
69*a32d241bSJagpal Singh Gill     {
70*a32d241bSJagpal Singh Gill         error("Error getting termios");
71*a32d241bSJagpal Singh Gill         return false;
72*a32d241bSJagpal Singh Gill     }
73*a32d241bSJagpal Singh Gill 
74*a32d241bSJagpal Singh Gill     if (inBaudRate != baudRate)
75*a32d241bSJagpal Singh Gill     {
76*a32d241bSJagpal Singh Gill         if (cfsetspeed(&tty, baudRateMap.at(inBaudRate)) != 0)
77*a32d241bSJagpal Singh Gill         {
78*a32d241bSJagpal Singh Gill             error("Error setting baud rate");
79*a32d241bSJagpal Singh Gill             return false;
80*a32d241bSJagpal Singh Gill         }
81*a32d241bSJagpal Singh Gill     }
82*a32d241bSJagpal Singh Gill 
83*a32d241bSJagpal Singh Gill     if (inParity != parity)
84*a32d241bSJagpal Singh Gill     {
85*a32d241bSJagpal Singh Gill         if (!applyParitySettings(inParity, tty))
86*a32d241bSJagpal Singh Gill         {
87*a32d241bSJagpal Singh Gill             error("Invalid parity");
88*a32d241bSJagpal Singh Gill             return false;
89*a32d241bSJagpal Singh Gill         }
90*a32d241bSJagpal Singh Gill     }
91*a32d241bSJagpal Singh Gill 
92*a32d241bSJagpal Singh Gill     // TODO: We might not need these again.
93*a32d241bSJagpal Singh Gill     tty.c_cflag |= CS8 | CLOCAL | CREAD;
94*a32d241bSJagpal Singh Gill     // Set non-blocking read behavior
95*a32d241bSJagpal Singh Gill     tty.c_cc[VMIN] = 1;  // Minimum characters to read
96*a32d241bSJagpal Singh Gill     tty.c_cc[VTIME] = 0; // Timeout in deciseconds (0 for no timeout)
97*a32d241bSJagpal Singh Gill 
98*a32d241bSJagpal Singh Gill     if (tcsetattr(fd, TCSAFLUSH, &tty) != 0)
99*a32d241bSJagpal Singh Gill     {
100*a32d241bSJagpal Singh Gill         error("Error setting termios");
101*a32d241bSJagpal Singh Gill         return false;
102*a32d241bSJagpal Singh Gill     }
103*a32d241bSJagpal Singh Gill 
104*a32d241bSJagpal Singh Gill     parity = inParity;
105*a32d241bSJagpal Singh Gill     baudRate = inBaudRate;
106*a32d241bSJagpal Singh Gill 
107*a32d241bSJagpal Singh Gill     debug("Properties set successfully");
108*a32d241bSJagpal Singh Gill 
109*a32d241bSJagpal Singh Gill     return true;
110*a32d241bSJagpal Singh Gill }
111*a32d241bSJagpal Singh Gill 
printMessage(uint8_t * data,size_t len)112*a32d241bSJagpal Singh Gill static auto printMessage(uint8_t* data, size_t len) -> void
113*a32d241bSJagpal Singh Gill {
114*a32d241bSJagpal Singh Gill     std::stringstream ss;
115*a32d241bSJagpal Singh Gill     ss << std::hex << std::setfill('0');
116*a32d241bSJagpal Singh Gill 
117*a32d241bSJagpal Singh Gill     for (size_t i = 0; i < len; ++i)
118*a32d241bSJagpal Singh Gill     {
119*a32d241bSJagpal Singh Gill         ss << std::setw(2) << static_cast<int>(data[i]) << " ";
120*a32d241bSJagpal Singh Gill     }
121*a32d241bSJagpal Singh Gill 
122*a32d241bSJagpal Singh Gill     debug("{MSG}", "MSG", ss.str());
123*a32d241bSJagpal Singh Gill }
124*a32d241bSJagpal Singh Gill 
readHoldingRegisters(uint8_t deviceAddress,uint16_t registerOffset,std::vector<uint16_t> & registers)125*a32d241bSJagpal Singh Gill auto Modbus::readHoldingRegisters(uint8_t deviceAddress,
126*a32d241bSJagpal Singh Gill                                   uint16_t registerOffset,
127*a32d241bSJagpal Singh Gill                                   std::vector<uint16_t>& registers)
128*a32d241bSJagpal Singh Gill     -> sdbusplus::async::task<bool>
129*a32d241bSJagpal Singh Gill {
130*a32d241bSJagpal Singh Gill     try
131*a32d241bSJagpal Singh Gill     {
132*a32d241bSJagpal Singh Gill         ReadHoldingRegistersRequest request(deviceAddress, registerOffset,
133*a32d241bSJagpal Singh Gill                                             registers.size());
134*a32d241bSJagpal Singh Gill         ReadHoldingRegistersResponse response(deviceAddress, registers);
135*a32d241bSJagpal Singh Gill 
136*a32d241bSJagpal Singh Gill         request.encode();
137*a32d241bSJagpal Singh Gill 
138*a32d241bSJagpal Singh Gill         debug(
139*a32d241bSJagpal Singh Gill             "Sending read holding registers request for {REGISTER_OFFSET} {DEVICE_ADDRESS}",
140*a32d241bSJagpal Singh Gill             "REGISTER_OFFSET", registerOffset, "DEVICE_ADDRESS", deviceAddress);
141*a32d241bSJagpal Singh Gill 
142*a32d241bSJagpal Singh Gill         if (!co_await writeRequest(deviceAddress, request))
143*a32d241bSJagpal Singh Gill         {
144*a32d241bSJagpal Singh Gill             co_return false;
145*a32d241bSJagpal Singh Gill         }
146*a32d241bSJagpal Singh Gill 
147*a32d241bSJagpal Singh Gill         debug(
148*a32d241bSJagpal Singh Gill             "Waiting for read holding registers response for {REGISTER_OFFSET} {DEVICE_ADDRESS}",
149*a32d241bSJagpal Singh Gill             "REGISTER_OFFSET", registerOffset, "DEVICE_ADDRESS", deviceAddress);
150*a32d241bSJagpal Singh Gill 
151*a32d241bSJagpal Singh Gill         if (!co_await readResponse(deviceAddress, response,
152*a32d241bSJagpal Singh Gill                                    request.functionCode))
153*a32d241bSJagpal Singh Gill         {
154*a32d241bSJagpal Singh Gill             co_return false;
155*a32d241bSJagpal Singh Gill         }
156*a32d241bSJagpal Singh Gill 
157*a32d241bSJagpal Singh Gill         response.decode();
158*a32d241bSJagpal Singh Gill     }
159*a32d241bSJagpal Singh Gill     catch (std::exception& e)
160*a32d241bSJagpal Singh Gill     {
161*a32d241bSJagpal Singh Gill         error(
162*a32d241bSJagpal Singh Gill             "Failed to read holding registers for {DEVICE_ADDRESS} with {ERROR}",
163*a32d241bSJagpal Singh Gill             "DEVICE_ADDRESS", deviceAddress, "ERROR", e);
164*a32d241bSJagpal Singh Gill         co_return false;
165*a32d241bSJagpal Singh Gill     }
166*a32d241bSJagpal Singh Gill 
167*a32d241bSJagpal Singh Gill     co_return true;
168*a32d241bSJagpal Singh Gill }
169*a32d241bSJagpal Singh Gill 
writeRequest(uint8_t deviceAddress,Message & request)170*a32d241bSJagpal Singh Gill auto Modbus::writeRequest(uint8_t deviceAddress, Message& request)
171*a32d241bSJagpal Singh Gill     -> sdbusplus::async::task<bool>
172*a32d241bSJagpal Singh Gill {
173*a32d241bSJagpal Singh Gill     printMessage(request.raw.data(), request.len);
174*a32d241bSJagpal Singh Gill 
175*a32d241bSJagpal Singh Gill     // Flush the input & output buffers for the fd
176*a32d241bSJagpal Singh Gill     tcflush(fd, TCIOFLUSH);
177*a32d241bSJagpal Singh Gill     auto ret = write(fd, request.raw.data(), request.len);
178*a32d241bSJagpal Singh Gill     if ((size_t)ret != request.len)
179*a32d241bSJagpal Singh Gill     {
180*a32d241bSJagpal Singh Gill         error("Failed to send request to device {DEVICE_ADDRESS} with {ERROR}",
181*a32d241bSJagpal Singh Gill               "DEVICE_ADDRESS", deviceAddress, "ERROR", strerror(errno));
182*a32d241bSJagpal Singh Gill         co_return false;
183*a32d241bSJagpal Singh Gill     }
184*a32d241bSJagpal Singh Gill 
185*a32d241bSJagpal Singh Gill     co_return true;
186*a32d241bSJagpal Singh Gill }
187*a32d241bSJagpal Singh Gill 
readResponse(uint8_t deviceAddress,Message & response,uint8_t expectedResponseCode)188*a32d241bSJagpal Singh Gill auto Modbus::readResponse(uint8_t deviceAddress, Message& response,
189*a32d241bSJagpal Singh Gill                           uint8_t expectedResponseCode)
190*a32d241bSJagpal Singh Gill     -> sdbusplus::async::task<bool>
191*a32d241bSJagpal Singh Gill {
192*a32d241bSJagpal Singh Gill     int expectedLen = response.len;
193*a32d241bSJagpal Singh Gill 
194*a32d241bSJagpal Singh Gill     do
195*a32d241bSJagpal Singh Gill     {
196*a32d241bSJagpal Singh Gill         debug("Waiting for response for {DEVICE_ADDRESS} with {EXPECTED} bytes",
197*a32d241bSJagpal Singh Gill               "DEVICE_ADDRESS", deviceAddress, "EXPECTED", expectedLen);
198*a32d241bSJagpal Singh Gill         co_await fdioInstance.next();
199*a32d241bSJagpal Singh Gill         // TODO: Handle FD timeout in case of no response
200*a32d241bSJagpal Singh Gill         auto ret = read(fd, response.raw.data() + response.len - expectedLen,
201*a32d241bSJagpal Singh Gill                         expectedLen);
202*a32d241bSJagpal Singh Gill         if (ret < 0)
203*a32d241bSJagpal Singh Gill         {
204*a32d241bSJagpal Singh Gill             error(
205*a32d241bSJagpal Singh Gill                 "Failed to read response for device {DEVICE_ADDRESS} with {ERROR}",
206*a32d241bSJagpal Singh Gill                 "DEVICE_ADDRESS", deviceAddress, "ERROR", strerror(errno));
207*a32d241bSJagpal Singh Gill             co_return false;
208*a32d241bSJagpal Singh Gill         }
209*a32d241bSJagpal Singh Gill 
210*a32d241bSJagpal Singh Gill         debug("Received response for {DEVICE_ADDRESS} with {SIZE}",
211*a32d241bSJagpal Singh Gill               "DEVICE_ADDRESS", deviceAddress, "SIZE", ret);
212*a32d241bSJagpal Singh Gill 
213*a32d241bSJagpal Singh Gill         printMessage(response.raw.data() + response.len - expectedLen, ret);
214*a32d241bSJagpal Singh Gill 
215*a32d241bSJagpal Singh Gill         expectedLen -= ret;
216*a32d241bSJagpal Singh Gill 
217*a32d241bSJagpal Singh Gill         if (expectedLen > 0)
218*a32d241bSJagpal Singh Gill         {
219*a32d241bSJagpal Singh Gill             debug(
220*a32d241bSJagpal Singh Gill                 "Read response for device {DEVICE_ADDRESS} with {EXPECTED} bytes remaining",
221*a32d241bSJagpal Singh Gill                 "DEVICE_ADDRESS", deviceAddress, "EXPECTED", expectedLen);
222*a32d241bSJagpal Singh Gill         }
223*a32d241bSJagpal Singh Gill         if (ret >= 2 && response.functionCode != expectedResponseCode)
224*a32d241bSJagpal Singh Gill         {
225*a32d241bSJagpal Singh Gill             // Update the length of the expected response to received error
226*a32d241bSJagpal Singh Gill             // message size
227*a32d241bSJagpal Singh Gill             response.len = ret;
228*a32d241bSJagpal Singh Gill             error("Received error response {CODE} for device {DEVICE_ADDRESS}",
229*a32d241bSJagpal Singh Gill                   "CODE", response.raw[1], "DEVICE_ADDRESS", deviceAddress);
230*a32d241bSJagpal Singh Gill             co_return false;
231*a32d241bSJagpal Singh Gill         }
232*a32d241bSJagpal Singh Gill     } while (expectedLen > 0);
233*a32d241bSJagpal Singh Gill 
234*a32d241bSJagpal Singh Gill     if (rtsDelay)
235*a32d241bSJagpal Singh Gill     {
236*a32d241bSJagpal Singh Gill         // Asynchronously sleep for rts_delay milliseconds in case bus needs
237*a32d241bSJagpal Singh Gill         // to be idle after each transaction
238*a32d241bSJagpal Singh Gill         co_await sdbusplus::async::sleep_for(
239*a32d241bSJagpal Singh Gill             ctx, std::chrono::milliseconds(rtsDelay));
240*a32d241bSJagpal Singh Gill     }
241*a32d241bSJagpal Singh Gill 
242*a32d241bSJagpal Singh Gill     co_return true;
243*a32d241bSJagpal Singh Gill }
244*a32d241bSJagpal Singh Gill 
245*a32d241bSJagpal Singh Gill } // namespace phosphor::modbus::rtu
246