blob: 16201566fbf93c4896c364d3b04bf0073acea1dc [file] [log] [blame]
Chris Caina8857c52021-01-27 11:53:05 -06001#include "config.h"
2
3#include "occ_command.hpp"
4
5#include "elog-errors.hpp"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <fmt/core.h>
10#include <unistd.h>
11
Chris Caina8857c52021-01-27 11:53:05 -060012#include <org/open_power/OCC/Device/error.hpp>
13#include <phosphor-logging/elog.hpp>
14#include <phosphor-logging/log.hpp>
George Liub5ca1012021-09-10 12:53:11 +080015
16#include <algorithm>
17#include <memory>
Chris Caina8857c52021-01-27 11:53:05 -060018#include <string>
19
Chris Cainf9fd1e52022-10-04 13:39:24 -050020// #define TRACE_PACKETS
Chris Caina8857c52021-01-27 11:53:05 -060021
22namespace open_power
23{
24namespace occ
25{
26
27using namespace phosphor::logging;
28
29// Trace block of data in hex
30void dump_hex(const std::vector<std::uint8_t>& data,
31 const unsigned int data_len)
32{
33 unsigned int dump_length = data.size();
34 if ((data_len > 0) && (data_len < dump_length))
35 {
36 dump_length = data_len;
37 }
38 std::string s;
39 for (uint32_t i = 0; i < dump_length; i++)
40 {
41 if (i % 16 == 0)
42 {
43 s += fmt::format("{:04X}: ", i);
44 }
45 else if (i % 4 == 0)
46 {
47 s += " ";
48 }
49
50 s += fmt::format("{:02X}", data.at(i));
51
52 if ((i % 16 == 15) || (i == (dump_length - 1)))
53 {
54 log<level::INFO>(s.c_str());
55 s.clear();
56 }
57 }
58}
59
George Liuf3b75142021-06-10 11:22:50 +080060OccCommand::OccCommand(uint8_t instance, const char* path) :
61 occInstance(instance), path(path),
Chris Caina8857c52021-01-27 11:53:05 -060062 devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
63 activeStatusSignal(
George Liuf3b75142021-06-10 11:22:50 +080064 utils::getBus(),
65 sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
Chris Caina8857c52021-01-27 11:53:05 -060066 std::bind(std::mem_fn(&OccCommand::activeStatusEvent), this,
67 std::placeholders::_1))
68{
69 log<level::DEBUG>(
70 fmt::format("OccCommand::OccCommand(path={}, devicePath={}", this->path,
71 devicePath)
72 .c_str());
73}
74
75void OccCommand::openDevice()
76{
77 using namespace sdbusplus::org::open_power::OCC::Device::Error;
78
79 log<level::DEBUG>(
80 fmt::format("OccCommand::openDevice: calling open {}", devicePath)
81 .c_str());
82 fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
83 if (fd < 0)
84 {
Chris Cainc567dc82022-04-01 15:09:17 -050085 const int openErrno = errno;
Chris Caina8857c52021-01-27 11:53:05 -060086 log<level::ERR>(
87 fmt::format(
88 "OccCommand::openDevice: open failed (errno={}, path={})",
Chris Cainc567dc82022-04-01 15:09:17 -050089 openErrno, devicePath)
Chris Caina8857c52021-01-27 11:53:05 -060090 .c_str());
Chris Caina8857c52021-01-27 11:53:05 -060091 }
92 else
93 {
94 log<level::DEBUG>("OccCommand::openDevice: open success");
95 }
96
97 return;
98}
99
100void OccCommand::closeDevice()
101{
102 if (fd >= 0)
103 {
104 log<level::DEBUG>("OccCommand::closeDevice: calling close()");
105 close(fd);
106 fd = -1;
107 }
108}
109
110CmdStatus OccCommand::send(const std::vector<uint8_t>& command,
111 std::vector<uint8_t>& response)
112{
113 using namespace sdbusplus::org::open_power::OCC::Device::Error;
114 CmdStatus status = CmdStatus::FAILURE;
115
116 response.clear();
117
118 log<level::DEBUG>("OccCommand::send: calling openDevice()");
119 openDevice();
120
121 if (fd < 0)
122 {
123 // OCC is inactive; empty response
Chris Cainc567dc82022-04-01 15:09:17 -0500124 return CmdStatus::COMM_FAILURE;
Chris Caina8857c52021-01-27 11:53:05 -0600125 }
126
Chris Cainc567dc82022-04-01 15:09:17 -0500127 const uint8_t cmd_type = command[0];
Chris Caina8857c52021-01-27 11:53:05 -0600128#ifdef TRACE_PACKETS
Chris Caina8857c52021-01-27 11:53:05 -0600129 log<level::INFO>(
130 fmt::format("OCC{}: Sending 0x{:02X} command (length={}, {})",
131 occInstance, cmd_type, command.size(), devicePath)
132 .c_str());
133 dump_hex(command);
134#else
135 log<level::DEBUG>("OccCommand::send: calling write()");
136#endif
Chris Caina8857c52021-01-27 11:53:05 -0600137
Chris Cain78e86012021-03-04 16:15:31 -0600138 int retries = 1; // Allow a retry if a command fails to get valid response
139 do
Chris Caina8857c52021-01-27 11:53:05 -0600140 {
Chris Cain78e86012021-03-04 16:15:31 -0600141 auto rc = write(fd, command.data(), command.size());
Chris Cainc567dc82022-04-01 15:09:17 -0500142 const int writeErrno = errno;
Chris Cain78e86012021-03-04 16:15:31 -0600143 if ((rc < 0) || (rc != (int)command.size()))
Chris Caina8857c52021-01-27 11:53:05 -0600144 {
Chris Cainc567dc82022-04-01 15:09:17 -0500145 log<level::ERR>(
146 fmt::format(
147 "OccCommand::send: write(OCC{}, command:0x{:02X}) failed with errno={} (retries={})",
148 occInstance, cmd_type, writeErrno, retries)
149 .c_str());
150 status = CmdStatus::COMM_FAILURE;
151 // retry if available
152 continue;
Chris Caina8857c52021-01-27 11:53:05 -0600153 }
Chris Cain78e86012021-03-04 16:15:31 -0600154 else
155 {
156 log<level::DEBUG>("OccCommand::send: write succeeded");
157 }
Chris Caina8857c52021-01-27 11:53:05 -0600158
Chris Cain78e86012021-03-04 16:15:31 -0600159 // Now read the response. This would be the content of occ-sram
160 while (1)
161 {
162 uint8_t data{};
163 auto len = read(fd, &data, sizeof(data));
Chris Cainc567dc82022-04-01 15:09:17 -0500164 const int readErrno = errno;
Chris Cain78e86012021-03-04 16:15:31 -0600165 if (len > 0)
166 {
167 response.emplace_back(data);
168 }
Chris Cainc567dc82022-04-01 15:09:17 -0500169 else if (len < 0 && readErrno == EAGAIN)
Chris Cain78e86012021-03-04 16:15:31 -0600170 {
171 // We may have data coming still.
172 // This driver does not need a sleep for a retry.
173 continue;
174 }
175 else if (len == 0)
176 {
177 log<level::DEBUG>("OccCommand::send: read completed");
178 // We have read all that we can.
179 status = CmdStatus::SUCCESS;
180 break;
181 }
182 else
183 {
Chris Cainc567dc82022-04-01 15:09:17 -0500184 log<level::ERR>(
185 fmt::format(
186 "OccCommand::send: read(OCC{}, command:0x{:02X}) failed with errno={} (rspSize={}, retries={})",
187 occInstance, cmd_type, readErrno, response.size(),
188 retries)
189 .c_str());
190 status = CmdStatus::COMM_FAILURE;
191 break;
Chris Cain78e86012021-03-04 16:15:31 -0600192 }
193 }
Chris Cainc567dc82022-04-01 15:09:17 -0500194 if (status != CmdStatus::SUCCESS)
195 {
196 // retry if available
197 continue;
198 }
Chris Cain78e86012021-03-04 16:15:31 -0600199
200 if (response.size() > 2)
201 {
Chris Caina8857c52021-01-27 11:53:05 -0600202#ifdef TRACE_PACKETS
Chris Cain78e86012021-03-04 16:15:31 -0600203 log<level::INFO>(
204 fmt::format(
205 "OCC{}: Received 0x{:02X} response (length={} w/checksum)",
206 occInstance, cmd_type, response.size())
207 .c_str());
208 dump_hex(response, 64);
Chris Caina8857c52021-01-27 11:53:05 -0600209#endif
210
Chris Cain78e86012021-03-04 16:15:31 -0600211 // Validate checksum (last 2 bytes of response)
212 const unsigned int csumIndex = response.size() - 2;
213 const uint32_t rspChecksum =
214 (response[csumIndex] << 8) + response[csumIndex + 1];
215 uint32_t calcChecksum = 0;
216 for (unsigned int index = 0; index < csumIndex; ++index)
217 {
218 calcChecksum += response[index];
219 }
220 while (calcChecksum > 0xFFFF)
221 {
222 calcChecksum = (calcChecksum & 0xFFFF) + (calcChecksum >> 16);
223 }
224 if (calcChecksum != rspChecksum)
225 {
226 log<level::ERR>(
227 fmt::format("OCC{}: Checksum Mismatch: response "
228 "0x{:04X}, calculated 0x{:04X}",
229 occInstance, rspChecksum, calcChecksum)
230 .c_str());
231 dump_hex(response);
Chris Cainc567dc82022-04-01 15:09:17 -0500232 status = CmdStatus::COMM_FAILURE;
Chris Cain78e86012021-03-04 16:15:31 -0600233 }
234 else
235 {
236 // Validate response was for the specified command
237 if (command[0] == response[1])
238 {
239 // Valid response received
240
241 // Strip off 2 byte checksum
242 response.pop_back();
243 response.pop_back();
244 break;
245 }
246 else
247 {
248 log<level::ERR>(
249 fmt::format(
250 "OccCommand::send: Response command mismatch "
251 "(sent: "
252 "0x{:02X}, rsp: 0x{:02X}, rsp seq#: 0x{:02X}",
253 command[0], response[1], response[0])
254 .c_str());
255 dump_hex(response, 64);
256 }
257 }
Chris Caina8857c52021-01-27 11:53:05 -0600258 }
259 else
260 {
Chris Cain78e86012021-03-04 16:15:31 -0600261 log<level::ERR>(
262 fmt::format(
263 "OccCommand::send: Invalid OCC{} response length: {}",
264 occInstance, response.size())
265 .c_str());
266 status = CmdStatus::FAILURE;
267 dump_hex(response);
Chris Caina8857c52021-01-27 11:53:05 -0600268 }
Chris Cain78e86012021-03-04 16:15:31 -0600269
270 if (retries > 0)
271 {
272 log<level::ERR>("OccCommand::send: Command will be retried");
273 response.clear();
274 }
Chris Cain78e86012021-03-04 16:15:31 -0600275 } while (retries-- > 0);
Chris Caina8857c52021-01-27 11:53:05 -0600276
277 closeDevice();
278
279 return status;
280}
281
282// Called at OCC Status change signal
Patrick Williamsaf408082022-07-22 19:26:54 -0500283void OccCommand::activeStatusEvent(sdbusplus::message_t& msg)
Chris Caina8857c52021-01-27 11:53:05 -0600284{
285 std::string statusInterface;
286 std::map<std::string, std::variant<bool>> msgData;
287 msg.read(statusInterface, msgData);
288
289 auto propertyMap = msgData.find("OccActive");
290 if (propertyMap != msgData.end())
291 {
292 // Extract the OccActive property
293 if (std::get<bool>(propertyMap->second))
294 {
295 occActive = true;
296 }
297 else
298 {
299 occActive = false;
300
301 this->closeDevice();
302 }
303 }
304 return;
305}
306
307} // namespace occ
308} // namespace open_power