libpldm: Add APIs to enable PLDM Requester flow

Implement requester APIs outlined at
https://github.com/openbmc/docs/blob/master/designs/pldm-stack.md#Requester.

Sync (send request msg and wait for response) and async (send request
msg and have caller poll an fd) APIs have been implemented. Example apps
that use both flavor of APIs have been implemented as well.

These APIs depend on openbmc/libmctp. For that reason and given the
OpenBMC agnostic nature of libpldm, these APIs must be conditionally
built via a meson feature (requester-api).

Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I666a534d8c876638a29074d62c2ed700f33229a8
diff --git a/utilities/requester/set_state_effecter_async.cpp b/utilities/requester/set_state_effecter_async.cpp
new file mode 100644
index 0000000..47d8dd1
--- /dev/null
+++ b/utilities/requester/set_state_effecter_async.cpp
@@ -0,0 +1,89 @@
+#include <CLI/CLI.hpp>
+#include <array>
+#include <iostream>
+#include <sdeventplus/event.hpp>
+#include <sdeventplus/source/io.hpp>
+
+#include "libpldm/base.h"
+#include "libpldm/platform.h"
+#include "libpldm/requester/pldm.h"
+
+using namespace sdeventplus;
+using namespace sdeventplus::source;
+
+int main(int argc, char** argv)
+{
+    CLI::App app{"Send PLDM command SetStateEffecterStates"};
+    uint8_t mctpEid{};
+    app.add_option("-m,--mctp_eid", mctpEid, "MCTP EID")->required();
+    uint16_t effecterId{};
+    app.add_option("-e,--effecter", effecterId, "Effecter Id")->required();
+    uint8_t state{};
+    app.add_option("-s,--state", state, "New state value")->required();
+    CLI11_PARSE(app, argc, argv);
+
+    // Encode PLDM Request message
+    uint8_t effecterCount = 1;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + sizeof(effecterId) +
+                            sizeof(effecterCount) +
+                            sizeof(set_effecter_state_field)>
+        requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    set_effecter_state_field stateField{PLDM_REQUEST_SET, state};
+    auto rc = encode_set_state_effecter_states_req(0, effecterId, effecterCount,
+                                                   &stateField, request);
+    if (rc != PLDM_SUCCESS)
+    {
+        std::cerr << "Message encode failure. PLDM error code = " << std::hex
+                  << std::showbase << rc << std::endl;
+        return -1;
+    }
+
+    // Get fd of MCTP socket
+    int fd = pldm_open();
+    if (-1 == fd)
+    {
+        std::cerr << "Failed to init mctp" << std::endl;
+        return -1;
+    }
+
+    // Create event loop and add a callback to handle EPOLLIN on fd
+    auto event = Event::get_default();
+    auto callback = [=](IO& io, int fd, uint32_t revents) {
+        if (!(revents & EPOLLIN))
+        {
+            return;
+        }
+
+        uint8_t* responseMsg = nullptr;
+        size_t responseMsgSize{};
+        auto rc = pldm_recv(mctpEid, fd, request->hdr.instance_id, &responseMsg,
+                            &responseMsgSize);
+        if (!rc)
+        {
+            // We've got the response meant for the PLDM request msg that was
+            // sent out
+            io.set_enabled(Enabled::Off);
+            pldm_msg* response = reinterpret_cast<pldm_msg*>(responseMsg);
+            std::cout << "Done. PLDM RC = " << std::hex << std::showbase
+                      << static_cast<uint16_t>(response->payload[0])
+                      << std::endl;
+            free(responseMsg);
+            exit(EXIT_SUCCESS);
+        }
+    };
+    IO io(event, fd, EPOLLIN, std::move(callback));
+
+    // Send PLDM Request message - pldm_send doesn't wait for response
+    rc = pldm_send(mctpEid, fd, requestMsg.data(), requestMsg.size());
+    if (0 > rc)
+    {
+        std::cerr << "Failed to send message/receive response. RC = " << rc
+                  << ", errno = " << errno << std::endl;
+        return -1;
+    }
+
+    event.loop();
+
+    return 0;
+}