dsp: platform: Add encode req & decode resp for GetEventReceiver command

Add encode and decode API for the Get Event Receiver command
This command is defined in DSP0248 as a conditional command.

The GetEventReceiver command is used to verify the values that were set
into an Event Generator using the SetEventReceiver command.

Change-Id: I411cc939d00bc69867507fe58911c813d0c1e2ae
Signed-off-by: Roger G. Coscojuela <roger.gili-coscojuela@sipearl.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23a058e..cef81ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
 - utils: Introduce `pldm_edac_crc8()`
 - pdr: Add pldm_pdr_delete_by_effecter_id() API
 - platform: Add encode req for GetPDRRepositoryInfo
+- platform: Add encode req & decode resp for GetEventReceiver
 - pdr: Add pldm_pdr_delete_by_sensor_id() API
 - pdr: Add pldm_entity_association_tree_delete_node() API
 
diff --git a/include/libpldm/platform.h b/include/libpldm/platform.h
index 89af565..4cce891 100644
--- a/include/libpldm/platform.h
+++ b/include/libpldm/platform.h
@@ -16,6 +16,7 @@
 #include <libpldm/compiler.h>
 #include <libpldm/pdr.h>
 #include <libpldm/pldm_types.h>
+#include <libpldm/utils.h>
 
 /**
  * @brief PLDM response transfer flag for the Platform and control commands
@@ -47,7 +48,8 @@
 
 #define PLDM_GET_PDR_REQ_BYTES 13
 
-#define PLDM_SET_EVENT_RECEIVER_RESP_BYTES 1
+#define PLDM_SET_EVENT_RECEIVER_RESP_BYTES     1
+#define PLDM_GET_EVENT_RECEIVER_MIN_RESP_BYTES 2
 
 /* Platform event supported request */
 #define PLDM_EVENT_MESSAGE_BUFFER_SIZE_REQ_BYTES  2
@@ -1081,6 +1083,19 @@
 	uint16_t heartbeat_timer;
 } __attribute__((packed));
 
