platform: Add support for GetStateEffecterStates command

Based on DSP0248 PLDM for Platform Monitoring
and Control Specification version 1.2.0a Section 22.6

Implement encode and decode for both request and response
for GetStateEffecterStates PLDM command.

Include unit-tests.

Change-Id: Ia3bd71151d40b56f91afe2fe23b8bf2f26915b64
Signed-off-by: Tal Yacobi <talycb8@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b13d589..f1a9dd7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -45,6 +45,7 @@
 14. oem: ibm: Support for the Real SAI entity id
 15. fw_update: Add encode req & decode resp for query_downstream_devices
 16. fw_update: Add encode req & decode resp for query_downstream_identifiers
+17. platform: Add support for GetStateEffecterStates command
 
 ### Changed
 
diff --git a/include/libpldm/platform.h b/include/libpldm/platform.h
index 324af66..db28468 100644
--- a/include/libpldm/platform.h
+++ b/include/libpldm/platform.h
@@ -17,6 +17,7 @@
 #define PLDM_SET_STATE_EFFECTER_STATES_REQ_BYTES  19
 #define PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES  4
 #define PLDM_GET_NUMERIC_EFFECTER_VALUE_REQ_BYTES 2
+#define PLDM_GET_STATE_EFFECTER_STATES_REQ_BYTES  2
 #define PLDM_GET_SENSOR_READING_REQ_BYTES	  3
 #define PLDM_SET_EVENT_RECEIVER_REQ_BYTES	  5
 /* Response lengths are inclusive of completion code */
@@ -44,6 +45,7 @@
 /* Minimum response length */
 #define PLDM_GET_PDR_MIN_RESP_BYTES		       12
 #define PLDM_GET_NUMERIC_EFFECTER_VALUE_MIN_RESP_BYTES 5
+#define PLDM_GET_STATE_EFFECTER_STATES_MIN_RESP_BYTES  2
 #define PLDM_GET_SENSOR_READING_MIN_RESP_BYTES	       8
 #define PLDM_GET_STATE_SENSOR_READINGS_MIN_RESP_BYTES  2
 #define PLDM_GET_PDR_REPOSITORY_INFO_RESP_BYTES	       41
@@ -98,6 +100,13 @@
 #define PLDM_STR_UTF_8_MAX_LEN	256
 #define PLDM_STR_UTF_16_MAX_LEN 256
 
+/* Wire-format substructure sizes */
+#define PLDM_GET_EFFECTER_STATE_FIELD_SIZE 3
+
+/* State fields count bounds */
+#define PLDM_GET_EFFECTER_STATE_FIELD_COUNT_MIN 1
+#define PLDM_GET_EFFECTER_STATE_FIELD_COUNT_MAX 8
+
 enum pldm_effecter_data_size {
 	PLDM_EFFECTER_DATA_SIZE_UINT8,
 	PLDM_EFFECTER_DATA_SIZE_SINT8,
@@ -167,6 +176,7 @@
 	PLDM_SET_NUMERIC_EFFECTER_VALUE = 0x31,
 	PLDM_GET_NUMERIC_EFFECTER_VALUE = 0x32,
 	PLDM_SET_STATE_EFFECTER_STATES = 0x39,
+	PLDM_GET_STATE_EFFECTER_STATES = 0x3a,
 	PLDM_GET_PDR_REPOSITORY_INFO = 0x50,
 	PLDM_GET_PDR = 0x51,
 };
@@ -858,6 +868,16 @@
 				//! that is associated with the sensor
 } __attribute__((packed)) get_sensor_state_field;
 
+/** @struct get_effecter_state_field
+ *
+ *  Structure representing a stateField in GetStateEffecterStates command
+ */
+typedef struct state_field_for_get_state_effecter_states {
+	uint8_t effecter_op_state; //!< The state of the effecter itself
+	uint8_t pending_state; //!< The state that is currently being processed
+	uint8_t present_state; //!< Return a state value
+} get_effecter_state_field;
+
 /** @struct PLDM_SetStateEffecterStates_Request
  *
  *  Structure representing PLDM set state effecter states request.
@@ -989,6 +1009,24 @@
 	get_sensor_state_field field[1];
 } __attribute__((packed));
 
+/** @struct pldm_get_state_effecter_states_req
+ *
+ *  structure representing GetStateEffecterStates request packet
+ */
+struct pldm_get_state_effecter_states_req {
+	uint16_t effecter_id;
+};
+
+/** @struct pldm_get_state_effecter_states_resp
+ *
+ *  Structure representing PLDM get state effecter states response.
+ */
+struct pldm_get_state_effecter_states_resp {
+	uint8_t completion_code;
+	uint8_t comp_effecter_count;
+	get_effecter_state_field field[PLDM_GET_EFFECTER_STATE_FIELD_COUNT_MAX];
+};
+
 /** @struct pldm_sensor_event
  *
  *  structure representing sensorEventClass
@@ -1617,6 +1655,67 @@
 					  uint8_t *comp_sensor_count,
 					  get_sensor_state_field *field);
 
+/* GetStateEffecterStates */
+
+/** @brief Decode GetStateEffecterStates request data
+ *
+ *  @param[in] msg - Request message
+ *  @param[in] payload_length - Length of request message payload
+ *  @param[out] effecter_id - used to identify and access the effecter
+ *  @return pldm_completion_codes
+ */
+int decode_get_state_effecter_states_req(const struct pldm_msg *msg,
+					 size_t payload_length,
+					 uint16_t *effecter_id);
+
+/** @brief Create a PLDM request message for GetStateEffecterStates
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] effecter_id - used to identify and access the effecter
+ *  @param[out] msg - Message will be written to this
+ *  @param[in] payload_length - Length of request message payload
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param
+ *         'msg.payload'
+ */
+int encode_get_state_effecter_states_req(uint8_t instance_id,
+					 uint16_t effecter_id,
+					 struct pldm_msg *msg,
+					 size_t payload_length);
+
+/** @brief Decode GetStateEffecterStates response data
+ *
+ *  @param[in] msg - Request message
+ *  @param[in] payload_length - Length of response message payload
+ *  @param[out] resp - Consists of PLDM completion code, the number
+*          of individual sets of effecters information that this command
+*          accesses and an instance of a stateField structure that is
+*          used to return the present operational state setting and
+*          the present state and event state for a particular set of effecter
+*          information contained within the state effecter
+ *  @return pldm_completion_codes
+ */
+int decode_get_state_effecter_states_resp(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_get_state_effecter_states_resp *resp);
+
+/** @brief Encode GetStateEffecterStates response data
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] resp - Consists of PLDM completion code, the number
+*          of individual sets of effecters information that this command
+*          accesses and an instance of a stateField structure that is
+*          used to return the present operational state setting and
+*          the present state and event state for a particular set of effecter
+*          information contained within the state effecter
+ *  @param[out] msg - Message will be written to this
+ *  @param[in] payload_length - Length of response message payload
+ *  @return pldm_completion_codes
+ */
+int encode_get_state_effecter_states_resp(
+	uint8_t instance_id, struct pldm_get_state_effecter_states_resp *resp,
+	struct pldm_msg *msg, size_t payload_length);
+
 /* PlatformEventMessage */
 
 /** @brief Decode PlatformEventMessage request data
diff --git a/src/platform.c b/src/platform.c
index 75f94c5..7db81b2 100644
--- a/src/platform.c
+++ b/src/platform.c
@@ -2520,3 +2520,170 @@
 
 	return pldm_msgbuf_destroy_consumed(buf);
 }
+
+LIBPLDM_ABI_TESTING
+int encode_get_state_effecter_states_req(uint8_t instance_id,
+					 uint16_t effecter_id,
+					 struct pldm_msg *msg,
+					 size_t payload_length)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	int rc;
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	struct pldm_header_info header = { 0 };
+	header.msg_type = PLDM_REQUEST;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_GET_STATE_EFFECTER_STATES;
+
+	rc = pack_pldm_header(&header, &msg->hdr);
+	if (rc != PLDM_SUCCESS) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_init(buf, PLDM_GET_STATE_EFFECTER_STATES_REQ_BYTES,
+			      msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, effecter_id);
+
+	return pldm_msgbuf_destroy_consumed(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_get_state_effecter_states_req(const struct pldm_msg *msg,
+					 size_t payload_length,
+					 uint16_t *effecter_id)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	int rc;
+
+	if (msg == NULL || effecter_id == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	rc = pldm_msgbuf_init(buf,
+			      PLDM_GET_STATE_EFFECTER_STATES_MIN_RESP_BYTES,
+			      msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract_p(buf, effecter_id);
+
+	return pldm_msgbuf_destroy_consumed(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_get_state_effecter_states_resp(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_get_state_effecter_states_resp *resp)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	get_effecter_state_field *field;
+	int rc;
+	int i;
+
+	if (msg == NULL || resp == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	rc = pldm_msgbuf_init(buf,
+			      PLDM_GET_STATE_EFFECTER_STATES_MIN_RESP_BYTES,
+			      msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_extract(buf, resp->completion_code);
+	if (rc) {
+		return rc;
+	}
+
+	if (PLDM_SUCCESS != resp->completion_code) {
+		return PLDM_SUCCESS;
+	}
+
+	rc = pldm_msgbuf_extract(buf, resp->comp_effecter_count);
+	if (rc) {
+		return rc;
+	}
+
+	uint8_t comp_effecter_count = resp->comp_effecter_count;
+
+	if (comp_effecter_count < PLDM_GET_EFFECTER_STATE_FIELD_COUNT_MIN ||
+	    comp_effecter_count > PLDM_GET_EFFECTER_STATE_FIELD_COUNT_MAX) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	for (i = 0, field = resp->field; i < comp_effecter_count;
+	     i++, field++) {
+		pldm_msgbuf_extract(buf, field->effecter_op_state);
+		pldm_msgbuf_extract(buf, field->pending_state);
+		pldm_msgbuf_extract(buf, field->present_state);
+	}
+
+	return pldm_msgbuf_destroy_consumed(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int encode_get_state_effecter_states_resp(
+	uint8_t instance_id, struct pldm_get_state_effecter_states_resp *resp,
+	struct pldm_msg *msg, size_t payload_length)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	get_effecter_state_field *field;
+	int rc;
+	int i;
+
+	if (msg == NULL || resp == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	uint8_t comp_effecter_count = resp->comp_effecter_count;
+
+	if (comp_effecter_count < PLDM_GET_EFFECTER_STATE_FIELD_COUNT_MIN ||
+	    comp_effecter_count > PLDM_GET_EFFECTER_STATE_FIELD_COUNT_MAX) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	struct pldm_header_info header = { 0 };
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_GET_STATE_EFFECTER_STATES;
+
+	rc = pack_pldm_header(&header, &msg->hdr);
+	if (rc != PLDM_SUCCESS) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_init(buf,
+			      PLDM_GET_STATE_EFFECTER_STATES_MIN_RESP_BYTES,
+			      msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, resp->completion_code);
+	pldm_msgbuf_insert(buf, comp_effecter_count);
+
+	for (i = 0, field = resp->field; i < comp_effecter_count;
+	     i++, field++) {
+		pldm_msgbuf_insert(buf, field->effecter_op_state);
+		pldm_msgbuf_insert(buf, field->pending_state);
+		pldm_msgbuf_insert(buf, field->present_state);
+	}
+
+	return pldm_msgbuf_destroy_consumed(buf);
+}
diff --git a/tests/libpldm_platform_test.cpp b/tests/libpldm_platform_test.cpp
index c260b10..d5d1f7d 100644
--- a/tests/libpldm_platform_test.cpp
+++ b/tests/libpldm_platform_test.cpp
@@ -4869,3 +4869,165 @@
     EXPECT_FLOAT_EQ(-300.003f, decodedPdr.rated_min.value_f32);
 }
 #endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetStateEffecterStates, testEncodeAndDecodeRequest)
+{
+    std::array<uint8_t, hdrSize + PLDM_GET_STATE_EFFECTER_STATES_REQ_BYTES>
+        requestMsg{};
+
+    constexpr std::array<uint8_t,
+                         hdrSize + PLDM_GET_STATE_EFFECTER_STATES_REQ_BYTES>
+        expectedRequestMsg{
+            {0x80, PLDM_PLATFORM, PLDM_GET_STATE_EFFECTER_STATES, 1, 0xab}};
+
+    constexpr uint16_t effecter_id = 0xab01;
+
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = encode_get_state_effecter_states_req(
+        0, effecter_id, request, PLDM_GET_STATE_EFFECTER_STATES_REQ_BYTES);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(requestMsg, expectedRequestMsg);
+
+    uint16_t ret_effecter_id;
+
+    rc = decode_get_state_effecter_states_req(
+        request, requestMsg.size() - hdrSize, &ret_effecter_id);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(effecter_id, ret_effecter_id);
+
+    // Test invalid length decode request
+
+    rc = decode_get_state_effecter_states_req(
+        request, requestMsg.size() - hdrSize - 1, &ret_effecter_id);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetStateEffecterStates, testBadEncodeRequest)
+{
+    std::vector<uint8_t> requestMsg(hdrSize +
+                                    PLDM_GET_STATE_EFFECTER_STATES_REQ_BYTES);
+
+    auto rc = encode_get_state_effecter_states_req(
+        0, 0, nullptr, PLDM_GET_STATE_EFFECTER_STATES_REQ_BYTES);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetStateEffecterStates, testBadDecodeRequest)
+{
+    std::array<uint8_t, hdrSize + PLDM_GET_NUMERIC_EFFECTER_VALUE_REQ_BYTES>
+        requestMsg{};
+
+    auto rc = decode_get_state_effecter_states_req(
+        nullptr, requestMsg.size() - hdrSize, nullptr);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetStateEffecterStates, testEncodeAndDecodeResponse)
+{
+    constexpr uint8_t comp_effecterCnt = 0x2;
+    constexpr uint8_t completionCode = 0;
+    std::array<uint8_t,
+               hdrSize + PLDM_GET_STATE_EFFECTER_STATES_MIN_RESP_BYTES +
+                   PLDM_GET_EFFECTER_STATE_FIELD_SIZE * comp_effecterCnt>
+        expectedResponseMsg{{0, PLDM_PLATFORM, PLDM_GET_STATE_EFFECTER_STATES,
+                             completionCode, comp_effecterCnt,
+                             EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING, 2, 2,
+                             EFFECTER_OPER_STATE_ENABLED_UPDATEPENDING, 2, 3}};
+
+    decltype(expectedResponseMsg) responseMsg{};
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+
+    std::array<get_effecter_state_field, comp_effecterCnt> stateField{
+        {{EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING, 2, 2},
+         {EFFECTER_OPER_STATE_ENABLED_UPDATEPENDING, 2, 3}}};
+
+    struct pldm_get_state_effecter_states_resp resp_fields
+    {
+        PLDM_SUCCESS, comp_effecterCnt,
+        {
+            stateField[0], stateField[1]
+        }
+    };
+
+    auto rc = encode_get_state_effecter_states_resp(
+        0, &resp_fields, response, responseMsg.size() - hdrSize);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(expectedResponseMsg, responseMsg);
+
+    struct pldm_get_state_effecter_states_resp ret_resp_fields;
+
+    rc = decode_get_state_effecter_states_resp(
+        response, responseMsg.size() - hdrSize, &ret_resp_fields);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(completionCode, ret_resp_fields.completion_code);
+    EXPECT_EQ(comp_effecterCnt, ret_resp_fields.comp_effecter_count);
+    EXPECT_EQ(stateField[0].effecter_op_state,
+              ret_resp_fields.field[0].effecter_op_state);
+    EXPECT_EQ(stateField[0].pending_state,
+              ret_resp_fields.field[0].pending_state);
+    EXPECT_EQ(stateField[0].present_state,
+              ret_resp_fields.field[0].present_state);
+    EXPECT_EQ(stateField[1].effecter_op_state,
+              ret_resp_fields.field[1].effecter_op_state);
+    EXPECT_EQ(stateField[1].pending_state,
+              ret_resp_fields.field[1].pending_state);
+    EXPECT_EQ(stateField[1].present_state,
+              ret_resp_fields.field[1].present_state);
+
+    // Test invalid length decode
+
+    rc = decode_get_state_effecter_states_resp(
+        response,
+        responseMsg.size() - hdrSize + PLDM_GET_EFFECTER_STATE_FIELD_SIZE,
+        &ret_resp_fields);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetStateEffecterStates, testBadEncodeResponse)
+{
+    struct pldm_get_state_effecter_states_resp resp
+    {
+        PLDM_SUCCESS, 0,
+        {
+        }
+    };
+    auto rc = decode_get_state_effecter_states_resp(nullptr, 0, &resp);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetStateEffecterStates, testBadDecodeResponse)
+{
+    std::array<uint8_t, hdrSize +
+                            PLDM_GET_STATE_EFFECTER_STATES_MIN_RESP_BYTES +
+                            PLDM_GET_EFFECTER_STATE_FIELD_SIZE * 2>
+        responseMsg{};
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+
+    auto rc = decode_get_state_effecter_states_resp(
+        response, responseMsg.size() - hdrSize, nullptr);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+#endif