core: Support transport control commands

This change introduces a control message request handler for MCTP
bindings. If a handler is provided, transport control messages will be
forwarded to the handler, otherwise they will be forwarded to the
default handler.

Change-Id: I62266d6bf2d512ec97759c0b8a3477c5e433d609
Signed-off-by: Wiktor Gołgowski <wiktor.golgowski@linux.intel.com>
[AJ: Split out general control message handler, formatting]
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
diff --git a/tests/test_cmds.c b/tests/test_cmds.c
new file mode 100644
index 0000000..7a05c35
--- /dev/null
+++ b/tests/test_cmds.c
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
+
+#include "test-utils.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <libmctp.h>
+#include <libmctp-alloc.h>
+#include <libmctp-cmds.h>
+
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+static const mctp_eid_t eid_1 = 9;
+static const mctp_eid_t eid_2 = 10;
+
+struct msg_payload {
+	struct mctp_hdr hdr;
+	struct mctp_ctrl_msg_hdr ctrl_hdr;
+};
+
+struct callback_data {
+	uint8_t invoked;
+	union {
+		uint8_t command_code;
+		uint8_t completion_code;
+	};
+};
+
+static void control_message_transport_callback(mctp_eid_t src, void *data,
+					       void *buf, size_t len)
+{
+	struct callback_data *ctx = data;
+	struct mctp_ctrl_msg_hdr *msg_hdr = buf;
+	printf("Transport control message received - command code: 0x%X\n",
+	       msg_hdr->command_code);
+	ctx->invoked++;
+	assert(msg_hdr->command_code == ctx->command_code);
+}
+
+static void rcv_ctrl_msg(struct mctp_binding *b, const void *buf, size_t len)
+{
+	struct mctp_pktbuf *pkt = mctp_pktbuf_alloc(b, len);
+	memcpy(mctp_pktbuf_hdr(pkt), buf, len);
+	mctp_bus_rx(b, pkt);
+}
+
+static void setup_test_binding(struct mctp_binding *test_binding,
+			       struct mctp *test_endpoint, void *callback_ctx)
+{
+	assert(test_binding != NULL);
+	assert(test_endpoint != NULL);
+	assert(callback_ctx != NULL);
+
+	memset(test_binding, 0, sizeof(*test_binding));
+	test_binding->name = "test";
+	test_binding->version = 1;
+	test_binding->tx = NULL;
+	test_binding->pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
+	test_binding->pkt_pad = 0;
+	test_binding->control_rx = control_message_transport_callback;
+	test_binding->control_rx_data = callback_ctx;
+
+	mctp_register_bus(test_endpoint, test_binding, eid_1);
+	mctp_binding_set_tx_enabled(test_binding, true);
+}
+
+static void send_transport_control_message(void)
+{
+	struct mctp *endpoint = mctp_init();
+	struct mctp_binding binding;
+	struct callback_data ctx;
+	static const struct msg_payload send_control_message_payload = {
+		.hdr = {
+			.dest = eid_1,
+			.src = eid_2,
+			.flags_seq_tag = MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM,
+		},
+		.ctrl_hdr = {
+			.ic_msg_type = MCTP_CTRL_HDR_MSG_TYPE,
+			.rq_dgram_inst = MCTP_CTRL_HDR_FLAG_REQUEST,
+			.command_code = 0xF2,
+		},
+	};
+
+	memset(&ctx, 0, sizeof(ctx));
+	setup_test_binding(&binding, endpoint, &ctx);
+	ctx.command_code = send_control_message_payload.ctrl_hdr.command_code;
+	printf("Sending transport control message: 0x%X\n",
+	       send_control_message_payload.ctrl_hdr.command_code);
+	rcv_ctrl_msg(&binding, (void *)&send_control_message_payload,
+		     sizeof(send_control_message_payload));
+	assert(ctx.invoked == 1);
+
+	mctp_destroy(endpoint);
+}
+
+int main(int argc, char *argv[])
+{
+	send_transport_control_message();
+
+	return EXIT_SUCCESS;
+}