blob: 500e57d030bc425ee269c125e04aa75f26d57050 [file] [log] [blame]
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +05301#include <chrono>
2#include <phosphor-logging/log.hpp>
3#include <phosphor-logging/elog-errors.hpp>
4#include <xyz/openbmc_project/Common/error.hpp>
5#include <systemintfcmds.h>
6#include <utils.hpp>
7#include <config.h>
8#include <host-cmd-manager.hpp>
Vishwanatha Subbanna6e8979d2017-07-13 16:48:20 +05309#include <timer.hpp>
10
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053011namespace phosphor
12{
13namespace host
14{
15namespace command
16{
17
18constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
19constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
20constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
21
22// For throwing exceptions
23using namespace phosphor::logging;
24using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
25 Error::InternalFailure;
26
Vishwanatha Subbanna6e8979d2017-07-13 16:48:20 +053027Manager::Manager(sdbusplus::bus::bus& bus, sd_event* event) :
28 bus(bus),
29 timer(event, std::bind(&Manager::hostTimeout, this))
30{
31 // Nothing to do here.
32}
33
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053034// Called as part of READ_MSG_DATA command
35IpmiCmdData Manager::getNextCommand()
36{
37 // Stop the timer. Don't have to Err failure doing so.
38 auto r = timer.setTimer(SD_EVENT_OFF);
39 if (r < 0)
40 {
41 log<level::ERR>("Failure to STOP the timer",
42 entry("ERROR=%s", strerror(-r)));
43 }
44
45 if(this->workQueue.empty())
46 {
47 // Just return a heartbeat in this case. A spurious SMS_ATN was
48 // asserted for the host (probably from a previous boot).
49 log<level::INFO>("Control Host work queue is empty!");
50
51 return std::make_pair(CMD_HEARTBEAT, 0x00);
52 }
53
54 // Pop the processed entry off the queue
55 auto command = this->workQueue.front();
56 this->workQueue.pop();
57
58 // IPMI command is the first element in pair
59 auto ipmiCmdData = std::get<0>(command);
60
61 // Now, call the user registered functions so that
62 // implementation specific CommandComplete signals
63 // can be sent. `true` indicating Success.
64 std::get<CallBack>(command)(ipmiCmdData, true);
65
66 // Check for another entry in the queue and kick it off
67 this->checkQueueAndAlertHost();
68
69 // Tuple of command and data
70 return ipmiCmdData;
71}
72
73// Called when initial timer goes off post sending SMS_ATN
74void Manager::hostTimeout()
75{
76 log<level::ERR>("Host control timeout hit!");
77
78 // Dequeue all entries and send fail signal
79 while(!this->workQueue.empty())
80 {
81 auto command = this->workQueue.front();
82 this->workQueue.pop();
83
84 // IPMI command is the first element in pair
85 auto ipmiCmdData = std::get<0>(command);
86
87 // Call the implementation specific Command Failure.
88 // `false` indicating Failure
89 std::get<CallBack>(command)(ipmiCmdData, false);
90 }
91}
92
93// Called for alerting the host
94void Manager::checkQueueAndAlertHost()
95{
96 if (this->workQueue.size() >= 1)
97 {
98 log<level::INFO>("Asserting SMS Attention");
99
100 std::string IPMI_PATH("/org/openbmc/HostIpmi/1");
101 std::string IPMI_INTERFACE("org.openbmc.HostIpmi");
102
103 auto host = ::ipmi::getService(this->bus,IPMI_INTERFACE,IPMI_PATH);
104
105 // Start the timer for this transaction
106 auto time = std::chrono::duration_cast<std::chrono::microseconds>(
107 std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS));
108
109 auto r = timer.startTimer(time);
110 if (r < 0)
111 {
112 log<level::ERR>("Error starting timer for control host");
113 return;
114 }
115
116 auto method = this->bus.new_method_call(host.c_str(),
117 IPMI_PATH.c_str(),
118 IPMI_INTERFACE.c_str(),
119 "setAttention");
120 auto reply = this->bus.call(method);
121
122 if (reply.is_method_error())
123 {
124 log<level::ERR>("Error in setting SMS attention");
125 elog<InternalFailure>();
126 }
127 log<level::INFO>("SMS Attention asserted");
128 }
129}
130
131// Called by specific implementations that provide commands
132void Manager::execute(CommandHandler command)
133{
134 log<level::INFO>("Pushing cmd on to queue",
135 entry("COMMAND=%d", std::get<0>(command).first));
136
137 this->workQueue.emplace(command);
138
139 // Alert host if this is only command in queue otherwise host will
140 // be notified of next message after processing the current one
141 if (this->workQueue.size() == 1)
142 {
143 this->checkQueueAndAlertHost();
144 }
145 else
146 {
147 log<level::INFO>("Command in process, no attention");
148 }
149
150 return;
151}
152
153} // namespace command
154} // namespace host
155} // namepsace phosphor