summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorGreg Kroah-Hartman2014-10-24 11:34:46 +0200
committerGreg Kroah-Hartman2014-10-24 11:40:59 +0200
commitf0f61b90427b776b884821cde483528580f6d630 (patch)
tree51666ef5ef41806e6d8f5f5b27ce69da286a460e /drivers
parentgreybus: update AP id service message (diff)
downloadkernel-qcow2-linux-f0f61b90427b776b884821cde483528580f6d630.tar.gz
kernel-qcow2-linux-f0f61b90427b776b884821cde483528580f6d630.tar.xz
kernel-qcow2-linux-f0f61b90427b776b884821cde483528580f6d630.zip
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/greybus/connection.c59
-rw-r--r--drivers/staging/greybus/connection.h2
-rw-r--r--drivers/staging/greybus/core.c50
-rw-r--r--drivers/staging/greybus/greybus.h1
-rw-r--r--drivers/staging/greybus/interface.c71
-rw-r--r--drivers/staging/greybus/interface.h4
-rw-r--r--drivers/staging/greybus/module.c36
7 files changed, 161 insertions, 62 deletions
diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c
index b1e933fc7044..2d71679ff66c 100644
--- a/drivers/staging/greybus/connection.c
+++ b/drivers/staging/greybus/connection.c
@@ -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)
diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h
index a16e52a8ba18..19dd91dae062 100644
--- a/drivers/staging/greybus/connection.h
+++ b/drivers/staging/greybus/connection.h
@@ -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);
diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c
index 9f4ae1c63fa0..853bfd6503f6 100644
--- a/drivers/staging/greybus/core.c
+++ b/drivers/staging/greybus/core.c
@@ -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);
}
diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h
index 617d55ca7e1d..f287f3b0a3e5 100644
--- a/drivers/staging/greybus/greybus.h
+++ b/drivers/staging/greybus/greybus.h
@@ -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);
diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c
index 0415db51051e..a7375a2809d5 100644
--- a/drivers/staging/greybus/interface.c
+++ b/drivers/staging/greybus/interface.c
@@ -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;
}
diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h
index 907b6341665c..50b0317d267e 100644
--- a/drivers/staging/greybus/interface.h
+++ b/drivers/staging/greybus/interface.h
@@ -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);
diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c
index b1ec0904fb28..f9415c0f735e 100644
--- a/drivers/staging/greybus/module.c
+++ b/drivers/staging/greybus/module.c
@@ -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)