greybus: hook up greybus to the driver model
This patch hooks up modules, interfaces, and connections to the driver model. Now we have a correct hierarchy, and drivers can be correctly bound to the proper portions in the future. Devices are correctly reference counted and torn down in the proper order on removal of a module. Some basic sysfs attributes have been created for interfaces and connections. Module attributes are not working properly, but that will be fixed in future changes. This has been tested on Alex's machine, with multiple hotplug and unplug operations of a module working correctly. Signed-off-by: Greg Kroah-Hartman <greg@kroah.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
2d5e4fa9dc
commit
f0f61b9042
|
@ -111,6 +111,44 @@ static void connection_timeout(struct work_struct *work)
|
|||
printk("timeout!\n");
|
||||
}
|
||||
|
||||
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct gb_connection *connection = to_gb_connection(dev);
|
||||
|
||||
return sprintf(buf, "%d", connection->state);
|
||||
}
|
||||
static DEVICE_ATTR_RO(state);
|
||||
|
||||
static ssize_t protocol_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct gb_connection *connection = to_gb_connection(dev);
|
||||
|
||||
return sprintf(buf, "%d", connection->protocol);
|
||||
}
|
||||
static DEVICE_ATTR_RO(protocol);
|
||||
|
||||
static struct attribute *connection_attrs[] = {
|
||||
&dev_attr_state.attr,
|
||||
&dev_attr_protocol.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(connection);
|
||||
|
||||
static void gb_connection_release(struct device *dev)
|
||||
{
|
||||
struct gb_connection *connection = to_gb_connection(dev);
|
||||
|
||||
kfree(connection);
|
||||
}
|
||||
|
||||
static struct device_type greybus_connection_type = {
|
||||
.name = "greybus_connection",
|
||||
.release = gb_connection_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up a Greybus connection, representing the bidirectional link
|
||||
* between a CPort on a (local) Greybus host device and a CPort on
|
||||
|
@ -127,6 +165,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface,
|
|||
{
|
||||
struct gb_connection *connection;
|
||||
struct greybus_host_device *hd;
|
||||
int retval;
|
||||
|
||||
connection = kzalloc(sizeof(*connection), GFP_KERNEL);
|
||||
if (!connection)
|
||||
|
@ -145,6 +184,21 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface,
|
|||
connection->protocol = protocol;
|
||||
connection->state = GB_CONNECTION_STATE_DISABLED;
|
||||
|
||||
connection->dev.parent = &interface->dev;
|
||||
connection->dev.driver = NULL;
|
||||
connection->dev.bus = &greybus_bus_type;
|
||||
connection->dev.type = &greybus_connection_type;
|
||||
connection->dev.groups = connection_groups;
|
||||
device_initialize(&connection->dev);
|
||||
dev_set_name(&connection->dev, "%s:%d",
|
||||
dev_name(&interface->dev), cport_id);
|
||||
|
||||
retval = device_add(&connection->dev);
|
||||
if (retval) {
|
||||
kfree(connection);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock_irq(&gb_connections_lock);
|
||||
_gb_hd_connection_insert(hd, connection);
|
||||
list_add_tail(&connection->interface_links, &interface->connections);
|
||||
|
@ -182,9 +236,8 @@ void gb_connection_destroy(struct gb_connection *connection)
|
|||
spin_unlock_irq(&gb_connections_lock);
|
||||
|
||||
gb_connection_hd_cport_id_free(connection);
|
||||
/* kref_put(connection->interface); */
|
||||
/* kref_put(connection->hd); */
|
||||
kfree(connection);
|
||||
|
||||
device_del(&connection->dev);
|
||||
}
|
||||
|
||||
u16 gb_connection_operation_id(struct gb_connection *connection)
|
||||
|
|
|
@ -24,6 +24,7 @@ enum gb_connection_state {
|
|||
struct gb_connection {
|
||||
struct greybus_host_device *hd;
|
||||
struct gb_interface *interface;
|
||||
struct device dev;
|
||||
u16 hd_cport_id;
|
||||
u16 interface_cport_id;
|
||||
|
||||
|
@ -39,6 +40,7 @@ struct gb_connection {
|
|||
|
||||
void *private;
|
||||
};
|
||||
#define to_gb_connection(d) container_of(d, struct gb_connection, dev)
|
||||
|
||||
struct gb_connection *gb_connection_create(struct gb_interface *interface,
|
||||
u16 cport_id, enum greybus_protocol protocol);
|
||||
|
|
|
@ -48,10 +48,14 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
/* struct gb_module *gmod = to_gb_module(dev); */
|
||||
|
||||
/* FIXME - add some uevents here... */
|
||||
|
||||
/* FIXME - be sure to check the type to know how to handle modules and
|
||||
* interfaces differently */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bus_type greybus_bus_type = {
|
||||
struct bus_type greybus_bus_type = {
|
||||
.name = "greybus",
|
||||
.match = greybus_module_match,
|
||||
.uevent = greybus_uevent,
|
||||
|
@ -115,18 +119,6 @@ void greybus_deregister(struct greybus_driver *driver)
|
|||
EXPORT_SYMBOL_GPL(greybus_deregister);
|
||||
|
||||
|
||||
static void greybus_module_release(struct device *dev)
|
||||
{
|
||||
struct gb_module *gmod = to_gb_module(dev);
|
||||
|
||||
gb_module_destroy(gmod);
|
||||
}
|
||||
|
||||
static struct device_type greybus_module_type = {
|
||||
.name = "greybus_module",
|
||||
.release = greybus_module_release,
|
||||
};
|
||||
|
||||
static const struct greybus_module_id fake_greybus_module_id = {
|
||||
GREYBUS_DEVICE(0x42, 0x42)
|
||||
};
|
||||
|
@ -142,7 +134,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id,
|
|||
u8 *data, int size)
|
||||
{
|
||||
struct gb_module *gmod;
|
||||
int retval;
|
||||
|
||||
gmod = gb_module_create(hd, module_id);
|
||||
if (!gmod) {
|
||||
|
@ -168,31 +159,10 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id,
|
|||
* configuring the switch to allow them to communicate).
|
||||
*/
|
||||
|
||||
gmod->dev.parent = hd->parent;
|
||||
gmod->dev.driver = NULL;
|
||||
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)
|
||||
goto err_device;
|
||||
|
||||
return;
|
||||
err_device:
|
||||
put_device(&gmod->dev);
|
||||
|
||||
err_module:
|
||||
greybus_module_release(&gmod->dev);
|
||||
}
|
||||
|
||||
static void gb_delete_module(struct gb_module *gmod)
|
||||
{
|
||||
/* FIXME - tear down interfaces first */
|
||||
|
||||
device_del(&gmod->dev);
|
||||
gb_module_destroy(gmod);
|
||||
}
|
||||
|
||||
void gb_remove_module(struct greybus_host_device *hd, u8 module_id)
|
||||
|
@ -207,7 +177,7 @@ void gb_remove_module(struct greybus_host_device *hd, u8 module_id)
|
|||
}
|
||||
|
||||
if (found)
|
||||
gb_delete_module(gmod);
|
||||
gb_module_destroy(gmod);
|
||||
else
|
||||
dev_err(hd->parent, "module id %d remove error\n", module_id);
|
||||
}
|
||||
|
@ -217,7 +187,7 @@ static 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_delete_module(gmod);
|
||||
gb_module_destroy(gmod);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,6 +226,8 @@ EXPORT_SYMBOL_GPL(greybus_create_hd);
|
|||
|
||||
void greybus_remove_hd(struct greybus_host_device *hd)
|
||||
{
|
||||
/* Tear down all modules that happen to be associated with this host
|
||||
* controller */
|
||||
gb_remove_modules(hd);
|
||||
kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
|
||||
}
|
||||
|
|
|
@ -261,6 +261,7 @@ int gb_register_cport_complete(struct gb_module *gmod,
|
|||
void *context);
|
||||
void gb_deregister_cport_complete(u16 cport_id);
|
||||
|
||||
extern struct bus_type greybus_bus_type;
|
||||
extern const struct attribute_group *greybus_module_groups[];
|
||||
|
||||
int gb_i2c_device_init(struct gb_connection *connection);
|
||||
|
|
|
@ -8,6 +8,35 @@
|
|||
|
||||
#include "greybus.h"
|
||||
|
||||
static ssize_t device_id_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct gb_interface *interface = to_gb_interface(dev);
|
||||
|
||||
return sprintf(buf, "%d", interface->device_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(device_id);
|
||||
|
||||
static struct attribute *interface_attrs[] = {
|
||||
&dev_attr_device_id.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(interface);
|
||||
|
||||
static void gb_interface_release(struct device *dev)
|
||||
{
|
||||
struct gb_interface *interface = to_gb_interface(dev);
|
||||
|
||||
kfree(interface);
|
||||
}
|
||||
|
||||
static struct device_type greybus_interface_type = {
|
||||
.name = "greybus_interface",
|
||||
.release = gb_interface_release,
|
||||
};
|
||||
|
||||
|
||||
/* XXX This could be per-host device or per-module */
|
||||
static DEFINE_SPINLOCK(gb_interfaces_lock);
|
||||
|
||||
|
@ -26,6 +55,7 @@ struct gb_interface *
|
|||
gb_interface_create(struct gb_module *gmod, u8 interface_id)
|
||||
{
|
||||
struct gb_interface *interface;
|
||||
int retval;
|
||||
|
||||
interface = kzalloc(sizeof(*interface), GFP_KERNEL);
|
||||
if (!interface)
|
||||
|
@ -33,8 +63,25 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id)
|
|||
|
||||
interface->gmod = gmod; /* XXX refcount? */
|
||||
interface->id = interface_id;
|
||||
interface->device_id = 0xff; /* Invalid device id to start with */
|
||||
INIT_LIST_HEAD(&interface->connections);
|
||||
|
||||
/* Build up the interface device structures and register it with the
|
||||
* driver core */
|
||||
interface->dev.parent = &gmod->dev;
|
||||
interface->dev.driver = NULL;
|
||||
interface->dev.bus = &greybus_bus_type;
|
||||
interface->dev.type = &greybus_interface_type;
|
||||
interface->dev.groups = interface_groups;
|
||||
device_initialize(&interface->dev);
|
||||
dev_set_name(&interface->dev, "%d:%d", gmod->module_id, interface_id);
|
||||
|
||||
retval = device_add(&interface->dev);
|
||||
if (retval) {
|
||||
kfree(interface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock_irq(&gb_interfaces_lock);
|
||||
list_add_tail(&interface->links, &gmod->interfaces);
|
||||
spin_unlock_irq(&gb_interfaces_lock);
|
||||
|
@ -45,19 +92,21 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id)
|
|||
/*
|
||||
* Tear down a previously set up interface.
|
||||
*/
|
||||
void gb_interface_destroy(struct gb_interface *interface)
|
||||
void gb_interface_destroy(struct gb_module *gmod)
|
||||
{
|
||||
if (WARN_ON(!interface))
|
||||
struct gb_interface *interface;
|
||||
struct gb_interface *temp;
|
||||
|
||||
if (WARN_ON(!gmod))
|
||||
return;
|
||||
|
||||
spin_lock_irq(&gb_interfaces_lock);
|
||||
list_del(&interface->links);
|
||||
list_for_each_entry_safe(interface, temp, &gmod->interfaces, links) {
|
||||
list_del(&interface->links);
|
||||
gb_interface_connections_exit(interface);
|
||||
device_del(&interface->dev);
|
||||
}
|
||||
spin_unlock_irq(&gb_interfaces_lock);
|
||||
|
||||
gb_interface_connections_exit(interface);
|
||||
|
||||
/* kref_put(gmod); */
|
||||
kfree(interface);
|
||||
}
|
||||
|
||||
struct gb_interface *gb_interface_find(struct gb_module *module,
|
||||
|
@ -65,9 +114,13 @@ struct gb_interface *gb_interface_find(struct gb_module *module,
|
|||
{
|
||||
struct gb_interface *interface;
|
||||
|
||||
spin_lock_irq(&gb_interfaces_lock);
|
||||
list_for_each_entry(interface, &module->interfaces, links)
|
||||
if (interface->id == interface_id)
|
||||
if (interface->id == interface_id) {
|
||||
spin_unlock_irq(&gb_interfaces_lock);
|
||||
return interface;
|
||||
}
|
||||
spin_unlock_irq(&gb_interfaces_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/list.h>
|
||||
|
||||
struct gb_interface {
|
||||
struct device dev;
|
||||
struct gb_module *gmod;
|
||||
u8 id;
|
||||
u8 device_id;
|
||||
|
@ -19,9 +20,10 @@ struct gb_interface {
|
|||
|
||||
struct list_head links; /* module->interfaces */
|
||||
};
|
||||
#define to_gb_interface(d) container_of(d, struct gb_interface, dev)
|
||||
|
||||
struct gb_interface *gb_interface_create(struct gb_module *gmod, u8 module_id);
|
||||
void gb_interface_destroy(struct gb_interface *interface);
|
||||
void gb_interface_destroy(struct gb_module *gmod);
|
||||
|
||||
struct gb_interface *gb_interface_find(struct gb_module *gmod, u8 interface_id);
|
||||
|
||||
|
|
|
@ -44,15 +44,18 @@ const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void gb_module_interfaces_exit(struct gb_module *gmod)
|
||||
static void greybus_module_release(struct device *dev)
|
||||
{
|
||||
struct gb_interface *interface;
|
||||
struct gb_interface *next;
|
||||
struct gb_module *gmod = to_gb_module(dev);
|
||||
|
||||
list_for_each_entry_safe(interface, next, &gmod->interfaces, links)
|
||||
gb_interface_destroy(interface);
|
||||
kfree(gmod);
|
||||
}
|
||||
|
||||
static struct device_type greybus_module_type = {
|
||||
.name = "greybus_module",
|
||||
.release = greybus_module_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* A Greybus module represents a user-replacable component on an Ara
|
||||
* phone.
|
||||
|
@ -65,6 +68,7 @@ static void gb_module_interfaces_exit(struct gb_module *gmod)
|
|||
struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id)
|
||||
{
|
||||
struct gb_module *gmod;
|
||||
int retval;
|
||||
|
||||
gmod = kzalloc(sizeof(*gmod), GFP_KERNEL);
|
||||
if (!gmod)
|
||||
|
@ -78,6 +82,21 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id)
|
|||
list_add_tail(&gmod->links, &hd->modules);
|
||||
spin_unlock_irq(&gb_modules_lock);
|
||||
|
||||
gmod->dev.parent = hd->parent;
|
||||
gmod->dev.driver = NULL;
|
||||
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) {
|
||||
put_device(&gmod->dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gmod;
|
||||
}
|
||||
|
||||
|
@ -93,18 +112,15 @@ void gb_module_destroy(struct gb_module *gmod)
|
|||
list_del(&gmod->links);
|
||||
spin_unlock_irq(&gb_modules_lock);
|
||||
|
||||
gb_module_interfaces_exit(gmod);
|
||||
/* XXX Do something with gmod->gb_tty */
|
||||
|
||||
put_device(&gmod->dev);
|
||||
/* kfree(gmod->dev->name); */
|
||||
gb_interface_destroy(gmod);
|
||||
|
||||
kfree(gmod->product_string);
|
||||
kfree(gmod->vendor_string);
|
||||
/* kref_put(module->hd); */
|
||||
|
||||
|
||||
kfree(gmod);
|
||||
device_del(&gmod->dev);
|
||||
}
|
||||
|
||||
struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id)
|
||||
|
|
Loading…
Reference in New Issue