]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
greybus: hook up greybus to the driver model
authorGreg Kroah-Hartman <greg@kroah.com>
Fri, 24 Oct 2014 09:34:46 +0000 (17:34 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 24 Oct 2014 09:40:59 +0000 (17:40 +0800)
This patch hooks up modules, interfaces, and connections to the driver
model.  Now we have a correct hierarchy, and drivers can be correctly
bound to the proper portions in the future.  Devices are correctly
reference counted and torn down in the proper order on removal of a
module.

Some basic sysfs attributes have been created for interfaces and
connections.  Module attributes are not working properly, but that will
be fixed in future changes.

This has been tested on Alex's machine, with multiple hotplug and unplug
operations of a module working correctly.

Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/greybus/connection.c
drivers/staging/greybus/connection.h
drivers/staging/greybus/core.c
drivers/staging/greybus/greybus.h
drivers/staging/greybus/interface.c
drivers/staging/greybus/interface.h
drivers/staging/greybus/module.c

index b1e933fc7044bd47e9115ecb13506ad18be45b7d..2d71679ff66cb66038c40e04ff9deadc952d6614 100644 (file)
@@ -111,6 +111,44 @@ static void connection_timeout(struct work_struct *work)
        printk("timeout!\n");
 }
 
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct gb_connection *connection = to_gb_connection(dev);
+
+       return sprintf(buf, "%d", connection->state);
+}
+static DEVICE_ATTR_RO(state);
+
+static ssize_t protocol_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct gb_connection *connection = to_gb_connection(dev);
+
+       return sprintf(buf, "%d", connection->protocol);
+}
+static DEVICE_ATTR_RO(protocol);
+
+static struct attribute *connection_attrs[] = {
+       &dev_attr_state.attr,
+       &dev_attr_protocol.attr,
+       NULL,
+};
+
+ATTRIBUTE_GROUPS(connection);
+
+static void gb_connection_release(struct device *dev)
+{
+       struct gb_connection *connection = to_gb_connection(dev);
+
+       kfree(connection);
+}
+
+static struct device_type greybus_connection_type = {
+       .name =         "greybus_connection",
+       .release =      gb_connection_release,
+};
+
 /*
  * Set up a Greybus connection, representing the bidirectional link
  * between a CPort on a (local) Greybus host device and a CPort on
@@ -127,6 +165,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface,
 {
        struct gb_connection *connection;
        struct greybus_host_device *hd;
+       int retval;
 
        connection = kzalloc(sizeof(*connection), GFP_KERNEL);
        if (!connection)
@@ -145,6 +184,21 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface,
        connection->protocol = protocol;
        connection->state = GB_CONNECTION_STATE_DISABLED;
 
+       connection->dev.parent = &interface->dev;
+       connection->dev.driver = NULL;
+       connection->dev.bus = &greybus_bus_type;
+       connection->dev.type = &greybus_connection_type;
+       connection->dev.groups = connection_groups;
+       device_initialize(&connection->dev);
+       dev_set_name(&connection->dev, "%s:%d",
+                    dev_name(&interface->dev), cport_id);
+
+       retval = device_add(&connection->dev);
+       if (retval) {
+               kfree(connection);
+               return NULL;
+       }
+
        spin_lock_irq(&gb_connections_lock);
        _gb_hd_connection_insert(hd, connection);
        list_add_tail(&connection->interface_links, &interface->connections);
@@ -182,9 +236,8 @@ void gb_connection_destroy(struct gb_connection *connection)
        spin_unlock_irq(&gb_connections_lock);
 
        gb_connection_hd_cport_id_free(connection);
-       /* kref_put(connection->interface); */
-       /* kref_put(connection->hd); */
-       kfree(connection);
+
+       device_del(&connection->dev);
 }
 
 u16 gb_connection_operation_id(struct gb_connection *connection)
