dsp: firmware update: Add encode/decode APIs for DD update

encode/decode command RequestDownstreamDeviceUpdate, based on
DSP0267 1.3.0.

Change-Id: I904a7229fe4a440904e6cf95c8e46b5956c3476c
Signed-off-by: Sora Su <baxiche@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cef81ea..511ea85 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,8 @@
   This ID is used to notify the remote PLDM terminus about the boot side change
   that occurs during an out-of-band code update.
 
+- firmware update: Add encode/decode API for downstream device update command
+
 ### Changed
 
 - clang-format: update latest spec and reformat with clang-20
diff --git a/include/libpldm/firmware_update.h b/include/libpldm/firmware_update.h
index 1610bd8..3571b9b 100644
--- a/include/libpldm/firmware_update.h
+++ b/include/libpldm/firmware_update.h
@@ -121,10 +121,16 @@
 	PLDM_TRANSFER_COMPLETE = 0x16,
 	PLDM_VERIFY_COMPLETE = 0x17,
 	PLDM_APPLY_COMPLETE = 0x18,
+	PLDM_GET_META_DATA = 0x19,
 	PLDM_ACTIVATE_FIRMWARE = 0x1a,
 	PLDM_GET_STATUS = 0x1b,
 	PLDM_CANCEL_UPDATE_COMPONENT = 0x1c,
