aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/staging/greybus/greybus.h1
-rw-r--r--drivers/staging/greybus/interface.c2
-rw-r--r--drivers/staging/greybus/interface.h1
-rw-r--r--drivers/staging/greybus/svc.c88
-rw-r--r--drivers/staging/greybus/svc.h3
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 */