aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/connection.c
diff options
context:
space:
mode:
authorJohan Hovold <johan@hovoldconsulting.com>2016-05-27 17:26:36 +0200
committerGreg Kroah-Hartman <gregkh@google.com>2016-05-27 12:24:17 -0700
commit3de5acfafb3c610bc6f7013a1583852cbc2747f1 (patch)
tree754df49b802847c5a4889cbbe43906df33200fb8 /drivers/staging/greybus/connection.c
parentgreybus: operation: add helper for creating core operations (diff)
downloadlinux-dev-3de5acfafb3c610bc6f7013a1583852cbc2747f1.tar.xz
linux-dev-3de5acfafb3c610bc6f7013a1583852cbc2747f1.zip
greybus: connection: implement proper connection closure
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>
Diffstat (limited to 'drivers/staging/greybus/connection.c')
-rw-r--r--drivers/staging/greybus/connection.c109
1 files changed, 102 insertions, 7 deletions
diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c
index f3a3915de272..77067515a28a 100644
--- a/drivers/staging/greybus/connection.c
+++ b/drivers/staging/greybus/connection.c
@@ -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);