From fb69cb506c16d38de2c3b62ea4cf6e8728bd4348 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Dec 2014 15:16:53 -0800 Subject: [PATCH] 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 Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 30 +++++++++++++++++ drivers/staging/greybus/bundle.h | 1 + drivers/staging/greybus/connection.c | 48 ++++++++++++++++++++++------ drivers/staging/greybus/connection.h | 5 +++ drivers/staging/greybus/operation.c | 3 ++ drivers/staging/greybus/protocol.c | 6 ++++ 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);