summaryrefslogtreecommitdiffstats
path: root/drivers/staging
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/greybus/connection.c109
-rw-r--r--drivers/staging/greybus/connection.h7
-rw-r--r--drivers/staging/greybus/greybus_protocols.h1
-rw-r--r--drivers/staging/greybus/hd.h1
-rw-r--r--drivers/staging/greybus/operation.c5
5 files changed, 112 insertions, 11 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);
diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h
index 9a35e6196b17..af171f5f0635 100644
--- a/drivers/staging/greybus/connection.h
+++ b/drivers/staging/greybus/connection.h
@@ -20,9 +20,10 @@
#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;
diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h
index 216a1d1c3481..6afd0a6a98cd 100644
--- a/drivers/staging/greybus/greybus_protocols.h
+++ b/drivers/staging/greybus/greybus_protocols.h
@@ -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
diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h
index ad229622654e..7321cfdd41d7 100644
--- a/drivers/staging/greybus/hd.h
+++ b/drivers/staging/greybus/hd.h
@@ -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);
diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c
index fb4a29e2c09f..7906a95899e5 100644
--- a/drivers/staging/greybus/operation.c
+++ b/drivers/staging/greybus/operation.c
@@ -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;
}