oem: meta: Add decode_oem_meta_file_io_read_req()

Add decode_oem_meta_file_io_read_req function.

This function is used to decode the message of reading file IO.
Need to include read_option (read file attributes or data), length
(the length of the read data) and read_info (The information
needed to read the file).
Take reading file data as an example:
read_info needs to contain transferFlag, offset to know the
starting position and transfer progress of the read file.

Change-Id: I425476d36e3cd69d2da45beceff1c4a2362640dc
Signed-off-by: Lora Lin <lora.lin.wiwynn@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae80421..95ff5db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
 ### Added
 
 1. oem: meta: Add decode_oem_meta_file_io_write_req()
+2. oem: meta: Add decode_oem_meta_file_io_read_req()
 
 ### Deprecated
 
diff --git a/docs/oem/meta/file-io.md b/docs/oem/meta/file-io.md
new file mode 100644
index 0000000..5bc2f4f
--- /dev/null
+++ b/docs/oem/meta/file-io.md
@@ -0,0 +1,54 @@
+# Description of file IO messages
+
+## Read Message Format
+
+### Table 1. Read Request
+
+| Offset   | Type   | Name     | Description                                                                                                                                                  |
+| -------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| 0        | uint8  | Handle   | This field is a handle that is used to identify PLDM command type.                                                                                           |
+| 1        | enum8  | Option   | This field is a read option is used to identify the read file option. <br> See Table 3 for the option.                                                       |
+| 2:3      | uint16 | Length   | The length in bytes N of data being sent in this part in the ReadInfo field.                                                                                 |
+| Variable | uint8  | ReadInfo | Portion of reading information. <br> There will be different reading information according to different ReadOption. <br> See Table 4 and Table 5 for details |
+
+### Table 2. Read response
+
+| Offset   | Type  | Name           | Description                                                                                                                                            |
+| -------- | ----- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| 0        | int   | CompletionCode | value: { PLDM_SUCCESS, PLDM_ERROR_INVALID_DATA, PLDM_ERROR_INVALID_LENGTH }                                                                            |
+| Variable | uint8 | ReadResponse   | Portion of reading response. <br> There will be different reading response according to different ReadOption. <br> See Table 6 and Table 7 for details |
+
+### Table 3. Option of message type
+
+| Value | Name              | Description                |
+| ----- | ----------------- | -------------------------- |
+| 0x00  | ReadFileAttribute | Get file size and checksum |
+| 0x01  | ReadFileData      | Get file data              |
+
+### Table 4. ReadInfo Definition when ReadOption is ReadFileAttribute in message type
+
+| Offset | Type | Name | Description     |
+| ------ | ---- | ---- | --------------- |
+| 0      | -    | -    | No request data |
+
+### Table 5. ReadInfo Definition when ReadOption is ReadFileData in message type
+
+| Offset | Type   | Name         | Description                                                                                                                                                    |
+| ------ | ------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 0      | uint8  | TransferFlag | The transfer flag that indiates what part of the transfer this request represents. <br> Possible values: {Start=0x01, Middle=0x02, End=0x04, StartAndEnd=0x05} |
+| 1:2    | uint16 | Offset       | Offset in read file.                                                                                                                                           |
+
+### Table 6. ReadResponse Definition when ReadOption is ReadFileAttribute in message type
+
+| Offset | Type   | Name     | Description                                                 |
+| ------ | ------ | -------- | ----------------------------------------------------------- |
+| 0:1    | uint16 | Size     | This field indicates the size of the entire file, in bytes. |
+| 2:5    | uint32 | Checksum | This field indicates the checksum of the entire file.       |
+
+### Table 7. ReadResponse Definition when ReadOption is ReadFileData in message type
+
+| Offset   | Type   | Name         | Description                                                                                                                                                     |
+| -------- | ------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 0        | uint8  | TransferFlag | The transfer flag that indiates what part of the transfer this response represents. <br> Possible values: {Start=0x01, Middle=0x02, End=0x04, StartAndEnd=0x05} |
+| 1:2      | uint16 | Offset       | Offset in read file.                                                                                                                                            |
+| Variable | uint8  | FileData     | File data can be up to 255 bytes.                                                                                                                               |
diff --git a/include/libpldm/oem/meta/file_io.h b/include/libpldm/oem/meta/file_io.h
index e30ab47..47077f1 100644
--- a/include/libpldm/oem/meta/file_io.h
+++ b/include/libpldm/oem/meta/file_io.h
@@ -20,6 +20,15 @@
 	PLDM_OEM_META_FILE_IO_CMD_READ_FILE = 0x3,
 };
 
