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 = utils::getService(CONTROL_HOST_PATH, CONTROL_HOST_INTF);
120 
121     auto& bus = utils::getBus();
122     auto method = bus.new_method_call(service.c_str(), CONTROL_HOST_PATH,
123                                       CONTROL_HOST_INTF, "Execute");
124     // OCC Reset control command
125     method.append(convertForMessage(Control::Host::Command::OCCReset).c_str());
126 
127     // OCC Sensor ID for callout reasons
128     method.append(std::variant<uint8_t>(std::get<0>(sensorMap.at(instance))));
129     bus.call_noreply(method);
130     return;
131 #endif
132 }
133 
134 // Handler called by Host control command handler to convey the
135 // status of the executed command
136 void Status::hostControlEvent(sdbusplus::message::message& msg)
137 {
138     using namespace phosphor::logging;
139 
140     std::string cmdCompleted{};
141     std::string cmdStatus{};
142 
143     msg.read(cmdCompleted, cmdStatus);
144 
145     log<level::DEBUG>("Host control signal values",
146                       entry("COMMAND=%s", cmdCompleted.c_str()),
147                       entry("STATUS=%s", cmdStatus.c_str()));
148 
149     if (Control::Host::convertResultFromString(cmdStatus) !=
150         Control::Host::Result::Success)
151     {
152         if (Control::Host::convertCommandFromString(cmdCompleted) ==
153             Control::Host::Command::OCCReset)
154         {
155             // Must be a Timeout. Log an Error trace
156             log<level::ERR>(
157                 "Error resetting the OCC.", entry("PATH=%s", path.c_str()),
158                 entry("SENSORID=0x%X", std::get<0>(sensorMap.at(instance))));
159         }
160     }
161     return;
162 }
163 
164 void Status::readOccState()
165 {
166     unsigned int state;
167     const fs::path filename =
168         fs::path(DEV_PATH) /
169         fs::path(sysfsName + "." + std::to_string(instance + 1)) / "occ_state";
170 
171     log<level::DEBUG>(
172         fmt::format("Status::readOccState: reading OCC{} state from {}",
173                     instance, filename.c_str())
174             .c_str());
175 
176     std::ifstream file(filename, std::ios::in);
177     const int open_errno = errno;
178     if (file)
179     {
180         file >> state;
181         if (state != lastState)
182         {
183             // Trace OCC state changes
184             log<level::INFO>(
185                 fmt::format("Status::readOccState: OCC{} state 0x{:02X}",
186                             instance, state)
187                     .c_str());
188             lastState = state;
189         }
190         file.close();
191     }
192     else
193     {
194         // If not able to read, OCC may be offline
195         log<level::DEBUG>(
196             fmt::format("Status::readOccState: open failed (errno={})",
197                         open_errno)
198                 .c_str());
199         lastState = 0;
200     }
201 }
202 
203 } // namespace occ
204 } // namespace open_power
205