]> git.karo-electronics.de Git - linux-beck.git/commitdiff
greybus: connection: implement proper connection closure
authorJohan Hovold <johan@hovoldconsulting.com>
Fri, 27 May 2016 15:26:36 +0000 (17:26 +0200)
committerGreg Kroah-Hartman <gregkh@google.com>
Fri, 27 May 2016 19:24:17 +0000 (12:24 -0700)
Implement proper connection closure, which includes sending ping
requests on the connection being tore down while coordinating with the
remote interface as well as the SVC.

This specifically implements the new ping operation, which in case of
offloaded connections is handled by the host-device driver in an
implementation-defined manner through a new callback.

Note that the normal connection tear-down procedure is executed in case
of failed connection establishment due to failed connected operation.
Specifically, the disconnecting request is sent also in case the
connected operation never succeeded. This is needed since the interface
may have enabled FCT flow upon receiving the connected request.

Signed-off-by: Johan Hovold <johan@hovoldconsulting.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/connection.c
drivers/staging/greybus/connection.h
drivers/staging/greybus/greybus_protocols.h
drivers/staging/greybus/hd.h
drivers/staging/greybus/operation.c

index f3a3915de272c91056779663f1d86ea6ea6fdcce..77067515a28a3e91d3b45246d05662d582123e2e 100644 (file)
@@ -389,6 +389,21 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection)
                                  connection->intf_cport_id);
 }
 
+static void
+gb_connection_svc_connection_quiescing(struct gb_connection *connection)
+{
+       struct gb_host_device *hd = connection->hd;
+
+       if (gb_connection_is_static(connection))
+               return;
+
+       gb_svc_connection_quiescing(hd->svc,
+                                       hd->svc->ap_intf_id,
+                                       connection->hd_cport_id,
+                                       connection->intf->interface_id,
+                                       connection->intf_cport_id);
+}
+
 /* Inform Interface about active CPorts */
 static int gb_connection_control_connected(struct gb_connection *connection)
 {
@@ -424,7 +439,26 @@ static int gb_connection_control_connected(struct gb_connection *connection)
        return 0;
 }
 
-/* Inform Interface about inactive CPorts */
+static void
+gb_connection_control_disconnecting(struct gb_connection *connection)
+{
+       struct gb_control *control;
+       u16 cport_id = connection->intf_cport_id;
+       int ret;
+
+       if (gb_connection_is_static(connection))
+               return;
+
+       control = connection->intf->control;
+
+       ret = gb_control_disconnecting_operation(control, cport_id);
+       if (ret) {
+               dev_err(&connection->hd->dev,
+                               "%s: failed to send disconnecting: %d\n",
+                               connection->name, ret);
+       }
+}
+
 static void
 gb_connection_control_disconnected(struct gb_connection *connection)
 {
@@ -447,10 +481,56 @@ gb_connection_control_disconnected(struct gb_connection *connection)
        }
 }
 
