dsp: base: Add encode req & decode resp for NegotiateTransferParameters

Added encode/decode APIs for NegotiateTransferParameters command
(0x07) which is defined in DSP0240 Version 1.2.0 section 9.6.

Change-Id: I9920e1c9c9e6d9e5999d568d8d20b4c80a1d8726
Signed-off-by: Chau Ly <chaul@amperecomputing.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9dbd07..ab9ba44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,8 @@
 
 - oem: ibm: Modified the state set id for slot effecter and sensor
 
+- base: Add encode req & decode resp for NegotiateTransferParameters.
+
 ### Changed
 
 - dsp: firmware_update: Expand "params" in symbol names
diff --git a/include/libpldm/base.h b/include/libpldm/base.h
index 9088d80..ae902ea 100644
--- a/include/libpldm/base.h
+++ b/include/libpldm/base.h
@@ -131,6 +131,9 @@
 #define PLDM_MULTIPART_RECEIVE_REQ_BYTES	   18
 #define PLDM_BASE_MULTIPART_RECEIVE_RESP_MIN_BYTES 10
 
+#define PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES  10
+#define PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_RESP_BYTES 11
+
 #define PLDM_VERSION_0	     0
 #define PLDM_CURRENT_VERSION PLDM_VERSION_0
 
@@ -365,6 +368,25 @@
 	struct variable_field data;
 };
 
+/** @struct pldm_base_negotiate_transfer_params_req
+ *
+ * Structure representing PLDM Negotiate Transfer Parameters request
+ */
+struct pldm_base_negotiate_transfer_params_req {
+	uint16_t requester_part_size;
+	bitfield8_t requester_protocol_support[8];
+};
+
+/** @struct pldm_base_negotiate_transfer_params_resp
+ *
+ * Structure representing PLDM Negotiate Transfer Parameters response
+ */
+struct pldm_base_negotiate_transfer_params_resp {
+	uint8_t completion_code;
+	uint16_t responder_part_size;
+	bitfield8_t responder_protocol_support[8];
+};
+
 /**
  * @brief Populate the PLDM message with the PLDM header.The caller of this API
  *        allocates buffer for the PLDM header when forming the PLDM message.
@@ -722,6 +744,42 @@
 			    uint8_t pldm_type, uint8_t command,
 			    struct pldm_msg *msg);
 
+/** @brief Encode a PLDM Negotiate Transfer Parameters request message
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] req - The pointer to the request message to be encoded
+ *  @param[in,out] msg - Message will be written to this
+ *  @param[in] payload_length - length of request message payload
+ *  @return 0 on success
+ *          -EINVAL if the input parameters' memory are not allocated,
+ *          or message type or instance in request header is invalid
+ *          -ENOMSG if the PLDM type in the request header is invalid
+ *          -EOVERFLOW if the input message length is invalid
+ */
+int encode_pldm_base_negotiate_transfer_params_req(
+	uint8_t instance_id,
+	const struct pldm_base_negotiate_transfer_params_req *req,
+	struct pldm_msg *msg, size_t payload_length);
+
+/** @brief Decode a PLDM Negotiate Transfer Parameters response message
+ *
+ *  @param[in] msg - Response message
+ *  @param[in] payload_length - length of request message payload
+ *  @param[out] resp - pointer to the decoded response message,
+ *         excluding the data integrity checksum
+ *  @return 0 on success
+ *          -EINVAL if the input parameters' memory are not allocated
+ *          -EOVERFLOW if the input message buffer is too short for the output
+ *          response struct
+ *          -EBADMSG if the input message buffer is too large for the output
+ *          response struct
+ *  @note  Caller is responsible for memory alloc and dealloc of param
+ *         'msg.payload'
+ */
+int decode_pldm_base_negotiate_transfer_params_resp(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_base_negotiate_transfer_params_resp *resp);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/dsp/base.c b/src/dsp/base.c
index 42f33e2..35e3eb0 100644
--- a/src/dsp/base.c
+++ b/src/dsp/base.c
@@ -702,3 +702,85 @@
 	}
 	return PLDM_SUCCESS;
 }
