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 <greg@kroah.com>
+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 <greg@kroah.com>
+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 <greg@kroah.com>
+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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
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 <greg@kroah.com>
debugfs.o \
hd.o \
manifest.o \
+ module.o \
interface.o \
bundle.o \
connection.o \
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;
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);
} 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);
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;
#include "hd.h"
#include "svc.h"
#include "control.h"
+#include "module.h"
#include "interface.h"
#include "bundle.h"
#include "connection.h"
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;
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;
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;
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;
};
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);
*
* 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);
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);
/* 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;
}
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);
}
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;
u16 version_minor;
struct gb_host_device *hd;
+ struct gb_module *module;
unsigned long quirks;
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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
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;
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;
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. */
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)
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);
}
}
flush_workqueue(svc->wq);
- gb_svc_remove_interfaces(svc);
+ gb_svc_remove_modules(svc);
}
void gb_svc_put(struct gb_svc *svc)