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/CMakeLists.txt b/CMakeLists.txt
index a0cace7..3b0097d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,10 @@
target_link_libraries (test_serial mctp)
add_test (NAME serial COMMAND test_serial)
+add_executable (test_cmds tests/test_cmds.c tests/test-utils.c)
+target_link_libraries (test_cmds mctp)
+add_test (NAME control_commands COMMAND test_cmds)
+
install (TARGETS mctp DESTINATION lib)
install (FILES libmctp.h DESTINATION include)
diff --git a/Makefile.am b/Makefile.am
index c93d452..75535d4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,8 @@
lib_LTLIBRARIES = libmctp.la
libmctp_la_SOURCES = core.c alloc.c log.c \
- libmctp-alloc.h libmctp-log.h
+ libmctp-alloc.h libmctp-log.h \
+ libmctp-cmds.h
include_HEADERS = libmctp.h
if LIBMCTP_BINDING_serial
@@ -40,7 +41,7 @@
TESTS = $(check_PROGRAMS)
check_PROGRAMS = tests/test_eid tests/test_seq tests/test_bridge \
- tests/test_astlpc tests/test_serial
+ tests/test_astlpc tests/test_serial tests/test_cmds
# We set a global LDADD here, as there's no way to specify it for all
# tests. This means other targets' LDADDs need to be overridden.
LDADD = tests/libtest-utils.a libmctp.la
diff --git a/core.c b/core.c
index 34bd75a..d3f8184 100644
--- a/core.c
+++ b/core.c
@@ -14,6 +14,7 @@
#include "libmctp.h"
#include "libmctp-alloc.h"
#include "libmctp-log.h"
+#include "libmctp-cmds.h"
/* Internal data structures */
@@ -313,12 +314,85 @@
return 0;
}
-static void mctp_rx(struct mctp *mctp, struct mctp_bus *bus,
- mctp_eid_t src, mctp_eid_t dest, void *buf, size_t len)
+static inline bool mctp_ctrl_cmd_is_transport(struct mctp_ctrl_msg_hdr *hdr)
{
+ return ((hdr->command_code >= MCTP_CTRL_CMD_FIRST_TRANSPORT) &&
+ (hdr->command_code <= MCTP_CTRL_CMD_LAST_TRANSPORT));
+}
+
+static bool mctp_ctrl_handle_msg(struct mctp *mctp, struct mctp_bus *bus,
+ mctp_eid_t src, mctp_eid_t dest, void *buffer,
+ size_t length)
+{
+ struct mctp_ctrl_msg_hdr *msg_hdr = buffer;
+
+ /*
+ * Control message is received. If a transport control message handler
+ * is provided, it will called. If there is no dedicated handler, this
+ * function returns false and data can be handled by the generic
+ * message handler. The transport control message handler will be
+ * provided with messages in the command range 0xF0 - 0xFF.
+ */
+ if (mctp_ctrl_cmd_is_transport(msg_hdr)) {
+ if (bus->binding->control_rx != NULL) {
+ /* MCTP bus binding handler */
+ bus->binding->control_rx(src,
+ bus->binding->control_rx_data,
+ buffer, length);
+ return true;
+ }
+ }
+
+ /*
+ * Command was not handled, due to lack of specific callback.
+ * It will be passed to regular message_rx handler.
+ */
+ return false;
+}
+
+static inline bool mctp_rx_dest_is_local(struct mctp_bus *bus, mctp_eid_t dest)
+{
+ return dest == bus->eid || dest == MCTP_EID_NULL ||
+ dest == MCTP_EID_BROADCAST;
+}
+
+static inline bool mctp_ctrl_cmd_is_request(struct mctp_ctrl_msg_hdr *hdr)
+{
+ return hdr->ic_msg_type == MCTP_CTRL_HDR_MSG_TYPE &&
+ hdr->rq_dgram_inst & MCTP_CTRL_HDR_FLAG_REQUEST;
+}
+
+/*
+ * Receive the complete MCTP message and route it.
+ * Asserts:
+ * 'buf' is not NULL.
+ */
+static void mctp_rx(struct mctp *mctp, struct mctp_bus *bus, mctp_eid_t src,
+ mctp_eid_t dest, void *buf, size_t len)
+{
+ assert(buf != NULL);
+
if (mctp->route_policy == ROUTE_ENDPOINT &&
- dest == bus->eid && mctp->message_rx)
- mctp->message_rx(src, mctp->message_rx_data, buf, len);
+ mctp_rx_dest_is_local(bus, dest)) {
+ /* Handle MCTP Control Messages: */
+ if (len >= sizeof(struct mctp_ctrl_msg_hdr)) {
+ struct mctp_ctrl_msg_hdr *msg_hdr = buf;
+
+ /*
+ * Identify if this is a control request message.
+ * See DSP0236 v1.3.0 sec. 11.5.
+ */
+ if (mctp_ctrl_cmd_is_request(msg_hdr)) {
+ bool handled;
+ handled = mctp_ctrl_handle_msg(mctp, bus, src,
+ dest, buf, len);
+ if (handled)
+ return;
+ }
+ }
+ if (mctp->message_rx)
+ mctp->message_rx(src, mctp->message_rx_data, buf, len);
+ }
if (mctp->route_policy == ROUTE_BRIDGE) {
int i;
diff --git a/libmctp-cmds.h b/libmctp-cmds.h
new file mode 100644
index 0000000..b35fc08
--- /dev/null
+++ b/libmctp-cmds.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
+#ifndef _LIBMCTP_CMDS_H
+#define _LIBMCTP_CMDS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libmctp.h"
+
+/*
+ * Helper structs and functions for MCTP control messages.
+ * See DSP0236 v1.3.0 sec. 11 for reference.
+ */
+
+struct mctp_ctrl_msg_hdr {
+ uint8_t ic_msg_type;
+ uint8_t rq_dgram_inst;
+ uint8_t command_code;
+ uint8_t completion_code;
+};
+
+#define MCTP_CTRL_HDR_MSG_TYPE 0
+#define MCTP_CTRL_HDR_FLAG_REQUEST (1 << 7)
+#define MCTP_CTRL_HDR_FLAG_DGRAM (1 << 6)
+#define MCTP_CTRL_HDR_INSTANCE_ID_MASK 0x1F
+
+/*
+ * MCTP Control Command IDs
+ * See DSP0236 v1.3.0 Table 12.
+ */
+#define MCTP_CTRL_CMD_RESERVED 0x00
+#define MCTP_CTRL_CMD_SET_ENDPOINT_ID 0x01
+#define MCTP_CTRL_CMD_GET_ENDPOINT_ID 0x02
+#define MCTP_CTRL_CMD_GET_ENDPOINT_UUID 0x03
+#define MCTP_CTRL_CMD_GET_VERSION_SUPPORT 0x04
+#define MCTP_CTRL_CMD_GET_MESSAGE_TYPE_SUPPORT 0x05
+#define MCTP_CTRL_CMD_GET_VENDOR_MESSAGE_SUPPORT 0x06
+#define MCTP_CTRL_CMD_RESOLVE_ENDPOINT_ID 0x07
+#define MCTP_CTRL_CMD_ALLOCATE_ENDPOINT_IDS 0x08
+#define MCTP_CTRL_CMD_ROUTING_INFO_UPDATE 0x09
+#define MCTP_CTRL_CMD_GET_ROUTING_TABLE_ENTRIES 0x0A
+#define MCTP_CTRL_CMD_PREPARE_ENDPOINT_DISCOVERY 0x0B
+#define MCTP_CTRL_CMD_ENDPOINT_DISCOVERY 0x0C
+#define MCTP_CTRL_CMD_DISCOVERY_NOTIFY 0x0D
+#define MCTP_CTRL_CMD_GET_NETWORK_ID 0x0E
+#define MCTP_CTRL_CMD_QUERY_HOP 0x0F
+#define MCTP_CTRL_CMD_RESOLVE_UUID 0x10
+#define MCTP_CTRL_CMD_QUERY_RATE_LIMIT 0x11
+#define MCTP_CTRL_CMD_REQUEST_TX_RATE_LIMIT 0x12
+#define MCTP_CTRL_CMD_UPDATE_RATE_LIMIT 0x13
+#define MCTP_CTRL_CMD_QUERY_SUPPORTED_INTERFACES 0x14
+#define MCTP_CTRL_CMD_MAX 0x15
+/* 0xF0 - 0xFF are transport specific */
+#define MCTP_CTRL_CMD_FIRST_TRANSPORT 0xF0
+#define MCTP_CTRL_CMD_LAST_TRANSPORT 0xFF
+
+/*
+ * MCTP Control Completion Codes
+ * See DSP0236 v1.3.0 Table 13.
+ */
+#define MCTP_CTRL_CC_SUCCESS 0x00
+#define MCTP_CTRL_CC_ERROR 0x01
+#define MCTP_CTRL_CC_ERROR_INVALID_DATA 0x02
+#define MCTP_CTRL_CC_ERROR_INVALID_LENGTH 0x03
+#define MCTP_CTRL_CC_ERROR_NOT_READY 0x04
+#define MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD 0x05
+/* 0x80 - 0xFF are command specific */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBMCTP_CMDS_H */
diff --git a/libmctp.h b/libmctp.h
index 0a40c0e..621e952 100644
--- a/libmctp.h
+++ b/libmctp.h
@@ -14,6 +14,10 @@
typedef uint8_t mctp_eid_t;
+/* Special Endpoint ID values */
+#define MCTP_EID_NULL 0
+#define MCTP_EID_BROADCAST 0xff
+
/* MCTP packet definitions */
struct mctp_hdr {
uint8_t ver;
@@ -92,15 +96,16 @@
/* hardware bindings */
struct mctp_binding {
- const char *name;
- uint8_t version;
- struct mctp_bus *bus;
- struct mctp *mctp;
- int pkt_size;
- int pkt_pad;
- int (*start)(struct mctp_binding *binding);
- int (*tx)(struct mctp_binding *binding,
- struct mctp_pktbuf *pkt);
+ const char *name;
+ uint8_t version;
+ struct mctp_bus *bus;
+ struct mctp *mctp;
+ int pkt_size;
+ int pkt_pad;
+ int (*start)(struct mctp_binding *binding);
+ int (*tx)(struct mctp_binding *binding, struct mctp_pktbuf *pkt);
+ mctp_rx_fn control_rx;
+ void *control_rx_data;
};
void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable);
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;
+}