+/** @struct pldm_get_event_receiver_resp
+ *
+ * Structure representing GetEventReceiver command.
+ */
+struct pldm_get_event_receiver_resp {
+	uint8_t completion_code;
+	uint8_t transport_protocol_type;
+	union {
+		uint8_t mctp_eid;
+		struct variable_field vendor_specific;
+	} event_receiver_address;
+};
+
 /** @struct pldm_event_message_buffer_size_req
  *
  *  Structure representing EventMessageBufferSizes command request data
@@ -2437,6 +2452,60 @@
 	uint8_t *present_state, uint8_t *previous_state, uint8_t *event_state,
 	uint8_t *present_reading);
 
+/** @brief Encode the GetEventReceiver request message
+ *
+ * @param[in] instance_id - Message's instance id
+ * @param[out] msg - Argument to capture the Message
+ * @param[in] payload_length - length of request message payload
+ * @return 0 on success
+ *         -EINVAL if the input parameters' memory are not allocated,
+ *         or message type or instance in request header is invalid
+ *         -ENOMSG if the PLDM type in the request header is invalid
+ *         -EOVERFLOW if the input message length is invalid
+ */
+int encode_get_event_receiver_req(uint8_t instance_id, struct pldm_msg *msg,
+				  size_t payload_length);
+
+/** @brief Decode the GetEventReceiver response message
+ *
+ * @param[in] msg - Request message
+ * @param[in] payload_length - Length of response message payload
+ * @param[out] resp - Structure to store decoded response
+ * @return 0 on success
+ *         -EINVAL if the input parameters' memory are not allocated,
+ *         or message type or instance in request header is invalid
+ *         -ENOMSG if the PLDM type in the request header is invalid
+ *         -EOVERFLOW if the input message length is invalid
+ *         -ENOTSUP if the transport protocol is not supported
+ */
+int decode_get_event_receiver_resp(const struct pldm_msg *msg,
+				   size_t payload_length,
+				   struct pldm_get_event_receiver_resp *resp);
+
+/** @brief Encode the GetEventReceiver response message
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] event_receiver_info -  Structure to encode. All members,
+ *  except those mentioned in the @note below, should be initialized by
+ * the caller.
+ *  @param[out] msg - Argument to capture the Message
+ *  @param[in/out] payload_lenght - The lenght of the supplied buffer for
+ payload
+ * @return 0 on success
+ *         -EINVAL if the input parameters' memory are not allocated,
+ *         or message type or instance in request header is invalid
+ *         -ENOMSG if the PLDM type in the request header is invalid
+ *         -EOVERFLOW if the input message length is invalid
+ *
+ * @note Caller is responsible for the allocation of the event_receiver_address_info
+ *       parameter. For MCTP transport event_receiver_info.mctp_eid should be set. For other
+ *       protocol types event_receiver_info.vendor_specific should be used.
+ */
+int encode_get_event_receiver_resp(
+	uint8_t instance_id,
+	struct pldm_get_event_receiver_resp *event_receiver_info,
+	struct pldm_msg *msg, size_t *payload_length);
+
 /** @brief Encode the SetEventReceiver request message
  *
  * @param[in] instance_id - Message's instance id
diff --git a/src/dsp/platform.c b/src/dsp/platform.c
index 7ad8e2f..21ada82 100644
--- a/src/dsp/platform.c
+++ b/src/dsp/platform.c
@@ -2503,6 +2503,100 @@
 	return PLDM_SUCCESS;
 }
 
+LIBPLDM_ABI_TESTING
+int encode_get_event_receiver_req(uint8_t instance_id, struct pldm_msg *msg,
+				  size_t payload_length LIBPLDM_CC_UNUSED)
+{
+	struct pldm_header_info header;
+	header.msg_type = PLDM_REQUEST;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_GET_EVENT_RECEIVER;
+
+	if (!msg) {
+		return -EINVAL;
+	}
+
+	return pack_pldm_header_errno(&header, &(msg->hdr));
+}
+
+LIBPLDM_ABI_TESTING
+int encode_get_event_receiver_resp(
+	uint8_t instance_id,
+	struct pldm_get_event_receiver_resp *event_receiver_info,
+	struct pldm_msg *msg, size_t *payload_length)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+	if (!msg || !event_receiver_info) {
+		return -EINVAL;
+	}
+
+	/* See Table 2, DSP0245 v1.4.0 */
+	if (event_receiver_info->transport_protocol_type !=
+	    PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP) {
+		return -ENOTSUP;
+	}
+
+	struct pldm_header_info header = { 0 };
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_GET_EVENT_RECEIVER;
+
+	rc = pack_pldm_header_errno(&header, &(msg->hdr));
+	if (rc) {
+		return rc;
+	}
+	rc = pldm_msgbuf_init_errno(buf, PLDM_GET_EVENT_RECEIVER_MIN_RESP_BYTES,
+				    msg->payload, *payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, event_receiver_info->completion_code);
+	pldm_msgbuf_insert(buf, event_receiver_info->transport_protocol_type);
+	pldm_msgbuf_insert(
+		buf, event_receiver_info->event_receiver_address.mctp_eid);
+	return pldm_msgbuf_complete_used(buf, *payload_length, payload_length);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_get_event_receiver_resp(const struct pldm_msg *msg,
+				   size_t payload_length,
+				   struct pldm_get_event_receiver_resp *resp)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+	if (!msg) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msg_has_error(msg, payload_length);
+	if (rc) {
+		resp->completion_code = rc;
+		return 0;
+	}
+
+	rc = pldm_msgbuf_init_errno(buf, PLDM_GET_EVENT_RECEIVER_MIN_RESP_BYTES,
+				    msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+	pldm_msgbuf_extract(buf, resp->completion_code);
+	rc = pldm_msgbuf_extract(buf, resp->transport_protocol_type);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+	if (resp->transport_protocol_type ==
+	    PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP) {
+		pldm_msgbuf_extract(buf, resp->event_receiver_address.mctp_eid);
+	} else {
+		return pldm_msgbuf_discard(buf, -ENOTSUP);
+	}
+	return pldm_msgbuf_complete_consumed(buf);
+}
+
 LIBPLDM_ABI_STABLE
 int encode_set_event_receiver_req(uint8_t instance_id,
 				  uint8_t event_message_global_enable,
diff --git a/tests/dsp/platform.cpp b/tests/dsp/platform.cpp
index 0586c2e..1cfdb0a 100644
--- a/tests/dsp/platform.cpp
+++ b/tests/dsp/platform.cpp
@@ -6,6 +6,7 @@
 
 #include <array>
 #include <cerrno>
+#include <cstddef>
 #include <cstdint>
 #include <cstring>
 #include <vector>
@@ -3592,6 +3593,129 @@
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
 }
 
