diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index ec52bdecb5c1..1714482bd34d 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -81,15 +81,9 @@ static struct gb_bundle *gb_bundle_find(struct gb_interface *intf, static void gb_bundle_release(struct device *dev) { struct gb_bundle *bundle = to_gb_bundle(dev); - struct gb_connection *connection; - struct gb_connection *tmp; - - list_for_each_entry_safe(connection, tmp, &bundle->connections, - bundle_links) { - gb_connection_destroy(connection); - } kfree(bundle->state); + kfree(bundle->cport_desc); kfree(bundle); } diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 837682d91e5b..48fb3fd76817 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -22,6 +22,9 @@ struct gb_bundle { u8 class_major; u8 class_minor; + size_t num_cports; + struct greybus_descriptor_cport *cport_desc; + struct list_head connections; u8 *state; diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 88c8a3fab2c6..c92af6e1a11f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -232,6 +232,7 @@ gb_connection_create_dynamic(struct gb_interface *intf, return gb_connection_create(intf->hd, -1, intf, bundle, cport_id, protocol_id); } +EXPORT_SYMBOL_GPL(gb_connection_create_dynamic); static int gb_connection_hd_cport_enable(struct gb_connection *connection) { @@ -546,6 +547,7 @@ void gb_connection_destroy(struct gb_connection *connection) gb_connection_put(connection); } +EXPORT_SYMBOL_GPL(gb_connection_destroy); void gb_connection_latency_tag_enable(struct gb_connection *connection) { diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 1bb685bae7d2..b9303c0cd5e4 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -151,8 +151,14 @@ static int greybus_probe(struct device *dev) return -ENODEV; retval = driver->probe(bundle, id); - if (retval) + if (retval) { + /* + * Catch buggy drivers that fail to destroy their connections. + */ + WARN_ON(!list_empty(&bundle->connections)); + return retval; + } return 0; } @@ -172,11 +178,8 @@ static int greybus_remove(struct device *dev) driver->disconnect(bundle); - /* Catch buggy drivers that fail to disable their connections. */ - list_for_each_entry(connection, &bundle->connections, bundle_links) { - if (WARN_ON(connection->state != GB_CONNECTION_STATE_DISABLED)) - gb_connection_disable(connection); - } + /* Catch buggy drivers that fail to destroy their connections. */ + WARN_ON(!list_empty(&bundle->connections)); return 0; } diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 1e878beea8a5..0f3dc8689fb4 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -14,6 +14,7 @@ struct legacy_data { size_t num_cports; + struct gb_connection **connections; }; @@ -128,25 +129,44 @@ static void legacy_connection_exit(struct gb_connection *connection) static int legacy_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { + struct greybus_descriptor_cport *cport_desc; struct legacy_data *data; struct gb_connection *connection; - int ret; + int i; + int ret = -ENOMEM; + + dev_dbg(&bundle->dev, + "%s - bundle class = 0x%02x, num_cports = %zu\n", + __func__, bundle->class, bundle->num_cports); data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->num_cports = 0; - list_for_each_entry(connection, &bundle->connections, bundle_links) - data->num_cports++; + data->num_cports = bundle->num_cports; + data->connections = kcalloc(data->num_cports, + sizeof(*data->connections), + GFP_KERNEL); + if (!data->connections) + goto err_free_data; - dev_dbg(&bundle->dev, - "%s - bundle class = 0x%02x, num_cports = %zu\n", - __func__, bundle->class, data->num_cports); + for (i = 0; i < data->num_cports; ++i) { + cport_desc = &bundle->cport_desc[i]; + + connection = gb_connection_create_dynamic(bundle->intf, + bundle, + le16_to_cpu(cport_desc->id), + cport_desc->protocol_id); + if (!connection) + goto err_connections_destroy; + + data->connections[i] = connection; + } greybus_set_drvdata(bundle, data); - list_for_each_entry(connection, &bundle->connections, bundle_links) { + for (i = 0; i < data->num_cports; ++i) { + connection = data->connections[i]; dev_dbg(&bundle->dev, "enabling connection %s\n", connection->name); @@ -158,10 +178,13 @@ static int legacy_probe(struct gb_bundle *bundle, return 0; err_connections_disable: - list_for_each_entry_reverse(connection, &bundle->connections, - bundle_links) { - legacy_connection_exit(connection); - } + for (--i; i >= 0; --i) + legacy_connection_exit(data->connections[i]); +err_connections_destroy: + for (i = 0; i < data->num_cports; ++i) + gb_connection_destroy(data->connections[i]); + kfree(data->connections); +err_free_data: kfree(data); return ret; @@ -170,16 +193,17 @@ err_connections_disable: static void legacy_disconnect(struct gb_bundle *bundle) { struct legacy_data *data = greybus_get_drvdata(bundle); - struct gb_connection *connection; + int i; dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__, bundle->class); - list_for_each_entry_reverse(connection, &bundle->connections, - bundle_links) { - legacy_connection_exit(connection); + for (i = 0; i < data->num_cports; ++i) { + legacy_connection_exit(data->connections[i]); + gb_connection_destroy(data->connections[i]); } + kfree(data->connections); kfree(data); } diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 357f9c64821b..64c60c2dfaf5 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -228,19 +228,18 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) */ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) { - struct gb_connection *connection; struct gb_interface *intf = bundle->intf; + struct greybus_descriptor_cport *desc_cport; struct manifest_desc *desc; struct manifest_desc *next; + LIST_HEAD(list); u8 bundle_id = bundle->id; - u8 protocol_id; u16 cport_id; u32 count = 0; + int i; /* Set up all cport descriptors associated with this bundle */ list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { - struct greybus_descriptor_cport *desc_cport; - if (desc->type != GREYBUS_TYPE_CPORT) continue; @@ -252,16 +251,26 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) if (cport_id > CPORT_ID_MAX) goto exit; - /* Found one. Set up its function structure */ - protocol_id = desc_cport->protocol_id; - - connection = gb_connection_create_dynamic(intf, bundle, - cport_id, - protocol_id); - if (!connection) - goto exit; - + /* Found one, move it to our temporary list. */ + list_move(&desc->links, &list); count++; + } + + if (!count) + return 0; + + bundle->cport_desc = kcalloc(count, sizeof(*bundle->cport_desc), + GFP_KERNEL); + if (!bundle->cport_desc) + goto exit; + + bundle->num_cports = count; + + i = 0; + list_for_each_entry_safe(desc, next, &list, links) { + desc_cport = desc->data; + memcpy(&bundle->cport_desc[i++], desc_cport, + sizeof(*desc_cport)); /* Release the cport descriptor */ release_manifest_descriptor(desc); @@ -269,7 +278,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) return count; exit: - + release_cport_descriptors(&list, bundle_id); /* * Free all cports for this bundle to avoid 'excess descriptors' * warnings.