greybus: add module support
authorGreg Kroah-Hartman <greg@kroah.com>
Sun, 21 Dec 2014 22:10:26 +0000 (14:10 -0800)
committerGreg Kroah-Hartman <greg@kroah.com>
Tue, 23 Dec 2014 23:30:00 +0000 (15:30 -0800)
Modules in the greybus system sit above the interface, so insert them
early in the sysfs tree.  We dynamically create them when we have an
interface that references a module, as we don't get a "module create"
message directly.  They also dynamically go away when the last interface
associated with a module is removed.

Naming scheme for modules/interfaces/bundles/connections is bumped up by
one ':', and now looks like the following:

/sys/bus/greybus $ tree
.
├── devices
│   ├── 7 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7
│   ├── 7:7 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7/7:7
│   ├── 7:7:0 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7/7:7/7:7:0
│   └── 7:7:0:1 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7/7:7/7:7:0/7:7:0:1
├── drivers
├── drivers_autoprobe
├── drivers_probe
└── uevent

6 directories, 3 files
/sys/bus/greybus $ grep . devices/*/uevent
devices/7/uevent:DEVTYPE=greybus_module
devices/7:7/uevent:DEVTYPE=greybus_interface
devices/7:7:0/uevent:DEVTYPE=greybus_bundle
devices/7:7:0:1/uevent:DEVTYPE=greybus_connection

We still have some "confusion" about interface ids and module ids, which
will be cleaned up later when the svc control protocol changes die down,
right now we just name a module after the interface as we don't have any
modules that have multiple interfaces in our systems.

This has been tested with gbsim.

Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
drivers/staging/greybus/Makefile
drivers/staging/greybus/bundle.c
drivers/staging/greybus/connection.c
drivers/staging/greybus/core.c
drivers/staging/greybus/greybus.h
drivers/staging/greybus/interface.c
drivers/staging/greybus/interface.h
drivers/staging/greybus/kernel_ver.h
drivers/staging/greybus/module.c [new file with mode: 0644]
drivers/staging/greybus/module.h [new file with mode: 0644]

index 6c0b0ca5be380038427fe012c0b1cfe4946835a8..0d3977d87cd2d838e288dd5e2526ea65b694c6c9 100644 (file)
@@ -2,6 +2,7 @@ greybus-y :=    core.o          \
                debugfs.o       \
                ap.o            \
                manifest.o      \
+               module.o        \
                interface.o     \
                bundle.o        \
                connection.o    \
index 2ac67a242c0f5c8e4b79e2992a8d685fbc2082d4..28a82229adebd11e0a58e5be5cc4442bc49d145b 100644 (file)
@@ -71,7 +71,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 interface_id)
        bundle->dev.type = &greybus_bundle_type;
        bundle->dev.groups = bundle_groups;
        device_initialize(&bundle->dev);
-       dev_set_name(&bundle->dev, "%d:%d", intf->module_id, interface_id);
+       dev_set_name(&bundle->dev, "%s:%d", dev_name(&intf->dev), interface_id);
 
        retval = device_add(&bundle->dev);
        if (retval) {
index 2d61ee788218c795c2b723833cba9cc467043e65..3f786bf53798182ff3c4ccf2f1f058de88d0796d 100644 (file)
@@ -237,7 +237,7 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...)
        vaf.va = &args;
 
        pr_err("greybus: [%hhu:%hhu:%hu]: %pV\n",
-               connection->bundle->intf->module_id,
+               connection->bundle->intf->module->module_id,
                connection->bundle->id,
                connection->bundle_cport_id, &vaf);
 
index c90f74c7a25eaaac54921b4ac53d9f2a92597dae..f6ca89a6b88ca43db3c5004df8431c6432df80d5 100644 (file)
@@ -46,11 +46,14 @@ 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_module *module = NULL;
        struct gb_interface *intf = NULL;
        struct gb_bundle *bundle = NULL;
        struct gb_connection *connection = NULL;
 
-       if (is_gb_interface(dev)) {
+       if (is_gb_module(dev)) {
+               module = to_gb_module(dev);
+       } else if (is_gb_interface(dev)) {
                intf = to_gb_interface(dev);
        } else if (is_gb_bundle(dev)) {
                bundle = to_gb_bundle(dev);
index ced329af2a2d26bbe6c77d5a84866f2f19f954f1..68382b383390527b5a9920052bc6be04d52170d9 100644 (file)
@@ -24,6 +24,7 @@
 #include "greybus_id.h"
 #include "greybus_manifest.h"
 #include "manifest.h"
+#include "module.h"
 #include "interface.h"
 #include "bundle.h"
 #include "connection.h"
@@ -168,10 +169,16 @@ void gb_uart_device_exit(struct gb_connection *connection);
 int svc_set_route_send(struct gb_bundle *bundle,
                               struct greybus_host_device *hd);
 
+extern struct device_type greybus_module_type;
 extern struct device_type greybus_interface_type;
 extern struct device_type greybus_bundle_type;
 extern struct device_type greybus_connection_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;
index 96897f27a5503026c0f597bd0288fcb9ddadf80e..4b502c69fce55ae7f06a5b9769b20e35d46015bb 100644 (file)
@@ -74,13 +74,15 @@ gb_interface_match_id(struct gb_interface *intf,
        return NULL;
 }
 
+// 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 greybus_host_device *hd,
                                       u8 module_id)
 {
        struct gb_interface *intf;
 
        list_for_each_entry(intf, &hd->interfaces, links)
-               if (intf->module_id == module_id)
+               if (intf->module->module_id == module_id)
                        return intf;
 
        return NULL;
@@ -105,43 +107,51 @@ struct device_type greybus_interface_type = {
  *
  * Create a gb_interface structure to represent a discovered module.
  * The position within the Endo is encoded in the "module_id" argument.
- * Returns a pointer to the new module or a null pointer if a
+ * Returns a pointer to the new interfce or a null pointer if a
  * failure occurs due to memory exhaustion.
  */
 static struct gb_interface *gb_interface_create(struct greybus_host_device *hd,
                                                u8 module_id)
 {
+       struct gb_module *module;
        struct gb_interface *intf;
        int retval;
+       u8 interface_id = module_id;
 
-       intf = gb_interface_find(hd, module_id);
+       // FIXME we need an interface id here to check for this properly!
+       intf = gb_interface_find(hd, interface_id);
        if (intf) {
                dev_err(hd->parent, "Duplicate module id %d will not be created\n",
                        module_id);
                return NULL;
        }
 
+       module = gb_module_find_or_create(hd, module_id);
+       if (!module)
+               return NULL;
+
        intf = kzalloc(sizeof(*intf), GFP_KERNEL);
        if (!intf)
                return NULL;
 
        intf->hd = hd;          /* XXX refcount? */
-       intf->module_id = module_id;
+       intf->module = module;
        INIT_LIST_HEAD(&intf->bundles);
 
-       intf->dev.parent = hd->parent;
+       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->parent->dma_mask;
        device_initialize(&intf->dev);
-       dev_set_name(&intf->dev, "%d", module_id);
+       dev_set_name(&intf->dev, "%s:%d", dev_name(&module->dev), interface_id);
 
        retval = device_add(&intf->dev);
        if (retval) {
                pr_err("failed to add module device for id 0x%02hhx\n",
                        module_id);
                put_device(&intf->dev);
+               put_device(&module->dev);
                kfree(intf);
                return NULL;
        }
@@ -169,6 +179,7 @@ static void gb_interface_destroy(struct gb_interface *intf)
 
        kfree(intf->product_string);
        kfree(intf->vendor_string);
+       put_device(&intf->module->dev);
        /* kref_put(module->hd); */
 
        device_del(&intf->dev);
index 5dd2c20dc286ca995725104068d289e0f5766e7d..fd5001e908bfd92efa48be27adbc932cfc4518d7 100644 (file)
@@ -16,7 +16,7 @@ struct gb_interface {
 
        struct list_head bundles;
        struct list_head links; /* greybus_host_device->interfaces */
-       u8 module_id;           /* Physical location within the Endo */
+       u8 interface_id;        /* Physical location within the Endo */
 
        /* Information taken from the manifest module descriptor */
        u16 vendor;
@@ -25,6 +25,7 @@ struct gb_interface {
        char *product_string;
        u64 unique_id;
 
+       struct gb_module *module;
        struct greybus_host_device *hd;
 };
 #define to_gb_interface(d) container_of(d, struct gb_interface, dev)
index 66c27132e4acb453af6a1be55d3df840f0060e42..f0010a865c22f8196df366706f9d6bf8204120ea 100644 (file)
 }
 #endif
 
+#ifndef __ATTR_RW
+#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO),            \
+                                        _name##_show, _name##_store)
+#endif
+
 #ifndef DEVICE_ATTR_RO
 #define DEVICE_ATTR_RO(_name) \
        struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
        struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
 #endif
 
+#ifndef DEVICE_ATTR_RW
+#define DEVICE_ATTR_RW(_name) \
+       struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
+#endif
+
 #ifndef U8_MAX
 #define U8_MAX ((u8)~0U)
 #endif /* ! U8_MAX */
diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c
new file mode 100644 (file)
index 0000000..625e2d4
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Greybus module code
+ *
+ * Copyright 2014 Google Inc.
+ * Copyright 2014 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include "greybus.h"
+
+
+/*
+ * List of modules in the system.  We really should just walk the list the
+ * driver core provides us, but as we have lots of different things on the same
+ * "bus" at the same time, a single list of modules is simplest for now.
+ */
+static DEFINE_SPINLOCK(gb_modules_lock);
+static LIST_HEAD(module_list);
+
+/* module sysfs attributes */
+#define gb_module_attr(field, type)                                    \
+static ssize_t field##_show(struct device *dev,                                \
+                           struct device_attribute *attr,              \
+                           char *buf)                                  \
+{                                                                      \
+       struct gb_module *module = to_gb_module(dev);                   \
+       return sprintf(buf, "%"#type"\n", module->field);               \
+}                                                                      \
+static DEVICE_ATTR_RO(field)
+
+// FIXME, do we really need this attribute?
+gb_module_attr(module_id, x);
+
+static ssize_t epm_show(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       // FIXME, implement something here
+       return sprintf(buf, "1\n");
+}
+
+static ssize_t epm_store(struct device *dev, struct device_attribute *attr,
+                        const char *buf, size_t size)
+{
+       // FIXME, implement something here.
+       return 0;
+}
+static DEVICE_ATTR_RW(epm);
+
+static struct attribute *module_attrs[] = {
+       &dev_attr_module_id.attr,
+       &dev_attr_epm.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(module);
+
+static void greybus_module_release(struct device *dev)
+{
+       struct gb_module *module = to_gb_module(dev);
+
+       spin_lock(&gb_modules_lock);
+       list_del(&module->list);
+       spin_unlock(&gb_modules_lock);
+
+       kfree(module);
+}
+
+struct device_type greybus_module_type = {
+       .name =         "greybus_module",
+       .release =      greybus_module_release,
+};
+
+/*
+ * Search the list of modules in the system.  If one is found, return it, with
+ * the reference count incremented.
+ */
+static struct gb_module *gb_module_find(u8 module_id)
+{
+       struct gb_module *module;
+
+       spin_lock(&gb_modules_lock);
+       list_for_each_entry(module, &module_list, list) {
+               if (module->module_id == module_id) {
+                       get_device(&module->dev);
+                       goto exit;
+               }
+       }
+       module = NULL;
+exit:
+       spin_unlock(&gb_modules_lock);
+       return module;
+}
+
+static struct gb_module *gb_module_create(struct greybus_host_device *hd,
+                                         u8 module_id)
+{
+       struct gb_module *module;
+       int retval;
+
+       module = kzalloc(sizeof(*module), GFP_KERNEL);
+       if (!module)
+               return NULL;
+
+       module->module_id = module_id;
+       module->dev.parent = hd->parent;
+       module->dev.bus = &greybus_bus_type;
+       module->dev.type = &greybus_module_type;
+       module->dev.groups = module_groups;
+       module->dev.dma_mask = hd->parent->dma_mask;
+       device_initialize(&module->dev);
+       dev_set_name(&module->dev, "%d", module_id);
+
+       retval = device_add(&module->dev);
+       if (retval) {
+               pr_err("failed to add module device for id 0x%02hhx\n",
+                       module_id);
+               put_device(&module->dev);
+               kfree(module);
+               return NULL;
+       }
+
+       spin_lock(&gb_modules_lock);
+       list_add_tail(&module->list, &module_list);
+       spin_unlock(&gb_modules_lock);
+
+       return module;
+}
+
+struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd,
+                                          u8 module_id)
+{
+       struct gb_module *module;
+
+       module = gb_module_find(module_id);
+       if (module)
+               return module;
+
+       return gb_module_create(hd, module_id);
+}
+
diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h
new file mode 100644 (file)
index 0000000..9ca7c28
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Greybus module code
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef __MODULE_H
+#define __MODULE_H
+
+/* Greybus "public" definitions" */
+struct gb_module {
+       struct device dev;
+
+       struct list_head list;
+       u8 module_id;           /* Physical location within the Endo */
+};
+#define to_gb_module(d) container_of(d, struct gb_module, dev)
+
+struct greybus_host_device;
+
+/* Greybus "private" definitions */
+struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd,
+                                          u8 module_id);
+
+
+#endif /* __MODULE_H */