1 #include "occ_status.hpp"
2 
3 #include "occ_sensor.hpp"
4 #include "utils.hpp"
5 
6 #include <fmt/core.h>
7 
8 #include <phosphor-logging/log.hpp>
9 namespace open_power
10 {
11 namespace occ
12 {
13 using namespace phosphor::logging;
14 
15 // Handles updates to occActive property
16 bool Status::occActive(bool value)
17 {
18     if (value != this->occActive())
19     {
20         log<level::INFO>(fmt::format("Status::occActive OCC{} changed to {}",
21                                      instance, value)
22                              .c_str());
23         if (value)
24         {
25             // Bind the device
26             device.bind();
27 
28             // Start watching for errors
29             addErrorWatch();
30 
31             // Reset last OCC state
32             lastState = 0;
33 
34             // Call into Manager to let know that we have bound
35             if (this->callBack)
36             {
37                 this->callBack(value);
38             }
39         }
40         else
41         {
42             // Call into Manager to let know that we will unbind.
43             if (this->callBack)
44             {
45                 this->callBack(value);
46             }
47 
48             // Stop watching for errors
49             removeErrorWatch();
50 
51             // Do the unbind.
52             device.unBind();
53         }
54     }
55     else if (value && !device.bound())
56     {
57         // Existing error watch is on a dead file descriptor.
58         removeErrorWatch();
59 
60         /*
61          * In it's constructor, Status checks Device::bound() to see if OCC is
62          * active or not.
63          * Device::bound() checks for occX-dev0 directory.
64          * We will lose occX-dev0 directories during FSI rescan.
65          * So, if we start this application (and construct Status), and then
66          * later do FSI rescan, we will end up with occActive = true and device
67          * NOT bound. Lets correct that situation here.
68          */
69         device.bind();
70 
71         // Add error watch again
72         addErrorWatch();
73     }
74     else if (!value && device.bound())
75     {
76         removeErrorWatch();
77 
78         // In the event that the application never receives the active signal
79         // even though the OCC is active (this can occur if the BMC is rebooted
80         // with the host on, since the initial OCC driver probe will discover
81         // the OCCs), this application needs to be able to unbind the device
82         // when we get the OCC inactive signal.
83         device.unBind();
84     }
85     return Base::Status::occActive(value);
86 }
87 
88 // Callback handler when a device error is reported.
89 void Status::deviceErrorHandler(bool error)
90 {
91     // Make sure we have an error
92     if (error)
93     {
94         // This would deem OCC inactive
95         this->occActive(false);
96 
97         // Reset the OCC
98         this->resetOCC();
99     }
100 }
101 
102 // Sends message to host control command handler to reset OCC
103 void Status::resetOCC()
104 {
105     log<level::INFO>(
106         fmt::format(">>Status::resetOCC() - requesting reset for OCC{}",
107                     instance)
108             .c_str());
109 #ifdef PLDM
110     if (resetCallBack)
111     {
112         this->resetCallBack(instance);
113     }
114 #else
115     constexpr auto CONTROL_HOST_PATH = "/org/open_power/control/host0";
116     constexpr auto CONTROL_HOST_INTF = "org.open_power.Control.Host";
117 
118     // This will throw exception on failure
119     auto service = getService(bus, CONTROL_HOST_PATH, CONTROL_HOST_INTF);
120 
121     auto method = bus.new_method_call(service.c_str(), CONTROL_HOST_PATH,
122                                       CONTROL_HOST_INTF, "Execute");
123     // OCC Reset control command
124     method.append(convertForMessage(Control::Host::Command::OCCReset).c_str());
125 
126     // OCC Sensor ID for callout reasons
127     method.append(std::variant<uint8_t>(std::get<0>(sensorMap.at(instance))));
128     bus.call_noreply(method);
129     return;
130 #endif
131 }
132 
133 // Handler called by Host control command handler to convey the
134 // status of the executed command
135 void Status::hostControlEvent(sdbusplus::message::message& msg)
136 {
137     using namespace phosphor::logging;
138 
139     std::string cmdCompleted{};
140     std::string cmdStatus{};
141 
142     msg.read(cmdCompleted, cmdStatus);
143 
144     log<level::DEBUG>("Host control signal values",
145                       entry("COMMAND=%s", cmdCompleted.c_str()),
146                       entry("STATUS=%s", cmdStatus.c_str()));
147 
148     if (Control::Host::convertResultFromString(cmdStatus) !=
149         Control::Host::Result::Success)
150     {
151         if (Control::Host::convertCommandFromString(cmdCompleted) ==
152             Control::Host::Command::OCCReset)
153         {
154             // Must be a Timeout. Log an Error trace
155             log<level::ERR>(
156                 "Error resetting the OCC.", entry("PATH=%s", path.c_str()),
157                 entry("SENSORID=0x%X", std::get<0>(sensorMap.at(instance))));
158         }
159     }
160     return;
161 }
162 
163 void Status::readOccState()
164 {
165     unsigned int state;
166     const fs::path filename =
167         fs::path(DEV_PATH) /
168         fs::path(sysfsName + "." + std::to_string(instance + 1)) / "occ_state";
169 
170     log<level::DEBUG>(
171         fmt::format("Status::readOccState: reading OCC{} state from {}",
172                     instance, filename.c_str())
173             .c_str());
174 
175     std::ifstream file(filename, std::ios::in);
176     const int open_errno = errno;
177     if (file)
178     {
179         file >> state;
180         if (state != lastState)
181         {
182             // Trace OCC state changes
183             log<level::INFO>(
184                 fmt::format("Status::readOccState: OCC{} state 0x{:02X}",
185                             instance, state)
186                     .c_str());
187             lastState = state;
188         }
189         file.close();
190     }
191     else
192     {
193         // If not able to read, OCC may be offline
194         log<level::DEBUG>(
195             fmt::format("Status::readOccState: open failed (errno={})",
196                         open_errno)
197                 .c_str());
198         lastState = 0;
199     }
200 }
201 
202 } // namespace occ
203 } // namespace open_power
204