greybus: svc: Add helpers to create AP<->SVC connection
authorViresh Kumar <viresh.kumar@linaro.org>
Tue, 21 Jul 2015 12:14:18 +0000 (17:44 +0530)
committerGreg Kroah-Hartman <gregkh@google.com>
Wed, 22 Jul 2015 17:12:41 +0000 (10:12 -0700)
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 <viresh.kumar@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/greybus.h
drivers/staging/greybus/interface.c
drivers/staging/greybus/interface.h
drivers/staging/greybus/svc.c
drivers/staging/greybus/svc.h

index e795016..2214f44 100644 (file)
@@ -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));
index 6d61285..f1e2956 100644 (file)
@@ -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;
index 04d330c..9a9260c 100644 (file)
@@ -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);
 
index ce789c9..1382384 100644 (file)
 /* 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);
 }
 
index ebabe5f..6649780 100644 (file)
@@ -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 */