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