-	PLDM_CANCEL_UPDATE = 0x1d
+	PLDM_CANCEL_UPDATE = 0x1d,
+	PLDM_ACTIVATE_PENDING_COMPONENT_IMAGE_SET = 0x1e,
+	PLDM_ACTIVATE_PENDING_COMPONENT_IMAGE = 0x1f,
+	PLDM_REQUEST_DOWNSTREAM_DEVICE_UPDATE = 0x20,
+	PLDM_GET_COMPONENT_OPAQUE_DATA = 0x21,
+	PLDM_UPTATE_SECURITY_REVISION = 0x22
 };
 
 /** @brief PLDM Firmware update completion codes
@@ -889,6 +895,29 @@
 	uint8_t fd_will_send_pkg_data;
 } __attribute__((packed));
 
+/** @struct pldm_request_downstream_dev_update_req
+ *
+ *  Structure representing Request Downstream Device Update request
+ */
+struct pldm_request_downstream_device_update_req {
+	uint32_t maximum_downstream_device_transfer_size;
+	uint8_t maximum_outstanding_transfer_requests;
+	uint16_t downstream_device_package_data_length;
+};
+#define PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES 7
+
+/** @struct pldm_request_downstream_dev_update_resp
+ *
+ *  Structure representing Request Downstream Device Update response
+ */
+struct pldm_request_downstream_device_update_resp {
+	uint8_t completion_code;
+	uint16_t downstream_device_meta_data_length;
+	uint8_t downstream_device_will_send_get_package_data;
+	uint16_t get_package_data_maximum_transfer_size;
+};
+#define PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES 6
+
 /** @struct pldm_pass_component_table_req
  *
  *  Structure representing PassComponentTable request, wire format.
@@ -1538,6 +1567,81 @@
 			       const struct pldm_request_update_resp *resp_data,
 			       struct pldm_msg *msg, size_t *payload_length);
 
+/** @brief Create PLDM request message for RequestDownstreamDeviceUpdate
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] req_data - Request data.
+ *  @param[in,out] msg - Message will be written to this
+ *  @param[in,out] payload_length - Length of response message payload
+ *
+ *  @return 0 on success,
+ *		-EINVAL if any argument is invalid,
+ *		-ENOMSG if the message type is incorrect,
+ *		-EOVERFLOW if the payload length is invalid
+ *
+ *  @note Caller is responsible for memory alloc and dealloc of param
+ *        'msg.payload'
+ */
+int encode_request_downstream_device_update_req(
+	uint8_t instance_id,
+	const struct pldm_request_downstream_device_update_req *req_data,
+	struct pldm_msg *msg, size_t *payload_length);
+
+/** @brief Decode PLDM request message for RequestDownstreamDeviceUpdate
+ *
+ *  @param[in] msg - Message
+ *  @param[in] payload_length - Length of request message payload
+ *  @param[out] req_data - RequestDownstreamDeviceUpdate request parameters
+ *
+ *  @return 0 on success,
+ *		-EINVAL if any argument is invalid,
+ *		-ENOMSG if the message type is incorrect,
+ *		-EOVERFLOW if the payload length is invalid,
+ *		-EBADMSG if the message buffer was not fully consumed
+ *
+ *  @note Caller is responsible for memory alloc and dealloc of param
+ *        'msg.payload'
+ */
+int decode_request_downstream_device_update_req(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_request_downstream_device_update_req *req_data);
+
+/** @brief Create PLDM response message for RequestDownstreamDeviceUpdate
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] resp_data - Response data
+ *  @param[out] msg - Message will be written to this
+ *  @param[inout] payload_length - Length of response message payload
+ *
+ *  @return 0 on success,
+ *		-EINVAL if any argument is invalid,
+ *		-ENOMSG if the message type is incorrect,
+ *		-EOVERFLOW if the payload length is invalid
+ *
+ *  @note  Caller is responsible for memory alloc and dealloc of param
+ *		   'msg.payload'
+ */
+int encode_request_downstream_device_update_resp(
+	uint8_t instance_id,
+	const struct pldm_request_downstream_device_update_resp *resp_data,
+	struct pldm_msg *msg, size_t *payload_length);
+
+/** @brief Decode a RequestDownstreamDeviceUpdate response message
+ *
+ *  @param[in] msg - Response message
+ *  @param[in] payload_length - Length of response message payload
+ *  @param[out] resp_data - RequestDownstreamDeviceUpdate respond parameters
+ *
+ *  @return 0 on success,
+ *		-EINVAL if any argument is invalid,
+ *		-ENOMSG if the message type is incorrect,
+ *		-EOVERFLOW if the payload length is invalid,
+ *		-EBADMSG if the message buffer was not fully consumed
+ */
+int decode_request_downstream_device_update_resp(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_request_downstream_device_update_resp *resp_data);
+
 /** @brief Create PLDM request message for PassComponentTable
  *
  *  @param[in] instance_id - Message's instance id
diff --git a/src/dsp/firmware_update.c b/src/dsp/firmware_update.c
index 25b29a7..7534e3a 100644
--- a/src/dsp/firmware_update.c
+++ b/src/dsp/firmware_update.c
@@ -1555,6 +1555,146 @@
 	return pldm_msgbuf_complete(buf);
 }
 
+LIBPLDM_ABI_TESTING
+int encode_request_downstream_device_update_req(
+	uint8_t instance_id,
+	const struct pldm_request_downstream_device_update_req *req_data,
+	struct pldm_msg *msg, size_t *payload_length)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (!req_data || !msg || !payload_length ||
+	    req_data->maximum_downstream_device_transfer_size <
+		    PLDM_FWUP_BASELINE_TRANSFER_SIZE ||
+	    req_data->maximum_outstanding_transfer_requests <
+		    PLDM_FWUP_MIN_OUTSTANDING_REQ) {
+		return -EINVAL;
+	}
+
+	rc = encode_pldm_header_only_errno(
+		PLDM_REQUEST, instance_id, PLDM_FWUP,
+		PLDM_REQUEST_DOWNSTREAM_DEVICE_UPDATE, msg);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_init_errno(buf,
+				    PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES,
+				    msg->payload, *payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf,
+			   req_data->maximum_downstream_device_transfer_size);
+	pldm_msgbuf_insert(buf,
+			   req_data->maximum_outstanding_transfer_requests);
+	pldm_msgbuf_insert(buf,
+			   req_data->downstream_device_package_data_length);
+
+	return pldm_msgbuf_complete_used(buf, *payload_length, payload_length);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_request_downstream_device_update_req(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_request_downstream_device_update_req *req)
+{
+	int rc;
+	PLDM_MSGBUF_DEFINE_P(buf);
+
+	if (!msg || !req) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msgbuf_init_errno(buf,
+				    PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES,
+				    msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, req->maximum_downstream_device_transfer_size);
+	pldm_msgbuf_extract(buf, req->maximum_outstanding_transfer_requests);
+	pldm_msgbuf_extract(buf, req->downstream_device_package_data_length);
+
+	return pldm_msgbuf_complete_consumed(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int encode_request_downstream_device_update_resp(
+	uint8_t instance_id,
+	const struct pldm_request_downstream_device_update_resp *resp_data,
+	struct pldm_msg *msg, size_t *payload_length)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (!resp_data || !msg || !payload_length) {
+		return -EINVAL;
+	}
+
+	rc = encode_pldm_header_only_errno(
+		PLDM_RESPONSE, instance_id, PLDM_FWUP,
+		PLDM_REQUEST_DOWNSTREAM_DEVICE_UPDATE, msg);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_init_errno(
+		buf, PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES, msg->payload,
+		*payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, resp_data->completion_code);
+	pldm_msgbuf_insert(buf, resp_data->downstream_device_meta_data_length);
+	pldm_msgbuf_insert(
+		buf, resp_data->downstream_device_will_send_get_package_data);
+	pldm_msgbuf_insert(buf,
+			   resp_data->get_package_data_maximum_transfer_size);
+
+	return pldm_msgbuf_complete_used(buf, *payload_length, payload_length);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_request_downstream_device_update_resp(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_request_downstream_device_update_resp *resp_data)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (!msg || !resp_data) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msg_has_error(msg,
+				PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES);
+	if (rc) {
+		resp_data->completion_code = rc;
+		return 0;
+	}
+
+	rc = pldm_msgbuf_init_errno(
+		buf, PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES, msg->payload,
+		payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, resp_data->completion_code);
+	pldm_msgbuf_extract(buf, resp_data->downstream_device_meta_data_length);
+	pldm_msgbuf_extract(
+		buf, resp_data->downstream_device_will_send_get_package_data);
+	pldm_msgbuf_extract(buf,
+			    resp_data->get_package_data_maximum_transfer_size);
+
+	return pldm_msgbuf_complete_consumed(buf);
+}
+
 LIBPLDM_ABI_STABLE
 int encode_request_update_req(uint8_t instance_id, uint32_t max_transfer_size,
 			      uint16_t num_of_comp,
diff --git a/tests/dsp/firmware_update.cpp b/tests/dsp/firmware_update.cpp
index 01ea091..fa2d60c 100644
--- a/tests/dsp/firmware_update.cpp
+++ b/tests/dsp/firmware_update.cpp
@@ -2957,6 +2957,180 @@
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
 }
 
+#ifdef LIBPLDM_API_TESTING
+TEST(RequestDownstreamDeviceUpdate, goodPathEncodeRequest)
+{
+    constexpr uint8_t instanceId = 1;
+
+    std::array<uint8_t, hdrSize + PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES>
+        request{};
+
+    auto requestMsg = new (request.data()) pldm_msg;
+
+    constexpr struct pldm_request_downstream_device_update_req req_data = {
+        .maximum_downstream_device_transfer_size = 512,
+        .maximum_outstanding_transfer_requests = 2,
+        .downstream_device_package_data_length = 0x1234,
+    };
+    size_t enc_payload_len = PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES;
+
+    auto rc = encode_request_downstream_device_update_req(
+        instanceId, &req_data, requestMsg, &enc_payload_len);
+
+    EXPECT_EQ(rc, 0);
+
+    std::array<uint8_t, hdrSize + PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES>
+        outRequest{0x81, 0x05, 0x20, 0x00, 0x02, 0x00, 0x00, 0x02, 0x34, 0x12};
+    EXPECT_EQ(request, outRequest);
+}
+#endif // LIBPLDM_API_TESTING
+
+#ifdef LIBPLDM_API_TESTING
+TEST(RequestDownstreamDeviceUpdate, errorPathEncodeRequest)
+{
+    constexpr uint8_t instanceId = 1;
+    size_t enc_payload_len = PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES;
+
+    std::array<uint8_t, hdrSize + PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES>
+        request{};
+
+    struct pldm_request_downstream_device_update_req req_data = {
+        .maximum_downstream_device_transfer_size = 512,
+        .maximum_outstanding_transfer_requests = 2,
+        .downstream_device_package_data_length = 0x1234,
+    };
+
+    auto requestMsg = new (request.data()) pldm_msg;
+
+    auto rc = encode_request_downstream_device_update_req(
+        instanceId, nullptr, requestMsg, &enc_payload_len);
+    EXPECT_EQ(rc, -EINVAL);
+    rc = encode_request_downstream_device_update_req(
+        instanceId, &req_data, requestMsg, &enc_payload_len);
+    EXPECT_EQ(rc, 0);
+
+    rc = encode_request_downstream_device_update_req(instanceId, &req_data,
+                                                     nullptr, &enc_payload_len);
+    EXPECT_EQ(rc, -EINVAL);
+    rc = encode_request_downstream_device_update_req(
+        instanceId, &req_data, requestMsg, &enc_payload_len);
+    EXPECT_EQ(rc, 0);
+
+    rc = encode_request_downstream_device_update_req(instanceId, &req_data,
+                                                     requestMsg, nullptr);
+    EXPECT_EQ(rc, -EINVAL);
+    rc = encode_request_downstream_device_update_req(
+        instanceId, &req_data, requestMsg, &enc_payload_len);
+    EXPECT_EQ(rc, 0);
+
+    enc_payload_len =
+        static_cast<size_t>(PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES) - 1;
+    rc = encode_request_downstream_device_update_req(
+        instanceId, &req_data, requestMsg, &enc_payload_len);
+    EXPECT_EQ(rc, -EOVERFLOW);
+    enc_payload_len =
+        static_cast<size_t>(PLDM_DOWNSTREAM_DEVICE_UPDATE_REQUEST_BYTES);
+    rc = encode_request_downstream_device_update_req(
+        instanceId, &req_data, requestMsg, &enc_payload_len);
+    EXPECT_EQ(rc, 0);
+
+    req_data.maximum_downstream_device_transfer_size = 31;
+    rc = encode_request_downstream_device_update_req(
+        instanceId, &req_data, requestMsg, &enc_payload_len);
+    EXPECT_EQ(rc, -EINVAL);
+    req_data.maximum_downstream_device_transfer_size =
+        PLDM_FWUP_BASELINE_TRANSFER_SIZE;
+
+    req_data.maximum_outstanding_transfer_requests = 0;
+    rc = encode_request_downstream_device_update_req(
+        instanceId, &req_data, requestMsg, &enc_payload_len);
+    EXPECT_EQ(rc, -EINVAL);
+    req_data.maximum_outstanding_transfer_requests = 2;
+    rc = encode_request_downstream_device_update_req(
+        instanceId, &req_data, requestMsg, &enc_payload_len);
+    EXPECT_EQ(rc, 0);
+}
+#endif // LIBPLDM_API_TESTING
+
+#ifdef LIBPLDM_API_TESTING
+TEST(RequestDownstreamDeviceUpdate, goodPathDecodeResponse)
+{
+    /* Test a success completion code */
+    constexpr uint16_t ddMetaDataLen = 1024;
+    constexpr uint8_t ddWillSendPkgData = 1;
+    constexpr uint16_t getPkgDataMaxTransferSize = 512;
+    std::array<uint8_t, hdrSize + PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES>
+        requestUpdateResponse1{0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x04, 0x01, 0x00, 0x02};
+
+    auto responseMsg1 = new (requestUpdateResponse1.data()) pldm_msg;
+
+    struct pldm_request_downstream_device_update_resp resp_data1 = {
+        .completion_code = 0,
+        .downstream_device_meta_data_length = 0,
+        .downstream_device_will_send_get_package_data = 0,
+        .get_package_data_maximum_transfer_size = 0};
+
+    auto rc = decode_request_downstream_device_update_resp(
+        responseMsg1, PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES,
+        &resp_data1);
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(resp_data1.completion_code, PLDM_SUCCESS);
+    EXPECT_EQ(resp_data1.downstream_device_meta_data_length, ddMetaDataLen);
+    EXPECT_EQ(resp_data1.downstream_device_will_send_get_package_data,
+              ddWillSendPkgData);
+    EXPECT_EQ(resp_data1.get_package_data_maximum_transfer_size,
+              getPkgDataMaxTransferSize);
+
+    /* Test a failure completion code */
+    std::array<uint8_t, hdrSize + PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES>
+        requestUpdateResponse2{0x00, 0x00, 0x00, 0x81};
+
+    auto responseMsg2 = new (requestUpdateResponse2.data()) pldm_msg;
+
+    struct pldm_request_downstream_device_update_resp resp_data2 = {
+        .completion_code = 0,
+        .downstream_device_meta_data_length = 0,
+        .downstream_device_will_send_get_package_data = 0,
+        .get_package_data_maximum_transfer_size = 0};
+
+    rc = decode_request_downstream_device_update_resp(
+        responseMsg2, PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES,
+        &resp_data2);
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(resp_data2.completion_code, PLDM_FWUP_ALREADY_IN_UPDATE_MODE);
+}
+#endif // LIBPLDM_API_TESTING
+
+#ifdef LIBPLDM_API_TESTING
+TEST(RequestDownstreamDeviceUpdate, errorPathDecodeResponse)
+{
+    std::array<uint8_t, hdrSize + PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES>
+        requestUpdateResponse{0x00, 0x00, 0x00, 0x00, 0x00,
+                              0x04, 0x01, 0x00, 0x02};
+
+    auto responseMsg = new (requestUpdateResponse.data()) pldm_msg;
+
+    struct pldm_request_downstream_device_update_resp resp_data = {
+        .completion_code = 0,
+        .downstream_device_meta_data_length = 0,
+        .downstream_device_will_send_get_package_data = 0,
+        .get_package_data_maximum_transfer_size = 0};
+
+    auto rc = decode_request_downstream_device_update_resp(
+        nullptr, PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES, &resp_data);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_request_downstream_device_update_resp(
+        responseMsg, PLDM_DOWNSTREAM_DEVICE_UPDATE_RESPONSE_BYTES, nullptr);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_request_downstream_device_update_resp(responseMsg, 0,
+                                                      &resp_data);
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif // LIBPLDM_API_TESTING
+
 TEST(PassComponentTable, goodPathEncodeRequest)
 {
     constexpr uint8_t instanceId = 1;