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 e795016106c22dc035a357c2ebee490e61a6b1ed..2214f447df2bda8d5acfda1e937a563cbc2bc48e 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 6d6128570837bc739a8d122288e8c137f1e5b8f9..f1e2956b25a7a305e0f58567afad982716f9868e 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 04d330c297e14c2ce782b811779ee8d540f3cc91..9a9260c43be4ad682a3a96c7a548e58d96d8e2c0 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 ce789c909c5fd31119a5415e7134980c67004320..138238457981d984e423d52106250f223aa62ab5 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 ebabe5ff7c6d2c910b566e51c15758cc88031fb5..66497808ee627ceb20dae152bb135d6b6ecc3f6f 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 */