oem: meta: Add decode_oem_meta_file_io_req()

Support decode_oem_meta_file_io_req() cmd to decode the incoming post
code.

PLDM OEM Meta Write File IO cmd:
Example:
  Request:
    Byte 0: 0x3F (OEM cmd)
    Byte 1: 0x02 (FILE IO)
    Byte 2: 0x00 (File io type : POST CODE)
    Byte 3-6: 0x04 (Data length)
    Byte 7-10: 0x93 0xE0 0x00 0xEA (post code)

  Response:
    Byte 0: 0x00 (success)

Tested:
- Unit Tests passed.

Change-Id: I85437698642dd3cbe6084acf1feada842d206eac
Signed-off-by: Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 636e20a..e06258c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
 2. transport: af-mctp: Add pldm_transport_af_mctp_bind()
 3. oem: ibm: Add chapdata file type support
 4. base: Added PLDM_SMBIOS & PLDM_RDE message types
+5. oem: meta: Add decode_oem_meta_file_io_req()
 
 ### Changed
 
diff --git a/include/libpldm/meson.build b/include/libpldm/meson.build
index f1988cd..235a7a6 100644
--- a/include/libpldm/meson.build
+++ b/include/libpldm/meson.build
@@ -36,4 +36,11 @@
   install_symlink('state_set_oem_ibm.h', pointing_to: 'oem/ibm/state_set.h', install_dir: 'include/libpldm')
 endif
 
+if get_option('oem-meta').allowed()
+  libpldm_headers += files(
+    'oem/meta/file_io.h',
+  )
+endif
+
+
 install_headers(libpldm_headers, subdir: 'libpldm', preserve_path: true)
diff --git a/include/libpldm/oem/meta/file_io.h b/include/libpldm/oem/meta/file_io.h
new file mode 100644
index 0000000..32a7109
--- /dev/null
+++ b/include/libpldm/oem/meta/file_io.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
+#ifndef LIBPLDM_OEM_META_FILE_IO_H
+#define LIBPLDM_OEM_META_FILE_IO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct pldm_msg;
+/** @brief PLDM Commands in OEM META type
+ */
+
+enum pldm_oem_meta_fileio_commands {
+	PLDM_OEM_META_FILEIO_CMD_WRITE_FILE = 0x2,
+	PLDM_OEM_META_FILEIO_CMD_READ_FILE = 0x3,
+};
+
+struct pldm_oem_meta_write_file_req {
+	uint8_t file_handle;
+	uint32_t length;
+	uint8_t file_data[1];
+};
+
+/** @brief Decode OEM meta file io req
+ *
+ *  @param[in] msg - Pointer to PLDM request message
+ *  @param[in] payload_length - Length of request payload
+ *  @param[out] file_handle - The handle of data
+ *  @param[out] length - Total size of data
+ *  @param[out] data - Message will be written to this
+ *  @return pldm_completion_codes
+ */
+int decode_oem_meta_file_io_req(const struct pldm_msg *msg,
+				size_t payload_length, uint8_t *file_handle,
+				uint32_t *length, uint8_t *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*LIBPLDM_OEM_META_FILE_IO_H*/
diff --git a/meson.options b/meson.options
index 7f39b00..26a7544 100644
--- a/meson.options
+++ b/meson.options
@@ -2,3 +2,4 @@
 option('tests', type: 'feature', description: 'Build tests')
 option('oem-ibm', type: 'feature', description: 'Enable IBM OEM PLDM')
 option('abi-compliance-check', type: 'feature', description: 'Detect public ABI/API changes')
+option('oem-meta', type: 'feature', description: 'Enable Meta OEM PLDM')
\ No newline at end of file
diff --git a/src/meson.build b/src/meson.build
index 99e79a2..9f4487c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -17,6 +17,10 @@
    subdir('oem/ibm')
 endif
 
+if get_option('oem-meta').allowed()
+   subdir('oem/meta')
+endif
+
 libpldm = library(
   'pldm',
    libpldm_sources,
diff --git a/src/oem/meta/file_io.c b/src/oem/meta/file_io.c
new file mode 100644
index 0000000..6662e8f
--- /dev/null
+++ b/src/oem/meta/file_io.c
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
+#include <libpldm/oem/meta/file_io.h>
+#include <endian.h>
+#include <string.h>
+#include <stdio.h>
+#include "msgbuf.h"
+
+#define PLDM_OEM_META_DECODE_WRITE_FILE_IO_MIN_SIZE 6
+LIBPLDM_ABI_TESTING
+int decode_oem_meta_file_io_req(const struct pldm_msg *msg,
+				size_t payload_length, uint8_t *file_handle,
+				uint32_t *length, uint8_t *data)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+
+	if (msg == NULL || file_handle == NULL || length == NULL ||
+	    data == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	int rc = pldm_msgbuf_init(buf,
+				  PLDM_OEM_META_DECODE_WRITE_FILE_IO_MIN_SIZE,
+				  msg->payload, payload_length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, file_handle);
+	pldm_msgbuf_extract(buf, length);
+	pldm_msgbuf_extract_array_uint8(buf, data, *length);
+
+	return pldm_msgbuf_destroy_consumed(buf);
+}
diff --git a/src/oem/meta/meson.build b/src/oem/meta/meson.build
new file mode 100644
index 0000000..6c89286
--- /dev/null
+++ b/src/oem/meta/meson.build
@@ -0,0 +1,3 @@
+libpldm_sources += files(
+    'file_io.c'
+  )
diff --git a/tests/meson.build b/tests/meson.build
index 76ca6ee..dba7870 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -45,6 +45,12 @@
   ]
 endif
 
+if get_option('oem-meta').allowed()
+  tests += [
+    'oem/meta/libpldm_fileio_test',
+  ]
+endif
+
 test_include_dirs = [ libpldm_include_dir, include_directories('../src') ]
 
 foreach t : tests
diff --git a/tests/oem/meta/libpldm_fileio_test.cpp b/tests/oem/meta/libpldm_fileio_test.cpp
new file mode 100644
index 0000000..663ed65
--- /dev/null
+++ b/tests/oem/meta/libpldm_fileio_test.cpp
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
+#include <endian.h>
+#include <libpldm/oem/meta/file_io.h>
+
+#include "msgbuf.h"
+
+#include <gtest/gtest.h>
+
+static constexpr size_t oemMetaDecodeWriteFileIoReqBytes = 9;
+static constexpr size_t postCodeSize = 4;
+static constexpr size_t invalidDataSize = 0;
+
+TEST(DecodeOemMetaFileIoReq, testGoodDecodeRequest)
+{
+    struct pldm_msgbuf _ctx;
+    struct pldm_msgbuf* ctx = &_ctx;
+    uint8_t fileHandle = 0x00;
+    int32_t dataLengthLE = 0x04;
+    uint8_t postCode[4] = {0x93, 0xE0, 0x00, 0xEA};
+
+    constexpr auto hdrSize = sizeof(pldm_msg_hdr);
+
+    uint8_t buf[hdrSize + sizeof(uint8_t) + sizeof(int32_t) +
+                (postCodeSize * sizeof(uint8_t))] = {};
+
+    pldm_msgbuf_init(ctx, 0, &buf[hdrSize], sizeof(buf) - hdrSize);
+
+    pldm_msgbuf_insert_uint8(ctx, fileHandle);
+    pldm_msgbuf_insert_int32(ctx, dataLengthLE);
+    pldm_msgbuf_insert_array_uint8(ctx, postCode, sizeof(postCode));
+
+    std::array<uint8_t, oemMetaDecodeWriteFileIoReqBytes> retDataField{};
+
+    uint8_t retfileHandle = 0;
+    uint32_t retFileDataCnt = 0;
+
+    auto request = reinterpret_cast<pldm_msg*>(buf);
+
+    auto rc = decode_oem_meta_file_io_req(request, sizeof(buf) - hdrSize,
+                                          &retfileHandle, &retFileDataCnt,
+                                          retDataField.data());
+
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_EQ(retfileHandle, fileHandle);
+    EXPECT_EQ(retFileDataCnt, dataLengthLE);
+    EXPECT_EQ(retDataField[0], postCode[0]);
+    EXPECT_EQ(retDataField[1], postCode[1]);
+    EXPECT_EQ(retDataField[2], postCode[2]);
+    EXPECT_EQ(retDataField[3], postCode[3]);
+}
+
+TEST(DecodeOemMetaFileIoReq, testInvalidFieldsDecodeRequest)
+{
+    std::array<uint8_t, oemMetaDecodeWriteFileIoReqBytes> requestMsg{};
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = decode_oem_meta_file_io_req(request, requestMsg.size(), NULL,
+                                          NULL, NULL);
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(DecodeOemMetaFileIoReq, testInvalidLengthDecodeRequest)
+{
+    uint8_t retfileHandle = 0;
+    uint32_t retFileDataCnt = 0;
+    uint8_t buf[1] = {};
+    std::array<uint8_t, oemMetaDecodeWriteFileIoReqBytes> retDataField{};
+
+    auto request = reinterpret_cast<pldm_msg*>(buf);
+
+    auto rc = decode_oem_meta_file_io_req(request, 0, &retfileHandle,
+                                          &retFileDataCnt, retDataField.data());
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+
+TEST(DecodeOemMetaFileIoReq, testInvalidDataRequest)
+{
+    uint8_t buf[1] = {};
+    uint8_t retfileHandle = 0;
+    uint32_t retFileDataCnt = 0;
+
+    std::array<uint8_t, invalidDataSize> retDataField{};
+
+    auto request = reinterpret_cast<pldm_msg*>(buf);
+
+    auto rc = decode_oem_meta_file_io_req(request, sizeof(buf), &retfileHandle,
+                                          &retFileDataCnt, retDataField.data());
+
+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
\ No newline at end of file