diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 6c0b0ca5be38..0d3977d87cd2 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -2,6 +2,7 @@ greybus-y := core.o \ debugfs.o \ ap.o \ manifest.o \ + module.o \ interface.o \ bundle.o \ connection.o \ diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 2ac67a242c0f..28a82229adeb 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -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) { diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2d61ee788218..3f786bf53798 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -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); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index c90f74c7a25e..f6ca89a6b88c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -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); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index ced329af2a2d..68382b383390 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -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; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 96897f27a550..4b502c69fce5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -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); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 5dd2c20dc286..fd5001e908bf 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -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) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 66c27132e4ac..f0010a865c22 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -21,6 +21,11 @@ } #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) @@ -31,6 +36,11 @@ 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 index 000000000000..625e2d436073 --- /dev/null +++ b/drivers/staging/greybus/module.c @@ -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 index 000000000000..9ca7c2899a3f --- /dev/null +++ b/drivers/staging/greybus/module.h @@ -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 */