2014-10-02 02:54:11 +00:00
|
|
|
/*
|
|
|
|
* Greybus modules
|
|
|
|
*
|
|
|
|
* Copyright 2014 Google Inc.
|
|
|
|
*
|
|
|
|
* Released under the GPLv2 only.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "greybus.h"
|
|
|
|
|
|
|
|
/* XXX This could be per-host device */
|
|
|
|
static DEFINE_SPINLOCK(gb_modules_lock);
|
|
|
|
|
|
|
|
static int gb_module_match_one_id(struct gb_module *gmod,
|
|
|
|
const struct greybus_module_id *id)
|
|
|
|
{
|
|
|
|
if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) &&
|
2014-10-02 02:54:16 +00:00
|
|
|
(id->vendor != gmod->vendor))
|
2014-10-02 02:54:11 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) &&
|
2014-10-02 02:54:16 +00:00
|
|
|
(id->product != gmod->product))
|
2014-10-02 02:54:11 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) &&
|
2014-10-02 17:30:02 +00:00
|
|
|
(id->unique_id != gmod->unique_id))
|
2014-10-02 02:54:11 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod,
|
|
|
|
const struct greybus_module_id *id)
|
|
|
|
{
|
|
|
|
if (id == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2014-10-02 17:30:02 +00:00
|
|
|
for (; id->vendor || id->product || id->unique_id ||
|
2014-10-02 02:54:11 +00:00
|
|
|
id->driver_info; id++) {
|
|
|
|
if (gb_module_match_one_id(gmod, id))
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-11-14 11:55:06 +00:00
|
|
|
struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id)
|
|
|
|
{
|
|
|
|
struct gb_module *module;
|
|
|
|
|
|
|
|
list_for_each_entry(module, &hd->modules, links)
|
|
|
|
if (module->module_id == module_id)
|
|
|
|
return module;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-10-24 09:34:46 +00:00
|
|
|
static void greybus_module_release(struct device *dev)
|
2014-10-21 04:01:04 +00:00
|
|
|
{
|
2014-10-24 09:34:46 +00:00
|
|
|
struct gb_module *gmod = to_gb_module(dev);
|
2014-10-21 04:01:04 +00:00
|
|
|
|
2014-10-24 09:34:46 +00:00
|
|
|
kfree(gmod);
|
2014-10-21 04:01:04 +00:00
|
|
|
}
|
|
|
|
|
2014-11-15 20:12:16 +00:00
|
|
|
struct device_type greybus_module_type = {
|
2014-10-24 09:34:46 +00:00
|
|
|
.name = "greybus_module",
|
|
|
|
.release = greybus_module_release,
|
|
|
|
};
|
|
|
|
|
2014-10-02 02:54:11 +00:00
|
|
|
/*
|
|
|
|
* A Greybus module represents a user-replacable component on an Ara
|
|
|
|
* phone.
|
|
|
|
*
|
|
|
|
* Create a gb_module 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
|
|
|
|
* failure occurs due to memory exhaustion.
|
|
|
|
*/
|
|
|
|
struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id)
|
|
|
|
{
|
2014-10-16 11:35:35 +00:00
|
|
|
struct gb_module *gmod;
|
2014-10-24 09:34:46 +00:00
|
|
|
int retval;
|
2014-10-02 02:54:11 +00:00
|
|
|
|
2014-10-24 10:46:15 +00:00
|
|
|
gmod = gb_module_find(hd, module_id);
|
|
|
|
if (gmod) {
|
|
|
|
dev_err(hd->parent, "Duplicate module id %d will not be created\n",
|
|
|
|
module_id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-10-16 11:35:35 +00:00
|
|
|
gmod = kzalloc(sizeof(*gmod), GFP_KERNEL);
|
|
|
|
if (!gmod)
|
2014-10-02 02:54:11 +00:00
|
|
|
return NULL;
|
|
|
|
|
2014-10-16 11:35:35 +00:00
|
|
|
gmod->hd = hd; /* XXX refcount? */
|
2014-10-27 10:00:13 +00:00
|
|
|
gmod->module_id = module_id;
|
2014-10-16 11:35:35 +00:00
|
|
|
INIT_LIST_HEAD(&gmod->interfaces);
|
2014-10-02 02:54:11 +00:00
|
|
|
|
2014-10-24 09:34:46 +00:00
|
|
|
gmod->dev.parent = hd->parent;
|
|
|
|
gmod->dev.bus = &greybus_bus_type;
|
|
|
|
gmod->dev.type = &greybus_module_type;
|
|
|
|
gmod->dev.groups = greybus_module_groups;
|
|
|
|
gmod->dev.dma_mask = hd->parent->dma_mask;
|
|
|
|
device_initialize(&gmod->dev);
|
|
|
|
dev_set_name(&gmod->dev, "%d", module_id);
|
|
|
|
|
|
|
|
retval = device_add(&gmod->dev);
|
|
|
|
if (retval) {
|
2014-11-05 22:03:12 +00:00
|
|
|
pr_err("failed to add module device for id 0x%02hhx\n",
|
|
|
|
module_id);
|
2014-10-24 09:34:46 +00:00
|
|
|
put_device(&gmod->dev);
|
2014-11-13 12:44:37 +00:00
|
|
|
kfree(gmod);
|
2014-10-24 09:34:46 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-11-13 12:44:37 +00:00
|
|
|
spin_lock_irq(&gb_modules_lock);
|
|
|
|
list_add_tail(&gmod->links, &hd->modules);
|
|
|
|
spin_unlock_irq(&gb_modules_lock);
|
|
|
|
|
2014-10-16 11:35:35 +00:00
|
|
|
return gmod;
|
2014-10-02 02:54:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tear down a previously set up module.
|
|
|
|
*/
|
2014-10-16 11:35:35 +00:00
|
|
|
void gb_module_destroy(struct gb_module *gmod)
|
2014-10-02 02:54:11 +00:00
|
|
|
{
|
2014-10-16 11:35:35 +00:00
|
|
|
if (WARN_ON(!gmod))
|
2014-10-02 02:54:11 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
spin_lock_irq(&gb_modules_lock);
|
2014-10-16 11:35:35 +00:00
|
|
|
list_del(&gmod->links);
|
2014-10-02 02:54:11 +00:00
|
|
|
spin_unlock_irq(&gb_modules_lock);
|
|
|
|
|
2014-10-21 04:01:04 +00:00
|
|
|
/* XXX Do something with gmod->gb_tty */
|
|
|
|
|
2014-10-24 09:34:46 +00:00
|
|
|
gb_interface_destroy(gmod);
|
2014-10-21 04:01:04 +00:00
|
|
|
|
|
|
|
kfree(gmod->product_string);
|
|
|
|
kfree(gmod->vendor_string);
|
2014-10-02 02:54:11 +00:00
|
|
|
/* kref_put(module->hd); */
|
|
|
|
|
2014-10-24 09:34:46 +00:00
|
|
|
device_del(&gmod->dev);
|
2014-10-16 11:35:35 +00:00
|
|
|
}
|
|
|
|
|
2014-11-14 11:55:07 +00:00
|
|
|
/**
|
|
|
|
* gb_add_module
|
|
|
|
*
|
|
|
|
* Pass in a buffer that _should_ contain a Greybus module manifest
|
|
|
|
* and register a greybus device structure with the kernel core.
|
|
|
|
*/
|
|
|
|
void gb_add_module(struct greybus_host_device *hd, u8 module_id,
|
|
|
|
u8 *data, int size)
|
|
|
|
{
|
|
|
|
struct gb_module *gmod;
|
|
|
|
|
|
|
|
gmod = gb_module_create(hd, module_id);
|
|
|
|
if (!gmod) {
|
|
|
|
dev_err(hd->parent, "failed to create module\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the manifest and build up our data structures
|
|
|
|
* representing what's in it.
|
|
|
|
*/
|
|
|
|
if (!gb_manifest_parse(gmod, data, size)) {
|
|
|
|
dev_err(hd->parent, "manifest error\n");
|
|
|
|
goto err_module;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX
|
|
|
|
* We've successfully parsed the manifest. Now we need to
|
|
|
|
* allocate CPort Id's for connecting to the CPorts found on
|
|
|
|
* other modules. For each of these, establish a connection
|
|
|
|
* between the local and remote CPorts (including
|
|
|
|
* configuring the switch to allow them to communicate).
|
|
|
|
*/
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
err_module:
|
|
|
|
gb_module_destroy(gmod);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gb_remove_module(struct greybus_host_device *hd, u8 module_id)
|
|
|
|
{
|
|
|
|
struct gb_module *gmod = gb_module_find(hd, module_id);
|
|
|
|
|
|
|
|
if (gmod)
|
|
|
|
gb_module_destroy(gmod);
|
|
|
|
else
|
|
|
|
dev_err(hd->parent, "module id %d not found\n", module_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gb_remove_modules(struct greybus_host_device *hd)
|
|
|
|
{
|
|
|
|
struct gb_module *gmod, *temp;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(gmod, temp, &hd->modules, links)
|
|
|
|
gb_module_destroy(gmod);
|
|
|
|
}
|