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