libpldm: Implement en/decode for setNumericEffecterValue

The SetNumericEffecterValue command is used to set the value for a PLDM
Numeric Effecter.
The spec of SetNumericEffecterValue command refers to DSP0248_1.2.0: 22.2.

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I890217e6090c5646c074e7a545c1168f72e9707c
diff --git a/libpldm/platform.c b/libpldm/platform.c
index 9c06854..8b9506f 100644
--- a/libpldm/platform.c
+++ b/libpldm/platform.c
@@ -2,7 +2,6 @@
 #include <string.h>
 
 #include "platform.h"
-#include "utils.h"
 
 int encode_set_state_effecter_states_resp(uint8_t instance_id,
 					  uint8_t completion_code,
@@ -261,3 +260,169 @@
 
 	return PLDM_SUCCESS;
 }
+
+int decode_set_numeric_effecter_value_req(const struct pldm_msg *msg,
+					  size_t payload_length,
+					  uint16_t *effecter_id,
+					  uint8_t *effecter_data_size,
+					  uint8_t *effecter_value)
+{
+	if (msg == NULL || effecter_id == NULL || effecter_data_size == NULL ||
+	    effecter_value == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length < PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_set_numeric_effecter_value_req *request =
+	    (struct pldm_set_numeric_effecter_value_req *)msg->payload;
+	*effecter_id = le16toh(request->effecter_id);
+	*effecter_data_size = request->effecter_data_size;
+
+	if (*effecter_data_size > PLDM_EFFECTER_DATA_SIZE_SINT32) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (*effecter_data_size == PLDM_EFFECTER_DATA_SIZE_UINT8 ||
+	    *effecter_data_size == PLDM_EFFECTER_DATA_SIZE_SINT8) {
+
+		if (payload_length !=
+		    PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES) {
+			return PLDM_ERROR_INVALID_LENGTH;
+		}
+
+		*effecter_value = request->effecter_value[0];
+	}
+
+	if (*effecter_data_size == PLDM_EFFECTER_DATA_SIZE_UINT16 ||
+	    *effecter_data_size == PLDM_EFFECTER_DATA_SIZE_SINT16) {
+
+		if (payload_length !=
+		    PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES + 1) {
+			return PLDM_ERROR_INVALID_LENGTH;
+		}
+
+		memcpy(effecter_value, request->effecter_value, 2);
+		*effecter_value = le16toh(*effecter_value);
+	}
+
+	if (*effecter_data_size == PLDM_EFFECTER_DATA_SIZE_UINT32 ||
+	    *effecter_data_size == PLDM_EFFECTER_DATA_SIZE_SINT32) {
+
+		if (payload_length !=
+		    PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES + 3) {
+			return PLDM_ERROR_INVALID_LENGTH;
+		}
+
+		memcpy(effecter_value, request->effecter_value, 4);
+		*effecter_value = le32toh(*effecter_value);
+	}
+
+	return PLDM_SUCCESS;
+}
+
+int encode_set_numeric_effecter_value_resp(uint8_t instance_id,
+					   uint8_t completion_code,
+					   struct pldm_msg *msg,
+					   size_t payload_length)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length != PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	msg->payload[0] = completion_code;
+
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_SET_NUMERIC_EFFECTER_VALUE;
+
+	rc = pack_pldm_header(&header, &(msg->hdr));
+
+	return rc;
+}
+
+int encode_set_numeric_effecter_value_req(
+    uint8_t instance_id, uint16_t effecter_id, uint8_t effecter_data_size,
+    uint8_t *effecter_value, struct pldm_msg *msg, size_t payload_length)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	if (msg == NULL || effecter_value == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+	struct pldm_set_numeric_effecter_value_req *request =
+	    (struct pldm_set_numeric_effecter_value_req *)msg->payload;
+
+	header.msg_type = PLDM_REQUEST;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_SET_NUMERIC_EFFECTER_VALUE;
+
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+
+	if (effecter_data_size > PLDM_EFFECTER_DATA_SIZE_SINT32) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (effecter_data_size == PLDM_EFFECTER_DATA_SIZE_UINT8 ||
+	    effecter_data_size == PLDM_EFFECTER_DATA_SIZE_SINT8) {
+		if (payload_length !=
+		    PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES) {
+			return PLDM_ERROR_INVALID_LENGTH;
+		}
+		request->effecter_value[0] = *effecter_value;
+	} else if (effecter_data_size == PLDM_EFFECTER_DATA_SIZE_UINT16 ||
+		   effecter_data_size == PLDM_EFFECTER_DATA_SIZE_SINT16) {
+		if (payload_length !=
+		    PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES + 1) {
+			return PLDM_ERROR_INVALID_LENGTH;
+		}
+
+		memcpy(request->effecter_value, effecter_value, 2);
+		*request->effecter_value = htole16(*request->effecter_value);
+	} else if (effecter_data_size == PLDM_EFFECTER_DATA_SIZE_UINT32 ||
+		   effecter_data_size == PLDM_EFFECTER_DATA_SIZE_SINT32) {
+		if (payload_length !=
+		    PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES + 3) {
+			return PLDM_ERROR_INVALID_LENGTH;
+		}
+
+		memcpy(request->effecter_value, effecter_value, 4);
+		*request->effecter_value = htole32(*request->effecter_value);
+	}
+
+	request->effecter_id = htole16(effecter_id);
+	request->effecter_data_size = effecter_data_size;
+
+	return PLDM_SUCCESS;
+}
+
+int decode_set_numeric_effecter_value_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;
+	}
+
+	if (payload_length != PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	*completion_code = msg->payload[0];
+
+	return PLDM_SUCCESS;
+}
diff --git a/libpldm/platform.h b/libpldm/platform.h
index 1062240..0935e04 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -15,15 +15,28 @@
 /* Response lengths are inclusive of completion code */
 #define PLDM_SET_STATE_EFFECTER_STATES_RESP_BYTES 1
 
