PLDM: Encode Decode For SetEventReceiver command

This commit adds the encode and decode functions for
the SetEventReceiver Command, which will be sent to
host with time interval and heartbeat interval, along
with enableAsyncKeepAlive for surveillance between
host and BMC

PLDM SPEC: DSP248_1.2.0 TABLE 13

Signed-off-by: Sagar Srinivas <sagar.srinivas@ibm.com>
Change-Id: I20613124a0787eb4f60945e923d1fc6afa4db31b
diff --git a/libpldm/base.h b/libpldm/base.h
index 7f87971..4813ee6 100644
--- a/libpldm/base.h
+++ b/libpldm/base.h
@@ -54,6 +54,13 @@
 	PLDM_START_AND_END = 0x05,
 };
 
+/** @brief PLDM transport protocol type
+ */
+enum pldm_transport_protocol_type {
+	PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP = 0x00,
+	PLDM_TRANSPORT_PROTOCOL_TYPE_OEM = 0xFF,
+};
+
 /** @enum MessageType
  *
  *  The different message types supported by the PLDM specification.
diff --git a/libpldm/platform.c b/libpldm/platform.c
index 7bcd502..804339d 100644
--- a/libpldm/platform.c
+++ b/libpldm/platform.c
@@ -1532,3 +1532,66 @@
 
 	return PLDM_SUCCESS;
 }
+
+int encode_set_event_receiver_req(uint8_t instance_id,
+				  uint8_t event_message_global_enable,
+				  uint8_t transport_protocol_type,
+				  uint8_t event_receiver_address_info,
+				  uint16_t heartbeat_timer,
+				  struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	header.msg_type = PLDM_REQUEST;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_SET_EVENT_RECEIVER;
+
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+
+	struct pldm_set_event_receiver_req *request =
+	    (struct pldm_set_event_receiver_req *)msg->payload;
+	request->event_message_global_enable = event_message_global_enable;
+
+	if (transport_protocol_type != PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+	request->transport_protocol_type = transport_protocol_type;
+	request->event_receiver_address_info = event_receiver_address_info;
+
+	if (event_message_global_enable ==
+	    PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE) {
+		if (heartbeat_timer == 0) {
+			return PLDM_ERROR_INVALID_DATA;
+		}
+		request->heartbeat_timer = htole16(heartbeat_timer);
+	}
+
+	return PLDM_SUCCESS;
+}
+
+int decode_set_event_receiver_resp(const struct pldm_msg *msg,
+				   size_t payload_length,
+				   uint8_t *completion_code)
+{
+	if (msg == NULL || completion_code == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	*completion_code = msg->payload[0];
+	if (PLDM_SUCCESS != *completion_code) {
+		return PLDM_SUCCESS;
+	}
+
+	if (payload_length > PLDM_SET_EVENT_RECEIVER_RESP_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	return PLDM_SUCCESS;
+}
diff --git a/libpldm/platform.h b/libpldm/platform.h
index 1db7aee..15f00c2 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -16,6 +16,7 @@
 #define PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES 4
 #define PLDM_GET_NUMERIC_EFFECTER_VALUE_REQ_BYTES 2
 #define PLDM_GET_SENSOR_READING_REQ_BYTES 4
+#define PLDM_SET_EVENT_RECEIVER_REQ_BYTES 5
 /* Response lengths are inclusive of completion code */
 #define PLDM_SET_STATE_EFFECTER_STATES_RESP_BYTES 1
 
@@ -23,6 +24,8 @@
 #define PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES 4
 
 #define PLDM_GET_PDR_REQ_BYTES 13
+
+#define PLDM_SET_EVENT_RECEIVER_RESP_BYTES 1
 /* Minimum response length */
 #define PLDM_GET_PDR_MIN_RESP_BYTES 12
 #define PLDM_GET_NUMERIC_EFFECTER_VALUE_MIN_RESP_BYTES 5
