greybus: svc: Allow consecutive hotplug events for the same module
authorViresh Kumar <viresh.kumar@linaro.org>
Wed, 23 Sep 2015 23:48:08 +0000 (16:48 -0700)
committerGreg Kroah-Hartman <gregkh@google.com>
Thu, 24 Sep 2015 22:16:54 +0000 (15:16 -0700)
There are two cases where the AP may receive hotplug event for an
existing interface, without first getting a hot-unplug request for it.

- bootrom loading the firmware image and booting into that, which
  only generates a hotplug event. i.e. no hot-unplug event, as the
  module never went away.
- Or the firmware on the module crashed and sent hotplug request again
  to the SVC, which got propagated to AP.

Handle such cases by first removing the interface, with a clear print
message shown to the user. And then following the normal hotplug
sequence to add the interface.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Johan Hovold <johan@hovoldconsulting.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/svc.c

index 056351f..0e029ce 100644 (file)
@@ -329,6 +329,25 @@ static int gb_svc_hello(struct gb_operation *op)
        return 0;
 }
 
+static void svc_intf_remove(struct gb_connection *connection,
+                           struct gb_interface *intf)
+{
+       struct greybus_host_device *hd = connection->hd;
+       struct gb_svc *svc = connection->private;
+       u8 intf_id = intf->interface_id;
+       u8 device_id;
+
+       device_id = intf->device_id;
+       gb_interface_remove(hd, intf_id);
+
+       /*
+        * Destroy the two-way route between the AP and the interface.
+        */
+       gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id);
+
+       ida_simple_remove(&svc->device_id_map, device_id);
+}
+
 /*
  * 'struct svc_hotplug' should be freed by svc_process_hotplug() before it
  * returns, irrespective of success or Failure in bringing up the module.
@@ -351,6 +370,27 @@ static void svc_process_hotplug(struct work_struct *work)
         */
        intf_id = hotplug->intf_id;
 
+       intf = gb_interface_find(hd, intf_id);
+       if (intf) {
+               /*
+                * We have received a hotplug request for an interface that
+                * already exists.
+                *
+                * This can happen in cases like:
+                * - bootrom loading the firmware image and booting into that,
+                *   which only generates a hotplug event. i.e. no hot-unplug
+                *   event.
+                * - Or the firmware on the module crashed and sent hotplug
+                *   request again to the SVC, which got propagated to AP.
+                *
+                * Remove the interface and add it again, and let user know
+                * about this with a print message.
+                */
+               dev_info(dev, "Removed interface (%hhu) to add it again\n",
+                        intf_id);
+               svc_intf_remove(connection, intf);
+       }
+
        intf = gb_interface_create(hd, intf_id);
        if (!intf) {
                dev_err(dev, "%s: Failed to create interface with id %hhu\n",
@@ -463,8 +503,6 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
        struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload;
        struct greybus_host_device *hd = op->connection->hd;
        struct device *dev = &op->connection->dev;
-       struct gb_svc *svc = op->connection->private;
-       u8 device_id;
        struct gb_interface *intf;
        u8 intf_id;
 
@@ -483,15 +521,7 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
                return -EINVAL;
        }
 
-       device_id = intf->device_id;
-       gb_interface_remove(hd, intf_id);
-
-       /*
-        * Destroy the two-way route between the AP and the interface.
-        */
-       gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id);
-
-       ida_simple_remove(&svc->device_id_map, device_id);
+       svc_intf_remove(op->connection, intf);
 
        return 0;
 }