diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index cf6d3eec793a..41ffc40b8efb 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -6,22 +6,45 @@ Description: The "root" greybus device for the Greybus device tree, or bus, where N is a dynamically assigned 1-based id. -What: /sys/bus/greybus/device/N-I +What: /sys/bus/greybus/device/N-M +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + A Module M on the bus N, where M is the 1-byte interface + ID of the module's primary interface. + +What: /sys/bus/greybus/device/N-M/module_id +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The ID of a Greybus module, corresponding to the ID of its + primary interface. + +What: /sys/bus/greybus/device/N-M/num_interfaces +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The number of interfaces of a module. + +What: /sys/bus/greybus/device/N-M.I Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - An Interface I on the bus N, where I is the 1-byte interface - ID. + An Interface I on the bus N and module N-M, where I is the + 1-byte interface ID. -What: /sys/bus/greybus/device/N-I/current_now +What: /sys/bus/greybus/device/N-M.I/current_now Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Current measurement of the interface in microamps (uA) -What: /sys/bus/greybus/device/N-I/ddbl1_manufacturer_id +What: /sys/bus/greybus/device/N-M.I/ddbl1_manufacturer_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -29,7 +52,7 @@ Description: Unipro Device Descriptor Block Level 1 manufacturer ID for the greybus Interface. -What: /sys/bus/greybus/device/N-I/ddbl1_product_id +What: /sys/bus/greybus/device/N-M.I/ddbl1_product_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -37,28 +60,28 @@ Description: Unipro Device Descriptor Block Level 1 product ID for the greybus Interface. -What: /sys/bus/greybus/device/N-I/interface_id +What: /sys/bus/greybus/device/N-M.I/interface_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The ID of a Greybus interface. -What: /sys/bus/greybus/device/N-I/power_now +What: /sys/bus/greybus/device/N-M.I/power_now Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Power measurement of the interface in microwatts (uW) -What: /sys/bus/greybus/device/N-I/product_id +What: /sys/bus/greybus/device/N-M.I/product_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Product ID of a Greybus interface. -What: /sys/bus/greybus/device/N-I/serial_number +What: /sys/bus/greybus/device/N-M.I/serial_number Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -66,14 +89,14 @@ Description: Serial Number of the Greybus interface, represented by a 64 bit hexadecimal number. -What: /sys/bus/greybus/device/N-I/vendor_id +What: /sys/bus/greybus/device/N-M.I/vendor_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Vendor ID of a Greybus interface. -What: /sys/bus/greybus/device/N-I/version +What: /sys/bus/greybus/device/N-M.I/version Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -81,14 +104,14 @@ Description: Interface version represented as <16 bit major number>.<16 bit minor number>. -What: /sys/bus/greybus/device/N-I/voltage_now +What: /sys/bus/greybus/device/N-M.I/voltage_now Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Voltage measurement of the interface in microvolts (uV) -What: /sys/bus/greybus/device/N-I.ctrl +What: /sys/bus/greybus/device/N-M.I.ctrl Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -96,21 +119,21 @@ Description: Abstract control device for interface I that represents the current mode of an enumerated Greybus interface. -What: /sys/bus/greybus/device/N-I.ctrl/product_string +What: /sys/bus/greybus/device/N-M.I.ctrl/product_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Product ID string of a Greybus interface. -What: /sys/bus/greybus/device/N-I.ctrl/vendor_string +What: /sys/bus/greybus/device/N-M.I.ctrl/vendor_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Vendor ID string of a Greybus interface. -What: /sys/bus/greybus/device/N-I.B +What: /sys/bus/greybus/device/N-M.I.B Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -118,21 +141,21 @@ Description: A bundle B on the Interface I, B is replaced by a 1-byte number representing the bundle. -What: /sys/bus/greybus/device/N-I.B/bundle_class +What: /sys/bus/greybus/device/N-M.I.B/bundle_class Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The greybus class of the bundle B. -What: /sys/bus/greybus/device/N-I.B/bundle_id +What: /sys/bus/greybus/device/N-M.I.B/bundle_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The interface-unique id of the bundle B. -What: /sys/bus/greybus/device/N-I.B/state +What: /sys/bus/greybus/device/N-M.I.B/state Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 65259ea9d111..8ec607460ec0 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -2,6 +2,7 @@ greybus-y := core.o \ debugfs.o \ hd.o \ manifest.o \ + module.o \ interface.o \ bundle.o \ connection.o \ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9f143e5a7c9c..70a66e28471d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -85,6 +85,7 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { struct gb_host_device *hd; + struct gb_module *module = NULL; struct gb_interface *intf = NULL; struct gb_control *control = NULL; struct gb_bundle *bundle = NULL; @@ -92,8 +93,12 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) if (is_gb_host_device(dev)) { hd = to_gb_host_device(dev); + } else if (is_gb_module(dev)) { + module = to_gb_module(dev); + hd = module->hd; } else if (is_gb_interface(dev)) { intf = to_gb_interface(dev); + module = intf->module; hd = intf->hd; } else if (is_gb_control(dev)) { control = to_gb_control(dev); @@ -102,6 +107,7 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); intf = bundle->intf; + module = intf->module; hd = intf->hd; } else if (is_gb_svc(dev)) { svc = to_gb_svc(dev); @@ -114,6 +120,11 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "BUS=%u", hd->bus_id)) return -ENOMEM; + if (module) { + if (add_uevent_var(env, "MODULE=%u", module->module_id)) + return -ENOMEM; + } + if (intf) { if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) return -ENOMEM; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4ab5f608b8cd..f5447e7592e9 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -28,6 +28,7 @@ #include "hd.h" #include "svc.h" #include "control.h" +#include "module.h" #include "interface.h" #include "bundle.h" #include "connection.h" @@ -112,6 +113,7 @@ struct dentry *gb_debugfs_get(void); extern struct bus_type greybus_bus_type; extern struct device_type greybus_hd_type; +extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_control_type; extern struct device_type greybus_bundle_type; @@ -122,6 +124,11 @@ static inline int is_gb_host_device(const struct device *dev) return dev->type == &greybus_hd_type; } +static inline int is_gb_module(const struct device *dev) +{ + return dev->type == &greybus_module_type; +} + static inline int is_gb_interface(const struct device *dev) { return dev->type == &greybus_interface_type; diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 2c13860e4a05..762cc035b128 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -89,7 +89,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, hd->bus_id = ret; hd->driver = driver; - INIT_LIST_HEAD(&hd->interfaces); + INIT_LIST_HEAD(&hd->modules); INIT_LIST_HEAD(&hd->connections); ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index b481dd03bd73..ff71936a7932 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -34,7 +34,7 @@ struct gb_host_device { int bus_id; const struct gb_hd_driver *driver; - struct list_head interfaces; + struct list_head modules; struct list_head connections; struct ida cport_id_map; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 89fe901cb0a3..d51c5635f22b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -333,21 +333,6 @@ static struct attribute *interface_attrs[] = { }; ATTRIBUTE_GROUPS(interface); - -// FIXME, odds are you don't want to call this function, rework the caller to -// not need it please. -struct gb_interface *gb_interface_find(struct gb_host_device *hd, - u8 interface_id) -{ - struct gb_interface *intf; - - list_for_each_entry(intf, &hd->interfaces, links) - if (intf->interface_id == interface_id) - return intf; - - return NULL; -} - static void gb_interface_release(struct device *dev) { struct gb_interface *intf = to_gb_interface(dev); @@ -371,13 +356,11 @@ struct device_type greybus_interface_type = { * * Returns a pointer to the new interfce or a null pointer if a * failure occurs due to memory exhaustion. - * - * Locking: Caller ensures serialisation with gb_interface_remove and - * gb_interface_find. */ -struct gb_interface *gb_interface_create(struct gb_host_device *hd, +struct gb_interface *gb_interface_create(struct gb_module *module, u8 interface_id) { + struct gb_host_device *hd = module->hd; struct gb_interface *intf; intf = kzalloc(sizeof(*intf), GFP_KERNEL); @@ -385,6 +368,7 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, return NULL; intf->hd = hd; /* XXX refcount? */ + intf->module = module; intf->interface_id = interface_id; INIT_LIST_HEAD(&intf->bundles); INIT_LIST_HEAD(&intf->manifest_descs); @@ -392,15 +376,14 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, /* Invalid device id to start with */ intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; - intf->dev.parent = &hd->dev; + intf->dev.parent = &module->dev; intf->dev.bus = &greybus_bus_type; intf->dev.type = &greybus_interface_type; intf->dev.groups = interface_groups; - intf->dev.dma_mask = hd->dev.dma_mask; + intf->dev.dma_mask = module->dev.dma_mask; device_initialize(&intf->dev); - dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id); - - list_add(&intf->links, &hd->interfaces); + dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev), + interface_id); return intf; } @@ -579,15 +562,16 @@ int gb_interface_add(struct gb_interface *intf) return 0; } -/* Deregister an interface and drop its reference. */ -void gb_interface_remove(struct gb_interface *intf) +/* Deregister an interface. */ +void gb_interface_del(struct gb_interface *intf) { if (device_is_registered(&intf->dev)) { device_del(&intf->dev); dev_info(&intf->dev, "Interface removed\n"); } +} - list_del(&intf->links); - +void gb_interface_put(struct gb_interface *intf) +{ put_device(&intf->dev); } diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 63ba696c14a5..9185c7f1bd32 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -20,7 +20,7 @@ struct gb_interface { struct gb_control *control; struct list_head bundles; - struct list_head links; /* gb_host_device->interfaces */ + struct list_head module_node; struct list_head manifest_descs; u8 interface_id; /* Physical location within the Endo */ u8 device_id; @@ -35,6 +35,7 @@ struct gb_interface { u16 version_minor; struct gb_host_device *hd; + struct gb_module *module; unsigned long quirks; @@ -46,13 +47,14 @@ struct gb_interface { struct gb_interface *gb_interface_find(struct gb_host_device *hd, u8 interface_id); -struct gb_interface *gb_interface_create(struct gb_host_device *hd, +struct gb_interface *gb_interface_create(struct gb_module *module, u8 interface_id); int gb_interface_activate(struct gb_interface *intf); void gb_interface_deactivate(struct gb_interface *intf); int gb_interface_enable(struct gb_interface *intf); void gb_interface_disable(struct gb_interface *intf); int gb_interface_add(struct gb_interface *intf); -void gb_interface_remove(struct gb_interface *intf); +void gb_interface_del(struct gb_interface *intf); +void gb_interface_put(struct gb_interface *intf); #endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c new file mode 100644 index 000000000000..5c498e0a4eaf --- /dev/null +++ b/drivers/staging/greybus/module.c @@ -0,0 +1,178 @@ +/* + * Greybus Module code + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + + +static ssize_t module_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_module *module = to_gb_module(dev); + + return sprintf(buf, "%u\n", module->module_id); +} +static DEVICE_ATTR_RO(module_id); + +static ssize_t num_interfaces_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_module *module = to_gb_module(dev); + + return sprintf(buf, "%zu\n", module->num_interfaces); +} +static DEVICE_ATTR_RO(num_interfaces); + +static struct attribute *module_attrs[] = { + &dev_attr_module_id.attr, + &dev_attr_num_interfaces.attr, + NULL, +}; +ATTRIBUTE_GROUPS(module); + +static void gb_module_release(struct device *dev) +{ + struct gb_module *module = to_gb_module(dev); + + kfree(module); +} + +struct device_type greybus_module_type = { + .name = "greybus_module", + .release = gb_module_release, +}; + +struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id, + size_t num_interfaces) +{ + struct gb_interface *intf; + struct gb_module *module; + int i; + + module = kzalloc(sizeof(*module) + num_interfaces * sizeof(intf), + GFP_KERNEL); + if (!module) + return NULL; + + module->hd = hd; + module->module_id = module_id; + module->num_interfaces = num_interfaces; + + module->dev.parent = &hd->dev; + module->dev.bus = &greybus_bus_type; + module->dev.type = &greybus_module_type; + module->dev.groups = module_groups; + module->dev.dma_mask = hd->dev.dma_mask; + device_initialize(&module->dev); + dev_set_name(&module->dev, "%d-%u", hd->bus_id, module_id); + + for (i = 0; i < num_interfaces; ++i) { + intf = gb_interface_create(module, module_id + i); + if (!intf) { + dev_err(&module->dev, "failed to create interface %u\n", + module_id + i); + goto err_put_interfaces; + } + module->interfaces[i] = intf; + } + + return module; + +err_put_interfaces: + for (--i; i > 0; --i) + gb_interface_put(module->interfaces[i]); + + put_device(&module->dev); + + return NULL; +} + +/* + * Register and enable an interface after first attempting to activate it. + */ +static void gb_module_register_interface(struct gb_interface *intf) +{ + struct gb_module *module = intf->module; + u8 intf_id = intf->interface_id; + int ret; + + ret = gb_interface_activate(intf); + if (ret) { + dev_err(&module->dev, "failed to activate interface %u: %d\n", + intf_id, ret); + gb_interface_add(intf); + return; + } + + ret = gb_interface_add(intf); + if (ret) + goto err_interface_deactivate; + + ret = gb_interface_enable(intf); + if (ret) { + dev_err(&module->dev, "failed to enable interface %u: %d\n", + intf_id, ret); + goto err_interface_deactivate; + } + + return; + +err_interface_deactivate: + gb_interface_deactivate(intf); +} + +static void gb_module_deregister_interface(struct gb_interface *intf) +{ + /* Mark as disconnected to prevent I/O during disable. */ + if (intf->module->disconnected) + intf->disconnected = true; + + gb_interface_disable(intf); + gb_interface_deactivate(intf); + + gb_interface_del(intf); +} + +/* Register a module and its interfaces. */ +int gb_module_add(struct gb_module *module) +{ + size_t i; + int ret; + + ret = device_add(&module->dev); + if (ret) { + dev_err(&module->dev, "failed to register module: %d\n", ret); + return ret; + } + + for (i = 0; i < module->num_interfaces; ++i) + gb_module_register_interface(module->interfaces[i]); + + return 0; +} + +/* Deregister a module and its interfaces. */ +void gb_module_del(struct gb_module *module) +{ + size_t i; + + for (i = 0; i < module->num_interfaces; ++i) + gb_module_deregister_interface(module->interfaces[i]); + + device_del(&module->dev); +} + +void gb_module_put(struct gb_module *module) +{ + size_t i; + + for (i = 0; i < module->num_interfaces; ++i) + gb_interface_put(module->interfaces[i]); + + put_device(&module->dev); +} diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h new file mode 100644 index 000000000000..88a97ce04243 --- /dev/null +++ b/drivers/staging/greybus/module.h @@ -0,0 +1,34 @@ +/* + * Greybus Module code + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __MODULE_H +#define __MODULE_H + +struct gb_module { + struct device dev; + struct gb_host_device *hd; + + struct list_head hd_node; + + u8 module_id; + size_t num_interfaces; + + bool disconnected; + + struct gb_interface *interfaces[0]; +}; +#define to_gb_module(d) container_of(d, struct gb_module, dev) + +struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id, + size_t num_interfaces); +int gb_module_add(struct gb_module *module); +void gb_module_del(struct gb_module *module); +void gb_module_put(struct gb_module *module); + +#endif /* __MODULE_H */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index cde59d02400b..94016954a6ab 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -665,6 +665,19 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } +static struct gb_module *gb_svc_module_lookup(struct gb_svc *svc, u8 module_id) +{ + struct gb_host_device *hd = svc->hd; + struct gb_module *module; + + list_for_each_entry(module, &hd->modules, hd_node) { + if (module->module_id == module_id) + return module; + } + + return NULL; +} + static void gb_svc_intf_reenable(struct gb_svc *svc, struct gb_interface *intf) { int ret; @@ -689,7 +702,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) struct gb_connection *connection = operation->connection; struct gb_svc *svc = gb_connection_get_data(connection); struct gb_host_device *hd = connection->hd; - struct gb_interface *intf; + struct gb_module *module; u8 intf_id; int ret; @@ -699,52 +712,35 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); - intf = gb_interface_find(hd, intf_id); - if (intf) { + /* All modules are considered 1x2 for now */ + module = gb_svc_module_lookup(svc, intf_id); + if (module) { dev_info(&svc->dev, "mode switch detected on interface %u\n", intf_id); - return gb_svc_intf_reenable(svc, intf); + return gb_svc_intf_reenable(svc, module->interfaces[0]); } - intf = gb_interface_create(hd, intf_id); - if (!intf) { - dev_err(&svc->dev, "failed to create interface %u\n", - intf_id); + module = gb_module_create(hd, intf_id, 1); + if (!module) { + dev_err(&svc->dev, "failed to create module\n"); return; } - ret = gb_interface_activate(intf); + ret = gb_module_add(module); if (ret) { - dev_err(&svc->dev, "failed to activate interface %u: %d\n", - intf_id, ret); - gb_interface_add(intf); + gb_module_put(module); return; } - ret = gb_interface_add(intf); - if (ret) - goto err_interface_deactivate; - - ret = gb_interface_enable(intf); - if (ret) { - dev_err(&svc->dev, "failed to enable interface %u: %d\n", - intf_id, ret); - goto err_interface_deactivate; - } - - return; - -err_interface_deactivate: - gb_interface_deactivate(intf); + list_add(&module->hd_node, &hd->modules); } static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) { struct gb_svc *svc = gb_connection_get_data(operation->connection); struct gb_svc_intf_hot_unplug_request *request; - struct gb_host_device *hd = operation->connection->hd; - struct gb_interface *intf; + struct gb_module *module; u8 intf_id; /* The request message size has already been verified. */ @@ -753,19 +749,19 @@ static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); - intf = gb_interface_find(hd, intf_id); - if (!intf) { + /* All modules are considered 1x2 for now */ + module = gb_svc_module_lookup(svc, intf_id); + if (!module) { dev_warn(&svc->dev, "could not find hot-unplug interface %u\n", intf_id); return; } - /* Mark as disconnected to prevent I/O during disable. */ - intf->disconnected = true; + module->disconnected = true; - gb_interface_disable(intf); - gb_interface_deactivate(intf); - gb_interface_remove(intf); + gb_module_del(module); + list_del(&module->hd_node); + gb_module_put(module); } static void gb_svc_process_deferred_request(struct work_struct *work) @@ -1104,14 +1100,15 @@ int gb_svc_add(struct gb_svc *svc) return 0; } -static void gb_svc_remove_interfaces(struct gb_svc *svc) +static void gb_svc_remove_modules(struct gb_svc *svc) { - struct gb_interface *intf, *tmp; + struct gb_host_device *hd = svc->hd; + struct gb_module *module, *tmp; - list_for_each_entry_safe(intf, tmp, &svc->hd->interfaces, links) { - gb_interface_disable(intf); - gb_interface_deactivate(intf); - gb_interface_remove(intf); + list_for_each_entry_safe(module, tmp, &hd->modules, hd_node) { + gb_module_del(module); + list_del(&module->hd_node); + gb_module_put(module); } } @@ -1132,7 +1129,7 @@ void gb_svc_del(struct gb_svc *svc) flush_workqueue(svc->wq); - gb_svc_remove_interfaces(svc); + gb_svc_remove_modules(svc); } void gb_svc_put(struct gb_svc *svc)