+static int gb_connection_ping_operation(struct gb_connection *connection)
+{
+       struct gb_operation *operation;
+       int ret;
+
+       operation = gb_operation_create_core(connection,
+                                               GB_REQUEST_TYPE_PING,
+                                               0, 0, 0,
+                                               GFP_KERNEL);
+       if (!operation)
+               return -ENOMEM;
+
+       ret = gb_operation_request_send_sync(operation);
+
+       gb_operation_put(operation);
+
+       return ret;
+}
+
+static int gb_connection_ping(struct gb_connection *connection)
+{
+       struct gb_host_device *hd = connection->hd;
+       int ret;
+
+       if (gb_connection_is_static(connection))
+               return 0;
+
+       if (gb_connection_is_offloaded(connection)) {
+               if (!hd->driver->cport_ping)
+                       return 0;
+
+               ret = hd->driver->cport_ping(hd, connection->intf_cport_id);
+       } else {
+               ret = gb_connection_ping_operation(connection);
+       }
+
+       if (ret) {
+               dev_err(&hd->dev, "%s: failed to send ping: %d\n",
+                               connection->name, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 /*
  * Cancel all active operations on a connection.
  *
- * Locking: Called with connection lock held and state set to DISABLED.
+ * Locking: Called with connection lock held and state set to DISABLED or
+ * DISCONNECTING.
  */
 static void gb_connection_cancel_operations(struct gb_connection *connection,
                                                int errno)
@@ -559,17 +639,24 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx)
 
        ret = gb_connection_control_connected(connection);
        if (ret)
-               goto err_flush_operations;
+               goto err_control_disconnecting;
 
        return 0;
 
-err_flush_operations:
+err_control_disconnecting:
+       gb_connection_control_disconnecting(connection);
+
        spin_lock_irq(&connection->lock);
-       connection->state = GB_CONNECTION_STATE_DISABLED;
+       connection->state = GB_CONNECTION_STATE_DISCONNECTING;
        gb_connection_cancel_operations(connection, -ESHUTDOWN);
        spin_unlock_irq(&connection->lock);
 
+       gb_connection_ping(connection);
        gb_connection_hd_cport_features_disable(connection);
+       gb_connection_svc_connection_quiescing(connection);
+       gb_connection_ping(connection);
+       gb_connection_control_disconnected(connection);
+       connection->state = GB_CONNECTION_STATE_DISABLED;
 err_svc_connection_destroy:
        gb_connection_svc_connection_destroy(connection);
 err_hd_cport_disable:
@@ -642,14 +729,22 @@ void gb_connection_disable(struct gb_connection *connection)
        if (connection->state == GB_CONNECTION_STATE_DISABLED)
                goto out_unlock;
 
-       gb_connection_control_disconnected(connection);
+       gb_connection_control_disconnecting(connection);
 
        spin_lock_irq(&connection->lock);
-       connection->state = GB_CONNECTION_STATE_DISABLED;
+       connection->state = GB_CONNECTION_STATE_DISCONNECTING;
        gb_connection_cancel_operations(connection, -ESHUTDOWN);
        spin_unlock_irq(&connection->lock);
 
+       gb_connection_ping(connection);
        gb_connection_hd_cport_features_disable(connection);
+       gb_connection_svc_connection_quiescing(connection);
+       gb_connection_ping(connection);
+
+       gb_connection_control_disconnected(connection);
+
+       connection->state = GB_CONNECTION_STATE_DISABLED;
+
        gb_connection_svc_connection_destroy(connection);
        gb_connection_hd_cport_disable(connection);
 
index 9a35e6196b17833459ffaddb56d9095c4f09d426..af171f5f0635da217242f063ecd2743e389870c3 100644 (file)
 #define GB_CONNECTION_FLAG_CONTROL     BIT(4)
 
 enum gb_connection_state {
-       GB_CONNECTION_STATE_DISABLED    = 0,
-       GB_CONNECTION_STATE_ENABLED_TX  = 1,
-       GB_CONNECTION_STATE_ENABLED     = 2,
+       GB_CONNECTION_STATE_DISABLED            = 0,
+       GB_CONNECTION_STATE_ENABLED_TX          = 1,
+       GB_CONNECTION_STATE_ENABLED             = 2,
+       GB_CONNECTION_STATE_DISCONNECTING       = 3,
 };
 
 struct gb_operation;
index 216a1d1c348151e89289830e49b98e385e21f40b..6afd0a6a98cd2c82560117ad3244052fe544425b 100644 (file)
@@ -96,6 +96,7 @@ struct gb_operation_msg_hdr {
 
 
 /* Generic request types */
+#define GB_REQUEST_TYPE_PING                   0x00
 #define GB_REQUEST_TYPE_PROTOCOL_VERSION       0x01
 #define GB_REQUEST_TYPE_INVALID                        0x7f
 
index ad229622654e85e6d69191fee4cfc37cf94ca9c8..7321cfdd41d7dad891468024b51e3633b6708f60 100644 (file)
@@ -21,6 +21,7 @@ struct gb_hd_driver {
        void (*cport_release)(struct gb_host_device *hd, u16 cport_id);
        int (*cport_enable)(struct gb_host_device *hd, u16 cport_id);
        int (*cport_disable)(struct gb_host_device *hd, u16 cport_id);
+       int (*cport_ping)(struct gb_host_device *hd, u16 cport_id);
        int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id,
                        struct gb_message *message, gfp_t gfp_mask);
        void (*message_cancel)(struct gb_message *message);
index fb4a29e2c09fea96a9fe56e4b38a3fdbcc33d9b6..7906a95899e5610f0aeaf680bad7752661171352 100644 (file)
@@ -46,7 +46,6 @@ static int gb_operation_get_active(struct gb_operation *operation)
        unsigned long flags;
 
        spin_lock_irqsave(&connection->lock, flags);
-
        switch (connection->state) {
        case GB_CONNECTION_STATE_ENABLED:
                break;
@@ -54,6 +53,10 @@ static int gb_operation_get_active(struct gb_operation *operation)
                if (gb_operation_is_incoming(operation))
                        goto err_unlock;
                break;
+       case GB_CONNECTION_STATE_DISCONNECTING:
+               if (!gb_operation_is_core(operation))
+                       goto err_unlock;
+               break;
        default:
                goto err_unlock;
        }