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     fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
39     if (fd < 0)
40     {
41         // This would log and terminate since its not handled.
42         elog<OpenFailure>(
43             phosphor::logging::org::open_power::OCC::Device::
44                 OpenFailure::CALLOUT_ERRNO(errno),
45             phosphor::logging::org::open_power::OCC::Device::
46                 OpenFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
47     }
48     return;
49 }
50 
51 void PassThrough::closeDevice()
52 {
53     if (fd >= 0)
54     {
55         close(fd);
56     }
57 }
58 
59 std::vector<int32_t> PassThrough::send(std::vector<int32_t> command)
60 {
61     using namespace phosphor::logging;
62     using namespace sdbusplus::org::open_power::OCC::Device::Error;
63 
64     std::vector<int32_t> response {};
65 
66     // OCC only understands [bytes] so need array of bytes. Doing this
67     // because rest-server currently treats all int* as 32 bit integer.
68     std::vector<uint8_t> cmdInBytes;
69     cmdInBytes.resize(command.size());
70 
71     // Populate uint8_t version of vector.
72     std::transform(command.begin(), command.end(), cmdInBytes.begin(),
73             [](decltype(cmdInBytes)::value_type x){return x;});
74 
75     ssize_t size = cmdInBytes.size() * sizeof(decltype(cmdInBytes)::value_type);
76     auto rc = write(fd, cmdInBytes.data(), size);
77     if (rc < 0 || (rc != size))
78     {
79         // This would log and terminate since its not handled.
80         elog<WriteFailure>(
81             phosphor::logging::org::open_power::OCC::Device::
82                 WriteFailure::CALLOUT_ERRNO(errno),
83             phosphor::logging::org::open_power::OCC::Device::
84                 WriteFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
85     }
86 
87     // Now read the response. This would be the content of occ-sram
88     while(1)
89     {
90         uint8_t data {};
91         auto len = read(fd, &data, sizeof(data));
92         if (len > 0)
93         {
94             response.emplace_back(data);
95         }
96         else if (len < 0 && errno == EAGAIN)
97         {
98             // We may have data coming still.
99             // This driver does not need a sleep for a retry.
100             continue;
101         }
102         else if (len == 0)
103         {
104             // We have read all that we can.
105             break;
106         }
107         else
108         {
109             // This would log and terminate since its not handled.
110             elog<ReadFailure>(
111                 phosphor::logging::org::open_power::OCC::Device::
112                     ReadFailure::CALLOUT_ERRNO(errno),
113                 phosphor::logging::org::open_power::OCC::Device::
114                     ReadFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
115         }
116     }
117 
118     return response;
119 }
120 
121 // Called at OCC Status change signal
122 void PassThrough::activeStatusEvent(sdbusplus::message::message& msg)
123 {
124     std::string statusInterface;
125     std::map<std::string, sdbusplus::message::variant<bool>> msgData;
126     msg.read(statusInterface, msgData);
127 
128     auto propertyMap = msgData.find("OccActive");
129     if (propertyMap != msgData.end())
130     {
131         // Extract the OccActive property
132         if (sdbusplus::message::variant_ns::get<bool>(propertyMap->second))
133         {
134             this->openDevice();
135         }
136         else
137         {
138             this->closeDevice();
139         }
140     }
141     return;
142 }
143 
144 } // namespace occ
145 } // namespace open_power
146