+#define PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES 1
+#define PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES 4
+
 #define PLDM_GET_PDR_REQ_BYTES 13
 /* Minimum response length */
 #define PLDM_GET_PDR_MIN_RESP_BYTES 12
 
+enum pldm_effecter_data_size {
+	PLDM_EFFECTER_DATA_SIZE_UINT8,
+	PLDM_EFFECTER_DATA_SIZE_SINT8,
+	PLDM_EFFECTER_DATA_SIZE_UINT16,
+	PLDM_EFFECTER_DATA_SIZE_SINT16,
+	PLDM_EFFECTER_DATA_SIZE_UINT32,
+	PLDM_EFFECTER_DATA_SIZE_SINT32
+};
+
 enum set_request { PLDM_NO_CHANGE = 0x00, PLDM_REQUEST_SET = 0x01 };
 
 enum effecter_state { PLDM_INVALID_VALUE = 0xFF };
 
 enum pldm_platform_commands {
+	PLDM_SET_NUMERIC_EFFECTER_VALUE = 0x31,
 	PLDM_SET_STATE_EFFECTER_STATES = 0x39,
 	PLDM_GET_PDR = 0x51,
 };
@@ -138,8 +151,53 @@
 	uint16_t record_change_number;
 } __attribute__((packed));
 
+/** @struct pldm_set_numeric_effecter_value_req
+ *
+ *  structure representing SetNumericEffecterValue request packet
+ */
+struct pldm_set_numeric_effecter_value_req {
+	uint16_t effecter_id;
+	uint8_t effecter_data_size;
+	uint8_t effecter_value[1];
+} __attribute__((packed));
+
 /* Responder */
 
+/* SetNumericEffecterValue */
+
+/** @brief Decode SetNumericEffecterValue 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
+ *  @param[out] effecter_data_size - The bit width and format of the setting
+ * 				value for the effecter.
+ * 				value:{uint8,sint8,uint16,sint16,uint32,sint32}
+ *  @param[out] effecter_value - The setting value of numeric effecter being
+ * 				requested.
+ *  @return pldm_completion_codes
+ */
+int decode_set_numeric_effecter_value_req(const struct pldm_msg *msg,
+					  size_t payload_length,
+					  uint16_t *effecter_id,
+					  uint8_t *effecter_data_size,
+					  uint8_t *effecter_value);
+
+/** @brief Create a PLDM response message for SetNumericEffecterValue
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] completion_code - PLDM completion code
+ *  @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.body.payload'
+ */
+int encode_set_numeric_effecter_value_resp(uint8_t instance_id,
+					   uint8_t completion_code,
+					   struct pldm_msg *msg,
+					   size_t payload_length);
+
 /* SetStateEffecterStates */
 
 /** @brief Create a PLDM response message for SetStateEffecterStates
@@ -317,6 +375,38 @@
 int decode_set_state_effecter_states_resp(const struct pldm_msg *msg,
 					  size_t payload_length,
 					  uint8_t *completion_code);
+
+/* SetNumericEffecterValue */
+
+/** @brief Create a PLDM request message for SetNumericEffecterValue
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] effecter_id - used to identify and access the effecter
+ *  @param[in] effecter_data_size - The bit width and format of the setting
+ * 				value for the effecter.
+ * 				value:{uint8,sint8,uint16,sint16,uint32,sint32}
+ *  @param[in] effecter_value - The setting value of numeric effecter being
+ * 				requested.
+ *  @param[in] payload_length - Length of request message payload
+ *  @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_set_numeric_effecter_value_req(
+    uint8_t instance_id, uint16_t effecter_id, uint8_t effecter_data_size,
+    uint8_t *effecter_value, struct pldm_msg *msg, size_t payload_length);
+
+/** @brief Decode SetNumericEffecterValue response data
+ *  @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_numeric_effecter_value_resp(const struct pldm_msg *msg,
+					   size_t payload_length,
+					   uint8_t *completion_code);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/test/libpldm_platform_test.cpp b/test/libpldm_platform_test.cpp
index 82501e9..244131d 100644
--- a/test/libpldm_platform_test.cpp
+++ b/test/libpldm_platform_test.cpp
@@ -384,3 +384,167 @@
         &retRespCnt, retRecordData, recordDataLength, &retTransferCRC);
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
 }
+
+TEST(SetNumericEffecterValue, testGoodDecodeRequest)
+{
+    std::array<uint8_t,
+               hdrSize + PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES + 3>
+        requestMsg{};
+
+    uint16_t effecter_id = 32768;
+    uint8_t effecter_data_size = PLDM_EFFECTER_DATA_SIZE_UINT32;
+    uint32_t effecter_value = 123456789;
+
+    uint16_t reteffecter_id;
+    uint8_t reteffecter_data_size;
+    uint8_t reteffecter_value[4];
+
+    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    struct pldm_set_numeric_effecter_value_req* request =
+        reinterpret_cast<struct pldm_set_numeric_effecter_value_req*>(
+            req->payload);
+
+    request->effecter_id = effecter_id;
+    request->effecter_data_size = effecter_data_size;
+    memcpy(request->effecter_value, &effecter_value, sizeof(effecter_value));
+
+    auto rc = decode_set_numeric_effecter_value_req(
+        req, requestMsg.size() - hdrSize, &reteffecter_id,
+        &reteffecter_data_size, reinterpret_cast<uint8_t*>(&reteffecter_value));
+
+    uint32_t value = *(reinterpret_cast<uint32_t*>(&reteffecter_value[0]));
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(reteffecter_id, effecter_id);
+    EXPECT_EQ(reteffecter_data_size, effecter_data_size);
+    EXPECT_EQ(value, effecter_value);
+}
+
+TEST(SetNumericEffecterValue, testBadDecodeRequest)
+{
+    std::array<uint8_t, hdrSize + PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES>
+        requestMsg{};
+
+    auto rc = decode_set_numeric_effecter_value_req(
+        NULL, requestMsg.size() - hdrSize, NULL, NULL, NULL);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    uint16_t effecter_id = 0x10;
+    uint8_t effecter_data_size = PLDM_EFFECTER_DATA_SIZE_UINT8;
+    uint8_t effecter_value = 1;
+
+    uint16_t reteffecter_id;
+    uint8_t reteffecter_data_size;
+    uint8_t reteffecter_value[4];
+
+    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    struct pldm_set_numeric_effecter_value_req* request =
+        reinterpret_cast<struct pldm_set_numeric_effecter_value_req*>(
+            req->payload);
+
+    request->effecter_id = effecter_id;
+    request->effecter_data_size = effecter_data_size;
+    memcpy(request->effecter_value, &effecter_value, sizeof(effecter_value));
+
+    rc = decode_set_numeric_effecter_value_req(
+        req, requestMsg.size() - hdrSize - 1, &reteffecter_id,
+        &reteffecter_data_size, reinterpret_cast<uint8_t*>(&reteffecter_value));
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+
+TEST(SetNumericEffecterValue, testGoodEncodeRequest)
+{
+    uint16_t effecter_id = 0;
+    uint8_t effecter_data_size = PLDM_EFFECTER_DATA_SIZE_UINT8;
+    uint8_t effecter_value = 1;
+
+    std::vector<uint8_t> requestMsg(
+        hdrSize + PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = encode_set_numeric_effecter_value_req(
+        0, effecter_id, effecter_data_size,
+        reinterpret_cast<uint8_t*>(&effecter_value), request,
+        PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES);
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    struct pldm_set_numeric_effecter_value_req* req =
+        reinterpret_cast<struct pldm_set_numeric_effecter_value_req*>(
+            request->payload);
+    EXPECT_EQ(effecter_id, req->effecter_id);
+    EXPECT_EQ(effecter_data_size, req->effecter_data_size);
+    EXPECT_EQ(effecter_value,
+              *(reinterpret_cast<uint8_t*>(&req->effecter_value[0])));
+}
+
+TEST(SetNumericEffecterValue, testBadEncodeRequest)
+{
+    std::vector<uint8_t> requestMsg(
+        hdrSize + PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = encode_set_numeric_effecter_value_req(
+        0, 0, 0, NULL, NULL, PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    uint16_t effecter_value;
+    rc = encode_set_numeric_effecter_value_req(
+        0, 0, 6, reinterpret_cast<uint8_t*>(&effecter_value), request,
+        PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(SetNumericEffecterValue, testGoodDecodeResponse)
+{
+    std::array<uint8_t, hdrSize + PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES>
+        responseMsg{};
+
+    uint8_t completion_code = 0xA0;
+
+    uint8_t retcompletion_code;
+
+    memcpy(responseMsg.data() + hdrSize, &completion_code,
+           sizeof(completion_code));
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+
+    auto rc = decode_set_numeric_effecter_value_resp(
+        response, responseMsg.size() - hdrSize, &retcompletion_code);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(completion_code, retcompletion_code);
+}
+
+TEST(SetNumericEffecterValue, testBadDecodeResponse)
+{
+    std::array<uint8_t, PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES>
+        responseMsg{};
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+
+    auto rc = decode_set_numeric_effecter_value_resp(response,
+                                                     responseMsg.size(), NULL);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(SetNumericEffecterValue, testGoodEncodeResponse)
+{
+    std::array<uint8_t, sizeof(pldm_msg_hdr) +
+                            PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES>
+        responseMsg{};
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    uint8_t completionCode = 0;
+
+    auto rc = encode_set_numeric_effecter_value_resp(
+        0, PLDM_SUCCESS, response, PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(completionCode, response->payload[0]);
+}
+
+TEST(SetNumericEffecterValue, testBadEncodeResponse)
+{
+    auto rc = encode_set_numeric_effecter_value_resp(
+        0, PLDM_SUCCESS, NULL, PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES);
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}