index a16e52a8ba18168c71ea001323d587f1c89e3bdd..19dd91dae0624137414e2d383040876c7b2d8cc4 100644 (file)
@@ -24,6 +24,7 @@ enum gb_connection_state {
 struct gb_connection {
        struct greybus_host_device      *hd;
        struct gb_interface             *interface;
+       struct device                   dev;
        u16                             hd_cport_id;
        u16                             interface_cport_id;
 
@@ -39,6 +40,7 @@ struct gb_connection {
 
        void                            *private;
 };
+#define to_gb_connection(d) container_of(d, struct gb_connection, dev)
 
 struct gb_connection *gb_connection_create(struct gb_interface *interface,
                                u16 cport_id, enum greybus_protocol protocol);
index 9f4ae1c63fa0426eeceae656a193707b6f2dbf18..853bfd6503f62ddb94649934d7169435159a8853 100644 (file)
@@ -48,10 +48,14 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
        /* struct gb_module *gmod = to_gb_module(dev); */
 
        /* FIXME - add some uevents here... */
+
+       /* FIXME - be sure to check the type to know how to handle modules and
+        * interfaces differently */
+
        return 0;
 }
 
-static struct bus_type greybus_bus_type = {
+struct bus_type greybus_bus_type = {
        .name =         "greybus",
        .match =        greybus_module_match,
        .uevent =       greybus_uevent,
@@ -115,18 +119,6 @@ void greybus_deregister(struct greybus_driver *driver)
 EXPORT_SYMBOL_GPL(greybus_deregister);
 
 
-static void greybus_module_release(struct device *dev)
-{
-       struct gb_module *gmod = to_gb_module(dev);
-
-       gb_module_destroy(gmod);
-}
-
-static struct device_type greybus_module_type = {
-       .name =         "greybus_module",
-       .release =      greybus_module_release,
-};
-
 static const struct greybus_module_id fake_greybus_module_id = {
        GREYBUS_DEVICE(0x42, 0x42)
 };
@@ -142,7 +134,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id,
                   u8 *data, int size)
 {
        struct gb_module *gmod;
-       int retval;
 
        gmod = gb_module_create(hd, module_id);
        if (!gmod) {
@@ -168,31 +159,10 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id,
         * configuring the switch to allow them to communicate).
         */
 
-       gmod->dev.parent = hd->parent;
-       gmod->dev.driver = NULL;
-       gmod->dev.bus = &greybus_bus_type;
-       gmod->dev.type = &greybus_module_type;
-       gmod->dev.groups = greybus_module_groups;
-       gmod->dev.dma_mask = hd->parent->dma_mask;
-       device_initialize(&gmod->dev);
-       dev_set_name(&gmod->dev, "%d", module_id);
-
-       retval = device_add(&gmod->dev);
-       if (retval)
-               goto err_device;
-
        return;
-err_device:
-       put_device(&gmod->dev);
-err_module:
-       greybus_module_release(&gmod->dev);
-}
-
-static void gb_delete_module(struct gb_module *gmod)
-{
-       /* FIXME - tear down interfaces first */
 
-       device_del(&gmod->dev);
+err_module:
+       gb_module_destroy(gmod);
 }
 
 void gb_remove_module(struct greybus_host_device *hd, u8 module_id)
@@ -207,7 +177,7 @@ void gb_remove_module(struct greybus_host_device *hd, u8 module_id)
                }
 
        if (found)
-               gb_delete_module(gmod);
+               gb_module_destroy(gmod);
        else
                dev_err(hd->parent, "module id %d remove error\n", module_id);
 }
@@ -217,7 +187,7 @@ static void gb_remove_modules(struct greybus_host_device *hd)
        struct gb_module *gmod, *temp;
 
        list_for_each_entry_safe(gmod, temp, &hd->modules, links) {
-               gb_delete_module(gmod);
+               gb_module_destroy(gmod);
        }
 }
 
@@ -256,6 +226,8 @@ EXPORT_SYMBOL_GPL(greybus_create_hd);
 
 void greybus_remove_hd(struct greybus_host_device *hd)
 {
+       /* Tear down all modules that happen to be associated with this host
+        * controller */
        gb_remove_modules(hd);
        kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
 }
