2014-10-02 02:54:11 +00:00
|
|
|
/*
|
2014-12-19 22:56:31 +00:00
|
|
|
* Greybus interface code
|
2014-10-02 02:54:11 +00:00
|
|
|
*
|
|
|
|
* Copyright 2014 Google Inc.
|
2014-12-12 18:08:42 +00:00
|
|
|
* Copyright 2014 Linaro Ltd.
|
2014-10-02 02:54:11 +00:00
|
|
|
*
|
|
|
|
* Released under the GPLv2 only.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "greybus.h"
|
|
|
|
|
2014-12-19 22:56:31 +00:00
|
|
|
/* interface sysfs attributes */
|
|
|
|
#define gb_interface_attr(field, type) \
|
|
|
|
static ssize_t field##_show(struct device *dev, \
|
|
|
|
struct device_attribute *attr, \
|
|
|
|
char *buf) \
|
2014-12-11 22:10:59 +00:00
|
|
|
{ \
|
2014-12-19 22:56:31 +00:00
|
|
|
struct gb_interface *intf = to_gb_interface(dev); \
|
2015-12-18 09:34:27 +00:00
|
|
|
return scnprintf(buf, PAGE_SIZE, type"\n", intf->field); \
|
2014-12-11 22:10:59 +00:00
|
|
|
} \
|
|
|
|
static DEVICE_ATTR_RO(field)
|
|
|
|
|
2015-12-22 16:34:34 +00:00
|
|
|
gb_interface_attr(ddbl1_manufacturer_id, "0x%08x");
|
|
|
|
gb_interface_attr(ddbl1_product_id, "0x%08x");
|
2015-12-18 09:34:27 +00:00
|
|
|
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");
|
2015-12-28 06:29:00 +00:00
|
|
|
gb_interface_attr(serial_number, "0x%016llx");
|
2014-12-11 22:10:59 +00:00
|
|
|
|
2015-12-28 06:29:01 +00:00
|
|
|
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);
|
|
|
|
|
2014-12-19 22:56:31 +00:00
|
|
|
static struct attribute *interface_attrs[] = {
|
2015-12-22 16:34:34 +00:00
|
|
|
&dev_attr_ddbl1_manufacturer_id.attr,
|
|
|
|
&dev_attr_ddbl1_product_id.attr,
|
2015-11-25 14:58:58 +00:00
|
|
|
&dev_attr_interface_id.attr,
|
2015-11-25 14:58:56 +00:00
|
|
|
&dev_attr_vendor_id.attr,
|
|
|
|
&dev_attr_product_id.attr,
|
2014-12-11 22:10:59 +00:00
|
|
|
&dev_attr_vendor_string.attr,
|
|
|
|
&dev_attr_product_string.attr,
|
2015-12-28 06:29:00 +00:00
|
|
|
&dev_attr_serial_number.attr,
|
2015-12-28 06:29:01 +00:00
|
|
|
&dev_attr_version.attr,
|
2014-12-11 22:10:59 +00:00
|
|
|
NULL,
|
|
|
|
};
|
2014-12-19 22:56:31 +00:00
|
|
|
ATTRIBUTE_GROUPS(interface);
|
2014-12-11 22:10:59 +00:00
|
|
|
|
|
|
|
|
2014-10-02 02:54:11 +00:00
|
|
|
/* XXX This could be per-host device */
|
2014-12-19 22:56:37 +00:00
|
|
|
static DEFINE_SPINLOCK(gb_interfaces_lock);
|
2014-10-02 02:54:11 +00:00
|
|
|
|
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.
|
2015-11-03 17:03:23 +00:00
|
|
|
struct gb_interface *gb_interface_find(struct gb_host_device *hd,
|
2015-04-01 15:01:58 +00:00
|
|
|
u8 interface_id)
|
2014-11-14 11:55:06 +00:00
|
|
|
{
|
2014-12-19 22:56:31 +00:00
|
|
|
struct gb_interface *intf;
|
2014-11-14 11:55:06 +00:00
|
|
|
|
2014-12-19 22:56:36 +00:00
|
|
|
list_for_each_entry(intf, &hd->interfaces, links)
|
2015-04-01 15:01:58 +00:00
|
|
|
if (intf->interface_id == interface_id)
|
2014-12-19 22:56:31 +00:00
|
|
|
return intf;
|
2014-11-14 11:55:06 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-05-20 12:03:51 +00:00
|
|
|
static void gb_interface_release(struct device *dev)
|
2014-10-21 04:01:04 +00:00
|
|
|
{
|
2014-12-19 22:56:31 +00:00
|
|
|
struct gb_interface *intf = to_gb_interface(dev);
|
2014-10-21 04:01:04 +00:00
|
|
|
|
2015-11-11 09:07:05 +00:00
|
|
|
kfree(intf->product_string);
|
|
|
|
kfree(intf->vendor_string);
|
|
|
|
|
2015-12-15 14:28:56 +00:00
|
|
|
if (intf->control)
|
|
|
|
gb_control_destroy(intf->control);
|
|
|
|
|
2014-12-19 22:56:31 +00:00
|
|
|
kfree(intf);
|
2014-10-21 04:01:04 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 22:56:31 +00:00
|
|
|
struct device_type greybus_interface_type = {
|
|
|
|
.name = "greybus_interface",
|
2015-05-20 12:03:51 +00:00
|
|
|
.release = gb_interface_release,
|
2014-10-24 09:34:46 +00:00
|
|
|
};
|
|
|
|
|
2014-10-02 02:54:11 +00:00
|
|
|
/*
|
2015-08-16 23:57:16 +00:00
|
|
|
* A Greybus module represents a user-replaceable component on an Ara
|
2014-12-19 22:56:31 +00:00
|
|
|
* phone. An interface is the physical connection on that module. A
|
|
|
|
* module may have more than one interface.
|
2014-10-02 02:54:11 +00:00
|
|
|
*
|
2015-04-01 15:01:58 +00:00
|
|
|
* Create a gb_interface structure to represent a discovered interface.
|
|
|
|
* The position of interface within the Endo is encoded in "interface_id"
|
|
|
|
* argument.
|
|
|
|
*
|
2014-12-21 22:10:26 +00:00
|
|
|
* Returns a pointer to the new interfce or a null pointer if a
|
2014-10-02 02:54:11 +00:00
|
|
|
* failure occurs due to memory exhaustion.
|
|
|
|
*/
|
2015-11-03 17:03:23 +00:00
|
|
|
struct gb_interface *gb_interface_create(struct gb_host_device *hd,
|
2015-06-22 11:12:27 +00:00
|
|
|
u8 interface_id)
|
2014-10-02 02:54:11 +00:00
|
|
|
{
|
2014-12-19 22:56:31 +00:00
|
|
|
struct gb_interface *intf;
|
2014-10-02 02:54:11 +00:00
|
|
|
|
2014-12-19 22:56:31 +00:00
|
|
|
intf = kzalloc(sizeof(*intf), GFP_KERNEL);
|
|
|
|
if (!intf)
|
2015-11-25 14:59:04 +00:00
|
|
|
return NULL;
|
2014-10-02 02:54:11 +00:00
|
|
|
|
2014-12-19 22:56:31 +00:00
|
|
|
intf->hd = hd; /* XXX refcount? */
|
2015-04-01 15:01:58 +00:00
|
|
|
intf->interface_id = interface_id;
|
2014-12-19 22:56:31 +00:00
|
|
|
INIT_LIST_HEAD(&intf->bundles);
|
2014-12-23 23:16:50 +00:00
|
|
|
INIT_LIST_HEAD(&intf->manifest_descs);
|
2014-10-02 02:54:11 +00:00
|
|
|
|
2015-07-01 06:43:58 +00:00
|
|
|
/* Invalid device id to start with */
|
|
|
|
intf->device_id = GB_DEVICE_ID_BAD;
|
|
|
|
|
2015-11-25 14:59:04 +00:00
|
|
|
intf->dev.parent = &hd->dev;
|
2014-12-19 22:56:31 +00:00
|
|
|
intf->dev.bus = &greybus_bus_type;
|
|
|
|
intf->dev.type = &greybus_interface_type;
|
|
|
|
intf->dev.groups = interface_groups;
|
2015-11-25 14:59:02 +00:00
|
|
|
intf->dev.dma_mask = hd->dev.dma_mask;
|
2014-12-19 22:56:31 +00:00
|
|
|
device_initialize(&intf->dev);
|
2015-11-25 14:59:04 +00:00
|
|
|
dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id);
|
2014-10-24 09:34:46 +00:00
|
|
|
|
2015-12-15 14:28:56 +00:00
|
|
|
intf->control = gb_control_create(intf);
|
|
|
|
if (!intf->control) {
|
|
|
|
put_device(&intf->dev);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-12-19 22:56:37 +00:00
|
|
|
spin_lock_irq(&gb_interfaces_lock);
|
2015-06-04 12:46:45 +00:00
|
|
|
list_add(&intf->links, &hd->interfaces);
|
2014-12-19 22:56:37 +00:00
|
|
|
spin_unlock_irq(&gb_interfaces_lock);
|
2014-11-13 12:44:37 +00:00
|
|
|
|
2014-12-19 22:56:31 +00:00
|
|
|
return intf;
|
2014-10-02 02:54:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-11-11 09:07:06 +00:00
|
|
|
* Tear down a previously set up interface.
|
2014-10-02 02:54:11 +00:00
|
|
|
*/
|
2015-09-23 23:48:10 +00:00
|
|
|
void gb_interface_remove(struct gb_interface *intf)
|
2014-10-02 02:54:11 +00:00
|
|
|
{
|
2015-06-12 15:21:11 +00:00
|
|
|
struct gb_bundle *bundle;
|
|
|
|
struct gb_bundle *next;
|
2015-04-02 12:23:47 +00:00
|
|
|
|
2015-12-15 14:28:57 +00:00
|
|
|
if (intf->disconnected)
|
|
|
|
gb_control_disable(intf->control);
|
|
|
|
|
2015-06-12 15:21:11 +00:00
|
|
|
list_for_each_entry_safe(bundle, next, &intf->bundles, links)
|
|
|
|
gb_bundle_destroy(bundle);
|
2014-10-21 04:01:04 +00:00
|
|
|
|
2015-12-07 14:05:45 +00:00
|
|
|
if (device_is_registered(&intf->dev))
|
|
|
|
device_del(&intf->dev);
|
|
|
|
|
2015-12-15 14:28:56 +00:00
|
|
|
gb_control_disable(intf->control);
|
2015-11-25 14:59:26 +00:00
|
|
|
|
2015-12-07 14:05:45 +00:00
|
|
|
spin_lock_irq(&gb_interfaces_lock);
|
|
|
|
list_del(&intf->links);
|
|
|
|
spin_unlock_irq(&gb_interfaces_lock);
|
|
|
|
|
|
|
|
put_device(&intf->dev);
|
2014-10-16 11:35:35 +00:00
|
|
|
}
|
|
|
|
|
2015-11-03 17:03:23 +00:00
|
|
|
void gb_interfaces_remove(struct gb_host_device *hd)
|
2015-09-23 23:48:10 +00:00
|
|
|
{
|
|
|
|
struct gb_interface *intf, *temp;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(intf, temp, &hd->interfaces, links)
|
|
|
|
gb_interface_remove(intf);
|
|
|
|
}
|
|
|
|
|
2014-11-14 11:55:07 +00:00
|
|
|
/**
|
2015-07-03 11:30:26 +00:00
|
|
|
* gb_interface_init
|
2014-11-14 11:55:07 +00:00
|
|
|
*
|
2015-06-22 11:12:27 +00:00
|
|
|
* Create connection for control CPort and then request/parse manifest.
|
|
|
|
* Finally initialize all the bundles to set routes via SVC and initialize all
|
|
|
|
* connections.
|
2014-11-14 11:55:07 +00:00
|
|
|
*/
|
2015-06-22 11:12:27 +00:00
|
|
|
int gb_interface_init(struct gb_interface *intf, u8 device_id)
|
2014-11-14 11:55:07 +00:00
|
|
|
{
|
2015-12-07 14:05:44 +00:00
|
|
|
struct gb_bundle *bundle, *tmp;
|
2015-06-22 11:12:27 +00:00
|
|
|
int ret, size;
|
|
|
|
void *manifest;
|
|
|
|
|
2015-07-01 06:43:58 +00:00
|
|
|
intf->device_id = device_id;
|
|
|
|
|
2015-12-15 14:28:56 +00:00
|
|
|
/* Establish control connection */
|
|
|
|
ret = gb_control_enable(intf->control);
|
|
|
|
if (ret)
|
2015-12-07 14:05:34 +00:00
|
|
|
return ret;
|
|
|
|
|
2015-06-22 11:12:27 +00:00
|
|
|
/* Get manifest size using control protocol on CPort */
|
|
|
|
size = gb_control_get_manifest_size_operation(intf);
|
|
|
|
if (size <= 0) {
|
2015-12-07 14:05:46 +00:00
|
|
|
dev_err(&intf->dev, "failed to get manifest size: %d\n", size);
|
2015-06-22 11:12:27 +00:00
|
|
|
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) {
|
2015-12-07 14:05:46 +00:00
|
|
|
dev_err(&intf->dev, "failed to get manifest: %d\n", ret);
|
2015-06-22 11:12:27 +00:00
|
|
|
goto free_manifest;
|
2014-11-14 11:55:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-06-22 11:12:27 +00:00
|
|
|
* Parse the manifest and build up our data structures representing
|
|
|
|
* what's in it.
|
2014-11-14 11:55:07 +00:00
|
|
|
*/
|
2015-06-22 11:12:27 +00:00
|
|
|
if (!gb_manifest_parse(intf, manifest, size)) {
|
2015-12-07 14:05:46 +00:00
|
|
|
dev_err(&intf->dev, "failed to parse manifest\n");
|
2015-06-22 11:12:27 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto free_manifest;
|
2014-11-14 11:55:07 +00:00
|
|
|
}
|
|
|
|
|
2015-12-28 06:29:01 +00:00
|
|
|
ret = gb_control_get_interface_version_operation(intf);
|
|
|
|
if (ret)
|
|
|
|
goto free_manifest;
|
|
|
|
|
2016-01-19 11:51:21 +00:00
|
|
|
ret = gb_control_get_bundle_versions(intf->control);
|
|
|
|
if (ret)
|
|
|
|
goto free_manifest;
|
|
|
|
|
2015-12-07 14:05:45 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2015-12-07 14:05:44 +00:00
|
|
|
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;
|
2014-11-14 11:55:07 +00:00
|
|
|
|
2015-06-22 11:12:27 +00:00
|
|
|
free_manifest:
|
|
|
|
kfree(manifest);
|
|
|
|
return ret;
|
2014-11-14 11:55:07 +00:00
|
|
|
}
|