1 #include "config.h"
2 
3 #include "occ_pass_through.hpp"
4 
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 
9 #include <org/open_power/OCC/Device/error.hpp>
10 #include <phosphor-logging/elog-errors.hpp>
11 #include <phosphor-logging/elog.hpp>
12 #include <phosphor-logging/log.hpp>
13 
14 #include <algorithm>
15 #include <format>
16 #include <memory>
17 #include <string>
18 
19 namespace open_power
20 {
21 namespace occ
22 {
23 
24 using namespace phosphor::logging;
25 using namespace sdbusplus::org::open_power::OCC::Device::Error;
26 
27 PassThrough::PassThrough(
28     const char* path
29 #ifdef POWER10
30     ,
31     std::unique_ptr<open_power::occ::powermode::PowerMode>& powerModeRef
32 #endif
33     ) :
34     Iface(utils::getBus(), path), path(path),
35 #ifdef POWER10
36     pmode(powerModeRef),
37 #endif
38     devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
39     occInstance(this->path.back() - '0'),
40     activeStatusSignal(
41         utils::getBus(),
42         sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
43         std::bind(std::mem_fn(&PassThrough::activeStatusEvent), this,
44                   std::placeholders::_1)),
45     occCmd(occInstance, path)
46 {
47     // Nothing to do.
48 }
49 
50 std::vector<int32_t> PassThrough::send(std::vector<int32_t> command)
51 {
52     std::vector<int32_t> response{};
53 
54     // OCC only understands [bytes] so need array of bytes. Doing this
55     // because rest-server currently treats all int* as 32 bit integer.
56     std::vector<uint8_t> cmdInBytes, rsp;
57     cmdInBytes.resize(command.size());
58 
59     // Populate uint8_t version of vector.
60     std::transform(command.begin(), command.end(), cmdInBytes.begin(),
61                    [](decltype(cmdInBytes)::value_type x) { return x; });
62 
63     rsp = send(cmdInBytes);
64 
65     response.resize(rsp.size());
66     std::transform(rsp.begin(), rsp.end(), response.begin(),
67                    [](decltype(response)::value_type x) { return x; });
68 
69     return response;
70 }
71 
72 std::vector<uint8_t> PassThrough::send(std::vector<uint8_t> command)
73 {
74     std::vector<uint8_t> response{};
75 
76     if (!occActive)
77     {
78         log<level::ERR>(
79             std::format(
80                 "PassThrough::send() - OCC{} not active, command not sent",
81                 occInstance)
82                 .c_str());
83         return response;
84     }
85 
86     if (command.size() >= 3)
87     {
88         const uint16_t dataLen = command[1] << 8 | command[2];
89         std::string dataString = "";
90         if (command.size() > 3)
91         {
92             // Trace first 4 bytes of command data
93             size_t index = 3;
94             dataString = "0x";
95             for (; (index < 7) && (index < command.size()); ++index)
96             {
97                 dataString += std::format("{:02X}", command[index]);
98             }
99             if (index < command.size())
100             {
101                 dataString += "...";
102             }
103         }
104         log<level::INFO>(
105             std::format(
106                 "PassThrough::send() Sending 0x{:02X} command to OCC{} (data len={}, data={})",
107                 command.front(), occInstance, dataLen, dataString)
108                 .c_str());
109     }
110     else
111     {
112         log<level::INFO>(
113             std::format("PassThrough::send() Sending 0x{:02X} command to OCC{}",
114                         command.front(), occInstance)
115                 .c_str());
116     }
117     CmdStatus status = occCmd.send(command, response);
118     if (status == CmdStatus::SUCCESS)
119     {
120         if (response.size() >= 5)
121         {
122             log<level::DEBUG>(
123                 std::format("PassThrough::send() response had {} bytes",
124                             response.size())
125                     .c_str());
126         }
127         else
128         {
129             log<level::ERR>("PassThrough::send() Invalid OCC response");
130             dump_hex(response);
131         }
132     }
133     else
134     {
135         log<level::ERR>(
136             std::format(
137                 "PassThrough::send(): OCC command failed with status {}",
138                 uint32_t(status))
139                 .c_str());
140     }
141 
142     return response;
143 }
144 
145 bool PassThrough::setMode(const uint8_t mode, const uint16_t modeData)
146 {
147 #ifdef POWER10
148     SysPwrMode newMode = SysPwrMode(mode);
149 
150     if (!pmode)
151     {
152         log<level::ERR>("PassThrough::setMode: PowerMode is not defined!");
153         return false;
154     }
155 
156     if (!pmode->isValidMode(SysPwrMode(mode)))
157     {
158         log<level::ERR>(
159             std::format(
160                 "PassThrough::setMode() Unsupported mode {} requested (0x{:04X})",
161                 newMode, modeData)
162                 .c_str());
163         return false;
164     }
165 
166     if (((newMode == SysPwrMode::FFO) || (newMode == SysPwrMode::SFP)) &&
167         (modeData == 0))
168     {
169         log<level::ERR>(
170             std::format(
171                 "PassThrough::setMode() Mode {} requires non-zero frequency point.",
172                 newMode)
173                 .c_str());
174         return false;
175     }
176 
177     log<level::INFO>(
178         std::format("PassThrough::setMode() Setting Power Mode {} (data: {})",
179                     newMode, modeData)
180             .c_str());
181     return pmode->setMode(newMode, modeData);
182 #else
183     log<level::DEBUG>(
184         std::format(
185             "PassThrough::setMode() No support to setting Power Mode {} (data: {})",
186             mode, modeData)
187             .c_str());
188     return false;
189 #endif
190 }
191 
192 // Called at OCC Status change signal
193 void PassThrough::activeStatusEvent(sdbusplus::message_t& msg)
194 {
195     std::string statusInterface;
196     std::map<std::string, std::variant<bool>> msgData;
197     msg.read(statusInterface, msgData);
198 
199     auto propertyMap = msgData.find("OccActive");
200     if (propertyMap != msgData.end())
201     {
202         // Extract the OccActive property
203         if (std::get<bool>(propertyMap->second))
204         {
205             occActive = true;
206         }
207         else
208         {
209             occActive = false;
210         }
211     }
212     return;
213 }
214 
215 } // namespace occ
216 } // namespace open_power
217