libpldm : add encode/decode for setStateEffecterStates

This commit implements the decode of the request and encode of
the response for the setStateEffecterStates command.

Change-Id: Idc5f1316386dc650564d9859a63fbeade24d82d8
Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
diff --git a/libpldm/Makefile.am b/libpldm/Makefile.am
index e19cecd..e778094 100644
--- a/libpldm/Makefile.am
+++ b/libpldm/Makefile.am
@@ -1,13 +1,16 @@
 nobase_include_HEADERS = \
 	base.h \
-	pldm_types.h
+	pldm_types.h \
+	platform.h
 
 libpldm_LTLIBRARIES = libpldm.la
 libpldmdir = ${libdir}
 libpldm_la_SOURCES = \
-	base.c
+	base.c \
+	platform.c
 libpldm_la_LDFLAGS = -version-info 1:0:0 -shared
 libpldm_la_CFLAGS = $(CODE_COVERAGE_CFLAGS)
 libpldm_la_LIBADD = $(CODE_COVERAGE_LIBS)
+
 pkgconfiglibdir = ${libdir}/pkgconfig
 pkgconfiglib_DATA = libpldm.pc
diff --git a/libpldm/base.h b/libpldm/base.h
index 4fb94fb..3673e9f 100644
--- a/libpldm/base.h
+++ b/libpldm/base.h
@@ -15,6 +15,7 @@
  */
 enum pldm_supported_types {
 	PLDM_BASE = 0x00,
+	PLDM_PLATFORM = 0x02,
 };
 
 /** @brief PLDM Commands
diff --git a/libpldm/platform.c b/libpldm/platform.c
new file mode 100644
index 0000000..8bdd406
--- /dev/null
+++ b/libpldm/platform.c
@@ -0,0 +1,42 @@
+#include <endian.h>
+#include <string.h>
+
+#include "platform.h"
+
+int encode_set_state_effecter_states_resp(uint8_t instance_id,
+					  uint8_t completion_code,
+					  struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	msg->body.payload[0] = completion_code;
+
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_SET_STATE_EFFECTER_STATES;
+
+	rc = pack_pldm_header(&header, &(msg->hdr));
+
+	return rc;
+}
+
+int decode_set_state_effecter_states_req(const struct pldm_msg_payload *msg,
+					 uint16_t *effecter_id,
+					 uint8_t *comp_effecter_count,
+					 set_effecter_state_field *field)
+{
+	if (msg == NULL || effecter_id == NULL || comp_effecter_count == NULL ||
+	    field == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+	const uint8_t *start = msg->payload;
+	*effecter_id = le16toh(*((uint16_t *)start));
+	*comp_effecter_count = *(start + sizeof(*effecter_id));
+	memcpy(field,
+	       (start + sizeof(*effecter_id) + sizeof(*comp_effecter_count)),
+	       (sizeof(set_effecter_state_field) * (*comp_effecter_count)));
+
+	return PLDM_SUCCESS;
+}
diff --git a/libpldm/platform.h b/libpldm/platform.h
new file mode 100644
index 0000000..7741863
--- /dev/null
+++ b/libpldm/platform.h
@@ -0,0 +1,78 @@
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base.h"
+
+/* Maximum size for request */
+#define PLDM_SET_STATE_EFFECTER_STATES_REQ_BYTES 19
+/* Response lengths are inclusive of completion code */
+#define PLDM_SET_STATE_EFFECTER_STATES_RESP_BYTES 1
+
+enum set_request { PLDM_NO_CHANGE = 0x00, PLDM_REQUEST_SET = 0x01 };
+
+enum effecter_state { PLDM_INVALID_VALUE = 0xFF };
+
+enum pldm_platform_commands {
+	PLDM_SET_STATE_EFFECTER_STATES = 0x39,
+};
+
+/** @struct set_effecter_state_field
+ *
+ *  Structure representing a stateField in SetStateEffecterStates command */
+
+typedef struct state_field_for_state_effecter_set {
+	uint8_t set_request;    //!< Whether to change the state
+	uint8_t effecter_state; //!< Expected state of the effecter
+} __attribute__((packed)) set_effecter_state_field;
+
+/* Responder */
+
+/* SetStateEffecterStates */
+
+/** @brief Create a PLDM response message for SetStateEffecterStates
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] completion_code - PLDM completion code
+ *  @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.body.payload'
+ */
+
+int encode_set_state_effecter_states_resp(uint8_t instance_id,
+					  uint8_t completion_code,
+					  struct pldm_msg *msg);
+
+/** @brief Decode SetStateEffecterStates request data
+ *
+ *  @param[in] msg - Request message payload
+ *  @param[out] effecter_id - used to identify and access the effecter
+ *  @param[out] comp_effecter_count - number of individual sets of effecter
+ *         information. Upto eight sets of state effecter info can be accessed
+ *         for a given effecter.
+ *  @param[out] field - each unit is an instance of the stateFileld structure
+ *         that is used to set the requested state for a particular effecter
+ *         within the state effecter. This field holds the starting address of
+ *         the stateField values. The user is responsible to allocate the
+ *         memory prior to calling this command. Since the state field count is
+ *         not known in advance, the user should allocate the maximum size
+ *         always, which is 8 in number.
+ *  @return pldm_completion_codes
+ */
+int decode_set_state_effecter_states_req(const struct pldm_msg_payload *msg,
+					 uint16_t *effecter_id,
+					 uint8_t *comp_effecter_count,
+					 set_effecter_state_field *field);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PLATFORM_H */
diff --git a/test/Makefile.am b/test/Makefile.am
index 35a7395..7211573 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -4,6 +4,7 @@
 
 check_PROGRAMS = \
 	libpldm_base_test \
