summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <greg@kroah.com>2014-12-23 15:16:53 -0800
committerGreg Kroah-Hartman <greg@kroah.com>2014-12-23 21:04:02 -0800
commitfb69cb506c16d38de2c3b62ea4cf6e8728bd4348 (patch)
treec7e86e2980862d4a235320ad34af7dbb8960f618 /drivers
parentdf469a942301c9bf4d939e1139c1a435079b288a (diff)
greybus: protocol: split binding of prototcols to connections out of init
When adding a new protocol to the system, walk all bundles and try to hook up any connections that do not have a protocol already. This sets the stage to allow for protocols to be loaded at any time, not just before the device is seen in the system. Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/greybus/bundle.c30
-rw-r--r--drivers/staging/greybus/bundle.h1
-rw-r--r--drivers/staging/greybus/connection.c48
-rw-r--r--drivers/staging/greybus/connection.h5
-rw-r--r--drivers/staging/greybus/operation.c3
-rw-r--r--drivers/staging/greybus/protocol.c6
6 files changed, 83 insertions, 10 deletions
diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c
index 28a82229adeb..973ea39dc407 100644
--- a/drivers/staging/greybus/bundle.c
+++ b/drivers/staging/greybus/bundle.c
@@ -45,6 +45,36 @@ struct device_type greybus_bundle_type = {
/* XXX This could be per-host device or per-module */
static DEFINE_SPINLOCK(gb_bundles_lock);
+static int __bundle_bind_protocols(struct device *dev, void *data)
+{
+ struct gb_bundle *bundle;
+ struct gb_connection *connection;
+
+ if (!is_gb_bundle(dev))
+ return 0;
+
+ bundle = to_gb_bundle(dev);
+
+ list_for_each_entry(connection, &bundle->connections, bundle_links) {
+ gb_connection_bind_protocol(connection);
+ }
+
+ return 0;
+}
+
+/*
+ * Walk all bundles in the system, and see if any connections are not bound to a
+ * specific prototcol. If they are not, then try to find one for it and bind it
+ * to it.
+ *
+ * This is called after registering a new protocol.
+ */
+void gb_bundle_bind_protocols(void)
+{
+ bus_for_each_dev(&greybus_bus_type, NULL, NULL,
+ __bundle_bind_protocols);
+}
+
/*
* Create a gb_bundle structure to represent a discovered
* bundle. Returns a pointer to the new bundle or a null
diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h
index c3c66faac6fc..e11d456430ef 100644
--- a/drivers/staging/greybus/bundle.h
+++ b/drivers/staging/greybus/bundle.h
@@ -30,5 +30,6 @@ void gb_bundle_destroy(struct gb_interface *intf);
int gb_bundle_init(struct gb_interface *intf, u8 module_id, u8 device_id);
struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id);
+void gb_bundle_bind_protocols(void);
#endif /* __BUNDLE_H */
diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c
index 191df5377bc3..2f70837ff4fe 100644
--- a/drivers/staging/greybus/connection.c
+++ b/drivers/staging/greybus/connection.c
@@ -124,6 +124,32 @@ struct device_type greybus_connection_type = {
.release = gb_connection_release,
};
+
+void gb_connection_bind_protocol(struct gb_connection *connection)
+{
+ struct gb_bundle *bundle;
+ struct gb_protocol *protocol;
+
+ /* If we already have a protocol bound here, just return */
+ if (connection->protocol)
+ return;
+
+ protocol = gb_protocol_get(connection->protocol_id,
+ connection->major,
+ connection->minor);
+ if (!protocol)
+ return;
+ connection->protocol = protocol;
+
+ /*
+ * If we have a valid device_id for the bundle, then we have an active
+ * device, so bring up the connection at the same time.
+ * */
+ bundle = connection->bundle;
+ if (bundle->device_id != 0xff)
+ gb_connection_init(connection);
+}
+
/*
* Set up a Greybus connection, representing the bidirectional link
* between a CPort on a (local) Greybus host device and a CPort on
@@ -148,13 +174,9 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle,
if (!connection)
return NULL;
- /* XXX Will have to establish connections to get version */
- connection->protocol = gb_protocol_get(protocol_id, major, minor);
- if (!connection->protocol) {
- pr_err("protocol 0x%02hhx not found\n", protocol_id);
- kfree(connection);
- return NULL;
- }
+ connection->protocol_id = protocol_id;
+ connection->major = major;
+ connection->minor = minor;
hd = bundle->intf->hd;
connection->hd = hd;
@@ -187,6 +209,12 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle,
return NULL;
}
+ /* XXX Will have to establish connections to get version */
+ gb_connection_bind_protocol(connection);
+ if (!connection->protocol)
+ dev_warn(&bundle->dev,
+ "protocol 0x%02hhx handler not found\n", protocol_id);
+
spin_lock_irq(&gb_connections_lock);
list_add_tail(&connection->hd_links, &hd->connections);
list_add_tail(&connection->bundle_links, &bundle->connections);
@@ -250,8 +278,8 @@ int gb_connection_init(struct gb_connection *connection)
int ret;
if (!connection->protocol) {
- gb_connection_err(connection, "uninitialized connection");
- return -EIO;
+ dev_warn(&connection->dev, "init without protocol.\n");
+ return 0;
}
/* Need to enable the connection to initialize it */
@@ -266,7 +294,7 @@ int gb_connection_init(struct gb_connection *connection)
void gb_connection_exit(struct gb_connection *connection)
{
if (!connection->protocol) {
- gb_connection_err(connection, "uninitialized connection");
+ dev_warn(&connection->dev, "exit without protocol.\n");
return;
}
connection->state = GB_CONNECTION_STATE_DESTROYING;
diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h
index caf52b8ef676..b07df79f9d86 100644
--- a/drivers/staging/greybus/connection.h
+++ b/drivers/staging/greybus/connection.h
@@ -33,6 +33,9 @@ struct gb_connection {
struct list_head bundle_links;
struct gb_protocol *protocol;
+ u8 protocol_id;
+ u8 major;
+ u8 minor;
enum gb_connection_state state;
@@ -58,4 +61,6 @@ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id,
__printf(2, 3)
void gb_connection_err(struct gb_connection *connection, const char *fmt, ...);
+void gb_connection_bind_protocol(struct gb_connection *connection);
+
#endif /* __CONNECTION_H */
diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c
index 087e0cc14a13..44cfd5057e71 100644
--- a/drivers/staging/greybus/operation.c
+++ b/drivers/staging/greybus/operation.c
@@ -206,6 +206,9 @@ static void gb_operation_request_handle(struct gb_operation *operation)
{
struct gb_protocol *protocol = operation->connection->protocol;
+ if (!protocol)
+ return;
+
/*
* If the protocol has no incoming request handler, report
* an error and mark the request bad.
diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c
index 7536a30e5b90..2527532b0514 100644
--- a/drivers/staging/greybus/protocol.c
+++ b/drivers/staging/greybus/protocol.c
@@ -84,6 +84,12 @@ int gb_protocol_register(struct gb_protocol *protocol)
list_add_tail(&protocol->links, &existing->links);
spin_unlock_irq(&gb_protocols_lock);
+ /*
+ * Go try to bind any unbound connections, as we have a
+ * new protocol in the system
+ */
+ gb_bundle_bind_protocols();
+
return 0;
}
EXPORT_SYMBOL_GPL(gb_protocol_register);