Commit f0f61b90 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman Committed by Greg Kroah-Hartman

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: default avatarGreg Kroah-Hartman <greg@kroah.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 2d5e4fa9
......@@ -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);
err_module:
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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment