blob: 72439f74a27e32b302b7afc5ec9e053ff2c82db1 [file] [log] [blame]
Yuan Li60e7aaf2019-05-28 14:22:40 +08001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#include "xyz/openbmc_project/Common/error.hpp"
17
18#include <fstream>
19#include <ipmid/api.hpp>
20#include <ipmid/utils.hpp>
21#include <nlohmann/json.hpp>
22#include <phosphor-logging/elog-errors.hpp>
23#include <regex>
24#include <sdbusplus/timer.hpp>
25
26namespace ipmi
27{
28const static constexpr char *idButtonPath =
29 "/xyz/openbmc_project/chassis/buttons/id";
30const static constexpr char *idButtonInterface =
31 "xyz.openbmc_project.Chassis.Buttons";
32const static constexpr char *idButtonProp = "ButtonPressed";
33
34const static constexpr char *ledService =
35 "xyz.openbmc_project.LED.GroupManager";
36const static constexpr char *ledIDOnObj =
37 "/xyz/openbmc_project/led/groups/enclosure_identify";
38const static constexpr char *ledIDBlinkObj =
39 "/xyz/openbmc_project/led/groups/enclosure_identify_blink";
40const static constexpr char *ledInterface = "xyz.openbmc_project.Led.Group";
41const static constexpr char *ledProp = "Asserted";
42
43constexpr size_t defaultIdentifyTimeOut = 15;
44
45std::unique_ptr<phosphor::Timer> identifyTimer
46 __attribute__((init_priority(101)));
47std::unique_ptr<sdbusplus::bus::match_t> matchPtr
48 __attribute__((init_priority(101)));
49
50static void registerChassisFunctions() __attribute__((constructor));
51
52static ipmi::ServiceCache LEDService(ledInterface, ledIDBlinkObj);
53
54void enclosureIdentifyLed(const char *objName, bool isIdLedOn)
55{
56 auto bus = getSdBus();
57
58 try
59 {
60 std::string service = LEDService.getService(*bus);
61 setDbusProperty(*bus, service, objName, ledInterface, ledProp,
62 isIdLedOn);
63 }
64 catch (const std::exception &e)
65 {
66 phosphor::logging::log<phosphor::logging::level::ERR>(
67 "enclosureIdentifyLed: can't set property",
68 phosphor::logging::entry("ERR=%s", e.what()));
69 }
70}
71
72bool getIDState(const char *objName, bool &state)
73{
74 auto bus = getSdBus();
75
76 try
77 {
78 std::string service = LEDService.getService(*bus);
79 ipmi::Value enabled =
80 getDbusProperty(*bus, service, objName, ledInterface, ledProp);
81 state = std::get<bool>(enabled);
82 }
83 catch (sdbusplus::exception::SdBusError &e)
84 {
85 phosphor::logging::log<phosphor::logging::level::ERR>(
86 "Fail to get property",
87 phosphor::logging::entry("PATH=%s", objName),
88 phosphor::logging::entry("ERROR=%s", e.what()));
89 return false;
90 }
91 return true;
92}
93
94void enclosureIdentifyLedBlinkOff()
95{
96 enclosureIdentifyLed(ledIDBlinkObj, false);
97}
98
99void idButtonPropChanged(sdbusplus::message::message &msg)
100{
101 bool asserted = false;
102 bool buttonPressed = false;
103
104 std::map<std::string, ipmi::Value> props;
105 std::vector<std::string> inval;
106 std::string iface;
107 msg.read(iface, props, inval);
108
109 for (const auto &t : props)
110 {
111 auto key = t.first;
112 auto value = t.second;
113
114 if (key == idButtonProp)
115 {
116 buttonPressed = std::get<bool>(value);
117 }
118 break;
119 }
120
121 if (buttonPressed)
122 {
123 if (identifyTimer->isRunning())
124 {
125 phosphor::logging::log<phosphor::logging::level::INFO>(
126 "ID timer is running");
127 }
128
129 // make sure timer is stopped
130 identifyTimer->stop();
131
132 if (!getIDState(ledIDBlinkObj, asserted))
133 {
134 return;
135 }
136
137 if (asserted)
138 {
139 // LED is blinking, turn off the LED
140 enclosureIdentifyLed(ledIDBlinkObj, false);
141 enclosureIdentifyLed(ledIDOnObj, false);
142 }
143 else
144 {
145 // toggle the IED on/off
146 if (!getIDState(ledIDOnObj, asserted))
147 {
148 return;
149 }
150 enclosureIdentifyLed(ledIDOnObj, !asserted);
151 }
152 }
153}
154
155void createIdentifyTimer()
156{
157 if (!identifyTimer)
158 {
159 identifyTimer =
160 std::make_unique<phosphor::Timer>(enclosureIdentifyLedBlinkOff);
161 }
162}
163
164ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
165 std::optional<uint8_t> force)
166{
167 uint8_t identifyInterval = interval.value_or(defaultIdentifyTimeOut);
168 bool forceIdentify = force.value_or(0) & 0x01;
169
170 enclosureIdentifyLed(ledIDOnObj, false);
171 identifyTimer->stop();
172
173 if (identifyInterval || forceIdentify)
174 {
175 enclosureIdentifyLed(ledIDBlinkObj, true);
176 if (forceIdentify)
177 {
178 return ipmi::responseSuccess();
179 }
180 // start the timer
181 auto time = std::chrono::duration_cast<std::chrono::microseconds>(
182 std::chrono::seconds(identifyInterval));
183 identifyTimer->start(time);
184 }
185 else
186 {
187 enclosureIdentifyLed(ledIDBlinkObj, false);
188 }
189 return ipmi::responseSuccess();
190}
191
192static void registerChassisFunctions(void)
193{
194 phosphor::logging::log<phosphor::logging::level::INFO>(
195 "Registering Chassis commands");
196
197 createIdentifyTimer();
198
199 if (matchPtr == nullptr)
200 {
201 using namespace sdbusplus::bus::match::rules;
202 auto bus = getSdBus();
203
204 matchPtr = std::make_unique<sdbusplus::bus::match_t>(
205 *bus,
206 sdbusplus::bus::match::rules::propertiesChanged(idButtonPath,
207 idButtonInterface),
208 std::bind(idButtonPropChanged, std::placeholders::_1));
209 }
210
211 // <Chassis Identify>
212 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
213 ipmi::chassis::cmdChassisIdentify,
214 ipmi::Privilege::Operator, ipmiChassisIdentify);
215}
216
217} // namespace ipmi