1 #include <memory>
2 #include <algorithm>
3 #include <fcntl.h>
4 #include <errno.h>
5 #include <phosphor-logging/log.hpp>
6 #include <phosphor-logging/elog.hpp>
7 #include <org/open_power/OCC/PassThrough/error.hpp>
8 #include "occ_pass_through.hpp"
9 #include "elog-errors.hpp"
10 namespace open_power
11 {
12 namespace occ
13 {
14 
15 PassThrough::PassThrough(
16     sdbusplus::bus::bus& bus,
17     const char* path) :
18     Iface(bus, path),
19     path(path),
20     fd(openDevice())
21 {
22     // Nothing to do.
23 }
24 
25 int PassThrough::openDevice()
26 {
27     using namespace phosphor::logging;
28     using namespace sdbusplus::org::open_power::OCC::PassThrough::Error;
29 
30     // Device instance number starts from 1.
31     devicePath.append(std::to_string((this->path.back() - '0') + 1));
32 
33     int fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
34     if (fd < 0)
35     {
36         // This would log and terminate since its not handled.
37         elog<OpenFailure>(
38             phosphor::logging::org::open_power::OCC::PassThrough::
39                 OpenFailure::CALLOUT_ERRNO(errno),
40             phosphor::logging::org::open_power::OCC::PassThrough::
41                 OpenFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
42     }
43     return fd;
44 }
45 
46 std::vector<int32_t> PassThrough::send(std::vector<int32_t> command)
47 {
48     using namespace phosphor::logging;
49     using namespace sdbusplus::org::open_power::OCC::PassThrough::Error;
50 
51     std::vector<int32_t> response {};
52 
53     // OCC only understands [bytes] so need array of bytes. Doing this
54     // because rest-server currently treats all int* as 32 bit integer.
55     std::vector<uint8_t> cmdInBytes;
56     cmdInBytes.resize(command.size());
57 
58     // Populate uint8_t version of vector.
59     std::transform(command.begin(), command.end(), cmdInBytes.begin(),
60             [](decltype(cmdInBytes)::value_type x){return x;});
61 
62     ssize_t size = cmdInBytes.size() * sizeof(decltype(cmdInBytes)::value_type);
63     auto rc = write((fd)(), cmdInBytes.data(), size);
64     if (rc < 0 || (rc != size))
65     {
66         // This would log and terminate since its not handled.
67         elog<WriteFailure>(
68             phosphor::logging::org::open_power::OCC::PassThrough::
69                 WriteFailure::CALLOUT_ERRNO(errno),
70             phosphor::logging::org::open_power::OCC::PassThrough::
71                 WriteFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
72     }
73 
74     // Now read the response. This would be the content of occ-sram
75     while(1)
76     {
77         uint8_t data {};
78         auto len = read((fd)(), &data, sizeof(data));
79         if (len > 0)
80         {
81             response.emplace_back(data);
82         }
83         else if (len < 0 && errno == EAGAIN)
84         {
85             // We may have data coming still.
86             // This driver does not need a sleep for a retry.
87             continue;
88         }
89         else if (len == 0)
90         {
91             // We have read all that we can.
92             break;
93         }
94         else
95         {
96             // This would log and terminate since its not handled.
97             elog<ReadFailure>(
98                 phosphor::logging::org::open_power::OCC::PassThrough::
99                     ReadFailure::CALLOUT_ERRNO(errno),
100                 phosphor::logging::org::open_power::OCC::PassThrough::
101                     ReadFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
102         }
103     }
104 
105     return response;
106 }
107 
108 } // namespace occ
109 } // namespace open_power
110