blob: 7bbb970ec2035240ed8bf27f19ebe3afdab8ef36 [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
17#include <boost/process/child.hpp>
18#include <ipmid/api.hpp>
19#include <manufacturingcommands.hpp>
20#include <oemcommands.hpp>
21
22namespace ipmi
23{
24
25Manufacturing mtm;
26
27static auto revertTimeOut =
28 std::chrono::duration_cast<std::chrono::microseconds>(
29 std::chrono::seconds(60)); // 1 minute timeout
30
31static constexpr const char* idButtonPath =
32 "/xyz/openbmc_project/Chassis/Buttons/ID0";
33static constexpr const char* idButtonInterface =
34 "xyz.openbmc_project.Chassis.Buttons.ID";
35static constexpr const char* idButtonMemberPressed = "Pressed";
36
37static constexpr const char* callbackMgrService =
38 "xyz.openbmc_project.CallbackManager";
39static constexpr const char* callbackMgrIntf =
40 "xyz.openbmc_project.CallbackManager";
41static constexpr const char* callbackMgrObjPath =
42 "/xyz/openbmc_project/CallbackManager";
43static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
44
45const static constexpr char* systemDService = "org.freedesktop.systemd1";
46const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
47const static constexpr char* systemDMgrIntf =
48 "org.freedesktop.systemd1.Manager";
49const static constexpr char* pidControlService = "phosphor-pid-control.service";
50
51// TODO: Temporary place to test the working code. Will be moved to
52// gpio daemon
53constexpr const char* passthroughPath = "/usr/bin/set-passthrough.sh";
54void disablePassthrough(bool value)
55{
56 boost::process::child c(passthroughPath, value ? "0" : "1");
57 c.wait();
58}
59
60ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState)
61{
62 LedProperty* ledProp = mtm.findLedProperty(signal);
63 if (ledProp == nullptr)
64 {
65 return IPMI_CC_INVALID_FIELD_REQUEST;
66 }
67
68 std::string ledName = ledProp->getName();
69 std::string ledService = ledServicePrefix + ledName;
70 std::string ledPath = ledPathPrefix + ledName;
71 ipmi::Value presentState;
72
73 if (false == ledProp->getLock())
74 {
75 if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
76 "State", &presentState) != 0)
77 {
78 return IPMI_CC_UNSPECIFIED_ERROR;
79 }
80 ledProp->setPrevState(std::get<std::string>(presentState));
81 ledProp->setLock(true);
82 if (signal == SmSignalSet::smPowerFaultLed ||
83 signal == SmSignalSet::smSystemReadyLed)
84 {
85 mtm.revertLedCallback = true;
86 }
87 }
88 if (mtm.setProperty(ledService.c_str(), ledPath.c_str(), ledIntf, "State",
89 ledStateStr + setState) != 0)
90 {
91 return IPMI_CC_UNSPECIFIED_ERROR;
92 }
93 return IPMI_CC_OK;
94}
95
96ipmi_ret_t ledRevert(SmSignalSet signal)
97{
98 LedProperty* ledProp = mtm.findLedProperty(signal);
99 if (ledProp == nullptr)
100 {
101 return IPMI_CC_INVALID_FIELD_REQUEST;
102 }
103 if (true == ledProp->getLock())
104 {
105 ledProp->setLock(false);
106 if (signal == SmSignalSet::smPowerFaultLed ||
107 signal == SmSignalSet::smSystemReadyLed)
108 {
109 try
110 {
111 ipmi::method_no_args::callDbusMethod(
112 *getSdBus(), callbackMgrService, callbackMgrObjPath,
113 callbackMgrIntf, retriggerLedUpdate);
114 }
115 catch (sdbusplus::exception_t& e)
116 {
117 return IPMI_CC_UNSPECIFIED_ERROR;
118 }
119 mtm.revertLedCallback = false;
120 }
121 else
122 {
123 std::string ledName = ledProp->getName();
124 std::string ledService = ledServicePrefix + ledName;
125 std::string ledPath = ledPathPrefix + ledName;
126 if (mtm.setProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
127 "State", ledProp->getPrevState()) != 0)
128 {
129 return IPMI_CC_UNSPECIFIED_ERROR;
130 }
131 }
132 }
133 return IPMI_CC_OK;
134}
135
136void Manufacturing::initData()
137{
138 gpioPaths[(uint8_t)SmSignalGet::smPowerButton] = "Power_Button";
139 gpioPaths[(uint8_t)SmSignalGet::smResetButton] = "Reset_Button";
140 gpioPaths[(uint8_t)SmSignalGet::smIdentifyButton] = "ID_Button";
141 gpioPaths[(uint8_t)SmSignalGet::smFpLcpEnterButton] = "Lcp_Enter_Button";
142 gpioPaths[(uint8_t)SmSignalGet::smFpLcpLeftButton] = "Lcp_Left_Button";
143 gpioPaths[(uint8_t)SmSignalGet::smFpLcpRightButton] = "Lcp_Right_Button";
144 gpioPaths[(uint8_t)SmSignalGet::smNmiButton] = "Nmi_Button";
145
146 ledPropertyList.push_back(
147 LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
148 ledPropertyList.push_back(
149 LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
150 ledPropertyList.push_back(
151 LedProperty(SmSignalSet::smIdentifyLed, "identify"));
152}
153
154void Manufacturing::revertTimerHandler()
155{
156 for (const auto& signal : revertSmSignalGetVector)
157 {
158 mtm.setProperty(gpioService,
159 mtm.getGpioPathForSmSignal((uint8_t)signal), gpioIntf,
160 "Ignore", false);
161 }
162 revertSmSignalGetVector.clear();
163 disablePassthrough(false);
164 if (revertFanPWM)
165 {
166 revertFanPWM = false;
167 disablePidControlService(false);
168 }
169
170 for (const auto& ledProperty : ledPropertyList)
171 {
172 const std::string& ledName = ledProperty.getName();
173 ledRevert(ledProperty.getSignal());
174 }
175}
176
177Manufacturing::Manufacturing() :
178 revertTimer([&](void) { revertTimerHandler(); })
179{
180 initData();
181}
182
183int8_t Manufacturing::getProperty(const char* service, std::string path,
184 const char* interface,
185 std::string propertyName, ipmi::Value* reply)
186{
187 try
188 {
189 *reply = ipmi::getDbusProperty(*getSdBus(), service, path.c_str(),
190 interface, propertyName);
191 }
192 catch (const sdbusplus::exception::SdBusError& e)
193 {
194 phosphor::logging::log<phosphor::logging::level::INFO>(
195 "ERROR: getProperty");
196 return -1;
197 }
198
199 return 0;
200}
201
202int8_t Manufacturing::setProperty(const char* service, std::string path,
203 const char* interface,
204 std::string propertyName, ipmi::Value value)
205{
206 try
207 {
208 ipmi::setDbusProperty(*getSdBus(), service, path.c_str(), interface,
209 propertyName, value);
210 }
211 catch (const sdbusplus::exception::SdBusError& e)
212 {
213 phosphor::logging::log<phosphor::logging::level::INFO>(
214 "ERROR: setProperty");
215 return -1;
216 }
217
218 return 0;
219}
220
221int8_t Manufacturing::disablePidControlService(const bool disable)
222{
223 try
224 {
225 auto dbus = getSdBus();
226 auto method = dbus->new_method_call(systemDService, systemDObjPath,
227 systemDMgrIntf,
228 disable ? "StopUnit" : "StartUnit");
229 method.append(pidControlService, "replace");
230 auto reply = dbus->call(method);
231 }
232 catch (const sdbusplus::exception::SdBusError& e)
233 {
234 phosphor::logging::log<phosphor::logging::level::INFO>(
235 "ERROR: phosphor-pid-control service start or stop failed");
236 return -1;
237 }
238 return 0;
239}
240
241std::tuple<uint8_t, ipmi_ret_t, uint8_t>
242 Manufacturing::proccessSignal(SmSignalGet signal, SmActionGet action)
243{
244 int8_t ret = 0;
245 uint8_t retCode = 0;
246 uint8_t dataLen = 0;
247 uint8_t value = 0;
248 ipmi::Value reply;
249
250 switch (action)
251 {
252 case SmActionGet::sample:
253 phosphor::logging::log<phosphor::logging::level::INFO>(
254 "case SmActionGet::sample");
255 break;
256 case SmActionGet::ignore:
257 {
258 phosphor::logging::log<phosphor::logging::level::INFO>(
259 "case SmActionGet::ignore");
260 if (std::find(revertSmSignalGetVector.begin(),
261 revertSmSignalGetVector.end(),
262 signal) == revertSmSignalGetVector.end())
263 {
264 // Todo: Needs to be replaced with pass-through of particular
265 // pin
266 disablePassthrough(true);
267 ret = mtm.setProperty(
268 gpioService, mtm.getGpioPathForSmSignal((uint8_t)signal),
269 gpioIntf, "Ignore", true);
270 if (ret < 0)
271 {
272 dataLen = 0;
273 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
274 }
275 revertSmSignalGetVector.push_back(signal);
276 revertTimer.start(revertTimeOut);
277 }
278 }
279 break;
280 case SmActionGet::revert:
281 {
282 phosphor::logging::log<phosphor::logging::level::INFO>(
283 "case SmActionGet::revert");
284 auto iter = std::find(revertSmSignalGetVector.begin(),
285 revertSmSignalGetVector.end(), signal);
286 if (iter != revertSmSignalGetVector.end())
287 {
288 ret = mtm.setProperty(
289 gpioService, mtm.getGpioPathForSmSignal((uint8_t)signal),
290 gpioIntf, "Ignore", false);
291 if (ret < 0)
292 {
293 dataLen = 0;
294 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
295 }
296 revertSmSignalGetVector.erase(iter);
297 // Todo: Needs to be replaced with pass-through of particular
298 // pin
299 disablePassthrough(true);
300 if (revertSmSignalGetVector.size() == 0)
301 {
302 revertTimer.stop();
303 }
304 }
305 }
306 break;
307
308 default:
309 dataLen = 0;
310 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
311 break;
312 }
313
314 if (ret == 0) // No error happend, cmd will return with gpio value
315 {
316 ret = mtm.getProperty(gpioService,
317 mtm.getGpioPathForSmSignal((uint8_t)signal),
318 gpioIntf, "SampledValue", &reply);
319 if (ret < 0)
320 {
321 dataLen = 0;
322 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
323 }
324 else
325 {
326 dataLen = 1;
327 value = std::get<bool>(reply);
328 }
329 }
330
331 return std::make_tuple(dataLen, retCode, value);
332}
333
334ipmi_ret_t ipmi_app_mtm_get_signal(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
335 ipmi_request_t request,
336 ipmi_response_t response,
337 ipmi_data_len_t data_len,
338 ipmi_context_t context)
339{
340 ipmi_ret_t retCode = IPMI_CC_OK;
341 int8_t ret = 0;
342 GetSmSignalReq* pReq = NULL;
343 GetSmSignalRsp* pRsp = NULL;
344
345 pReq = static_cast<GetSmSignalReq*>(request);
346 pRsp = static_cast<GetSmSignalRsp*>(response);
347
348 ipmi::Value reply;
349
350 if ((*data_len == sizeof(*pReq)) &&
351 (mtm.getAccessLvl() >= MtmLvl::mtmAvailable))
352 {
353 switch (pReq->Signal)
354 {
355 case SmSignalGet::smFanPwmGet:
356 {
357 std::string fullPath =
358 fanPwmPath + std::to_string(pReq->Instance);
359 ret = mtm.getProperty(fanService, fullPath, fanIntf, "Value",
360 &reply);
361 if (ret < 0)
362 {
363 *data_len = 0;
364 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
365 break;
366 }
367 *data_len = 1;
368 pRsp->SigVal = std::get<double>(reply);
369 }
370 break;
371 case SmSignalGet::smFanTachometerGet:
372 {
373 // Full path calculation pattern:
374 // Instance 1 path is
375 // /xyz/openbmc_project/sensors/fan_tach/Fan_1a Instance 2 path
376 // is /xyz/openbmc_project/sensors/fan_tach/Fan_1b Instance 3
377 // path is /xyz/openbmc_project/sensors/fan_tach/Fan_2a
378 // and so on...
379 std::string fullPath = fanTachPathPrefix;
380 std::string fanAb = (pReq->Instance % 2) == 0 ? "b" : "a";
381 if (0 == pReq->Instance)
382 {
383 *data_len = 0;
384 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
385 break;
386 }
387 else if (0 == pReq->Instance / 2)
388 {
389 fullPath += std::string("1") + fanAb;
390 }
391 else
392 {
393 fullPath += std::to_string(pReq->Instance / 2) + fanAb;
394 }
395
396 ret = mtm.getProperty(fanService, fullPath, fanIntf, "Value",
397 &reply);
398 if (ret < 0)
399 {
400 *data_len = 0;
401 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
402 break;
403 }
404
405 uint16_t value = std::get<double>(reply);
406 *data_len = sizeof(*pRsp);
407
408 pRsp->SigVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
409 pRsp->SigVal1 = value & 0x00FF;
410 pRsp->SigVal2 = (value >> 8) & 0xFF;
411 }
412 break;
413 case SmSignalGet::smResetButton: // gpio32
414 case SmSignalGet::smPowerButton: // gpio34
415 case SmSignalGet::smFpLcpEnterButton: // gpio51
416 case SmSignalGet::smFpLcpLeftButton: // gpio52
417 case SmSignalGet::smFpLcpRightButton: // gpio53
418 case SmSignalGet::smNmiButton: // gpio217
419 case SmSignalGet::smIdentifyButton: // gpio218
420 std::tie(*data_len, retCode, pRsp->SigVal) =
421 mtm.proccessSignal(pReq->Signal, pReq->Action);
422 *data_len = sizeof(pRsp->SigVal);
423 break;
424 default:
425 *data_len = 0;
426 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
427 break;
428 }
429 }
430 else
431 {
432 *data_len = 0;
433 retCode = IPMI_CC_REQ_DATA_LEN_INVALID;
434 }
435
436 return retCode;
437}
438
439ipmi_ret_t ipmi_app_mtm_set_signal(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
440 ipmi_request_t request,
441 ipmi_response_t response,
442 ipmi_data_len_t data_len,
443 ipmi_context_t context)
444{
445 uint8_t ret = 0;
446 ipmi_ret_t retCode = IPMI_CC_OK;
447 SetSmSignalReq* pReq = static_cast<SetSmSignalReq*>(request);
448 std::string ledName;
449 /////////////////// Signal to led configuration ////////////////
450 // {SM_SYSTEM_READY_LED, STAT_GRN_LED}, GPIOS4 gpio148
451 // {SM_POWER_FAULT_LED, STAT_AMB_LED}, GPIOS5 gpio149
452 // {SM_IDENTIFY_LED, IDENTIFY_LED}, GPIOS6 gpio150
453 // {SM_SPEAKER, SPEAKER}, GPIOAB0 gpio216
454 /////////////////////////////////////////////////////////////////
455 if ((*data_len == sizeof(*pReq)) &&
456 (mtm.getAccessLvl() >= MtmLvl::mtmAvailable))
457 {
458 switch (pReq->Signal)
459 {
460 case SmSignalSet::smPowerFaultLed:
461 case SmSignalSet::smSystemReadyLed:
462 case SmSignalSet::smIdentifyLed:
463 switch (pReq->Action)
464 {
465 case SmActionSet::forceDeasserted:
466 {
467 phosphor::logging::log<phosphor::logging::level::INFO>(
468 "case SmActionSet::forceDeasserted");
469
470 retCode =
471 ledStoreAndSet(pReq->Signal, std::string("Off"));
472 if (retCode != IPMI_CC_OK)
473 {
474 break;
475 }
476 mtm.revertTimer.start(revertTimeOut);
477 }
478 break;
479 case SmActionSet::forceAsserted:
480 {
481 phosphor::logging::log<phosphor::logging::level::INFO>(
482 "case SmActionSet::forceAsserted");
483
484 retCode =
485 ledStoreAndSet(pReq->Signal, std::string("On"));
486 if (retCode != IPMI_CC_OK)
487 {
488 break;
489 }
490 mtm.revertTimer.start(revertTimeOut);
491 if (SmSignalSet::smPowerFaultLed == pReq->Signal)
492 {
493 // Deassert "system ready"
494 retCode =
495 ledStoreAndSet(SmSignalSet::smSystemReadyLed,
496 std::string("Off"));
497 if (retCode != IPMI_CC_OK)
498 {
499 break;
500 }
501 }
502 else if (SmSignalSet::smSystemReadyLed == pReq->Signal)
503 {
504 // Deassert "fault led"
505 retCode =
506 ledStoreAndSet(SmSignalSet::smPowerFaultLed,
507 std::string("Off"));
508 if (retCode != IPMI_CC_OK)
509 {
510 break;
511 }
512 }
513 }
514 break;
515 case SmActionSet::revert:
516 {
517 phosphor::logging::log<phosphor::logging::level::INFO>(
518 "case SmActionSet::revert");
519 retCode = ledRevert(pReq->Signal);
520 if (retCode != IPMI_CC_OK)
521 {
522 break;
523 }
524 }
525 break;
526 default:
527 {
528 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
529 }
530 break;
531 }
532 break;
533 case SmSignalSet::smFanPowerSpeed:
534 {
535 if (((pReq->Action == SmActionSet::forceAsserted) &&
536 (*data_len != sizeof(*pReq)) && (pReq->Value > 100)) ||
537 pReq->Instance == 0)
538 {
539 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
540 break;
541 }
542 uint8_t pwmValue = 0;
543 switch (pReq->Action)
544 {
545 case SmActionSet::revert:
546 {
547 if (mtm.revertFanPWM)
548 {
549 ret = mtm.disablePidControlService(false);
550 if (ret < 0)
551 {
552 retCode = IPMI_CC_UNSPECIFIED_ERROR;
553 break;
554 }
555 mtm.revertFanPWM = false;
556 }
557 }
558 break;
559 case SmActionSet::forceAsserted:
560 {
561 pwmValue = pReq->Value;
562 } // fall-through
563 case SmActionSet::forceDeasserted:
564 {
565 if (!mtm.revertFanPWM)
566 {
567 ret = mtm.disablePidControlService(true);
568 if (ret < 0)
569 {
570 retCode = IPMI_CC_UNSPECIFIED_ERROR;
571 break;
572 }
573 mtm.revertFanPWM = true;
574 }
575 mtm.revertTimer.start(revertTimeOut);
576 std::string fanPwmInstancePath =
577 fanPwmPath + std::to_string(pReq->Instance);
578
579 ret = mtm.setProperty(
580 fanService, fanPwmInstancePath.c_str(), fanIntf,
581 "Value", static_cast<double>(pwmValue));
582 if (ret < 0)
583 {
584 retCode = IPMI_CC_UNSPECIFIED_ERROR;
585 }
586 }
587 break;
588 default:
589 {
590 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
591 }
592 break;
593 }
594 }
595 break;
596 default:
597 {
598 retCode = IPMI_CC_INVALID_FIELD_REQUEST;
599 }
600 break;
601 }
602 }
603 else
604 {
605 retCode = IPMI_CC_ILLEGAL_COMMAND;
606 }
607
608 *data_len = 0; // Only CC is return for SetSmSignal cmd
609 return retCode;
610}
611
612} // namespace ipmi
613
614void register_mtm_commands() __attribute__((constructor));
615void register_mtm_commands()
616{
617 ipmi_register_callback(
618 netfnIntcOEMGeneral,
619 static_cast<ipmi_cmd_t>(IPMINetFnIntelOemGeneralCmds::GetSmSignal),
620 NULL, ipmi::ipmi_app_mtm_get_signal, PRIVILEGE_USER);
621
622 ipmi_register_callback(
623 netfnIntcOEMGeneral,
624 static_cast<ipmi_cmd_t>(IPMINetFnIntelOemGeneralCmds::SetSmSignal),
625 NULL, ipmi::ipmi_app_mtm_set_signal, PRIVILEGE_USER);
626
627 return;
628}