1a8857c50SChris Cain #include "config.h"
2a8857c50SChris Cain
3a8857c50SChris Cain #include "occ_command.hpp"
4a8857c50SChris Cain
5a8857c50SChris Cain #include <errno.h>
6a8857c50SChris Cain #include <fcntl.h>
7a8857c50SChris Cain #include <unistd.h>
8a8857c50SChris Cain
9a8857c50SChris Cain #include <org/open_power/OCC/Device/error.hpp>
10*37abe9beSChris Cain #include <phosphor-logging/lg2.hpp>
11b5ca1015SGeorge Liu
12b5ca1015SGeorge Liu #include <algorithm>
1348002498SPatrick Williams #include <format>
14a8857c50SChris Cain #include <string>
15a8857c50SChris Cain
16a8857c50SChris Cain // #define TRACE_PACKETS
17a8857c50SChris Cain
18a8857c50SChris Cain namespace open_power
19a8857c50SChris Cain {
20a8857c50SChris Cain namespace occ
21a8857c50SChris Cain {
22a8857c50SChris Cain
23a8857c50SChris Cain // Trace block of data in hex
dump_hex(const std::vector<std::uint8_t> & data,const unsigned int data_len)24a8857c50SChris Cain void dump_hex(const std::vector<std::uint8_t>& data,
25a8857c50SChris Cain const unsigned int data_len)
26a8857c50SChris Cain {
27a8857c50SChris Cain unsigned int dump_length = data.size();
28a8857c50SChris Cain if ((data_len > 0) && (data_len < dump_length))
29a8857c50SChris Cain {
30a8857c50SChris Cain dump_length = data_len;
31a8857c50SChris Cain }
32a8857c50SChris Cain std::string s;
33a8857c50SChris Cain for (uint32_t i = 0; i < dump_length; i++)
34a8857c50SChris Cain {
35a8857c50SChris Cain if (i % 16 == 0)
36a8857c50SChris Cain {
3748002498SPatrick Williams s += std::format("{:04X}: ", i);
38a8857c50SChris Cain }
39a8857c50SChris Cain else if (i % 4 == 0)
40a8857c50SChris Cain {
41a8857c50SChris Cain s += " ";
42a8857c50SChris Cain }
43a8857c50SChris Cain
4448002498SPatrick Williams s += std::format("{:02X}", data.at(i));
45a8857c50SChris Cain
46a8857c50SChris Cain if ((i % 16 == 15) || (i == (dump_length - 1)))
47a8857c50SChris Cain {
48*37abe9beSChris Cain lg2::info("{STRING}", "STRING", s);
49a8857c50SChris Cain s.clear();
50a8857c50SChris Cain }
51a8857c50SChris Cain }
52a8857c50SChris Cain }
53a8857c50SChris Cain
OccCommand(uint8_t instance,const char * path)54f3b7514eSGeorge Liu OccCommand::OccCommand(uint8_t instance, const char* path) :
55f3b7514eSGeorge Liu occInstance(instance), path(path),
56a8857c50SChris Cain devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
57a8857c50SChris Cain activeStatusSignal(
58f3b7514eSGeorge Liu utils::getBus(),
59f3b7514eSGeorge Liu sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
60a8857c50SChris Cain std::bind(std::mem_fn(&OccCommand::activeStatusEvent), this,
61a8857c50SChris Cain std::placeholders::_1))
62a8857c50SChris Cain {
63*37abe9beSChris Cain lg2::debug("OccCommand::OccCommand(path={PATH}, devicePath={DEVICE}",
64*37abe9beSChris Cain "PATH", this->path, "DEVICE", devicePath);
65a8857c50SChris Cain }
66a8857c50SChris Cain
openDevice()67a8857c50SChris Cain void OccCommand::openDevice()
68a8857c50SChris Cain {
69a8857c50SChris Cain using namespace sdbusplus::org::open_power::OCC::Device::Error;
70a8857c50SChris Cain
71*37abe9beSChris Cain lg2::debug("OccCommand::openDevice: calling open {PATH}", "PATH",
72*37abe9beSChris Cain devicePath);
73a8857c50SChris Cain fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
74a8857c50SChris Cain if (fd < 0)
75a8857c50SChris Cain {
76c567dc8dSChris Cain const int openErrno = errno;
77*37abe9beSChris Cain lg2::error(
78*37abe9beSChris Cain "OccCommand::openDevice: open failed (errno={ERR}, path={PATH})",
79*37abe9beSChris Cain "ERR", openErrno, "PATH", devicePath);
80a8857c50SChris Cain }
81a8857c50SChris Cain else
82a8857c50SChris Cain {
83*37abe9beSChris Cain lg2::debug("OccCommand::openDevice: open success");
84a8857c50SChris Cain }
85a8857c50SChris Cain
86a8857c50SChris Cain return;
87a8857c50SChris Cain }
88a8857c50SChris Cain
closeDevice()89a8857c50SChris Cain void OccCommand::closeDevice()
90a8857c50SChris Cain {
91a8857c50SChris Cain if (fd >= 0)
92a8857c50SChris Cain {
93*37abe9beSChris Cain lg2::debug("OccCommand::closeDevice: calling close()");
94a8857c50SChris Cain close(fd);
95a8857c50SChris Cain fd = -1;
96a8857c50SChris Cain }
97a8857c50SChris Cain }
98a8857c50SChris Cain
send(const std::vector<uint8_t> & command,std::vector<uint8_t> & response)99a8857c50SChris Cain CmdStatus OccCommand::send(const std::vector<uint8_t>& command,
100a8857c50SChris Cain std::vector<uint8_t>& response)
101a8857c50SChris Cain {
102a8857c50SChris Cain using namespace sdbusplus::org::open_power::OCC::Device::Error;
103a8857c50SChris Cain CmdStatus status = CmdStatus::FAILURE;
104a8857c50SChris Cain
105a8857c50SChris Cain response.clear();
106a8857c50SChris Cain
107*37abe9beSChris Cain lg2::debug("OccCommand::send: calling openDevice()");
108a8857c50SChris Cain openDevice();
109a8857c50SChris Cain
110a8857c50SChris Cain if (fd < 0)
111a8857c50SChris Cain {
112a8857c50SChris Cain // OCC is inactive; empty response
113c567dc8dSChris Cain return CmdStatus::COMM_FAILURE;
114a8857c50SChris Cain }
115a8857c50SChris Cain
116c567dc8dSChris Cain const uint8_t cmd_type = command[0];
117a8857c50SChris Cain #ifdef TRACE_PACKETS
118*37abe9beSChris Cain lg2::info("OCC{INST}: Sending {CMD} command (length={LEN}, {PATH})", "INST",
119*37abe9beSChris Cain occInstance, "CMD", lg2::hex, cmd_type, "LEN", command.size(),
120*37abe9beSChris Cain "PATH", devicePath);
121a8857c50SChris Cain dump_hex(command);
122a8857c50SChris Cain #else
123*37abe9beSChris Cain lg2::debug("OccCommand::send: calling write()");
124a8857c50SChris Cain #endif
12578e86012SChris Cain
12678e86012SChris Cain int retries = 1; // Allow a retry if a command fails to get valid response
12778e86012SChris Cain do
12878e86012SChris Cain {
129a8857c50SChris Cain auto rc = write(fd, command.data(), command.size());
130c567dc8dSChris Cain const int writeErrno = errno;
131a8857c50SChris Cain if ((rc < 0) || (rc != (int)command.size()))
132a8857c50SChris Cain {
133*37abe9beSChris Cain lg2::error(
134*37abe9beSChris Cain "OccCommand::send: write(OCC{INST}, command:{CMD}) failed with errno={ERR} (retries={RETRIES})",
135*37abe9beSChris Cain "INST", occInstance, "CMD", lg2::hex, cmd_type, "ERR",
136*37abe9beSChris Cain writeErrno, "RETRIES", retries);
137c567dc8dSChris Cain status = CmdStatus::COMM_FAILURE;
138c567dc8dSChris Cain // retry if available
139c567dc8dSChris Cain continue;
140a8857c50SChris Cain }
141a8857c50SChris Cain else
142a8857c50SChris Cain {
143*37abe9beSChris Cain lg2::debug("OccCommand::send: write succeeded");
144a8857c50SChris Cain }
145a8857c50SChris Cain
146a8857c50SChris Cain // Now read the response. This would be the content of occ-sram
147a8857c50SChris Cain while (1)
148a8857c50SChris Cain {
149a8857c50SChris Cain uint8_t data{};
150a8857c50SChris Cain auto len = read(fd, &data, sizeof(data));
151c567dc8dSChris Cain const int readErrno = errno;
152a8857c50SChris Cain if (len > 0)
153a8857c50SChris Cain {
154a8857c50SChris Cain response.emplace_back(data);
155a8857c50SChris Cain }
156c567dc8dSChris Cain else if (len < 0 && readErrno == EAGAIN)
157a8857c50SChris Cain {
158a8857c50SChris Cain // We may have data coming still.
159a8857c50SChris Cain // This driver does not need a sleep for a retry.
160a8857c50SChris Cain continue;
161a8857c50SChris Cain }
162a8857c50SChris Cain else if (len == 0)
163a8857c50SChris Cain {
164*37abe9beSChris Cain lg2::debug("OccCommand::send: read completed");
165a8857c50SChris Cain // We have read all that we can.
166a8857c50SChris Cain status = CmdStatus::SUCCESS;
167a8857c50SChris Cain break;
168a8857c50SChris Cain }
169a8857c50SChris Cain else
170a8857c50SChris Cain {
171*37abe9beSChris Cain lg2::error(
172*37abe9beSChris Cain "OccCommand::send: read(OCC{INST}, command:{CMD) failed with errno={ERR} (rspSize={LEN}, retries={RETRIES})",
173*37abe9beSChris Cain "INST", occInstance, "CMD", lg2::hex, cmd_type, "ERR",
174*37abe9beSChris Cain readErrno, "LEN", response.size(), "RETRIES", retries);
175c567dc8dSChris Cain status = CmdStatus::COMM_FAILURE;
176c567dc8dSChris Cain break;
177a8857c50SChris Cain }
178a8857c50SChris Cain }
179c567dc8dSChris Cain if (status != CmdStatus::SUCCESS)
180c567dc8dSChris Cain {
181c567dc8dSChris Cain // retry if available
182c567dc8dSChris Cain continue;
183c567dc8dSChris Cain }
184a8857c50SChris Cain
185a8857c50SChris Cain if (response.size() > 2)
186a8857c50SChris Cain {
187a8857c50SChris Cain #ifdef TRACE_PACKETS
188*37abe9beSChris Cain lg2::info(
189*37abe9beSChris Cain "OCC{INST}: Received {CMD} response (length={LEN} w/checksum)",
190*37abe9beSChris Cain "INST", occInstance, "CMD", lg2::hex, cmd_type, "LEN",
191*37abe9beSChris Cain response.size());
192a8857c50SChris Cain dump_hex(response, 64);
193a8857c50SChris Cain #endif
194a8857c50SChris Cain
195a8857c50SChris Cain // Validate checksum (last 2 bytes of response)
196a8857c50SChris Cain const unsigned int csumIndex = response.size() - 2;
197d7542c83SPatrick Williams const uint32_t rspChecksum =
198d7542c83SPatrick Williams (response[csumIndex] << 8) + response[csumIndex + 1];
199358d3966SChris Cain // OCC checksum is the 2 byte sum of data (ignoring overflow)
200358d3966SChris Cain uint16_t calcChecksum = 0;
201a8857c50SChris Cain for (unsigned int index = 0; index < csumIndex; ++index)
202a8857c50SChris Cain {
203a8857c50SChris Cain calcChecksum += response[index];
204a8857c50SChris Cain }
205a8857c50SChris Cain if (calcChecksum != rspChecksum)
206a8857c50SChris Cain {
207*37abe9beSChris Cain lg2::error("OCC{INST}: Checksum Mismatch: response "
208*37abe9beSChris Cain "{RCVD}, calculated {EXPECT}",
209*37abe9beSChris Cain "INST", occInstance, "RCVD", rspChecksum, "EXPECT",
210*37abe9beSChris Cain calcChecksum);
211a8857c50SChris Cain dump_hex(response);
212c567dc8dSChris Cain status = CmdStatus::COMM_FAILURE;
213a8857c50SChris Cain }
214a8857c50SChris Cain else
215a8857c50SChris Cain {
21678e86012SChris Cain // Validate response was for the specified command
21778e86012SChris Cain if (command[0] == response[1])
21878e86012SChris Cain {
21978e86012SChris Cain // Valid response received
22078e86012SChris Cain
221a8857c50SChris Cain // Strip off 2 byte checksum
222a8857c50SChris Cain response.pop_back();
223a8857c50SChris Cain response.pop_back();
22478e86012SChris Cain break;
22578e86012SChris Cain }
22678e86012SChris Cain else
22778e86012SChris Cain {
228*37abe9beSChris Cain lg2::error("OccCommand::send: Response command mismatch "
22978e86012SChris Cain "(sent: "
230*37abe9beSChris Cain "{CMD}, rsp: {STATUS}, rsp seq#: {SEQ}",
231*37abe9beSChris Cain "CMD", lg2::hex, command[0], "STATUS", lg2::hex,
232*37abe9beSChris Cain response[1], "SEQ", lg2::hex, response[0]);
23378e86012SChris Cain dump_hex(response, 64);
23478e86012SChris Cain }
235a8857c50SChris Cain }
236a8857c50SChris Cain }
237a8857c50SChris Cain else
238a8857c50SChris Cain {
239*37abe9beSChris Cain lg2::error(
240*37abe9beSChris Cain "OccCommand::send: Invalid OCC{INST} response length: {LEN}",
241*37abe9beSChris Cain "INST", occInstance, "LEN", response.size());
242a8857c50SChris Cain status = CmdStatus::FAILURE;
243a8857c50SChris Cain dump_hex(response);
244a8857c50SChris Cain }
245a8857c50SChris Cain
24678e86012SChris Cain if (retries > 0)
24778e86012SChris Cain {
248*37abe9beSChris Cain lg2::error("OccCommand::send: Command will be retried");
24978e86012SChris Cain response.clear();
25078e86012SChris Cain }
25178e86012SChris Cain } while (retries-- > 0);
25278e86012SChris Cain
253a8857c50SChris Cain closeDevice();
254a8857c50SChris Cain
255a8857c50SChris Cain return status;
256a8857c50SChris Cain }
257a8857c50SChris Cain
258a8857c50SChris Cain // Called at OCC Status change signal
activeStatusEvent(sdbusplus::message_t & msg)259af40808fSPatrick Williams void OccCommand::activeStatusEvent(sdbusplus::message_t& msg)
260a8857c50SChris Cain {
261a8857c50SChris Cain std::string statusInterface;
262a8857c50SChris Cain std::map<std::string, std::variant<bool>> msgData;
263a8857c50SChris Cain msg.read(statusInterface, msgData);
264a8857c50SChris Cain
265a8857c50SChris Cain auto propertyMap = msgData.find("OccActive");
266a8857c50SChris Cain if (propertyMap != msgData.end())
267a8857c50SChris Cain {
268a8857c50SChris Cain // Extract the OccActive property
269a8857c50SChris Cain if (std::get<bool>(propertyMap->second))
270a8857c50SChris Cain {
271a8857c50SChris Cain occActive = true;
272a8857c50SChris Cain }
273a8857c50SChris Cain else
274a8857c50SChris Cain {
275a8857c50SChris Cain occActive = false;
276a8857c50SChris Cain
277a8857c50SChris Cain this->closeDevice();
278a8857c50SChris Cain }
279a8857c50SChris Cain }
280a8857c50SChris Cain return;
281a8857c50SChris Cain }
282a8857c50SChris Cain
283a8857c50SChris Cain } // namespace occ
284a8857c50SChris Cain } // namespace open_power
285