Add RPC demux component

To support multiple co-located service providers, the rpc_demux
may be used.  It presents a single input rpc_interface and
demultiplexes incoming call requests to 1..* attached outputs
using the interface_id parameter carried by the underlying
RPC.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I03d7423892cd07d8a8fcd445dbb7dc7e62497455
diff --git a/components/rpc/common/demux/component.cmake b/components/rpc/common/demux/component.cmake
new file mode 100644
index 0000000..a02eefc
--- /dev/null
+++ b/components/rpc/common/demux/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/rpc_demux.c"
+	)
diff --git a/components/rpc/common/demux/rpc_demux.c b/components/rpc/common/demux/rpc_demux.c
new file mode 100644
index 0000000..efcc162
--- /dev/null
+++ b/components/rpc/common/demux/rpc_demux.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include "rpc_demux.h"
+
+static rpc_status_t receive(struct rpc_interface *rpc_iface, struct call_req *req)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERFACE_DOES_NOT_EXIST;
+	struct rpc_demux *context = (struct rpc_demux*)rpc_iface->context;
+
+	unsigned int iface_id = call_req_get_interface_id(req);
+
+	if ((iface_id < RPC_DEMUX_MAX_OUTPUTS) && context->outputs[iface_id]) {
+
+		rpc_status = rpc_interface_receive(context->outputs[iface_id], req);
+	}
+
+	return rpc_status;
+}
+
+struct rpc_interface *rpc_demux_init(struct rpc_demux *context)
+{
+	context->input.receive = receive;
+	context->input.context = context;
+
+	for (unsigned int i = 0; i < RPC_DEMUX_MAX_OUTPUTS; ++i) {
+
+		context->outputs[i] = NULL;
+	}
+
+	return &context->input;
+}
+
+void rpc_demux_deinit(struct rpc_demux *context)
+{
+	(void)context;
+}
+
+void rpc_demux_attach(struct rpc_demux *context,
+	unsigned int iface_id, struct rpc_interface *output)
+{
+	assert(iface_id < RPC_DEMUX_MAX_OUTPUTS);
+	context->outputs[iface_id] = output;
+}
+
+void rpc_demux_dettach(struct rpc_demux *context,
+	unsigned int iface_id)
+{
+	assert(iface_id < RPC_DEMUX_MAX_OUTPUTS);
+	context->outputs[iface_id] = NULL;
+}
diff --git a/components/rpc/common/demux/rpc_demux.h b/components/rpc/common/demux/rpc_demux.h
new file mode 100644
index 0000000..46d7b0f
--- /dev/null
+++ b/components/rpc/common/demux/rpc_demux.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef RPC_DEMUX_H
+#define RPC_DEMUX_H
+
+#include <rpc/common/endpoint/rpc_interface.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The default maximum number of output interfaces.  May be
+ * overridden to meet needs of deployment if necessary.
+ */
+#ifndef RPC_DEMUX_MAX_OUTPUTS
+#define RPC_DEMUX_MAX_OUTPUTS				(8)
+#endif
+
+/** \brief RPC demux
+ *
+ * An rpc_demux is an rpc_interface that demultiplexes incoming call requests
+ * to 1..* output interfaces.  Use an rpc_demux when multiple service
+ * providers are co-located and associated with a single RPC endpoint.
+ */
+struct rpc_demux
+{
+	struct rpc_interface input;
+	struct rpc_interface *outputs[RPC_DEMUX_MAX_OUTPUTS];
+};
+
+/**
+ * \brief Initialize an rpc_demux
+ *
+ * After initialization, the required number of output interfaces need
+ * to be attached,
+ *
+ * \param[in] context   The instance to initialize
+ *
+ * \return The input rpc_interface
+ */
+struct rpc_interface *rpc_demux_init(struct rpc_demux *context);
+
+/**
+ * \brief Cleans up when the instance is no longer needed
+ *
+ * \param[in] context   The instance to de-initialize
+ */
+void rpc_demux_deinit(struct rpc_demux *context);
+
+/**
+ * \brief Attach an output interface
+ *
+ * \param[in] context   The rpc_demux instance
+ * \param[in] iface_id	The interface id (small integer)
+ * \param[in] output	The interface to attach
+ */
+void rpc_demux_attach(struct rpc_demux *context,
+	unsigned int iface_id, struct rpc_interface *output);
+
+/**
+ * \brief Dettach an output interface
+ *
+ * \param[in] context   The rpc_demux instance
+ * \param[in] iface_id	The interface id (small integer)
+ */
+void rpc_demux_dettach(struct rpc_demux *context,
+	unsigned int iface_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RPC_DEMUX_H */
diff --git a/components/rpc/common/endpoint/rpc_interface.h b/components/rpc/common/endpoint/rpc_interface.h
index 44e5783..ba949de 100644
--- a/components/rpc/common/endpoint/rpc_interface.h
+++ b/components/rpc/common/endpoint/rpc_interface.h
@@ -10,6 +10,7 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <rpc_status.h>
+#include <protocols/rpc/common/packed-c/status.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -120,7 +121,14 @@
 static inline rpc_status_t rpc_interface_receive(struct rpc_interface *iface,
 					  struct call_req *req)
 {
-	return iface->receive(iface, req);
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERFACE_DOES_NOT_EXIST;
+
+	if (iface) {
+
+		rpc_status = iface->receive(iface, req);
+	}
+
+	return rpc_status;
 }
 
 #ifdef __cplusplus
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index dfa0068..974e4e2 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -26,6 +26,7 @@
 		"components/config/ramstore/test"
 		"components/rpc/common/caller"
 		"components/rpc/common/interface"
+		"components/rpc/common/demux"
 		"components/rpc/common/test/protocol"
 		"components/rpc/direct"
 		"components/rpc/dummy"
diff --git a/protocols/rpc/common/packed-c/status.h b/protocols/rpc/common/packed-c/status.h
index 3838240..534f0df 100644
--- a/protocols/rpc/common/packed-c/status.h
+++ b/protocols/rpc/common/packed-c/status.h
@@ -25,7 +25,8 @@
     TS_RPC_ERROR_NOT_READY                          = -7,
     TS_RPC_ERROR_INVALID_TRANSACTION                = -8,
     TS_RPC_ERROR_INTERNAL                           = -9,
-    TS_RPC_ERROR_INVALID_PARAMETER                  = -10
+    TS_RPC_ERROR_INVALID_PARAMETER                  = -10,
+    TS_RPC_ERROR_INTERFACE_DOES_NOT_EXIST           = -11
 };
 
 #endif /* PROTOCOLS_RPC_COMMON_STATUS_H */