oem: meta: Add encode_oem_meta_file_io_read_resp()

Add encode_oem_meta_file_io_read_resp function

This function is used to encode the message of reading file IO.
Assemble responses to read file IO messages, but the response
content other than the completion code needs to be additionally
appended.

Change-Id: Ib030ac9f37fe73b66fb762dcf8b8297bce79836a
Signed-off-by: Lora Lin <lora.lin.wiwynn@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95ff5db..9bd9b0a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
 
 1. oem: meta: Add decode_oem_meta_file_io_write_req()
 2. oem: meta: Add decode_oem_meta_file_io_read_req()
+3. oem: meta: Add encode_oem_meta_file_io_read_resp()
 
 ### Deprecated
 
diff --git a/include/libpldm/oem/meta/file_io.h b/include/libpldm/oem/meta/file_io.h
index 47077f1..26d539b 100644
--- a/include/libpldm/oem/meta/file_io.h
+++ b/include/libpldm/oem/meta/file_io.h
@@ -73,6 +73,26 @@
 };
 #define PLDM_OEM_META_FILE_IO_READ_REQ_MIN_LENGTH 3u
 
+/** @struct pldm_oem_meta_file_io_read_resp
+ *
+ *  Structure representing PLDM read file response
+ */
+struct pldm_oem_meta_file_io_read_resp {
+	size_t version;
+	uint8_t completion_code;
+	uint8_t handle;
+	uint8_t option;
+	uint8_t length;
+	union {
+		struct pldm_oem_meta_file_io_read_attr_info attr;
+		struct pldm_oem_meta_file_io_read_data_info data;
+	} info;
+#ifndef __cplusplus
+	uint8_t data[] LIBPLDM_CC_COUNTED_BY(length);
+#endif
+};
+#define PLDM_OEM_META_FILE_IO_READ_RESP_MIN_SIZE 4u
+
 /** @brief Obtain the pointer to the data array of a write request
  *
  * @param[in] req - The pointer to the write request struct
@@ -118,6 +138,29 @@
 				     size_t payload_length,
 				     struct pldm_oem_meta_file_io_read_req *req);
 
+/** @brief Obtain the pointer to the data array of a read response
+ *
+ * @param[in] resp - The pointer to the read response struct
+ *
+ * @return The read response data pointer.
+ */
+void *pldm_oem_meta_file_io_read_resp_data(
+	struct pldm_oem_meta_file_io_read_resp *resp);
+
+/**
+ * @brief Encode OEM meta read file io resp
+ *
+ * @param[in] instance_id - The instance ID of the PLDM entity
+ * @param[out] resp - Pointer to the reqponse message
+ * @param[in] resp_len - Length of response message
+ * @param[out] responseMsg - Pointer to the buffer to store the response data
+ * @param[in] payload_length - Length of response payload
+ * @return 0 on success, negative errno value on failure
+ */
+int encode_oem_meta_file_io_read_resp(
+	uint8_t instance_id, struct pldm_oem_meta_file_io_read_resp *resp,
+	size_t resp_len, struct pldm_msg *responseMsg, size_t payload_length);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/oem/meta/file_io.c b/src/oem/meta/file_io.c
index 0c82f82..43d59e0 100644
--- a/src/oem/meta/file_io.c
+++ b/src/oem/meta/file_io.c
@@ -140,3 +140,75 @@
 
 	return pldm_msgbuf_destroy_consumed(buf);
 }
+
+LIBPLDM_ABI_TESTING
+void *pldm_oem_meta_file_io_read_resp_data(
+	struct pldm_oem_meta_file_io_read_resp *resp)
+{
+	return resp->data;
+}
+
+LIBPLDM_ABI_TESTING
+int encode_oem_meta_file_io_read_resp(
+	uint8_t instance_id, struct pldm_oem_meta_file_io_read_resp *resp,
+	size_t resp_len, struct pldm_msg *responseMsg, size_t payload_length)
+{
+	int rc;
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	struct pldm_header_info header = { 0 };
+
+	if (resp == NULL || responseMsg == NULL) {
+		return -EINVAL;
+	}
+
+	if (resp_len < sizeof(*resp)) {
+		return -EINVAL;
+	}
+
+	if (resp->version > sizeof(*resp)) {
+		return -E2BIG;
+	}
+
+	header.instance = instance_id;
+	header.msg_type = PLDM_RESPONSE;
+	header.pldm_type = PLDM_OEM;
+	header.command = PLDM_OEM_META_FILE_IO_CMD_READ_FILE;
+	rc = pack_pldm_header_errno(&header, &(responseMsg->hdr));
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_init_errno(buf,
+				    PLDM_OEM_META_FILE_IO_READ_RESP_MIN_SIZE,
+				    responseMsg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, resp->completion_code);
+	pldm_msgbuf_insert(buf, resp->handle);
+	pldm_msgbuf_insert(buf, resp->option);
+	pldm_msgbuf_insert(buf, resp->length);
+
+	switch (resp->option) {
+	case PLDM_OEM_META_FILE_IO_READ_ATTR:
+		pldm_msgbuf_insert(buf, resp->info.attr.size);
+		pldm_msgbuf_insert(buf, resp->info.attr.crc32);
+		break;
+	case PLDM_OEM_META_FILE_IO_READ_DATA:
+		pldm_msgbuf_insert(buf, resp->info.data.transferFlag);
+		pldm_msgbuf_insert(buf, resp->info.data.offset);
+		rc = pldm_msgbuf_insert_array_uint8(buf, resp->length,
+						    resp->data,
+						    resp_len - sizeof(*resp));
+		if (rc) {
+			return rc;
+		}
+		break;
+	default:
+		return -EPROTO;
+	}
+
+	return pldm_msgbuf_destroy(buf);
+}
diff --git a/tests/oem/meta/fileio.cpp b/tests/oem/meta/fileio.cpp
index a40e43b..de004a4 100644
--- a/tests/oem/meta/fileio.cpp
+++ b/tests/oem/meta/fileio.cpp
@@ -7,6 +7,7 @@
 
 #include "msgbuf.h"
 