@@ -110,6 +113,7 @@
 };
 
 enum pldm_platform_commands {
+	PLDM_SET_EVENT_RECEIVER = 0x04,
 	PLDM_GET_SENSOR_READING = 0x11,
 	PLDM_GET_STATE_SENSOR_READINGS = 0x21,
 	PLDM_SET_NUMERIC_EFFECTER_VALUE = 0x31,
@@ -171,6 +175,10 @@
 	PLDM_PLATFORM_TRANSFER_TIMEOUT = 0x84,
 
 	PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE = 0x82,
+
+	PLDM_PLATFORM_INVALID_PROTOCOL_TYPE = 0x80,
+	PLDM_PLATFORM_ENABLE_METHOD_NOT_SUPPORTED = 0x81,
+	PLDM_PLATFORM_HEARTBEAT_FREQUENCY_TOO_HIGH = 0x82,
 };
 
 /** @brief PLDM Event types
@@ -262,6 +270,16 @@
 	PLDM_TERMINUS_LOCATOR_TYPE_SYS_SW
 };
 
+/** @brief PLDM event message global enable for
+ *  SetEventReceiver command
+ */
+enum pldm_event_message_global_enable {
+	PLDM_EVENT_MESSAGE_GLOBAL_DISABLE,
+	PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC,
+	PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_POLLING,
+	PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE
+};
+
 /** @struct pldm_pdr_hdr
  *
  *  Structure representing PLDM common PDR header
@@ -559,6 +577,18 @@
 	uint16_t record_change_number;
 } __attribute__((packed));
 
+/** @struct pldm_set_event_receiver_req
+ *
+ * Structure representing SetEventReceiver command.
+ * This structure applies only for MCTP as a transport type.
+ */
+struct pldm_set_event_receiver_req {
+	uint8_t event_message_global_enable;
+	uint8_t transport_protocol_type;
+	uint8_t event_receiver_address_info;
+	uint16_t heartbeat_timer;
+} __attribute__((packed));
+
 /** @struct pldm_set_numeric_effecter_value_req
  *
  *  structure representing SetNumericEffecterValue request packet
@@ -1461,6 +1491,41 @@
     uint8_t *sensor_event_message_enable, uint8_t *present_state,
     uint8_t *previous_state, uint8_t *event_state, uint8_t *present_reading);
 
+/** @brief Encode the SetEventReceiver request message
+ *
+ * @param[in] instance_id - Message's instance id
+ * @param[in] event_message_global_enable - This value is used to enable or
+ *        disable event message generation from the terminus value: {
+ *        disable, enableAsync, enablePolling, enableAsyncKeepAlive }
+ * @param[in] transport_protocol_type - This value is provided in the request
+ *        to help the responder verify that the content of the
+ *        eventReceiverAddressInfo field used in this request is correct for
+ *        the messaging protocol supported by the terminus.
+ * @param[in] event_receiver_address_info - this value is a medium and
+ *        protocol-specific address that the responder should use when
+ *        transmitting event messages using the indicated protocol
+ * @param[in] heartbeat_timer - Amount of time in seconds after each elapsing
+ *        of which the terminus shall emit a heartbeat event to the receiver
+ * @param[out] msg - Argument to capture the Message
+ * @return pldm_completion_codes
+ */
+int encode_set_event_receiver_req(uint8_t instance_id,
+				  uint8_t event_message_global_enable,
+				  uint8_t transport_protocol_type,
+				  uint8_t event_receiver_address_info,
+				  uint16_t heartbeat_timer,
+				  struct pldm_msg *msg);
+
+/** @brief Decode the SetEventReceiver response message
+ *
+ * @param[in] msg - Request message
+ * @param[in] payload_length - Length of response message payload
+ * @param[out] completion_code - PLDM completion code
+ * @return pldm_completion_codes
+ */
+int decode_set_event_receiver_resp(const struct pldm_msg *msg,
+				   size_t payload_length,
+				   uint8_t *completion_code);
 #ifdef __cplusplus
 }
 #endif
diff --git a/libpldm/tests/libpldm_platform_test.cpp b/libpldm/tests/libpldm_platform_test.cpp
index 49b08f6..11d6777 100644
--- a/libpldm/tests/libpldm_platform_test.cpp
+++ b/libpldm/tests/libpldm_platform_test.cpp
@@ -1862,3 +1862,83 @@
 
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
 }
+
+TEST(SetEventReceiver, testGoodEncodeRequest)
+{
+    uint8_t eventMessageGlobalEnable =
+        PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE;
+    uint8_t transportProtocolType = PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP;
+    uint8_t eventReceiverAddressInfo = 0x08;
+    uint16_t heartbeatTimer = 0x78;
+
+    std::vector<uint8_t> requestMsg(hdrSize +
+                                    PLDM_SET_EVENT_RECEIVER_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = encode_set_event_receiver_req(
+        0, eventMessageGlobalEnable, transportProtocolType,
+        eventReceiverAddressInfo, heartbeatTimer, request);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    struct pldm_set_event_receiver_req* req =
+        reinterpret_cast<struct pldm_set_event_receiver_req*>(request->payload);
+    EXPECT_EQ(eventMessageGlobalEnable, req->event_message_global_enable);
+    EXPECT_EQ(transportProtocolType, req->transport_protocol_type);
+    EXPECT_EQ(eventReceiverAddressInfo, req->event_receiver_address_info);
+    EXPECT_EQ(heartbeatTimer, le16toh(req->heartbeat_timer));
+}
+
+TEST(SetEventReceiver, testBadEncodeRequest)
+{
+    uint8_t eventMessageGlobalEnable =
+        PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE;
+    uint8_t transportProtocolType = PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP;
+    uint8_t eventReceiverAddressInfo = 0x08;
+    uint16_t heartbeatTimer = 0;
+
+    std::vector<uint8_t> requestMsg(hdrSize +
+                                    PLDM_SET_EVENT_RECEIVER_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = encode_set_event_receiver_req(
+        0, eventMessageGlobalEnable, transportProtocolType,
+        eventReceiverAddressInfo, heartbeatTimer, request);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(SetEventReceiver, testGoodDecodeResponse)
+{
+    std::array<uint8_t, hdrSize + PLDM_SET_EVENT_RECEIVER_RESP_BYTES>
+        responseMsg{};
+
+    uint8_t retcompletion_code = 0;
+    responseMsg[hdrSize] = PLDM_SUCCESS;
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    auto rc = decode_set_event_receiver_resp(
+        response, responseMsg.size() - sizeof(pldm_msg_hdr),
+        &retcompletion_code);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(PLDM_SUCCESS, retcompletion_code);
+}
+
+TEST(SetEventReceiver, testBadDecodeResponse)
+{
+    std::array<uint8_t, hdrSize + PLDM_SET_EVENT_RECEIVER_RESP_BYTES>
+        responseMsg{};
+    uint8_t retcompletion_code = 0;
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+
+    auto rc = decode_set_event_receiver_resp(
+        response, responseMsg.size() - sizeof(pldm_msg_hdr), NULL);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_set_event_receiver_resp(
+        nullptr, responseMsg.size() - sizeof(pldm_msg_hdr),
+        &retcompletion_code);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}