blob: 30d84246ee6ef3a348cceecf81f14a1a12acdb89 [file] [log] [blame]
Chris Caina8857c52021-01-27 11:53:05 -06001#include "config.h"
2
3#include "occ_command.hpp"
4
Chris Caina8857c52021-01-27 11:53:05 -06005#include <errno.h>
6#include <fcntl.h>
Chris Caina8857c52021-01-27 11:53:05 -06007#include <unistd.h>
8
Chris Caina8857c52021-01-27 11:53:05 -06009#include <org/open_power/OCC/Device/error.hpp>
Chris Cain37abe9b2024-10-31 17:20:31 -050010#include <phosphor-logging/lg2.hpp>
George Liub5ca1012021-09-10 12:53:11 +080011
12#include <algorithm>
Patrick Williams48002492024-02-13 21:43:32 -060013#include <format>
Chris Caina8857c52021-01-27 11:53:05 -060014#include <string>
15
Chris Cainf9fd1e52022-10-04 13:39:24 -050016// #define TRACE_PACKETS
Chris Caina8857c52021-01-27 11:53:05 -060017
18namespace open_power
19{
20namespace occ
21{
22
Chris Caina8857c52021-01-27 11:53:05 -060023// Trace block of data in hex
24void dump_hex(const std::vector<std::uint8_t>& data,
25 const unsigned int data_len)
26{
27 unsigned int dump_length = data.size();
28 if ((data_len > 0) && (data_len < dump_length))
29 {
30 dump_length = data_len;
31 }
32 std::string s;
33 for (uint32_t i = 0; i < dump_length; i++)
34 {
35 if (i % 16 == 0)
36 {
Patrick Williams48002492024-02-13 21:43:32 -060037 s += std::format("{:04X}: ", i);
Chris Caina8857c52021-01-27 11:53:05 -060038 }
39 else if (i % 4 == 0)
40 {
41 s += " ";
42 }
43
Patrick Williams48002492024-02-13 21:43:32 -060044 s += std::format("{:02X}", data.at(i));
Chris Caina8857c52021-01-27 11:53:05 -060045
46 if ((i % 16 == 15) || (i == (dump_length - 1)))
47 {
Chris Cain37abe9b2024-10-31 17:20:31 -050048 lg2::info("{STRING}", "STRING", s);
Chris Caina8857c52021-01-27 11:53:05 -060049 s.clear();
50 }
51 }
52}
53
George Liuf3b75142021-06-10 11:22:50 +080054OccCommand::OccCommand(uint8_t instance, const char* path) :
55 occInstance(instance), path(path),
Chris Caina8857c52021-01-27 11:53:05 -060056 devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
57 activeStatusSignal(
George Liuf3b75142021-06-10 11:22:50 +080058 utils::getBus(),
59 sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
Chris Caina8857c52021-01-27 11:53:05 -060060 std::bind(std::mem_fn(&OccCommand::activeStatusEvent), this,
61 std::placeholders::_1))
62{
Chris Cain37abe9b2024-10-31 17:20:31 -050063 lg2::debug("OccCommand::OccCommand(path={PATH}, devicePath={DEVICE}",
64 "PATH", this->path, "DEVICE", devicePath);
Chris Caina8857c52021-01-27 11:53:05 -060065}
66
67void OccCommand::openDevice()
68{
69 using namespace sdbusplus::org::open_power::OCC::Device::Error;
70
Chris Cain37abe9b2024-10-31 17:20:31 -050071 lg2::debug("OccCommand::openDevice: calling open {PATH}", "PATH",
72 devicePath);
Chris Caina8857c52021-01-27 11:53:05 -060073 fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
74 if (fd < 0)
75 {
Chris Cainc567dc82022-04-01 15:09:17 -050076 const int openErrno = errno;
Chris Cain37abe9b2024-10-31 17:20:31 -050077 lg2::error(
78 "OccCommand::openDevice: open failed (errno={ERR}, path={PATH})",
79 "ERR", openErrno, "PATH", devicePath);
Chris Caina8857c52021-01-27 11:53:05 -060080 }
81 else
82 {
Chris Cain37abe9b2024-10-31 17:20:31 -050083 lg2::debug("OccCommand::openDevice: open success");
Chris Caina8857c52021-01-27 11:53:05 -060084 }
85
86 return;
87}
88
89void OccCommand::closeDevice()
90{
91 if (fd >= 0)
92 {
Chris Cain37abe9b2024-10-31 17:20:31 -050093 lg2::debug("OccCommand::closeDevice: calling close()");
Chris Caina8857c52021-01-27 11:53:05 -060094 close(fd);
95 fd = -1;
96 }
97}
98
99CmdStatus OccCommand::send(const std::vector<uint8_t>& command,
100 std::vector<uint8_t>& response)
101{
102 using namespace sdbusplus::org::open_power::OCC::Device::Error;
103 CmdStatus status = CmdStatus::FAILURE;
104
105 response.clear();
106
Chris Cain37abe9b2024-10-31 17:20:31 -0500107 lg2::debug("OccCommand::send: calling openDevice()");
Chris Caina8857c52021-01-27 11:53:05 -0600108 openDevice();
109
110 if (fd < 0)
111 {
112 // OCC is inactive; empty response
Chris Cainc567dc82022-04-01 15:09:17 -0500113 return CmdStatus::COMM_FAILURE;
Chris Caina8857c52021-01-27 11:53:05 -0600114 }
115
Chris Cainc567dc82022-04-01 15:09:17 -0500116 const uint8_t cmd_type = command[0];
Chris Caina8857c52021-01-27 11:53:05 -0600117#ifdef TRACE_PACKETS
Chris Cain37abe9b2024-10-31 17:20:31 -0500118 lg2::info("OCC{INST}: Sending {CMD} command (length={LEN}, {PATH})", "INST",
119 occInstance, "CMD", lg2::hex, cmd_type, "LEN", command.size(),
120 "PATH", devicePath);
Chris Caina8857c52021-01-27 11:53:05 -0600121 dump_hex(command);
122#else
Chris Cain37abe9b2024-10-31 17:20:31 -0500123 lg2::debug("OccCommand::send: calling write()");
Chris Caina8857c52021-01-27 11:53:05 -0600124#endif
Chris Caina8857c52021-01-27 11:53:05 -0600125
Chris Cain78e86012021-03-04 16:15:31 -0600126 int retries = 1; // Allow a retry if a command fails to get valid response
127 do
Chris Caina8857c52021-01-27 11:53:05 -0600128 {
Chris Cain78e86012021-03-04 16:15:31 -0600129 auto rc = write(fd, command.data(), command.size());
Chris Cainc567dc82022-04-01 15:09:17 -0500130 const int writeErrno = errno;
Chris Cain78e86012021-03-04 16:15:31 -0600131 if ((rc < 0) || (rc != (int)command.size()))
Chris Caina8857c52021-01-27 11:53:05 -0600132 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500133 lg2::error(
134 "OccCommand::send: write(OCC{INST}, command:{CMD}) failed with errno={ERR} (retries={RETRIES})",
135 "INST", occInstance, "CMD", lg2::hex, cmd_type, "ERR",
136 writeErrno, "RETRIES", retries);
Chris Cainc567dc82022-04-01 15:09:17 -0500137 status = CmdStatus::COMM_FAILURE;
138 // retry if available
139 continue;
Chris Caina8857c52021-01-27 11:53:05 -0600140 }
Chris Cain78e86012021-03-04 16:15:31 -0600141 else
142 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500143 lg2::debug("OccCommand::send: write succeeded");
Chris Cain78e86012021-03-04 16:15:31 -0600144 }
Chris Caina8857c52021-01-27 11:53:05 -0600145
Chris Cain78e86012021-03-04 16:15:31 -0600146 // Now read the response. This would be the content of occ-sram
147 while (1)
148 {
149 uint8_t data{};
150 auto len = read(fd, &data, sizeof(data));
Chris Cainc567dc82022-04-01 15:09:17 -0500151 const int readErrno = errno;
Chris Cain78e86012021-03-04 16:15:31 -0600152 if (len > 0)
153 {
154 response.emplace_back(data);
155 }
Chris Cainc567dc82022-04-01 15:09:17 -0500156 else if (len < 0 && readErrno == EAGAIN)
Chris Cain78e86012021-03-04 16:15:31 -0600157 {
158 // We may have data coming still.
159 // This driver does not need a sleep for a retry.
160 continue;
161 }
162 else if (len == 0)
163 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500164 lg2::debug("OccCommand::send: read completed");
Chris Cain78e86012021-03-04 16:15:31 -0600165 // We have read all that we can.
166 status = CmdStatus::SUCCESS;
167 break;
168 }
169 else
170 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500171 lg2::error(
172 "OccCommand::send: read(OCC{INST}, command:{CMD) failed with errno={ERR} (rspSize={LEN}, retries={RETRIES})",
173 "INST", occInstance, "CMD", lg2::hex, cmd_type, "ERR",
174 readErrno, "LEN", response.size(), "RETRIES", retries);
Chris Cainc567dc82022-04-01 15:09:17 -0500175 status = CmdStatus::COMM_FAILURE;
176 break;
Chris Cain78e86012021-03-04 16:15:31 -0600177 }
178 }
Chris Cainc567dc82022-04-01 15:09:17 -0500179 if (status != CmdStatus::SUCCESS)
180 {
181 // retry if available
182 continue;
183 }
Chris Cain78e86012021-03-04 16:15:31 -0600184
185 if (response.size() > 2)
186 {
Chris Caina8857c52021-01-27 11:53:05 -0600187#ifdef TRACE_PACKETS
Chris Cain37abe9b2024-10-31 17:20:31 -0500188 lg2::info(
189 "OCC{INST}: Received {CMD} response (length={LEN} w/checksum)",
190 "INST", occInstance, "CMD", lg2::hex, cmd_type, "LEN",
191 response.size());
Chris Cain78e86012021-03-04 16:15:31 -0600192 dump_hex(response, 64);
Chris Caina8857c52021-01-27 11:53:05 -0600193#endif
194
Chris Cain78e86012021-03-04 16:15:31 -0600195 // Validate checksum (last 2 bytes of response)
196 const unsigned int csumIndex = response.size() - 2;
Patrick Williamsd7542c82024-08-16 15:20:28 -0400197 const uint32_t rspChecksum =
198 (response[csumIndex] << 8) + response[csumIndex + 1];
Chris Cain358d3962024-08-23 15:29:38 -0500199 // OCC checksum is the 2 byte sum of data (ignoring overflow)
200 uint16_t calcChecksum = 0;
Chris Cain78e86012021-03-04 16:15:31 -0600201 for (unsigned int index = 0; index < csumIndex; ++index)
202 {
203 calcChecksum += response[index];
204 }
Chris Cain78e86012021-03-04 16:15:31 -0600205 if (calcChecksum != rspChecksum)
206 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500207 lg2::error("OCC{INST}: Checksum Mismatch: response "
208 "{RCVD}, calculated {EXPECT}",
209 "INST", occInstance, "RCVD", rspChecksum, "EXPECT",
210 calcChecksum);
Chris Cain78e86012021-03-04 16:15:31 -0600211 dump_hex(response);
Chris Cainc567dc82022-04-01 15:09:17 -0500212 status = CmdStatus::COMM_FAILURE;
Chris Cain78e86012021-03-04 16:15:31 -0600213 }
214 else
215 {
216 // Validate response was for the specified command
217 if (command[0] == response[1])
218 {
219 // Valid response received
220
221 // Strip off 2 byte checksum
222 response.pop_back();
223 response.pop_back();
224 break;
225 }
226 else
227 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500228 lg2::error("OccCommand::send: Response command mismatch "
229 "(sent: "
230 "{CMD}, rsp: {STATUS}, rsp seq#: {SEQ}",
231 "CMD", lg2::hex, command[0], "STATUS", lg2::hex,
232 response[1], "SEQ", lg2::hex, response[0]);
Chris Cain78e86012021-03-04 16:15:31 -0600233 dump_hex(response, 64);
234 }
235 }
Chris Caina8857c52021-01-27 11:53:05 -0600236 }
237 else
238 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500239 lg2::error(
240 "OccCommand::send: Invalid OCC{INST} response length: {LEN}",
241 "INST", occInstance, "LEN", response.size());
Chris Cain78e86012021-03-04 16:15:31 -0600242 status = CmdStatus::FAILURE;
243 dump_hex(response);
Chris Caina8857c52021-01-27 11:53:05 -0600244 }
Chris Cain78e86012021-03-04 16:15:31 -0600245
246 if (retries > 0)
247 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500248 lg2::error("OccCommand::send: Command will be retried");
Chris Cain78e86012021-03-04 16:15:31 -0600249 response.clear();
250 }
Chris Cain78e86012021-03-04 16:15:31 -0600251 } while (retries-- > 0);
Chris Caina8857c52021-01-27 11:53:05 -0600252
253 closeDevice();
254
255 return status;
256}
257
258// Called at OCC Status change signal
Patrick Williamsaf408082022-07-22 19:26:54 -0500259void OccCommand::activeStatusEvent(sdbusplus::message_t& msg)
Chris Caina8857c52021-01-27 11:53:05 -0600260{
261 std::string statusInterface;
262 std::map<std::string, std::variant<bool>> msgData;
263 msg.read(statusInterface, msgData);
264
265 auto propertyMap = msgData.find("OccActive");
266 if (propertyMap != msgData.end())
267 {
268 // Extract the OccActive property
269 if (std::get<bool>(propertyMap->second))
270 {
271 occActive = true;
272 }
273 else
274 {
275 occActive = false;
276
277 this->closeDevice();
278 }
279 }
280 return;
281}
282
283} // namespace occ
284} // namespace open_power