greybus: create host-device compilation unit

Move everything host-device related to hd.c and hd.h.

Signed-off-by: Johan Hovold <johan@hovoldconsulting.com>
Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c
new file mode 100644
index 0000000..3ac8507
--- /dev/null
+++ b/drivers/staging/greybus/hd.c
@@ -0,0 +1,115 @@
+/*
+ * Greybus Host Device
+ *
+ * Copyright 2014-2015 Google Inc.
+ * Copyright 2014-2015 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "greybus.h"
+
+static DEFINE_MUTEX(hd_mutex);
+
+
+static void free_hd(struct kref *kref)
+{
+	struct greybus_host_device *hd;
+
+	hd = container_of(kref, struct greybus_host_device, kref);
+
+	ida_destroy(&hd->cport_id_map);
+	kfree(hd);
+	mutex_unlock(&hd_mutex);
+}
+
+struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
+					      struct device *parent,
+					      size_t buffer_size_max,
+					      size_t num_cports)
+{
+	struct greybus_host_device *hd;
+
+	/*
+	 * Validate that the driver implements all of the callbacks
+	 * so that we don't have to every time we make them.
+	 */
+	if ((!driver->message_send) || (!driver->message_cancel)) {
+		pr_err("Must implement all greybus_host_driver callbacks!\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) {
+		dev_err(parent, "greybus host-device buffers too small\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (num_cports == 0 || num_cports > CPORT_ID_MAX) {
+		dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/*
+	 * Make sure to never allocate messages larger than what the Greybus
+	 * protocol supports.
+	 */
+	if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) {
+		dev_warn(parent, "limiting buffer size to %u\n",
+			 GB_OPERATION_MESSAGE_SIZE_MAX);
+		buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX;
+	}
+
+	hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
+	if (!hd)
+		return ERR_PTR(-ENOMEM);
+
+	kref_init(&hd->kref);
+	hd->parent = parent;
+	hd->driver = driver;
+	INIT_LIST_HEAD(&hd->interfaces);
+	INIT_LIST_HEAD(&hd->connections);
+	ida_init(&hd->cport_id_map);
+	hd->buffer_size_max = buffer_size_max;
+	hd->num_cports = num_cports;
+
+	/*
+	 * Initialize AP's SVC protocol connection:
+	 *
+	 * This is required as part of early initialization of the host device
+	 * as we need this connection in order to start any kind of message
+	 * exchange between the AP and the SVC. SVC will start with a
+	 * 'get-version' request followed by a 'svc-hello' message and at that
+	 * time we will create a fully initialized svc-connection, as we need
+	 * endo-id and AP's interface id for that.
+	 */
+	if (!gb_ap_svc_connection_create(hd)) {
+		kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return hd;
+}
+EXPORT_SYMBOL_GPL(greybus_create_hd);
+
+void greybus_remove_hd(struct greybus_host_device *hd)
+{
+	/*
+	 * Tear down all interfaces, modules, and the endo that is associated
+	 * with this host controller before freeing the memory associated with
+	 * the host controller.
+	 */
+	gb_interfaces_remove(hd);
+	gb_endo_remove(hd->endo);
+
+	/* Is the SVC still using the partially uninitialized connection ? */
+	if (hd->initial_svc_connection)
+		gb_connection_destroy(hd->initial_svc_connection);
+
+	kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
+}
+EXPORT_SYMBOL_GPL(greybus_remove_hd);