blob: 0b9faa11a25e34ae301abaddd2f41bc53bde4b65 [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>
Patrick Williamsd8aab2a2023-04-21 11:15:54 -050010#include <phosphor-logging/elog-errors.hpp>
Chris Caina8857c52021-01-27 11:53:05 -060011#include <phosphor-logging/elog.hpp>
12#include <phosphor-logging/log.hpp>
George Liub5ca1012021-09-10 12:53:11 +080013
14#include <algorithm>
Patrick Williams48002492024-02-13 21:43:32 -060015#include <format>
George Liub5ca1012021-09-10 12:53:11 +080016#include <memory>
Chris Caina8857c52021-01-27 11:53:05 -060017#include <string>
18
Chris Cainf9fd1e52022-10-04 13:39:24 -050019// #define TRACE_PACKETS
Chris Caina8857c52021-01-27 11:53:05 -060020
21namespace open_power
22{
23namespace occ
24{
25
26using namespace phosphor::logging;
27
28// Trace block of data in hex
29void dump_hex(const std::vector<std::uint8_t>& data,
30 const unsigned int data_len)
31{
32 unsigned int dump_length = data.size();
33 if ((data_len > 0) && (data_len < dump_length))
34 {
35 dump_length = data_len;
36 }
37 std::string s;
38 for (uint32_t i = 0; i < dump_length; i++)
39 {
40 if (i % 16 == 0)
41 {
Patrick Williams48002492024-02-13 21:43:32 -060042 s += std::format("{:04X}: ", i);
Chris Caina8857c52021-01-27 11:53:05 -060043 }
44 else if (i % 4 == 0)
45 {
46 s += " ";
47 }
48
Patrick Williams48002492024-02-13 21:43:32 -060049 s += std::format("{:02X}", data.at(i));
Chris Caina8857c52021-01-27 11:53:05 -060050
51 if ((i % 16 == 15) || (i == (dump_length - 1)))
52 {
53 log<level::INFO>(s.c_str());
54 s.clear();
55 }
56 }
57}
58
George Liuf3b75142021-06-10 11:22:50 +080059OccCommand::OccCommand(uint8_t instance, const char* path) :
60 occInstance(instance), path(path),
Chris Caina8857c52021-01-27 11:53:05 -060061 devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
62 activeStatusSignal(
George Liuf3b75142021-06-10 11:22:50 +080063 utils::getBus(),
64 sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
Chris Caina8857c52021-01-27 11:53:05 -060065 std::bind(std::mem_fn(&OccCommand::activeStatusEvent), this,
66 std::placeholders::_1))
67{
68 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -060069 std::format("OccCommand::OccCommand(path={}, devicePath={}", this->path,
Chris Caina8857c52021-01-27 11:53:05 -060070 devicePath)
71 .c_str());
72}
73
74void OccCommand::openDevice()
75{
76 using namespace sdbusplus::org::open_power::OCC::Device::Error;
77
78 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -060079 std::format("OccCommand::openDevice: calling open {}", devicePath)
Chris Caina8857c52021-01-27 11:53:05 -060080 .c_str());
81 fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
82 if (fd < 0)
83 {
Chris Cainc567dc82022-04-01 15:09:17 -050084 const int openErrno = errno;
Chris Caina8857c52021-01-27 11:53:05 -060085 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -060086 std::format(
Chris Caina8857c52021-01-27 11:53:05 -060087 "OccCommand::openDevice: open failed (errno={}, path={})",
Chris Cainc567dc82022-04-01 15:09:17 -050088 openErrno, devicePath)
Chris Caina8857c52021-01-27 11:53:05 -060089 .c_str());
Chris Caina8857c52021-01-27 11:53:05 -060090 }
91 else
92 {
93 log<level::DEBUG>("OccCommand::openDevice: open success");
94 }
95
96 return;
97}
98
99void OccCommand::closeDevice()
100{
101 if (fd >= 0)
102 {
103 log<level::DEBUG>("OccCommand::closeDevice: calling close()");
104 close(fd);
105 fd = -1;
106 }
107}
108
109CmdStatus OccCommand::send(const std::vector<uint8_t>& command,
110 std::vector<uint8_t>& response)
111{
112 using namespace sdbusplus::org::open_power::OCC::Device::Error;
113 CmdStatus status = CmdStatus::FAILURE;
114
115 response.clear();
116
117 log<level::DEBUG>("OccCommand::send: calling openDevice()");
118 openDevice();
119
120 if (fd < 0)
121 {
122 // OCC is inactive; empty response
Chris Cainc567dc82022-04-01 15:09:17 -0500123 return CmdStatus::COMM_FAILURE;
Chris Caina8857c52021-01-27 11:53:05 -0600124 }
125
Chris Cainc567dc82022-04-01 15:09:17 -0500126 const uint8_t cmd_type = command[0];
Chris Caina8857c52021-01-27 11:53:05 -0600127#ifdef TRACE_PACKETS
Chris Caina8857c52021-01-27 11:53:05 -0600128 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600129 std::format("OCC{}: Sending 0x{:02X} command (length={}, {})",
Chris Caina8857c52021-01-27 11:53:05 -0600130 occInstance, cmd_type, command.size(), devicePath)
131 .c_str());
132 dump_hex(command);
133#else
134 log<level::DEBUG>("OccCommand::send: calling write()");
135#endif
Chris Caina8857c52021-01-27 11:53:05 -0600136
Chris Cain78e86012021-03-04 16:15:31 -0600137 int retries = 1; // Allow a retry if a command fails to get valid response
138 do
Chris Caina8857c52021-01-27 11:53:05 -0600139 {
Chris Cain78e86012021-03-04 16:15:31 -0600140 auto rc = write(fd, command.data(), command.size());
Chris Cainc567dc82022-04-01 15:09:17 -0500141 const int writeErrno = errno;
Chris Cain78e86012021-03-04 16:15:31 -0600142 if ((rc < 0) || (rc != (int)command.size()))
Chris Caina8857c52021-01-27 11:53:05 -0600143 {
Chris Cainc567dc82022-04-01 15:09:17 -0500144 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600145 std::format(
Chris Cainc567dc82022-04-01 15:09:17 -0500146 "OccCommand::send: write(OCC{}, command:0x{:02X}) failed with errno={} (retries={})",
147 occInstance, cmd_type, writeErrno, retries)
148 .c_str());
149 status = CmdStatus::COMM_FAILURE;
150 // retry if available
151 continue;
Chris Caina8857c52021-01-27 11:53:05 -0600152 }
Chris Cain78e86012021-03-04 16:15:31 -0600153 else
154 {
155 log<level::DEBUG>("OccCommand::send: write succeeded");
156 }
Chris Caina8857c52021-01-27 11:53:05 -0600157
Chris Cain78e86012021-03-04 16:15:31 -0600158 // Now read the response. This would be the content of occ-sram
159 while (1)
160 {
161 uint8_t data{};
162 auto len = read(fd, &data, sizeof(data));
Chris Cainc567dc82022-04-01 15:09:17 -0500163 const int readErrno = errno;
Chris Cain78e86012021-03-04 16:15:31 -0600164 if (len > 0)
165 {
166 response.emplace_back(data);
167 }
Chris Cainc567dc82022-04-01 15:09:17 -0500168 else if (len < 0 && readErrno == EAGAIN)
Chris Cain78e86012021-03-04 16:15:31 -0600169 {
170 // We may have data coming still.
171 // This driver does not need a sleep for a retry.
172 continue;
173 }
174 else if (len == 0)
175 {
176 log<level::DEBUG>("OccCommand::send: read completed");
177 // We have read all that we can.
178 status = CmdStatus::SUCCESS;
179 break;
180 }
181 else
182 {
Chris Cainc567dc82022-04-01 15:09:17 -0500183 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600184 std::format(
Chris Cainc567dc82022-04-01 15:09:17 -0500185 "OccCommand::send: read(OCC{}, command:0x{:02X}) failed with errno={} (rspSize={}, retries={})",
186 occInstance, cmd_type, readErrno, response.size(),
187 retries)
188 .c_str());
189 status = CmdStatus::COMM_FAILURE;
190 break;
Chris Cain78e86012021-03-04 16:15:31 -0600191 }
192 }
Chris Cainc567dc82022-04-01 15:09:17 -0500193 if (status != CmdStatus::SUCCESS)
194 {
195 // retry if available
196 continue;
197 }
Chris Cain78e86012021-03-04 16:15:31 -0600198
199 if (response.size() > 2)
200 {
Chris Caina8857c52021-01-27 11:53:05 -0600201#ifdef TRACE_PACKETS
Chris Cain78e86012021-03-04 16:15:31 -0600202 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600203 std::format(
Chris Cain78e86012021-03-04 16:15:31 -0600204 "OCC{}: Received 0x{:02X} response (length={} w/checksum)",
205 occInstance, cmd_type, response.size())
206 .c_str());
207 dump_hex(response, 64);
Chris Caina8857c52021-01-27 11:53:05 -0600208#endif
209
Chris Cain78e86012021-03-04 16:15:31 -0600210 // Validate checksum (last 2 bytes of response)
211 const unsigned int csumIndex = response.size() - 2;
Patrick Williamsd7542c82024-08-16 15:20:28 -0400212 const uint32_t rspChecksum =
213 (response[csumIndex] << 8) + response[csumIndex + 1];
Chris Cain358d3962024-08-23 15:29:38 -0500214 // OCC checksum is the 2 byte sum of data (ignoring overflow)
215 uint16_t calcChecksum = 0;
Chris Cain78e86012021-03-04 16:15:31 -0600216 for (unsigned int index = 0; index < csumIndex; ++index)
217 {
218 calcChecksum += response[index];
219 }
Chris Cain78e86012021-03-04 16:15:31 -0600220 if (calcChecksum != rspChecksum)
221 {
222 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600223 std::format("OCC{}: Checksum Mismatch: response "
Chris Cain78e86012021-03-04 16:15:31 -0600224 "0x{:04X}, calculated 0x{:04X}",
225 occInstance, rspChecksum, calcChecksum)
226 .c_str());
227 dump_hex(response);
Chris Cainc567dc82022-04-01 15:09:17 -0500228 status = CmdStatus::COMM_FAILURE;
Chris Cain78e86012021-03-04 16:15:31 -0600229 }
230 else
231 {
232 // Validate response was for the specified command
233 if (command[0] == response[1])
234 {
235 // Valid response received
236
237 // Strip off 2 byte checksum
238 response.pop_back();
239 response.pop_back();
240 break;
241 }
242 else
243 {
244 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600245 std::format(
Chris Cain78e86012021-03-04 16:15:31 -0600246 "OccCommand::send: Response command mismatch "
247 "(sent: "
248 "0x{:02X}, rsp: 0x{:02X}, rsp seq#: 0x{:02X}",
249 command[0], response[1], response[0])
250 .c_str());
251 dump_hex(response, 64);
252 }
253 }
Chris Caina8857c52021-01-27 11:53:05 -0600254 }
255 else
256 {
Chris Cain78e86012021-03-04 16:15:31 -0600257 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600258 std::format(
Chris Cain78e86012021-03-04 16:15:31 -0600259 "OccCommand::send: Invalid OCC{} response length: {}",
260 occInstance, response.size())
261 .c_str());
262 status = CmdStatus::FAILURE;
263 dump_hex(response);
Chris Caina8857c52021-01-27 11:53:05 -0600264 }
Chris Cain78e86012021-03-04 16:15:31 -0600265
266 if (retries > 0)
267 {
268 log<level::ERR>("OccCommand::send: Command will be retried");
269 response.clear();
270 }
Chris Cain78e86012021-03-04 16:15:31 -0600271 } while (retries-- > 0);
Chris Caina8857c52021-01-27 11:53:05 -0600272
273 closeDevice();
274
275 return status;
276}
277
278// Called at OCC Status change signal
Patrick Williamsaf408082022-07-22 19:26:54 -0500279void OccCommand::activeStatusEvent(sdbusplus::message_t& msg)
Chris Caina8857c52021-01-27 11:53:05 -0600280{
281 std::string statusInterface;
282 std::map<std::string, std::variant<bool>> msgData;
283 msg.read(statusInterface, msgData);
284
285 auto propertyMap = msgData.find("OccActive");
286 if (propertyMap != msgData.end())
287 {
288 // Extract the OccActive property
289 if (std::get<bool>(propertyMap->second))
290 {
291 occActive = true;
292 }
293 else
294 {
295 occActive = false;
296
297 this->closeDevice();
298 }
299 }
300 return;
301}
302
303} // namespace occ
304} // namespace open_power