index 617d55ca7e1d3152de48b073fa7593629de99fe3..f287f3b0a3e553b2ddfcd760706f4b53eb14e75f 100644 (file)
@@ -261,6 +261,7 @@ int gb_register_cport_complete(struct gb_module *gmod,
                               void *context);
 void gb_deregister_cport_complete(u16 cport_id);
 
+extern struct bus_type greybus_bus_type;
 extern const struct attribute_group *greybus_module_groups[];
 
 int gb_i2c_device_init(struct gb_connection *connection);
index 0415db51051ece8b0ee230482f0d7084e7a158a7..a7375a2809d534cdbeeaec951440150a1e721999 100644 (file)
@@ -8,6 +8,35 @@
 
 #include "greybus.h"
 
+static ssize_t device_id_show(struct device *dev, struct device_attribute *attr,
+                             char *buf)
+{
+       struct gb_interface *interface = to_gb_interface(dev);
+
+       return sprintf(buf, "%d", interface->device_id);
+}
+static DEVICE_ATTR_RO(device_id);
+
+static struct attribute *interface_attrs[] = {
+       &dev_attr_device_id.attr,
+       NULL,
+};
+
+ATTRIBUTE_GROUPS(interface);
+
+static void gb_interface_release(struct device *dev)
+{
+       struct gb_interface *interface = to_gb_interface(dev);
+
+       kfree(interface);
+}
+
+static struct device_type greybus_interface_type = {
+       .name =         "greybus_interface",
+       .release =      gb_interface_release,
+};
+
+
 /* XXX This could be per-host device or per-module */
 static DEFINE_SPINLOCK(gb_interfaces_lock);
 
@@ -26,6 +55,7 @@ struct gb_interface *
 gb_interface_create(struct gb_module *gmod, u8 interface_id)
 {
        struct gb_interface *interface;
+       int retval;
 
        interface = kzalloc(sizeof(*interface), GFP_KERNEL);
        if (!interface)
@@ -33,8 +63,25 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id)
 
        interface->gmod = gmod;         /* XXX refcount? */
        interface->id = interface_id;
+       interface->device_id = 0xff;    /* Invalid device id to start with */
        INIT_LIST_HEAD(&interface->connections);
 
+       /* Build up the interface device structures and register it with the
+        * driver core */
+       interface->dev.parent = &gmod->dev;
+       interface->dev.driver = NULL;
+       interface->dev.bus = &greybus_bus_type;
+       interface->dev.type = &greybus_interface_type;
+       interface->dev.groups = interface_groups;
+       device_initialize(&interface->dev);
+       dev_set_name(&interface->dev, "%d:%d", gmod->module_id, interface_id);
+
+       retval = device_add(&interface->dev);
+       if (retval) {
+               kfree(interface);
+               return NULL;
+       }
+
        spin_lock_irq(&gb_interfaces_lock);
        list_add_tail(&interface->links, &gmod->interfaces);
        spin_unlock_irq(&gb_interfaces_lock);
@@ -45,19 +92,21 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id)
 /*
  * Tear down a previously set up interface.
  */
-void gb_interface_destroy(struct gb_interface *interface)
+void gb_interface_destroy(struct gb_module *gmod)
 {
-       if (WARN_ON(!interface))
+       struct gb_interface *interface;
+       struct gb_interface *temp;
+
+       if (WARN_ON(!gmod))
                return;
 
        spin_lock_irq(&gb_interfaces_lock);
-       list_del(&interface->links);
+       list_for_each_entry_safe(interface, temp, &gmod->interfaces, links) {
+               list_del(&interface->links);
+               gb_interface_connections_exit(interface);
+               device_del(&interface->dev);
+       }
        spin_unlock_irq(&gb_interfaces_lock);
-
-       gb_interface_connections_exit(interface);
-
-       /* kref_put(gmod); */
-       kfree(interface);
 }
 
 struct gb_interface *gb_interface_find(struct gb_module *module,
@@ -65,9 +114,13 @@ struct gb_interface *gb_interface_find(struct gb_module *module,
 {
        struct gb_interface *interface;
 
+       spin_lock_irq(&gb_interfaces_lock);
        list_for_each_entry(interface, &module->interfaces, links)
-               if (interface->id == interface_id)
+               if (interface->id == interface_id) {
+                       spin_unlock_irq(&gb_interfaces_lock);
                        return interface;
+               }
+       spin_unlock_irq(&gb_interfaces_lock);
 
        return NULL;
 }
index 907b6341665c3edc84835584cf77d6618880e254..50b0317d267e6621e4d02452cef3a842a82e25ee 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/list.h>
 
 struct gb_interface {
+       struct device           dev;
        struct gb_module        *gmod;
        u8                      id;
        u8                      device_id;
@@ -19,9 +20,10 @@ struct gb_interface {
 
        struct list_head        links;  /* module->interfaces */
 };
+#define to_gb_interface(d) container_of(d, struct gb_interface, dev)
 
 struct gb_interface *gb_interface_create(struct gb_module *gmod, u8 module_id);
-void gb_interface_destroy(struct gb_interface *interface);
+void gb_interface_destroy(struct gb_module *gmod);
 
 struct gb_interface *gb_interface_find(struct gb_module *gmod, u8 interface_id);
 
index b1ec0904fb28d4015ab5a2a9b67993f05f564e7e..f9415c0f735ee17c0dd655236009a5c90b437fe4 100644 (file)
@@ -44,15 +44,18 @@ const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod,
        return NULL;
 }
 
-static void gb_module_interfaces_exit(struct gb_module *gmod)
+static void greybus_module_release(struct device *dev)
 {
-       struct gb_interface *interface;
-       struct gb_interface *next;
+       struct gb_module *gmod = to_gb_module(dev);
 
-       list_for_each_entry_safe(interface, next, &gmod->interfaces, links)
-               gb_interface_destroy(interface);
+       kfree(gmod);
 }
 
+static struct device_type greybus_module_type = {
+       .name =         "greybus_module",
+       .release =      greybus_module_release,
+};
+
 /*
  * A Greybus module represents a user-replacable component on an Ara
  * phone.
@@ -65,6 +68,7 @@ static void gb_module_interfaces_exit(struct gb_module *gmod)
 struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id)
 {
        struct gb_module *gmod;
+       int retval;
 
        gmod = kzalloc(sizeof(*gmod), GFP_KERNEL);
        if (!gmod)
@@ -78,6 +82,21 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id)
        list_add_tail(&gmod->links, &hd->modules);
        spin_unlock_irq(&gb_modules_lock);
 
+       gmod->dev.parent = hd->parent;
+       gmod->dev.driver = NULL;
+       gmod->dev.bus = &greybus_bus_type;
+       gmod->dev.type = &greybus_module_type;
+       gmod->dev.groups = greybus_module_groups;
+       gmod->dev.dma_mask = hd->parent->dma_mask;
+       device_initialize(&gmod->dev);
+       dev_set_name(&gmod->dev, "%d", module_id);
+
+       retval = device_add(&gmod->dev);
+       if (retval) {
+               put_device(&gmod->dev);
+               return NULL;
+       }
+
        return gmod;
 }
 
@@ -93,18 +112,15 @@ void gb_module_destroy(struct gb_module *gmod)
        list_del(&gmod->links);
        spin_unlock_irq(&gb_modules_lock);
 
-       gb_module_interfaces_exit(gmod);
        /* XXX Do something with gmod->gb_tty */
 
-       put_device(&gmod->dev);
-       /* kfree(gmod->dev->name); */
+       gb_interface_destroy(gmod);
 
        kfree(gmod->product_string);
        kfree(gmod->vendor_string);
        /* kref_put(module->hd); */
 
-
-       kfree(gmod);
+       device_del(&gmod->dev);
 }
 
 struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id)