fw_update: Add encode req & decode resp for query_downstream_devices

Add support for Query Downstream Devices to ask if a endpoint supports
downstream devices.

The code is developed based on the definition of
'QueryDownstreamDevices' in DSP0267_1.1.0. Section 10.3

Change-Id: I5925290de5023eb48f675e736429fe9f257170c8
Signed-off-by: Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>
diff --git a/tests/libpldm_firmware_update_test.cpp b/tests/libpldm_firmware_update_test.cpp
index 52e867a..3870fee 100644
--- a/tests/libpldm_firmware_update_test.cpp
+++ b/tests/libpldm_firmware_update_test.cpp
@@ -13,6 +13,8 @@
 #include <string_view>
 #include <vector>
 
+#include "msgbuf.h"
+
 #include <gtest/gtest.h>
 
 constexpr auto hdrSize = sizeof(pldm_msg_hdr);
@@ -1276,6 +1278,153 @@
                         outPendingCompVerStr.length));
 }
 
+TEST(QueryDownstreamDevices, goodPathEncodeRequest)
+{
+    constexpr uint8_t instanceId = 1;
+    std::array<uint8_t, sizeof(pldm_msg_hdr)> requestMsg{};
+    auto requestPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = encode_query_downstream_devices_req(instanceId, requestPtr);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(requestPtr->hdr.request, PLDM_REQUEST);
+    EXPECT_EQ(requestPtr->hdr.instance_id, instanceId);
+    EXPECT_EQ(requestPtr->hdr.type, PLDM_FWUP);
+    EXPECT_EQ(requestPtr->hdr.command, PLDM_QUERY_DOWNSTREAM_DEVICES);
+}
+
+TEST(QueryDownstreamDevices, encodeRequestInvalidData)
+{
+    constexpr uint8_t instanceId = 1;
+
+    auto rc = encode_query_downstream_devices_req(instanceId, nullptr);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(QueryDownstreamDevices, goodPathDecodeResponse)
+{
+    uint8_t completion_code_resp = PLDM_SUCCESS;
+    uint8_t downstream_device_update_supported_resp =
+        PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_SUPPORTED;
+    uint16_t number_of_downstream_devices_resp = 1;
+    uint16_t max_number_of_downstream_devices_resp = 1;
+    /** Capabilities of updating downstream devices
+     * FDP supports downstream devices dynamically attached [Bit position 0] &
+     * FDP supports downstream devices dynamically removed [Bit position 1]
+     */
+    bitfield32_t capabilities_resp = {.value = 0x0002};
+    int rc;
+
+    std::array<uint8_t, hdrSize + PLDM_QUERY_DOWNSTREAM_DEVICES_RESP_BYTES>
+        responseMsg{};
+
+    struct pldm_msgbuf _buf;
+    struct pldm_msgbuf* buf = &_buf;
+    rc = pldm_msgbuf_init(buf, 0, responseMsg.data() + hdrSize,
+                          responseMsg.size() - hdrSize);
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    pldm_msgbuf_insert_uint8(buf, completion_code_resp);
+    pldm_msgbuf_insert_uint8(buf, downstream_device_update_supported_resp);
+    pldm_msgbuf_insert_uint16(buf, number_of_downstream_devices_resp);
+    pldm_msgbuf_insert_uint16(buf, max_number_of_downstream_devices_resp);
+    pldm_msgbuf_insert_uint32(buf, capabilities_resp.value);
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    struct pldm_query_downstream_devices_resp resp_data;
+
+    rc = decode_query_downstream_devices_resp(
+        response, responseMsg.size() - hdrSize, &resp_data);
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(resp_data.completion_code, completion_code_resp);
+    EXPECT_EQ(resp_data.downstream_device_update_supported,
+              downstream_device_update_supported_resp);
+    EXPECT_EQ(resp_data.number_of_downstream_devices,
+              number_of_downstream_devices_resp);
+    EXPECT_EQ(resp_data.max_number_of_downstream_devices,
+              max_number_of_downstream_devices_resp);
+    EXPECT_EQ(resp_data.capabilities.value, capabilities_resp.value);
+}
+
+TEST(QueryDownstreamDevices, decodeRequestUndefinedValue)
+{
+    uint8_t completion_code_resp = PLDM_SUCCESS;
+    uint8_t downstream_device_update_supported_resp = 0xe; /*Undefined value*/
+    uint16_t number_of_downstream_devices_resp = 1;
+    uint16_t max_number_of_downstream_devices_resp = 1;
+    /** Capabilities of updating downstream devices
+     * FDP supports downstream devices dynamically attached [Bit position 0] &
+     * FDP supports downstream devices dynamically removed [Bit position 1]
+     */
+    bitfield32_t capabilities_resp = {.value = 0x0002};
+    int rc;
+
+    std::array<uint8_t, hdrSize + PLDM_QUERY_DOWNSTREAM_DEVICES_RESP_BYTES>
+        responseMsg{};
+
+    struct pldm_msgbuf _buf;
+    struct pldm_msgbuf* buf = &_buf;
+    rc = pldm_msgbuf_init(buf, 0, responseMsg.data() + hdrSize,
+                          responseMsg.size() - hdrSize);
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    pldm_msgbuf_insert_uint8(buf, completion_code_resp);
+    pldm_msgbuf_insert_uint8(buf, downstream_device_update_supported_resp);
+    pldm_msgbuf_insert_uint16(buf, number_of_downstream_devices_resp);
+    pldm_msgbuf_insert_uint16(buf, max_number_of_downstream_devices_resp);
+    pldm_msgbuf_insert_uint32(buf, capabilities_resp.value);
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    struct pldm_query_downstream_devices_resp resp_data;
+
+    rc = decode_query_downstream_devices_resp(
+        response, responseMsg.size() - hdrSize, &resp_data);
+
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(QueryDownstreamDevices, decodeRequestErrorBufSize)
+{
+    uint8_t completion_code_resp = PLDM_SUCCESS;
+    uint8_t downstream_device_update_supported_resp =
+        PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_SUPPORTED;
+    uint16_t number_of_downstream_devices_resp = 1;
+    uint16_t max_number_of_downstream_devices_resp = 1;
+    /** Capabilities of updating downstream devices
+     * FDP supports downstream devices dynamically attached [Bit position 0] &
+     * FDP supports downstream devices dynamically removed [Bit position 1]
+     */
+    bitfield32_t capabilities_resp = {.value = 0x0002};
+    int rc;
+
+    std::array<uint8_t, hdrSize + PLDM_QUERY_DOWNSTREAM_DEVICES_RESP_BYTES -
+                            2 /* Inject error length*/>
+        responseMsg{};
+
+    struct pldm_msgbuf _buf;
+    struct pldm_msgbuf* buf = &_buf;
+    rc = pldm_msgbuf_init(buf, 0, responseMsg.data() + hdrSize,
+                          responseMsg.size() - hdrSize);
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    pldm_msgbuf_insert_uint8(buf, completion_code_resp);
+    pldm_msgbuf_insert_uint8(buf, downstream_device_update_supported_resp);
+    pldm_msgbuf_insert_uint16(buf, number_of_downstream_devices_resp);
+    pldm_msgbuf_insert_uint16(buf, max_number_of_downstream_devices_resp);
+    // Inject error value
+    pldm_msgbuf_insert_uint16(buf, (uint16_t)capabilities_resp.value);
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    struct pldm_query_downstream_devices_resp resp_data;
+
+    rc = decode_query_downstream_devices_resp(
+        response, responseMsg.size() - hdrSize, &resp_data);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+
 TEST(RequestUpdate, goodPathEncodeRequest)
 {
     constexpr uint8_t instanceId = 1;