+
+LIBPLDM_ABI_TESTING
+int encode_pldm_base_negotiate_transfer_params_req(
+	uint8_t instance_id,
+	const struct pldm_base_negotiate_transfer_params_req *req,
+	struct pldm_msg *msg, size_t payload_length)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (req == NULL || msg == NULL) {
+		return -EINVAL;
+	}
+
+	struct pldm_header_info header = { 0 };
+	header.instance = instance_id;
+	header.msg_type = PLDM_REQUEST;
+	header.pldm_type = PLDM_BASE;
+	header.command = PLDM_NEGOTIATE_TRANSFER_PARAMETERS;
+
+	rc = pack_pldm_header_errno(&header, &msg->hdr);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_init_errno(
+		buf, PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES,
+		msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, req->requester_part_size);
+	rc = pldm_msgbuf_insert_array(
+		buf, sizeof(req->requester_protocol_support),
+		(uint8_t *)req->requester_protocol_support,
+		sizeof(req->requester_protocol_support));
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	return pldm_msgbuf_complete(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_pldm_base_negotiate_transfer_params_resp(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_base_negotiate_transfer_params_resp *resp)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (msg == NULL || resp == NULL) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msg_has_error(msg, payload_length);
+
+	if (rc) {
+		resp->completion_code = rc;
+		return 0;
+	}
+
+	rc = pldm_msgbuf_init_errno(
+		buf, PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_RESP_BYTES,
+		msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, resp->completion_code);
+	pldm_msgbuf_extract(buf, resp->responder_part_size);
+	rc = pldm_msgbuf_extract_array(
+		buf, sizeof(resp->responder_protocol_support),
+		(uint8_t *)resp->responder_protocol_support,
+		sizeof(resp->responder_protocol_support));
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	return pldm_msgbuf_complete_consumed(buf);
+}
diff --git a/tests/dsp/base.cpp b/tests/dsp/base.cpp
index 7b5bfcf..dafe09b 100644
--- a/tests/dsp/base.cpp
+++ b/tests/dsp/base.cpp
@@ -1180,3 +1180,194 @@
     ASSERT_EQ(pldm_msg_hdr_correlate_response(&req, &resp), false);
 }
 #endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeNegotiateTransferParamsRequest, GoodTest)
