Encode & Decode APIs for PlatformEventMessage

Added the encode and decode APIs for the PlatformEventMessage command.

Currently supported PLDM event classes are,
  - sensorEvent
  - pldmPDRRepositoryChgEvent
Corresponding C structures were also defined which will
enable a user to typecast the event data that is part of the
PlatformEventMessage request.

Added Unit tests for the APIs.

Change-Id: Ia64944ef51c414423ad49f19ff9f88bab78cc224
Signed-off-by: Zahed Hossain <zahzahed@in.ibm.com>
diff --git a/libpldm/platform.c b/libpldm/platform.c
index cd332e6..45abf5a 100644
--- a/libpldm/platform.c
+++ b/libpldm/platform.c
@@ -555,3 +555,57 @@
 
 	return PLDM_SUCCESS;
 }
+
+int decode_platform_event_message_req(const struct pldm_msg *msg,
+				      size_t payload_length,
+				      uint8_t *format_version, uint8_t *tid,
+				      uint8_t *event_class,
+				      size_t *event_data_offset)
+{
+
+	if (msg == NULL || format_version == NULL || tid == NULL ||
+	    event_class == NULL || event_data_offset == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length <= PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+	struct pldm_platform_event_message_req *response =
+	    (struct pldm_platform_event_message_req *)msg->payload;
+
+	*format_version = response->format_version;
+	*tid = response->tid;
+	*event_class = response->event_class;
+	*event_data_offset =
+	    sizeof(*format_version) + sizeof(*tid) + sizeof(*event_class);
+
+	return PLDM_SUCCESS;
+}
+
+int encode_platform_event_message_resp(uint8_t instance_id,
+				       uint8_t completion_code, uint8_t status,
+				       struct pldm_msg *msg)
+{
+	int rc = PLDM_SUCCESS;
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	struct pldm_platform_event_message_resp *response =
+	    (struct pldm_platform_event_message_resp *)msg->payload;
+	response->completion_code = completion_code;
+	response->status = status;
+
+	struct pldm_header_info header = {0};
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_PLATFORM_EVENT_MESSAGE;
+
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+	return PLDM_SUCCESS;
+}
diff --git a/libpldm/platform.h b/libpldm/platform.h
index adb6d90..181fd3e 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -24,6 +24,10 @@
 /* Minimum response length */
 #define PLDM_GET_PDR_MIN_RESP_BYTES 12
 
+/* Minimum length for PLDM PlatformEventMessage request */
+#define PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES 3
+#define PLDM_PLATFORM_EVENT_MESSAGE_STATE_SENSOR_STATE_REQ_BYTES 6
+
 enum pldm_effecter_data_size {
 	PLDM_EFFECTER_DATA_SIZE_UINT8,
 	PLDM_EFFECTER_DATA_SIZE_SINT8,
@@ -67,6 +71,7 @@
 	PLDM_SET_NUMERIC_EFFECTER_VALUE = 0x31,
 	PLDM_SET_STATE_EFFECTER_STATES = 0x39,
 	PLDM_GET_PDR = 0x51,
+	PLDM_PLATFORM_EVENT_MESSAGE = 0x0A
 };
 
 /** @brief PLDM PDR types
@@ -95,6 +100,47 @@
 	PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE = 0x82,
 };
 
+/** @brief PLDM Event types
+ */
+enum pldm_event_types {
+	PLDM_SENSOR_EVENT = 0x00,
+	PLDM_EFFECTER_EVENT = 0x01,
+	PLDM_REDFISH_TASK_EXECUTED_EVENT = 0x02,
+	PLDM_REDFISH_MESSAGE_EVENT = 0x03,
+	PLDM_PDR_REPOSITORY_CHG_EVENT = 0x04,
+	PLDM_MESSAGE_POLL_EVENT = 0x05,
+	PLDM_HEARTBEAT_TIMER_ELAPSED_EVENT = 0x06
+};
+
+/** @brief PLDM sensorEventClass states
+ */
+enum sensor_event_class_states {
+	PLDM_SENSOR_OP_STATE,
+	PLDM_STATE_SENSOR_STATE,
+	PLDM_NUMERIC_SENSOR_STATE
+};
+
+/** @brief PLDM sensor supported states
+ */
+enum pldm_sensor_operational_state {
+	PLDM_SENSOR_ENABLED,
+	PLDM_SENSOR_DISABLED,
+	PLDM_SENSOR_UNAVAILABLE,
+	PLDM_SENSOR_STATUSUNKOWN,
+	PLDM_SENSOR_FAILED,
+	PLDM_SENSOR_INITIALIZING,
+	PLDM_SENSOR_SHUTTINGDOWN,
+	PLDM_SENSOR_INTEST
+};
+
+/** @brief PLDM pldmPDRRepositoryChgEvent class eventData format
+ */
+enum pldm_pdr_repository_chg_event_data_format {
+	REFRESH_ENTIRE_REPOSITORY,
+	FORMAT_IS_PDR_TYPES,
+	FORMAT_IS_PDR_HANDLES
+};
+
 /** @struct pldm_pdr_hdr
  *
  *  Structure representing PLDM common PDR header
@@ -237,6 +283,87 @@
 	get_sensor_state_field field[1];
 } __attribute__((packed));
 
+/** @struct pldm_sensor_event
+ *
+ *  structure representing sensorEventClass
+ */
+struct pldm_sensor_event_data {
+	uint16_t sensor_id;
+	uint8_t sensor_event_class_type;
+	uint8_t event_class[1];
+} __attribute__((packed));
+
+/** @struct pldm_state_sensor_state
+ *
+ *  structure representing sensorEventClass for stateSensorState
+ */
+struct pldm_sensor_event_state_sensor_state {
+	uint8_t sensor_offset;
+	uint8_t event_state;
+	uint8_t previous_event_state;
+} __attribute__((packed));
+
+/** @struct pldm_sensor_event_numeric_sensor_state
+ *
+ *  structure representing sensorEventClass for stateSensorState
+ */
+struct pldm_sensor_event_numeric_sensor_state {
+	uint8_t event_state;
+	uint8_t previous_event_state;
+	uint8_t sensor_data_size;
+	uint8_t present_reading[1];
+} __attribute__((packed));
+
+/** @struct pldm_sensor_event_sensor_op_state
+ *
+ *  structure representing sensorEventClass for SensorOpState
+ */
+struct pldm_sensor_event_sensor_op_state {
+	uint8_t present_op_state;
+	uint8_t previous_op_state;
+} __attribute__((packed));
+
+/** @struct pldm_platform_event_message_req
+ *
+ *  structure representing PlatformEventMessage command request data
+ */
+struct pldm_platform_event_message_req {
+	uint8_t format_version;
+	uint8_t tid;
+	uint8_t event_class;
+	uint8_t event_data[1];
+} __attribute__((packed));
+
+/** @struct pldm_platform_event_message_response
+ *
+ *  structure representing PlatformEventMessage command response data
+ */
+struct pldm_platform_event_message_resp {
+	uint8_t completion_code;
+	uint8_t status;
+} __attribute__((packed));
+
+/** @struct pldm_pdr_repository_chg_event_data
+ *
+ *  structure representing pldmPDRRepositoryChgEvent class eventData
+ */
+struct pldm_pdr_repository_chg_event_data {
+	uint8_t event_data_format;
+	uint8_t number_of_change_records;
+	uint8_t change_records[1];
+} __attribute__((packed));
+
+/** @struct pldm_pdr_repository_chg_event_change_record_data
+ *
+ *  structure representing pldmPDRRepositoryChgEvent class eventData's change
+ * record data
+ */
+struct pldm_pdr_repository_change_record_data {
+	uint8_t event_data_operation;
+	uint8_t number_of_change_entries;
+	uint32_t change_entry[1];
+} __attribute__((packed));
+
 /* Responder */
 
 /* SetNumericEffecterValue */
@@ -553,7 +680,6 @@
  *  @note  Caller is responsible for memory alloc and dealloc of param
  *         'msg.payload'
  */
-
 int encode_get_state_sensor_readings_req(uint8_t instance_id,
 					 uint16_t sensor_id,
 					 bitfield8_t sensor_rearm,
@@ -580,6 +706,38 @@
 					  uint8_t *comp_sensor_count,
 					  get_sensor_state_field *field);
 
+/* PlatformEventMessage */
+
+/** @brief Decode PlatformEventMessage request data
+ *  @param[in] msg - Request message
+ *  @param[in] payload_length - Length of response message payload
+ *  @param[out] format_version - Version of the event format
+ *  @param[out] tid - Terminus ID for the terminus that originated the event
+ * message
+ *  @param[out] event_class - The class of event being sent
+ *  @param[out] event_data_offset - Offset where the event data should be read
+ * from pldm msg
+ *  @return pldm_completion_codes
+ */
+int decode_platform_event_message_req(const struct pldm_msg *msg,
+				      size_t payload_length,
+				      uint8_t *format_version, uint8_t *tid,
+				      uint8_t *event_class,
+				      size_t *event_data_offset);
+
+/** @brief Encode PlatformEventMessage response data
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] completion_code - PLDM completion code
+ *  @param[in] status - Response status of the event message command
+ *  @param[out] msg - Message will be written to this
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param
+ *         'msg.payload'
+ */
+int encode_platform_event_message_resp(uint8_t instance_id,
+				       uint8_t completion_code, uint8_t status,
+				       struct pldm_msg *msg);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libpldm/tests/libpldm_platform_test.cpp b/libpldm/tests/libpldm_platform_test.cpp
index b6f47c8..18f1062 100644
--- a/libpldm/tests/libpldm_platform_test.cpp
+++ b/libpldm/tests/libpldm_platform_test.cpp
@@ -766,3 +766,91 @@
 
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
 }