+/** @brief read options in read file io command
+ */
+enum pldm_oem_meta_file_io_read_option {
+	// Read file attribute
+	PLDM_OEM_META_FILE_IO_READ_ATTR = 0x00,
+	// Read file data
+	PLDM_OEM_META_FILE_IO_READ_DATA = 0x01,
+};
+
 struct pldm_oem_meta_file_io_write_req {
 	uint8_t handle;
 	uint32_t length;
@@ -29,6 +38,41 @@
 };
 #define PLDM_OEM_META_FILE_IO_WRITE_REQ_MIN_LENGTH 5u
 
+/** @struct pldm_oem_meta_file_io_read_data_info
+ *
+ *  Structure representing PLDM read file data info
+ */
+struct pldm_oem_meta_file_io_read_data_info {
+	uint8_t transferFlag;
+	uint16_t offset;
+};
+#define PLDM_OEM_META_FILE_IO_READ_DATA_INFO_LENGTH 3u
+
+/** @struct pldm_oem_meta_file_io_read_attr_info
+ *
+ *  Structure representing PLDM read file attribute info
+ */
+struct pldm_oem_meta_file_io_read_attr_info {
+	uint16_t size;
+	uint32_t crc32;
+};
+#define PLDM_OEM_META_FILE_IO_READ_ATTR_INFO_LENGTH 6u
+
+/** @struct pldm_oem_meta_file_io_read_req
+ *
+ *  Structure representing PLDM read file request
+ */
+struct pldm_oem_meta_file_io_read_req {
+	size_t version;
+	uint8_t handle;
+	uint8_t option;
+	uint8_t length;
+	union {
+		struct pldm_oem_meta_file_io_read_data_info data;
+	} info;
+};
+#define PLDM_OEM_META_FILE_IO_READ_REQ_MIN_LENGTH 3u
+
 /** @brief Obtain the pointer to the data array of a write request
  *
  * @param[in] req - The pointer to the write request struct
@@ -63,6 +107,17 @@
 				size_t payload_length, uint8_t *file_handle,
 				uint32_t *length, uint8_t *data);
 
+/** @brief Decode OEM meta read file io req
+ *
+ *  @param[in] msg - Pointer to PLDM request message
+ *  @param[in] payload_length - Length of request payload
+ *  @param[out] req - Pointer to the structure to store the decoded response data
+ *  @return 0 on success, negative errno value on failure
+ */
+int decode_oem_meta_file_io_read_req(const struct pldm_msg *msg,
+				     size_t payload_length,
+				     struct pldm_oem_meta_file_io_read_req *req);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/oem/meta/file_io.c b/src/oem/meta/file_io.c
index ce7d9e3..0c82f82 100644
--- a/src/oem/meta/file_io.c
+++ b/src/oem/meta/file_io.c
@@ -89,3 +89,54 @@
 
 	return 0;
 }
+
+LIBPLDM_ABI_TESTING
+int decode_oem_meta_file_io_read_req(const struct pldm_msg *msg,
+				     size_t payload_length,
+				     struct pldm_oem_meta_file_io_read_req *req)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+
+	if (msg == NULL || req == NULL) {
+		return -EINVAL;
+	}
+
+	if (req->version > sizeof(struct pldm_oem_meta_file_io_read_req)) {
+		return -E2BIG;
+	}
+
+	int rc = pldm_msgbuf_init_errno(
+		buf, PLDM_OEM_META_FILE_IO_READ_REQ_MIN_LENGTH, msg->payload,
+		payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, req->handle);
+	rc = pldm_msgbuf_extract(buf, req->option);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_extract(buf, req->length);
+	if (rc) {
+		return rc;
+	}
+
+	switch (req->option) {
+	case PLDM_OEM_META_FILE_IO_READ_ATTR:
+		if (req->length != 0) {
+			return -EPROTO;
+		}
+		break;
+	case PLDM_OEM_META_FILE_IO_READ_DATA:
+		pldm_msgbuf_extract(buf, req->info.data.transferFlag);
+		pldm_msgbuf_extract(buf, req->info.data.offset);
+		break;
+	default:
+		return -EPROTO;
+	}
+
+	return pldm_msgbuf_destroy_consumed(buf);
+}
diff --git a/tests/oem/meta/fileio.cpp b/tests/oem/meta/fileio.cpp
index 48c0778..a40e43b 100644
--- a/tests/oem/meta/fileio.cpp
+++ b/tests/oem/meta/fileio.cpp
@@ -87,3 +87,74 @@
     EXPECT_EQ(rc, -EOVERFLOW);
 }
 #endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeOemMetaFileIoReadReq, testGoodDecodeRequest)
+{
+    struct pldm_msgbuf _ctx;
+    struct pldm_msgbuf* ctx = &_ctx;
+    int rc;
+
+    constexpr size_t payloadLen = PLDM_OEM_META_FILE_IO_READ_REQ_MIN_LENGTH +
+                                  PLDM_OEM_META_FILE_IO_READ_DATA_INFO_LENGTH;
+    alignas(pldm_msg) unsigned char buf[sizeof(pldm_msg_hdr) + payloadLen]{};
+    auto* msg = new (buf) pldm_msg;
+
+    rc = pldm_msgbuf_init_errno(ctx, 0, msg->payload, payloadLen);
+    ASSERT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint8(ctx, 0);
+    pldm_msgbuf_insert_uint8(ctx, PLDM_OEM_META_FILE_IO_READ_DATA);
+    pldm_msgbuf_insert_uint8(ctx, PLDM_OEM_META_FILE_IO_READ_DATA_INFO_LENGTH);
+    pldm_msgbuf_insert_uint8(ctx, 1);
+    pldm_msgbuf_insert_uint16(ctx, 1223);
+
+    rc = pldm_msgbuf_destroy_consumed(ctx);
+    ASSERT_EQ(rc, 0);
+
+    struct pldm_oem_meta_file_io_read_req req = {};
+    req.version = sizeof(req);
+    rc = decode_oem_meta_file_io_read_req(msg, payloadLen, &req);
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_EQ(req.handle, 0);
+    EXPECT_EQ(req.option, PLDM_OEM_META_FILE_IO_READ_DATA);
+    EXPECT_EQ(req.length, PLDM_OEM_META_FILE_IO_READ_DATA_INFO_LENGTH);
+    EXPECT_EQ(req.info.data.transferFlag, 1);
+    EXPECT_EQ(req.info.data.offset, 1223);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeOemMetaFileIoReadReq, testInvalidFieldsDecodeRequest)
+{
+    struct pldm_msg msg = {};
+
+    auto rc = decode_oem_meta_file_io_read_req(
+        &msg, PLDM_OEM_META_FILE_IO_READ_REQ_MIN_LENGTH, NULL);
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeOemMetaFileIoReadReq, testInvalidLengthDecodeRequest)
+{
+    struct pldm_oem_meta_file_io_read_req req = {};
+    struct pldm_msg msg = {};
+
+    auto rc = decode_oem_meta_file_io_read_req(&msg, 0, &req);
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodeOemMetaFileIoReadReq, testInvalidDataRequest)
+{
+    struct pldm_oem_meta_file_io_read_req req = {};
+    struct pldm_msg msg = {};
+
+    auto rc = decode_oem_meta_file_io_read_req(
+        &msg, PLDM_OEM_META_FILE_IO_READ_REQ_MIN_LENGTH - 1, &req);
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif