From d3d448406772e421fb7ed8efd4d02d96cb2ab6f9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:18 +0530 Subject: [PATCH] greybus: svc: Add helpers to create AP<->SVC connection SVC connection is required before the AP knows its position on the endo and type of endo. To enable message processing between the AP and SVC at this time, we need a partially initialized connection which can handle these messages. Once the AP receives more information from the SVC, it can discard this partially initialized connection and create a proper one, tied to a bundle and interface. Destroying the partially initialized connection is a bit tricky, as it is required to send a response to svc-hello. That part will be properly fixed separately. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/svc.c | 88 +++++++++++++++++++++++++++++++++++-- drivers/staging/greybus/svc.h | 3 ++ 5 files changed, 90 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index e795016106c2..2214f447df2b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -102,6 +102,7 @@ struct greybus_host_device { size_t buffer_size_max; struct gb_endo *endo; + struct gb_connection *initial_svc_connection; /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 6d6128570837..f1e2956b25a7 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -183,7 +183,7 @@ put_module: /* * Tear down a previously set up module. */ -static void gb_interface_destroy(struct gb_interface *intf) +void gb_interface_destroy(struct gb_interface *intf) { struct gb_module *module; struct gb_bundle *bundle; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 04d330c297e1..9a9260c43be4 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -53,6 +53,7 @@ struct gb_interface *gb_interface_find(struct greybus_host_device *hd, struct gb_interface *gb_interface_create(struct greybus_host_device *hd, u8 interface_id); int gb_interface_init(struct gb_interface *intf, u8 device_id); +void gb_interface_destroy(struct gb_interface *intf); void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id); void gb_interfaces_remove(struct greybus_host_device *hd); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ce789c909c5f..138238457981 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -12,6 +12,66 @@ /* Define get_version() routine */ define_get_version(gb_svc, SVC); +/* + * AP's SVC cport is required early to get messages from the SVC. This happens + * even before the Endo is created and hence any modules or interfaces. + * + * This is a temporary connection, used only at initial bootup. + */ +struct gb_connection * +gb_ap_svc_connection_create(struct greybus_host_device *hd) +{ + struct gb_connection *connection; + + connection = gb_connection_create_range(hd, NULL, hd->parent, + GB_SVC_CPORT_ID, + GREYBUS_PROTOCOL_SVC, + GB_SVC_CPORT_ID, + GB_SVC_CPORT_ID + 1); + + return connection; +} +EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create); + +/* + * We know endo-type and AP's interface id now, lets create a proper svc + * connection (and its interface/bundle) now and get rid of the initial + * 'partially' initialized one svc connection. + */ +static struct gb_interface * +gb_ap_interface_create(struct greybus_host_device *hd, + struct gb_connection *connection, u8 interface_id) +{ + struct gb_interface *intf; + struct device *dev = &hd->endo->dev; + int ret; + + intf = gb_interface_create(hd, interface_id); + if (!intf) { + dev_err(dev, "%s: Failed to create interface with id %hhu\n", + __func__, interface_id); + return NULL; + } + + intf->device_id = GB_DEVICE_ID_AP; + + /* + * XXX: Disable the initial svc connection here, but don't destroy it + * yet. We do need to send a response of 'svc-hello message' on that. + */ + + /* Establish new control CPort connection */ + ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_SVC); + if (ret) { + dev_err(&intf->dev, "%s: Failed to create svc connection (%d %d)\n", + __func__, interface_id, ret); + gb_interface_destroy(intf); + intf = NULL; + } + + return intf; +} + static int intf_device_id_operation(struct gb_svc *svc, u8 intf_id, u8 device_id) { @@ -207,6 +267,22 @@ static int gb_svc_connection_init(struct gb_connection *connection) svc->connection = connection; connection->private = svc; + + /* + * SVC connection is created twice: + * - before the interface-id of the AP and the endo type is known. + * - after receiving endo type and interface-id of the AP from the SVC. + * + * We should do light-weight initialization for the first case. + */ + if (!connection->bundle) { + WARN_ON(connection->hd->initial_svc_connection); + connection->hd->initial_svc_connection = connection; + return 0; + } + + ida_init(&greybus_svc_device_id_map); + ret = gb_svc_device_setup(svc); if (ret) kfree(svc); @@ -221,11 +297,15 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; - if (WARN_ON(connection->bundle->intf->svc != svc)) - return; - - connection->bundle->intf->svc = NULL; + if (connection->hd->initial_svc_connection == connection) { + connection->hd->initial_svc_connection = NULL; + } else { + if (WARN_ON(connection->bundle->intf->svc != svc)) + return; + connection->bundle->intf->svc = NULL; + } + connection->private = NULL; kfree(svc); } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index ebabe5ff7c6d..66497808ee62 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -26,4 +26,7 @@ int gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); + +struct gb_connection * +gb_ap_svc_connection_create(struct greybus_host_device *hd); #endif /* __SVC_H */ -- 2.11.0