diff options
author | Johan Hovold <johan@hovoldconsulting.com> | 2015-11-03 18:03:22 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@google.com> | 2015-11-04 20:25:57 -0800 |
commit | 7bc6faaca7d829d4e6f5d65909d5068f73b76bda (patch) | |
tree | 80ebab1a7366c6aaae4fd197cac39d4cd3b236c3 /drivers/staging/greybus/hd.c | |
parent | 04fdd6a51a5b3b448047b83624dbfac85d3daf9a (diff) |
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>
Diffstat (limited to 'drivers/staging/greybus/hd.c')
-rw-r--r-- | drivers/staging/greybus/hd.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c new file mode 100644 index 000000000000..3ac85077b431 --- /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); |