1 #include <memory>
2 #include <algorithm>
3 #include <fcntl.h>
4 #include <errno.h>
5 #include <string>
6 #include <unistd.h>
7 #include <phosphor-logging/log.hpp>
8 #include <phosphor-logging/elog.hpp>
9 #include <org/open_power/OCC/Device/error.hpp>
10 #include "occ_pass_through.hpp"
11 #include "elog-errors.hpp"
12 #include "config.h"
13 namespace open_power
14 {
15 namespace occ
16 {
17 
18 PassThrough::PassThrough(
19     sdbusplus::bus::bus& bus,
20     const char* path) :
21     Iface(bus, path),
22     path(path),
23     devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
24     activeStatusSignal(
25             bus,
26             sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
27             std::bind(std::mem_fn(&PassThrough::activeStatusEvent),
28                 this, std::placeholders::_1))
29 {
30     // Nothing to do.
31 }
32 
33 void PassThrough::openDevice()
34 {
35     using namespace phosphor::logging;
36     using namespace sdbusplus::org::open_power::OCC::Device::Error;
37 
38     if (!occActive)
39     {
40          log<level::INFO>("OCC is inactive; cannot perform pass-through");
41          return;
42     }
43 
44     fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
45     if (fd < 0)
46     {
47         // This would log and terminate since its not handled.
48         elog<OpenFailure>(
49             phosphor::logging::org::open_power::OCC::Device::
50                 OpenFailure::CALLOUT_ERRNO(errno),
51             phosphor::logging::org::open_power::OCC::Device::
52                 OpenFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
53     }
54     return;
55 }
56 
57 void PassThrough::closeDevice()
58 {
59     if (fd >= 0)
60     {
61         close(fd);
62         fd = -1;
63     }
64 }
65 
66 std::vector<int32_t> PassThrough::send(std::vector<int32_t> command)
67 {
68     using namespace phosphor::logging;
69     using namespace sdbusplus::org::open_power::OCC::Device::Error;
70 
71     std::vector<int32_t> response {};
72 
73     openDevice();
74 
75     if (fd < 0)
76     {
77         // OCC is inactive; empty response
78         return response;
79     }
80 
81     // OCC only understands [bytes] so need array of bytes. Doing this
82     // because rest-server currently treats all int* as 32 bit integer.
83     std::vector<uint8_t> cmdInBytes;
84     cmdInBytes.resize(command.size());
85 
86     // Populate uint8_t version of vector.
87     std::transform(command.begin(), command.end(), cmdInBytes.begin(),
88             [](decltype(cmdInBytes)::value_type x){return x;});
89 
90     ssize_t size = cmdInBytes.size() * sizeof(decltype(cmdInBytes)::value_type);
91     auto rc = write(fd, cmdInBytes.data(), size);
92     if (rc < 0 || (rc != size))
93     {
94         // This would log and terminate since its not handled.
95         elog<WriteFailure>(
96             phosphor::logging::org::open_power::OCC::Device::
97                 WriteFailure::CALLOUT_ERRNO(errno),
98             phosphor::logging::org::open_power::OCC::Device::
99                 WriteFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
100     }
101 
102     // Now read the response. This would be the content of occ-sram
103     while(1)
104     {
105         uint8_t data {};
106         auto len = read(fd, &data, sizeof(data));
107         if (len > 0)
108         {
109             response.emplace_back(data);
110         }
111         else if (len < 0 && errno == EAGAIN)
112         {
113             // We may have data coming still.
114             // This driver does not need a sleep for a retry.
115             continue;
116         }
117         else if (len == 0)
118         {
119             // We have read all that we can.
120             break;
121         }
122         else
123         {
124             // This would log and terminate since its not handled.
125             elog<ReadFailure>(
126                 phosphor::logging::org::open_power::OCC::Device::
127                     ReadFailure::CALLOUT_ERRNO(errno),
128                 phosphor::logging::org::open_power::OCC::Device::
129                     ReadFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
130         }
131     }
132 
133     closeDevice();
134 
135     return response;
136 }
137 
138 // Called at OCC Status change signal
139 void PassThrough::activeStatusEvent(sdbusplus::message::message& msg)
140 {
141     std::string statusInterface;
142     std::map<std::string, sdbusplus::message::variant<bool>> msgData;
143     msg.read(statusInterface, msgData);
144 
145     auto propertyMap = msgData.find("OccActive");
146     if (propertyMap != msgData.end())
147     {
148         // Extract the OccActive property
149         if (sdbusplus::message::variant_ns::get<bool>(propertyMap->second))
150         {
151             occActive = true;
152         }
153         else
154         {
155             occActive = false;
156             this->closeDevice();
157         }
158     }
159     return;
160 }
161 
162 } // namespace occ
163 } // namespace open_power
164