+
+TEST(PlatformEventMessage, testGoodStateSensorDecodeRequest)
+{
+    std::array<uint8_t,
+               hdrSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
+                   PLDM_PLATFORM_EVENT_MESSAGE_STATE_SENSOR_STATE_REQ_BYTES>
+        requestMsg{};
+
+    uint8_t retFormatVersion = 0;
+    uint8_t retTid = 0;
+    uint8_t retEventClass = 0;
+    size_t retEventDataOffset = 0;
+
+    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    struct pldm_platform_event_message_req* request =
+        reinterpret_cast<struct pldm_platform_event_message_req*>(req->payload);
+
+    uint8_t formatVersion = 0x01;
+    uint8_t tid = 0x02;
+    // Sensor Event
+    uint8_t eventClass = 0x00;
+
+    request->format_version = formatVersion;
+    request->tid = tid;
+    request->event_class = eventClass;
+    size_t eventDataOffset =
+        sizeof(formatVersion) + sizeof(tid) + sizeof(eventClass);
+
+    auto rc = decode_platform_event_message_req(
+        req, requestMsg.size() - hdrSize, &retFormatVersion, &retTid,
+        &retEventClass, &retEventDataOffset);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(retFormatVersion, formatVersion);
+    EXPECT_EQ(retTid, tid);
+    EXPECT_EQ(retEventClass, eventClass);
+    EXPECT_EQ(retEventDataOffset, eventDataOffset);
+}
+
+TEST(PlatformEventMessage, testBadDecodeRequest)
+{
+    const struct pldm_msg* msg = NULL;
+    std::array<uint8_t,
+               hdrSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
+                   PLDM_PLATFORM_EVENT_MESSAGE_STATE_SENSOR_STATE_REQ_BYTES - 1>
+        requestMsg{};
+    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    uint8_t retFormatVersion;
+    uint8_t retTid = 0;
+    uint8_t retEventClass = 0;
+    size_t retEventDataOffset;
+
+    auto rc = decode_platform_event_message_req(msg, sizeof(*msg), NULL, NULL,
+                                                NULL, NULL);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    rc = decode_platform_event_message_req(
+        req,
+        requestMsg.size() - hdrSize -
+            PLDM_PLATFORM_EVENT_MESSAGE_STATE_SENSOR_STATE_REQ_BYTES,
+        &retFormatVersion, &retTid, &retEventClass, &retEventDataOffset);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+
+TEST(PlatformEventMessage, testGoodEncodeResponse)
+{
+    std::array<uint8_t,
+               hdrSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
+                   PLDM_PLATFORM_EVENT_MESSAGE_STATE_SENSOR_STATE_REQ_BYTES - 1>
+        responseMsg{};
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    uint8_t completionCode = 0;
+    uint8_t instanceId = 0x01;
+    uint8_t status = 1;
+
+    auto rc = encode_platform_event_message_resp(instanceId, PLDM_SUCCESS,
+                                                 status, response);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(completionCode, response->payload[0]);
+    EXPECT_EQ(status, response->payload[1]);
+}
+
+TEST(PlatformEventMessage, testBadEncodeResponse)
+{
+    auto rc = encode_platform_event_message_resp(0, PLDM_SUCCESS, 1, NULL);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}