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/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;