blob: 49935667ca8a44739b87ff94c4f2d84566a30070 [file] [log] [blame]
Vernon Mauerya3702c12019-05-22 13:20:59 -07001/*
2// Copyright (c) 2018 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
Vernon Mauerya3702c12019-05-22 13:20:59 -070017#include <ipmid/api.hpp>
18#include <manufacturingcommands.hpp>
19#include <oemcommands.hpp>
20
21namespace ipmi
22{
23
24Manufacturing mtm;
25
26static auto revertTimeOut =
27 std::chrono::duration_cast<std::chrono::microseconds>(
28 std::chrono::seconds(60)); // 1 minute timeout
29
Vernon Mauerya3702c12019-05-22 13:20:59 -070030static constexpr const char* callbackMgrService =
31 "xyz.openbmc_project.CallbackManager";
32static constexpr const char* callbackMgrIntf =
33 "xyz.openbmc_project.CallbackManager";
34static constexpr const char* callbackMgrObjPath =
35 "/xyz/openbmc_project/CallbackManager";
36static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
37
38const static constexpr char* systemDService = "org.freedesktop.systemd1";
39const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
40const static constexpr char* systemDMgrIntf =
41 "org.freedesktop.systemd1.Manager";
42const static constexpr char* pidControlService = "phosphor-pid-control.service";
43
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070044int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
Vernon Mauerya3702c12019-05-22 13:20:59 -070045{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070046 switch (signal)
47 {
48 case SmSignalGet::smPowerButton:
49 path = "/xyz/openbmc_project/chassis/buttons/power";
50 break;
51 case SmSignalGet::smResetButton:
52 path = "/xyz/openbmc_project/chassis/buttons/reset";
53 break;
54 case SmSignalGet::smNMIButton:
55 path = "/xyz/openbmc_project/chassis/buttons/nmi";
56 break;
57 default:
58 return -1;
59 break;
60 }
61 return 0;
Vernon Mauerya3702c12019-05-22 13:20:59 -070062}
63
64ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState)
65{
66 LedProperty* ledProp = mtm.findLedProperty(signal);
67 if (ledProp == nullptr)
68 {
69 return IPMI_CC_INVALID_FIELD_REQUEST;
70 }
71
72 std::string ledName = ledProp->getName();
73 std::string ledService = ledServicePrefix + ledName;
74 std::string ledPath = ledPathPrefix + ledName;
75 ipmi::Value presentState;
76
77 if (false == ledProp->getLock())
78 {
79 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
80 "State", &presentState) != 0)
81 {
82 return IPMI_CC_UNSPECIFIED_ERROR;
83 }
84 ledProp->setPrevState(std::get<std::string>(presentState));
85 ledProp->setLock(true);
86 if (signal == SmSignalSet::smPowerFaultLed ||
87 signal == SmSignalSet::smSystemReadyLed)
88 {
89 mtm.revertLedCallback = true;
90 }
91 }
Jason M. Bills38d2b5a2019-06-03 16:26:11 -070092 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
Vernon Mauerya3702c12019-05-22 13:20:59 -070093 ledStateStr + setState) != 0)
94 {
95 return IPMI_CC_UNSPECIFIED_ERROR;
96 }
97 return IPMI_CC_OK;
98}
99
100ipmi_ret_t ledRevert(SmSignalSet signal)
101{
102 LedProperty* ledProp = mtm.findLedProperty(signal);
103 if (ledProp == nullptr)
104 {
105 return IPMI_CC_INVALID_FIELD_REQUEST;
106 }
107 if (true == ledProp->getLock())
108 {
109 ledProp->setLock(false);
110 if (signal == SmSignalSet::smPowerFaultLed ||
111 signal == SmSignalSet::smSystemReadyLed)
112 {
113 try
114 {
115 ipmi::method_no_args::callDbusMethod(
116 *getSdBus(), callbackMgrService, callbackMgrObjPath,
117 callbackMgrIntf, retriggerLedUpdate);
118 }
119 catch (sdbusplus::exception_t& e)
120 {
121 return IPMI_CC_UNSPECIFIED_ERROR;
122 }
123 mtm.revertLedCallback = false;
124 }
125 else
126 {
127 std::string ledName = ledProp->getName();
128 std::string ledService = ledServicePrefix + ledName;
129 std::string ledPath = ledPathPrefix + ledName;
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700130 if (mtm.setProperty(ledService, ledPath, ledIntf, "State",
131 ledProp->getPrevState()) != 0)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700132 {
133 return IPMI_CC_UNSPECIFIED_ERROR;
134 }
135 }
136 }
137 return IPMI_CC_OK;
138}
139
140void Manufacturing::initData()
141{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700142 ledPropertyList.push_back(
143 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
144 ledPropertyList.push_back(
145 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
146 ledPropertyList.push_back(
147 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
148}
149
150void Manufacturing::revertTimerHandler()
151{
Vernon Mauerya3702c12019-05-22 13:20:59 -0700152 if (revertFanPWM)
153 {
154 revertFanPWM = false;
155 disablePidControlService(false);
156 }
157
158 for (const auto& ledProperty : ledPropertyList)
159 {
160 const std::string& ledName = ledProperty.getName();
161 ledRevert(ledProperty.getSignal());
162 }
163}
164
165Manufacturing::Manufacturing() :
166 revertTimer([&](void) { revertTimerHandler(); })
167{
168 initData();
169}
170
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700171int8_t Manufacturing::getProperty(const std::string& service,
172 const std::string& path,
173 const std::string& interface,
174 const std::string& propertyName,
175 ipmi::Value* reply)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700176{
177 try
178 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700179 *reply = ipmi::getDbusProperty(*getSdBus(), service, path, interface,
180 propertyName);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700181 }
182 catch (const sdbusplus::exception::SdBusError& e)
183 {
184 phosphor::logging::log<phosphor::logging::level::INFO>(
185 "ERROR: getProperty");
186 return -1;
187 }
188
189 return 0;
190}
191
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700192int8_t Manufacturing::setProperty(const std::string& service,
193 const std::string& path,
194 const std::string& interface,
195 const std::string& propertyName,
196 ipmi::Value value)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700197{
198 try
199 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700200 ipmi::setDbusProperty(*getSdBus(), service, path, interface,
Vernon Mauerya3702c12019-05-22 13:20:59 -0700201 propertyName, value);
202 }
203 catch (const sdbusplus::exception::SdBusError& e)
204 {
205 phosphor::logging::log<phosphor::logging::level::INFO>(
206 "ERROR: setProperty");
207 return -1;
208 }
209
210 return 0;
211}
212
213int8_t Manufacturing::disablePidControlService(const bool disable)
214{
215 try
216 {
217 auto dbus = getSdBus();
218 auto method = dbus->new_method_call(systemDService, systemDObjPath,
219 systemDMgrIntf,
220 disable ? "StopUnit" : "StartUnit");
221 method.append(pidControlService, "replace");
222 auto reply = dbus->call(method);
223 }
224 catch (const sdbusplus::exception::SdBusError& e)
225 {
226 phosphor::logging::log<phosphor::logging::level::INFO>(
227 "ERROR: phosphor-pid-control service start or stop failed");
228 return -1;
229 }
230 return 0;
231}
232
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700233ipmi::RspType<uint8_t, // Signal value
234 std::optional<uint16_t> // Fan tach value
235 >
236 appMTMGetSignal(uint8_t signalTypeByte, uint8_t instance,
237 uint8_t actionByte)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700238{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700239 if (mtm.getAccessLvl() < MtmLvl::mtmAvailable)
Vernon Mauerya3702c12019-05-22 13:20:59 -0700240 {
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700241 return ipmi::responseInvalidCommand();
242 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700243
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700244 SmSignalGet signalType = static_cast<SmSignalGet>(signalTypeByte);
245 SmActionGet action = static_cast<SmActionGet>(actionByte);
246
247 switch (signalType)
248 {
249 case SmSignalGet::smFanPwmGet:
250 {
251 ipmi::Value reply;
252 std::string fullPath = fanPwmPath + std::to_string(instance);
253 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
254 &reply) < 0)
255 {
256 return ipmi::responseInvalidFieldRequest();
257 }
258 double* doubleVal = std::get_if<double>(&reply);
259 if (doubleVal == nullptr)
260 {
261 return ipmi::responseUnspecifiedError();
262 }
263 uint8_t sensorVal = std::round(*doubleVal);
264 return ipmi::responseSuccess(sensorVal, std::nullopt);
265 }
266 break;
267 case SmSignalGet::smFanTachometerGet:
268
269 {
270 // Full path calculation pattern:
271 // Instance 1 path is
272 // /xyz/openbmc_project/sensors/fan_tach/Fan_1a Instance 2 path
273 // is /xyz/openbmc_project/sensors/fan_tach/Fan_1b Instance 3
274 // path is /xyz/openbmc_project/sensors/fan_tach/Fan_2a
275 // and so on...
276 std::string fullPath = fanTachPathPrefix;
277 std::string fanAb = (instance % 2) == 0 ? "b" : "a";
278 if (0 == instance)
279 {
280 return ipmi::responseInvalidFieldRequest();
281 }
282 else if (0 == instance / 2)
283 {
284 fullPath += std::string("1") + fanAb;
285 }
286 else
287 {
288 fullPath += std::to_string(instance / 2) + fanAb;
289 }
290
291 ipmi::Value reply;
292 if (mtm.getProperty(fanService, fullPath, fanIntf, "Value",
293 &reply) < 0)
294 {
295 return ipmi::responseInvalidFieldRequest();
296 }
297
298 double* doubleVal = std::get_if<double>(&reply);
299 if (doubleVal == nullptr)
300 {
301 return ipmi::responseUnspecifiedError();
302 }
303 uint8_t sensorVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
304 std::optional<uint16_t> fanTach = std::round(*doubleVal);
305
306 return ipmi::responseSuccess(sensorVal, fanTach);
307 }
308 break;
309 case SmSignalGet::smResetButton:
310 case SmSignalGet::smPowerButton:
311 case SmSignalGet::smNMIButton:
312 case SmSignalGet::smIdentifyButton:
313 {
314 std::string path;
315 if (getGpioPathForSmSignal(signalType, path) < 0)
316 {
317 return ipmi::responseInvalidFieldRequest();
318 }
319
320 switch (action)
321 {
322 case SmActionGet::sample:
323 phosphor::logging::log<phosphor::logging::level::INFO>(
324 "case SmActionGet::sample");
325 break;
326 case SmActionGet::ignore:
327 {
328 phosphor::logging::log<phosphor::logging::level::INFO>(
329 "case SmActionGet::ignore");
330 if (mtm.setProperty(buttonService, path, buttonIntf,
331 "ButtonMasked", true) < 0)
332 {
333 return ipmi::responseUnspecifiedError();
334 }
335 }
336 break;
337 case SmActionGet::revert:
338 {
339 phosphor::logging::log<phosphor::logging::level::INFO>(
340 "case SmActionGet::revert");
341 if (mtm.setProperty(buttonService, path, buttonIntf,
342 "ButtonMasked", false) < 0)
343 {
344 return ipmi::responseUnspecifiedError();
345 }
346 }
347 break;
348
349 default:
350 return ipmi::responseInvalidFieldRequest();
351 break;
352 }
353
354 ipmi::Value reply;
355 if (mtm.getProperty(buttonService, path, buttonIntf,
356 "ButtonPressed", &reply) < 0)
357 {
358 return ipmi::responseUnspecifiedError();
359 }
360 bool* valPtr = std::get_if<bool>(&reply);
361 if (valPtr == nullptr)
362 {
363 return ipmi::responseUnspecifiedError();
364 }
365 uint8_t sensorVal = *valPtr;
366 return ipmi::responseSuccess(sensorVal, std::nullopt);
367 }
368 break;
Vernon Mauerya3702c12019-05-22 13:20:59 -0700369 default:
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700370 return ipmi::responseInvalidFieldRequest();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700371 break;
372 }
Vernon Mauerya3702c12019-05-22 13:20:59 -0700373}
374
375ipmi_ret_t ipmi_app_mtm_set_signal(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
376 ipmi_request_t request,
377 ipmi_response_t response,
378 ipmi_data_len_t data_len,
379 ipmi_context_t context)
380{
381 uint8_t ret = 0;
382 ipmi_ret_t retCode = IPMI_CC_OK;
383 SetSmSignalReq* pReq = static_cast<SetSmSignalReq*>(request);
384 std::string ledName;
385 /////////////////// Signal to led configuration ////////////////
386 // {SM_SYSTEM_READY_LED, STAT_GRN_LED}, GPIOS4 gpio148
387 // {SM_POWER_FAULT_LED, STAT_AMB_LED}, GPIOS5 gpio149
388 // {SM_IDENTIFY_LED, IDENTIFY_LED}, GPIOS6 gpio150
389 // {SM_SPEAKER, SPEAKER}, GPIOAB0 gpio216
390 /////////////////////////////////////////////////////////////////
391 if ((*data_len == sizeof(*pReq)) &&
392 (mtm.getAccessLvl() >= MtmLvl::mtmAvailable))
393 {
394 switch (pReq->Signal)
395 {
396 case SmSignalSet::smPowerFaultLed:
397 case SmSignalSet::smSystemReadyLed:
398 case SmSignalSet::smIdentifyLed:
399 switch (pReq->Action)
400 {
401 case SmActionSet::forceDeasserted:
402 {
403 phosphor::logging::log<phosphor::logging::level::INFO>(
404 "case SmActionSet::forceDeasserted");
405
406 retCode =
407 ledStoreAndSet(pReq->Signal, std::string("Off"));
408 if (retCode != IPMI_CC_OK)
409 {
410 break;
411 }
412 mtm.revertTimer.start(revertTimeOut);
413 }
414 break;
415 case SmActionSet::forceAsserted:
416 {
417 phosphor::logging::log<phosphor::logging::level::INFO>(
418 "case SmActionSet::forceAsserted");
419
420 retCode =
421 ledStoreAndSet(pReq->Signal, std::string("On"));
422 if (retCode != IPMI_CC_OK)
423 {
424 break;
425 }
426 mtm.revertTimer.start(revertTimeOut);
427 if (SmSignalSet::smPowerFaultLed == pReq->Signal)
428 {
429 // Deassert "system ready"
430 retCode =
431 ledStoreAndSet(SmSignalSet::smSystemReadyLed,
432 std::string("Off"));
433 if (retCode != IPMI_CC_OK)
434 {
435 break;
436 }
437 }
438 else if (SmSignalSet::smSystemReadyLed == pReq->Signal)
439 {
440 // Deassert "fault led"
441 retCode =
442 ledStoreAndSet(SmSignalSet::smPowerFaultLed,
443 std::string("Off"));
444 if (retCode != IPMI_CC_OK)
445 {
446 break;
447 }
448 }
449 }
450 break;
451 case SmActionSet::revert:
452 {
453 phosphor::logging::log<phosphor::logging::level::INFO>(
454 "case SmActionSet::revert");
455 retCode = ledRevert(pReq->Signal);
456 if (retCode != IPMI_CC_OK)
457 {
458 break;
459 }
460 }
461 break;
462 default:
463 {
464 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
465 }
466 break;
467 }
468 break;
469 case SmSignalSet::smFanPowerSpeed:
470 {
471 if (((pReq->Action == SmActionSet::forceAsserted) &&
472 (*data_len != sizeof(*pReq)) && (pReq->Value > 100)) ||
473 pReq->Instance == 0)
474 {
475 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
476 break;
477 }
478 uint8_t pwmValue = 0;
479 switch (pReq->Action)
480 {
481 case SmActionSet::revert:
482 {
483 if (mtm.revertFanPWM)
484 {
485 ret = mtm.disablePidControlService(false);
486 if (ret < 0)
487 {
488 retCode = IPMI_CC_UNSPECIFIED_ERROR;
489 break;
490 }
491 mtm.revertFanPWM = false;
492 }
493 }
494 break;
495 case SmActionSet::forceAsserted:
496 {
497 pwmValue = pReq->Value;
498 } // fall-through
499 case SmActionSet::forceDeasserted:
500 {
501 if (!mtm.revertFanPWM)
502 {
503 ret = mtm.disablePidControlService(true);
504 if (ret < 0)
505 {
506 retCode = IPMI_CC_UNSPECIFIED_ERROR;
507 break;
508 }
509 mtm.revertFanPWM = true;
510 }
511 mtm.revertTimer.start(revertTimeOut);
512 std::string fanPwmInstancePath =
513 fanPwmPath + std::to_string(pReq->Instance);
514
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700515 ret = mtm.setProperty(fanService, fanPwmInstancePath,
516 fanIntf, "Value",
517 static_cast<double>(pwmValue));
Vernon Mauerya3702c12019-05-22 13:20:59 -0700518 if (ret < 0)
519 {
520 retCode = IPMI_CC_UNSPECIFIED_ERROR;
521 }
522 }
523 break;
524 default:
525 {
526 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
527 }
528 break;
529 }
530 }
531 break;
532 default:
533 {
534 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
535 }
536 break;
537 }
538 }
539 else
540 {
541 retCode = IPMI_CC_ILLEGAL_COMMAND;
542 }
543
544 *data_len = 0; // Only CC is return for SetSmSignal cmd
545 return retCode;
546}
547
548} // namespace ipmi
549
550void register_mtm_commands() __attribute__((constructor));
551void register_mtm_commands()
552{
Jason M. Bills38d2b5a2019-06-03 16:26:11 -0700553 // <Get SM Signal>
554 ipmi::registerHandler(
555 ipmi::prioOemBase, ipmi::netFnOemOne,
556 static_cast<ipmi::Cmd>(IPMINetFnIntelOemGeneralCmds::GetSmSignal),
557 ipmi::Privilege::User, ipmi::appMTMGetSignal);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700558
559 ipmi_register_callback(
560 netfnIntcOEMGeneral,
561 static_cast<ipmi_cmd_t>(IPMINetFnIntelOemGeneralCmds::SetSmSignal),
562 NULL, ipmi::ipmi_app_mtm_set_signal, PRIVILEGE_USER);
563
564 return;
565}