+#include "gmock/gmock.h"
 #include <gtest/gtest.h>
 
 #ifdef LIBPLDM_API_TESTING
@@ -158,3 +159,108 @@
     EXPECT_EQ(rc, -EOVERFLOW);
 }
 #endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeOemMetaFileIoReadResp, testGoodEncodeReadAttrResponse)
+{
+    int rc;
+
+    alignas(pldm_oem_meta_file_io_read_resp) unsigned char
+        decodedBuf[sizeof(pldm_oem_meta_file_io_read_resp)];
+    auto* resp = new (decodedBuf) pldm_oem_meta_file_io_read_resp;
+    resp->version = sizeof(struct pldm_oem_meta_file_io_read_resp);
+    resp->completion_code = PLDM_SUCCESS;
+    resp->handle = 1;
+    resp->option = PLDM_OEM_META_FILE_IO_READ_ATTR;
+    resp->length = 0;
+    resp->info.attr.size = 0x1284;
+    resp->info.attr.crc32 = 0xab715432;
+
+    constexpr size_t payloadLen = PLDM_OEM_META_FILE_IO_READ_RESP_MIN_SIZE +
+                                  PLDM_OEM_META_FILE_IO_READ_ATTR_INFO_LENGTH;
+    alignas(pldm_msg) unsigned char
+        encodedBuf[sizeof(pldm_msg_hdr) + payloadLen] = {};
+    auto* msg = new (encodedBuf) pldm_msg;
+
+    rc = encode_oem_meta_file_io_read_resp(
+        0, resp, sizeof(pldm_oem_meta_file_io_read_resp), msg, payloadLen);
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_THAT(encodedBuf, testing::ElementsAreArray(
+                                {0x00, 0x3f, 0x03, 0x00, 0x01, 0x00, 0x00, 0x84,
+                                 0x12, 0x32, 0x54, 0x71, 0xab}));
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeOemMetaFileIoReadResp, testGoodEncodeReadDataResponse)
+{
+    constexpr static const uint8_t readbuf[4] = {0x23, 0xca, 0x84, 0x9d};
+    int rc;
+
+    alignas(pldm_oem_meta_file_io_read_resp) unsigned char
+        decodedBuf[sizeof(pldm_oem_meta_file_io_read_resp) + sizeof(readbuf)];
+    auto* resp = new (decodedBuf) pldm_oem_meta_file_io_read_resp;
+    resp->version = sizeof(struct pldm_oem_meta_file_io_read_resp);
+    resp->completion_code = PLDM_SUCCESS;
+    resp->handle = 1;
+    resp->option = PLDM_OEM_META_FILE_IO_READ_DATA;
+    resp->length = 4;
+    resp->info.data.transferFlag = 0x05;
+    resp->info.data.offset = 0x75cd;
+    memcpy(pldm_oem_meta_file_io_read_resp_data(resp), readbuf,
+           sizeof(readbuf));
+
+    constexpr size_t payloadLen = PLDM_OEM_META_FILE_IO_READ_RESP_MIN_SIZE +
+                                  PLDM_OEM_META_FILE_IO_READ_DATA_INFO_LENGTH +
+                                  sizeof(readbuf);
+    alignas(pldm_msg) unsigned char
+        encodedBuf[sizeof(pldm_msg_hdr) + payloadLen] = {};
+    auto* msg = new (encodedBuf) pldm_msg;
+
+    rc = encode_oem_meta_file_io_read_resp(
+        0, resp, sizeof(pldm_oem_meta_file_io_read_resp) + sizeof(readbuf), msg,
+        payloadLen);
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_THAT(encodedBuf, testing::ElementsAreArray(
+                                {0x00, 0x3f, 0x03, 0x00, 0x01, 0x01, 0x04, 0x05,
+                                 0xcd, 0x75, 0x23, 0xca, 0x84, 0x9d}));
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeOemMetaFileIoReadResp, testInvalidFieldsEncodeResponse)
+{
+    struct pldm_msg msg = {};
+
+    auto rc = encode_oem_meta_file_io_read_resp(
+        0, NULL, 0, &msg, PLDM_OEM_META_FILE_IO_READ_RESP_MIN_SIZE);
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeOemMetaFileIoReadResp, testInvalidLengthEncodeResponse)
+{
+    struct pldm_oem_meta_file_io_read_resp resp = {};
+    struct pldm_msg msg = {};
+
+    auto rc =
+        encode_oem_meta_file_io_read_resp(0, &resp, sizeof(resp), &msg, 0);
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(EncodeOemMetaFileIoReadResp, testInvalidDataEncodeResponse)
+{
+    struct pldm_oem_meta_file_io_read_resp resp = {};
+    struct pldm_msg msg = {};
+
+    auto rc = encode_oem_meta_file_io_read_resp(
+        0, &resp, sizeof(resp), &msg,
+        PLDM_OEM_META_FILE_IO_READ_RESP_MIN_SIZE - 1);
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif