base: Provide pldm_msg_hdr_correlate_response()

Users of the asynchronous pldm_transport_send_msg() and
pldm_transport_recv_msg() APIs likely need some way to correlate a
received message as a response to a sent request.

Change-Id: I232400aebf06845e3eabf24205e31aa5668de9ba
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 484009e..408645a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@
 ### Added
 
 1. state-set: Add new enum for Operational Fault Status enum
+2. base: Provide pldm_msg_hdr_correlate_response()
 
 ### Changed
 
diff --git a/include/libpldm/base.h b/include/libpldm/base.h
index c3d1ac9..9264d29 100644
--- a/include/libpldm/base.h
+++ b/include/libpldm/base.h
@@ -6,6 +6,7 @@
 #endif
 
 #include <asm/byteorder.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -152,6 +153,22 @@
 	uint8_t payload[1]; //!< &payload[0] is the beginning of the payload
 } __attribute__((packed));
 
+/**
+ * @brief Compare the headers from two PLDM messages to determine if the latter
+ * is a message representing a response to the former, where the former must be
+ * a request.
+ *
+ * @param[in] req - A pointer to a PLDM header object, which must represent a
+ *                  request
+ * @param[in] resp - A pointer to a PLDM header object, which may represent a
+ *		     response to the provided request.
+ *
+ * @return true if the header pointed to by resp represents a message that is a
+ *	   response to the header pointed to by req, otherwise false.
+ */
+bool pldm_msg_hdr_correlate_response(const struct pldm_msg_hdr *req,
+				     const struct pldm_msg_hdr *resp);
+
 /** @struct pldm_header_info
  *
  *  The information needed to prepare PLDM header and this is passed to the
diff --git a/src/base.c b/src/base.c
index 02dca1a..39dcd65 100644
--- a/src/base.c
+++ b/src/base.c
@@ -65,6 +65,15 @@
 	return PLDM_SUCCESS;
 }
 
+LIBPLDM_ABI_TESTING
+bool pldm_msg_hdr_correlate_response(const struct pldm_msg_hdr *req,
+				     const struct pldm_msg_hdr *resp)
+{
+	return req->instance_id == resp->instance_id && req->request &&
+	       !resp->request && req->type == resp->type &&
+	       req->command == resp->command;
+}
+
 LIBPLDM_ABI_STABLE
 int encode_get_types_req(uint8_t instance_id, struct pldm_msg *msg)
 {
diff --git a/tests/libpldm_base_test.cpp b/tests/libpldm_base_test.cpp
index 7d67009..20289fc 100644
--- a/tests/libpldm_base_test.cpp
+++ b/tests/libpldm_base_test.cpp
@@ -792,3 +792,159 @@
     rc = encode_set_tid_req(0, 0xff, request);
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
 }
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PldmMsgHdr, correlateSuccess)
+{
+    static const struct pldm_msg_hdr req = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 1,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+    static const struct pldm_msg_hdr resp = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 0,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+
+    ASSERT_EQ(pldm_msg_hdr_correlate_response(&req, &resp), true);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PldmMsgHdr, correlateFailInstanceID)
+{
+    static const struct pldm_msg_hdr req = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 1,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+    static const struct pldm_msg_hdr resp = {
+        .instance_id = 1,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 0,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+
+    ASSERT_EQ(pldm_msg_hdr_correlate_response(&req, &resp), false);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PldmMsgHdr, correlateFailRequest)
+{
+    static const struct pldm_msg_hdr req = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 1,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+    static const struct pldm_msg_hdr resp = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 1,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+
+    ASSERT_EQ(pldm_msg_hdr_correlate_response(&req, &resp), false);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PldmMsgHdr, correlateFailType)
+{
+    static const struct pldm_msg_hdr req = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 1,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+    static const struct pldm_msg_hdr resp = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 0,
+        .type = 1,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+
+    ASSERT_EQ(pldm_msg_hdr_correlate_response(&req, &resp), false);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PldmMsgHdr, correlateFailCommand)
+{
+    static const struct pldm_msg_hdr req = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 1,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+    static const struct pldm_msg_hdr resp = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 0,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x02,
+    };
+
+    ASSERT_EQ(pldm_msg_hdr_correlate_response(&req, &resp), false);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PldmMsgHdr, correlateFailRequestIsResponse)
+{
+    static const struct pldm_msg_hdr req = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 0,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x01,
+    };
+    static const struct pldm_msg_hdr resp = {
+        .instance_id = 0,
+        .reserved = 0,
+        .datagram = 0,
+        .request = 0,
+        .type = 0,
+        .header_ver = 1,
+        .command = 0x02,
+    };
+
+    ASSERT_EQ(pldm_msg_hdr_correlate_response(&req, &resp), false);
+}
+#endif