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:
Greg Kroah-Hartman 2014-10-24 17:34:46 +08:00 committed by Greg Kroah-Hartman
parent 2d5e4fa9dc
commit f0f61b9042
7 changed files with 161 additions and 62 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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)