linux-stable/drivers/staging/greybus/interface.c

256 lines
6.1 KiB
C
Raw Normal View History

/*
* Greybus interface code
*
* Copyright 2014 Google Inc.
* Copyright 2014 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include "greybus.h"
/* interface sysfs attributes */
#define gb_interface_attr(field, type) \
static ssize_t field##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct gb_interface *intf = to_gb_interface(dev); \
return scnprintf(buf, PAGE_SIZE, type"\n", intf->field); \
} \
static DEVICE_ATTR_RO(field)
gb_interface_attr(ddbl1_manufacturer_id, "0x%08x");
gb_interface_attr(ddbl1_product_id, "0x%08x");
gb_interface_attr(interface_id, "%u");
gb_interface_attr(vendor_id, "0x%08x");
gb_interface_attr(product_id, "0x%08x");
gb_interface_attr(vendor_string, "%s");
gb_interface_attr(product_string, "%s");
gb_interface_attr(serial_number, "0x%016llx");
static ssize_t version_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct gb_interface *intf = to_gb_interface(dev);
return scnprintf(buf, PAGE_SIZE, "%u.%u\n", intf->version_major,
intf->version_minor);
}
static DEVICE_ATTR_RO(version);
static struct attribute *interface_attrs[] = {
&dev_attr_ddbl1_manufacturer_id.attr,
&dev_attr_ddbl1_product_id.attr,
&dev_attr_interface_id.attr,
&dev_attr_vendor_id.attr,
&dev_attr_product_id.attr,
&dev_attr_vendor_string.attr,
&dev_attr_product_string.attr,
&dev_attr_serial_number.attr,
&dev_attr_version.attr,
NULL,
};
ATTRIBUTE_GROUPS(interface);
/* XXX This could be per-host device */
static DEFINE_SPINLOCK(gb_interfaces_lock);
greybus: add module support 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>
2014-12-21 22:10:26 +00:00
// 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);
kfree(intf->product_string);
kfree(intf->vendor_string);
if (intf->control)
gb_control_destroy(intf->control);
kfree(intf);
}
struct device_type greybus_interface_type = {
.name = "greybus_interface",
.release = gb_interface_release,
};
/*
* A Greybus module represents a user-replaceable component on an Ara
* phone. An interface is the physical connection on that module. A
* module may have more than one interface.
*
* Create a gb_interface structure to represent a discovered interface.
* The position of interface within the Endo is encoded in "interface_id"
* argument.
*
greybus: add module support 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>
2014-12-21 22:10:26 +00:00
* Returns a pointer to the new interfce or a null pointer if a
* failure occurs due to memory exhaustion.
*/
struct gb_interface *gb_interface_create(struct gb_host_device *hd,
u8 interface_id)
{
struct gb_interface *intf;
intf = kzalloc(sizeof(*intf), GFP_KERNEL);
if (!intf)
return NULL;
intf->hd = hd; /* XXX refcount? */
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_DEVICE_ID_BAD;
intf->dev.parent = &hd->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;
device_initialize(&intf->dev);
dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id);
intf->control = gb_control_create(intf);
if (!intf->control) {
put_device(&intf->dev);
return NULL;
}
spin_lock_irq(&gb_interfaces_lock);
list_add(&intf->links, &hd->interfaces);
spin_unlock_irq(&gb_interfaces_lock);
return intf;
}
/*
* Tear down a previously set up interface.
*/
void gb_interface_remove(struct gb_interface *intf)
{
struct gb_bundle *bundle;
struct gb_bundle *next;
if (intf->disconnected)
gb_control_disable(intf->control);
list_for_each_entry_safe(bundle, next, &intf->bundles, links)
gb_bundle_destroy(bundle);
if (device_is_registered(&intf->dev))
device_del(&intf->dev);
gb_control_disable(intf->control);
spin_lock_irq(&gb_interfaces_lock);
list_del(&intf->links);
spin_unlock_irq(&gb_interfaces_lock);
put_device(&intf->dev);
}
void gb_interfaces_remove(struct gb_host_device *hd)
{
struct gb_interface *intf, *temp;
list_for_each_entry_safe(intf, temp, &hd->interfaces, links)
gb_interface_remove(intf);
}
/**
* gb_interface_init
*
* Create connection for control CPort and then request/parse manifest.
* Finally initialize all the bundles to set routes via SVC and initialize all
* connections.
*/
int gb_interface_init(struct gb_interface *intf, u8 device_id)
{
struct gb_bundle *bundle, *tmp;
int ret, size;
void *manifest;
intf->device_id = device_id;
/* Establish control connection */
ret = gb_control_enable(intf->control);
if (ret)
return ret;
/* Get manifest size using control protocol on CPort */
size = gb_control_get_manifest_size_operation(intf);
if (size <= 0) {
dev_err(&intf->dev, "failed to get manifest size: %d\n", size);
if (size)
return size;
else
return -EINVAL;
}
manifest = kmalloc(size, GFP_KERNEL);
if (!manifest)
return -ENOMEM;
/* Get manifest using control protocol on CPort */
ret = gb_control_get_manifest_operation(intf, manifest, size);
if (ret) {
dev_err(&intf->dev, "failed to get manifest: %d\n", ret);
goto free_manifest;
}
/*
* Parse the manifest and build up our data structures representing
* what's in it.
*/
if (!gb_manifest_parse(intf, manifest, size)) {
dev_err(&intf->dev, "failed to parse manifest\n");
ret = -EINVAL;
goto free_manifest;
}
ret = gb_control_get_interface_version_operation(intf);
if (ret)
goto free_manifest;
ret = gb_control_get_bundle_versions(intf->control);
if (ret)
goto free_manifest;
/* Register the interface and its bundles. */
ret = device_add(&intf->dev);
if (ret) {
dev_err(&intf->dev, "failed to register interface: %d\n", ret);
goto free_manifest;
}
list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) {
ret = gb_bundle_add(bundle);
if (ret) {
gb_bundle_destroy(bundle);
continue;
}
}
ret = 0;
free_manifest:
kfree(manifest);
return ret;
}