core: Add mctp_message_tx_request() function

This allocates a tag for messages sent with TO bit set.

Change-Id: Ia8403a06aa449e0218a30edf5ad69781c70d7c52
Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
diff --git a/tests/test_core.c b/tests/test_core.c
index 2f5d2c7..9c17100 100644
--- a/tests/test_core.c
+++ b/tests/test_core.c
@@ -1,5 +1,4 @@
 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
-
 #define _GNU_SOURCE
 
 #ifdef NDEBUG
@@ -18,6 +17,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include "compiler.h"
 #include "libmctp-alloc.h"
@@ -602,6 +602,76 @@
 	mctp_destroy(mctp);
 }
 
+/*
+ * This test case tests tag allocation. 8 tags
+ * are allowed to be pending.
+ */
+static void mctp_core_test_tx_alloc_tag()
+{
+	struct mctp *mctp = NULL;
+	struct mctp_binding_test *binding = NULL;
+	struct test_params test_param;
+	uint8_t msg_tag;
+	void *msg;
+	int rc;
+	mctp_eid_t dest_eid1 = 30;
+	size_t msg_len = 10;
+
+	mctp_test_stack_init(&mctp, &binding, dest_eid1);
+	mctp_set_rx_all(mctp, rx_message, &test_param);
+
+	uint8_t used = 0;
+	for (int i = 0; i < 8; i++) {
+		test_param.seen = false;
+		test_param.msg_tag = 0xff;
+		test_param.tag_owner = false;
+
+		msg = __mctp_alloc(msg_len);
+		memset(msg, 0x99, msg_len);
+		rc = mctp_message_tx_request(mctp, dest_eid1, msg, msg_len,
+					     &msg_tag);
+		assert(rc == 0);
+		assert(test_param.seen == true);
+		assert(test_param.msg_tag == msg_tag);
+		assert(test_param.tag_owner == true);
+		used |= (1 << msg_tag);
+	}
+	assert(used == 0xff);
+
+	/* Ran out of tags */
+	test_param.seen = false;
+	msg = __mctp_alloc(msg_len);
+	memset(msg, 0x99, msg_len);
+	rc = mctp_message_tx_request(mctp, dest_eid1, msg, msg_len, &msg_tag);
+	assert(rc == -EBUSY);
+	assert(test_param.seen == false);
+
+	/* Send/Receive a response to one of those tags */
+	test_param.seen = false;
+	msg = __mctp_alloc(msg_len);
+	memset(msg, 0x99, msg_len);
+	/* Arbitrary one */
+	uint8_t replied_tag = 3;
+	rc = mctp_message_tx_alloced(mctp, dest_eid1, false, replied_tag, msg,
+				     msg_len);
+	assert(rc == 0);
+	assert(test_param.seen == true);
+	assert(test_param.msg_tag == replied_tag);
+	assert(test_param.tag_owner == false);
+
+	/* Now sending allocates that tag again, since it is the only spare one */
+	test_param.seen = false;
+	msg = __mctp_alloc(msg_len);
+	memset(msg, 0x99, msg_len);
+	rc = mctp_message_tx_request(mctp, dest_eid1, msg, msg_len, &msg_tag);
+	assert(rc == 0);
+	assert(test_param.seen == true);
+	assert(msg_tag == replied_tag);
+
+	mctp_binding_test_destroy(binding);
+	mctp_destroy(mctp);
+}
+
 /* clang-format off */
 #define TEST_CASE(test) { #test, test }
 static const struct {
@@ -620,6 +690,7 @@
 	TEST_CASE(mctp_core_test_rx_with_tag_multifragment),
 	TEST_CASE(mctp_core_test_rx_with_null_dst_eid),
 	TEST_CASE(mctp_core_test_rx_with_broadcast_dst_eid),
+	TEST_CASE(mctp_core_test_tx_alloc_tag),
 };
 /* clang-format on */