+{
+    uint8_t instance_id = 0;
+
+    const struct pldm_base_negotiate_transfer_params_req req_data = {
+        0x0001, // BE 256
+        {{0x00}, {0x00}, {0x00}, {0x00}, {0x00}, {0x00}, {0x00}, {0x81}}};
+
+    std::array<uint8_t, PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES>
+        requestMsg = {0x01, 0x00, // requester_part_size = 256
+                      0x00, 0x00, 0x00, 0x00,
+                      0x00, 0x00, 0x00, 0x81}; // requester_protocol_support =
+                                               // PLDM_BASE & PLDM_FILE
+
+    PLDM_MSG_DEFINE_P(requestPtr,
+                      PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+    auto rc = encode_pldm_base_negotiate_transfer_params_req(
+        instance_id, &req_data, requestPtr,
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+
+    ASSERT_EQ(rc, 0);
+    EXPECT_EQ(
+        0, memcmp(requestPtr->payload, requestMsg.data(), sizeof(requestMsg)));
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeNegotiateTransferParamsRequest, BadTestUnAllocatedPtrParams)
+{
+    int rc;
+    uint8_t instance_id = 0;
+    const struct pldm_base_negotiate_transfer_params_req req_data = {
+        0x0001, // BE 256
+        {{0x00}, {0x00}, {0x00}, {0x00}, {0x00}, {0x00}, {0x00}, {0x81}}};
+
+    PLDM_MSG_DEFINE_P(requestPtr,
+                      PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+    rc = encode_pldm_base_negotiate_transfer_params_req(
+        instance_id, nullptr, requestPtr,
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = encode_pldm_base_negotiate_transfer_params_req(
+        instance_id, &req_data, nullptr,
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeNegotiateTransferParamsRequest,
+     BadTestInvalidExpectedOutputMsgLength)
+{
+    int rc;
+    uint8_t instance_id = 0;
+    const struct pldm_base_negotiate_transfer_params_req req_data = {
+        0x0001, // BE 256
+        {{0x00}, {0x00}, {0x00}, {0x00}, {0x00}, {0x00}, {0x00}, {0x81}}};
+
+    PLDM_MSG_DEFINE_P(requestPtr,
+                      PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_REQ_BYTES);
+
+    rc = encode_pldm_base_negotiate_transfer_params_req(instance_id, &req_data,
+                                                        requestPtr, 1);
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeNegotiateTransferParamsResponse, GoodTest)
+{
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint16_t responderPartSize = 128;
+    std::array<uint8_t, 8> responderProtocolSupport = {0x00, 0x00, 0x00, 0x00,
+                                                       0x00, 0x00, 0x00, 0x81};
+
+    struct pldm_base_negotiate_transfer_params_resp resp_data = {};
+
+    PLDM_MSGBUF_DEFINE_P(buf);
+    int rc;
+
+    static constexpr const size_t payload_length =
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_RESP_BYTES;
+    PLDM_MSG_DEFINE_P(responseMsg, payload_length);
+
+    rc = pldm_msgbuf_init_errno(buf, 0, responseMsg->payload, payload_length);
+    ASSERT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint8(buf, completionCode);
+    pldm_msgbuf_insert_uint16(buf, responderPartSize);
+    rc = pldm_msgbuf_insert_array_uint8(
+        buf, sizeof(resp_data.responder_protocol_support),
+        responderProtocolSupport.data(),
+        sizeof(resp_data.responder_protocol_support));
+    EXPECT_EQ(rc, 0);
+
+    ASSERT_EQ(pldm_msgbuf_complete_consumed(buf), 0);
+
+    rc = decode_pldm_base_negotiate_transfer_params_resp(
+        responseMsg, payload_length, &resp_data);
+
+    ASSERT_EQ(rc, 0);
+    EXPECT_EQ(resp_data.completion_code, completionCode);
+    EXPECT_EQ(resp_data.responder_part_size, responderPartSize);
+    EXPECT_EQ(0, memcmp(responderProtocolSupport.data(),
+                        resp_data.responder_protocol_support,
+                        sizeof(resp_data.responder_protocol_support)));
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeNegotiateTransferParamsResponse, BadTestUnAllocatedPtrParams)
+{
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint16_t responderPartSize = 128;
+    std::array<uint8_t, 8> responderProtocolSupport = {0x00, 0x00, 0x00, 0x00,
+                                                       0x00, 0x00, 0x00, 0x81};
+
+    struct pldm_base_negotiate_transfer_params_resp resp_data = {};
+
+    PLDM_MSGBUF_DEFINE_P(buf);
+    int rc;
+
+    static constexpr const size_t payload_length =
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_RESP_BYTES;
+    PLDM_MSG_DEFINE_P(responseMsg, payload_length);
+
+    rc = pldm_msgbuf_init_errno(buf, 0, responseMsg->payload, payload_length);
+    ASSERT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint8(buf, completionCode);
+    pldm_msgbuf_insert_uint16(buf, responderPartSize);
+    rc = pldm_msgbuf_insert_array_uint8(
+        buf, sizeof(resp_data.responder_protocol_support),
+        responderProtocolSupport.data(),
+        sizeof(resp_data.responder_protocol_support));
+    EXPECT_EQ(rc, 0);
+
+    ASSERT_EQ(pldm_msgbuf_complete_consumed(buf), 0);
+
+    rc = decode_pldm_base_negotiate_transfer_params_resp(
+        nullptr, payload_length, &resp_data);
+
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_pldm_base_negotiate_transfer_params_resp(
+        responseMsg, payload_length, nullptr);
+
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeNegotiateTransferParamsResponse,
+     BadTestInvalidExpectedInputMsgLength)
+{
+    uint8_t completionCode = PLDM_SUCCESS;
+    uint16_t responderPartSize = 128;
+    std::array<uint8_t, 8> responderProtocolSupport = {0x00, 0x00, 0x00, 0x00,
+                                                       0x00, 0x00, 0x00, 0x81};
+
+    struct pldm_base_negotiate_transfer_params_resp resp_data = {};
+
+    PLDM_MSGBUF_DEFINE_P(buf);
+    int rc;
+
+    static constexpr const size_t payload_length =
+        PLDM_BASE_NEGOTIATE_TRANSFER_PARAMETERS_RESP_BYTES;
+    PLDM_MSG_DEFINE_P(responseMsg, payload_length);
+
+    rc = pldm_msgbuf_init_errno(buf, 0, responseMsg->payload, payload_length);
+    ASSERT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint8(buf, completionCode);
+    pldm_msgbuf_insert_uint16(buf, responderPartSize);
+    rc = pldm_msgbuf_insert_array_uint8(
+        buf, sizeof(resp_data.responder_protocol_support),
+        responderProtocolSupport.data(),
+        sizeof(resp_data.responder_protocol_support));
+    EXPECT_EQ(rc, 0);
+
+    ASSERT_EQ(pldm_msgbuf_complete_consumed(buf), 0);
+
+    rc = decode_pldm_base_negotiate_transfer_params_resp(responseMsg, 0,
+                                                         &resp_data);
+
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif
\ No newline at end of file