+#ifdef LIBPLDM_API_TESTING
+TEST(GetEventReceiver, testGoodEncodeRequest)
+{
+    std::array<uint8_t, hdrSize> requestMsg{};
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+    auto request = new (requestMsg.data()) pldm_msg;
+    auto rc =
+        encode_get_event_receiver_req(0, request, sizeof(struct pldm_msg));
+    ASSERT_EQ(rc, 0);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetEventReceiver, testBadEncodeRequest)
+{
+    auto rc =
+        encode_get_event_receiver_req(0, nullptr, sizeof(struct pldm_msg));
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetEventReceiver, testGoodEncodeResponse)
+{
+    struct pldm_get_event_receiver_resp request_event_receiver_values;
+    request_event_receiver_values.completion_code = 0;
+    request_event_receiver_values.transport_protocol_type =
+        PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP;
+    request_event_receiver_values.event_receiver_address.mctp_eid = 84;
+    size_t payload_lenght = PLDM_GET_EVENT_RECEIVER_MIN_RESP_BYTES + 1;
+    std::array<uint8_t, hdrSize + sizeof(pldm_get_event_receiver_resp)>
+        responseMsg{};
+    auto response = new (responseMsg.data()) pldm_msg;
+    auto rc = encode_get_event_receiver_resp(0, &request_event_receiver_values,
+                                             response, &payload_lenght);
+    EXPECT_EQ(rc, 0);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetEventReceiver, testBadEncodeResponse)
+{
+    std::array<uint8_t, hdrSize + sizeof(pldm_get_event_receiver_resp)>
+        responseMsg{};
+    auto response = new (responseMsg.data()) pldm_msg;
+    struct pldm_get_event_receiver_resp request_event_receiver_values;
+    request_event_receiver_values.completion_code = 0;
+    request_event_receiver_values.transport_protocol_type =
+        PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP;
+    request_event_receiver_values.event_receiver_address.mctp_eid = 64;
+    size_t payload_lenght = PLDM_GET_EVENT_RECEIVER_MIN_RESP_BYTES;
+    // msg can not be null
+    auto rc = encode_get_event_receiver_resp(0, &request_event_receiver_values,
+                                             nullptr, &payload_lenght);
+    EXPECT_EQ(rc, -EINVAL);
+    // unsupported protocol
+    request_event_receiver_values.transport_protocol_type = 1;
+    rc = encode_get_event_receiver_resp(0, &request_event_receiver_values,
+                                        response, &payload_lenght);
+    EXPECT_EQ(rc, -ENOTSUP);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetEventReceiver, testGoodDecodeResponse)
+{
+    struct pldm_get_event_receiver_resp request_event_receiver_values;
+    request_event_receiver_values.completion_code = 0;
+    request_event_receiver_values.transport_protocol_type =
+        PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP;
+    request_event_receiver_values.event_receiver_address.mctp_eid = 34;
+    size_t payload_lenght = PLDM_GET_EVENT_RECEIVER_MIN_RESP_BYTES + 1;
+    struct pldm_get_event_receiver_resp decoded_resp;
+    std::array<uint8_t, hdrSize + sizeof(pldm_get_event_receiver_resp)>
+        responseMsg{};
+    auto response = new (responseMsg.data()) pldm_msg;
+    auto rc = encode_get_event_receiver_resp(0, &request_event_receiver_values,
+                                             response, &payload_lenght);
+    EXPECT_EQ(rc, 0);
+    rc = decode_get_event_receiver_resp(
+        response, PLDM_GET_EVENT_RECEIVER_MIN_RESP_BYTES + 1, &decoded_resp);
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(decoded_resp.completion_code, PLDM_SUCCESS);
+    EXPECT_EQ(decoded_resp.transport_protocol_type,
+              request_event_receiver_values.transport_protocol_type);
+    EXPECT_EQ(decoded_resp.event_receiver_address.mctp_eid,
+              request_event_receiver_values.event_receiver_address.mctp_eid);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetEventReceiver, testBadDecodeResponse)
+{
+    struct pldm_get_event_receiver_resp decoded_resp;
+    struct pldm_get_event_receiver_resp expected_resp;
+    expected_resp.completion_code = 0;
+    expected_resp.transport_protocol_type = PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP;
+    expected_resp.event_receiver_address.mctp_eid = 34;
+    std::array<uint8_t, hdrSize + sizeof(pldm_get_event_receiver_resp)>
+        responseMsg{};
+    auto response = new (responseMsg.data()) pldm_msg;
+    size_t payload_lenght = PLDM_GET_EVENT_RECEIVER_MIN_RESP_BYTES + 1;
+    auto rc = encode_get_event_receiver_resp(0, &expected_resp, response,
+                                             &payload_lenght);
+    EXPECT_EQ(rc, 0);
+    // message can not be null
+    rc = decode_get_event_receiver_resp(
+        nullptr, responseMsg.size() - sizeof(pldm_msg_hdr), &decoded_resp);
+    EXPECT_EQ(rc, -EINVAL);
+    // Allocated less than expected
+    rc = decode_get_event_receiver_resp(
+        response, PLDM_GET_EVENT_RECEIVER_MIN_RESP_BYTES - 1, &decoded_resp);
+    EXPECT_EQ(rc, -EOVERFLOW);
+    // Not supported protocol
+    size_t transport_protocol_type_offset = hdrSize + 1;
+    // Manually modify the transport_protocol_type to a not supported one
+    responseMsg[transport_protocol_type_offset] = 1;
+    rc = decode_get_event_receiver_resp(
+        response, responseMsg.size() - sizeof(pldm_msg_hdr), &decoded_resp);
+    EXPECT_EQ(rc, -ENOTSUP);
+}
+#endif
+
 TEST(SetEventReceiver, testGoodEncodeRequest)
 {
     uint8_t eventMessageGlobalEnable =