+	libpldm_platform_test \
 	libpldmresponder_base_test
 
 test_cppflags = \
@@ -27,6 +28,16 @@
 libpldm_base_test_LDADD = $(top_builddir)/libpldm/libpldm_la-base.o $(CODE_COVERAGE_LIBS)
 libpldm_base_test_SOURCES = libpldm_base_test.cpp
 
+libpldm_platform_test_CPPFLAGS = $(test_cppflags)
+libpldm_platform_test_CXXFLAGS = $(test_cxxflags)
+libpldm_platform_test_LDFLAGS = $(test_ldflags)
+libpldm_platform_test_LDADD = \
+	$(top_builddir)/libpldm/libpldm_la-platform.o \
+	$(top_builddir)/libpldm/libpldm_la-base.o \
+	$(CODE_COVERAGE_LIBS)
+libpldm_platform_test_SOURCES = libpldm_platform_test.cpp
+
+
 libpldmresponder_base_test_CPPFLAGS = $(test_cppflags)
 libpldmresponder_base_test_CXXFLAGS = $(test_cxxflags)
 libpldmresponder_base_test_LDFLAGS = $(test_ldflags)
diff --git a/test/libpldm_platform_test.cpp b/test/libpldm_platform_test.cpp
new file mode 100644
index 0000000..0de82e4
--- /dev/null
+++ b/test/libpldm_platform_test.cpp
@@ -0,0 +1,76 @@
+#include <string.h>
+
+#include <array>
+
+#include "libpldm/base.h"
+#include "libpldm/platform.h"
+
+#include <gtest/gtest.h>
+
+TEST(SetStateEffecterStates, testEncodeResponse)
+{
+    pldm_msg response{};
+    uint8_t completionCode = 0;
+
+    std::array<uint8_t, PLDM_SET_STATE_EFFECTER_STATES_RESP_BYTES>
+        responseMsg{};
+
+    response.body.payload = responseMsg.data();
+    response.body.payload_length = responseMsg.size();
+
+    auto rc = encode_set_state_effecter_states_resp(0, PLDM_SUCCESS, &response);
+
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(completionCode, response.body.payload[0]);
+}
+
+TEST(SetStateEffecterStates, testGoodDecodeRequest)
+{
+    std::array<uint8_t, PLDM_SET_STATE_EFFECTER_STATES_REQ_BYTES> requestMsg{};
+
+    pldm_msg_payload request{};
+    request.payload = requestMsg.data();
+    request.payload_length = requestMsg.size();
+
+    uint16_t effecterId = 0x32;
+    uint8_t compEffecterCnt = 0x2;
+
+    std::array<set_effecter_state_field, 8> stateField{};
+    stateField[0] = {PLDM_REQUEST_SET, 3};
+    stateField[1] = {PLDM_REQUEST_SET, 4};
+
+    uint16_t retEffecterId = 0;
+    uint8_t retCompEffecterCnt = 0;
+
+    std::array<set_effecter_state_field, 8> retStateField{};
+
+    memcpy(request.payload, &effecterId, sizeof(effecterId));
+    memcpy(request.payload + sizeof(effecterId), &compEffecterCnt,
+           sizeof(compEffecterCnt));
+    memcpy(request.payload + sizeof(effecterId) + sizeof(compEffecterCnt),
+           &stateField, sizeof(stateField));
+
+    auto rc = decode_set_state_effecter_states_req(
+        &request, &retEffecterId, &retCompEffecterCnt, retStateField.data());
+
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(effecterId, retEffecterId);
+    ASSERT_EQ(retCompEffecterCnt, compEffecterCnt);
+    ASSERT_EQ(retStateField[0].set_request, stateField[0].set_request);
+    ASSERT_EQ(retStateField[0].effecter_state, stateField[0].effecter_state);
+    ASSERT_EQ(retStateField[1].set_request, stateField[1].set_request);
+    ASSERT_EQ(retStateField[1].effecter_state, stateField[1].effecter_state);
+}
+
+TEST(SetStateEffecterStates, testBadDecodeRequest)
+{
+    std::array<uint8_t, PLDM_SET_STATE_EFFECTER_STATES_REQ_BYTES> requestMsg{};
+
+    pldm_msg_payload request{};
+    request.payload = requestMsg.data();
+    request.payload_length = requestMsg.size();
+
+    auto rc = decode_set_state_effecter_states_req(&request, NULL, NULL, NULL);
+
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}