Commit 4ccb6b7a authored by Alex Elder's avatar Alex Elder Committed by Greg Kroah-Hartman

greybus: introduce protocol abstraction

Define a protocol structure that will allow protocols to be
registered dynamically.  For now we just introduce a bookkeeping
data structure.  Upcoming patches will move protocol-related methods
into the protocol structure, and will start registering protocol
handlers dynamically.

A list of connections using a given protocol is maintained so we can
tell when a protocol is no longer in use.  This may not be necessary
(we could use a kref instead) but it may turn out to be a good way
to clean things up.

The interface is gb_protocol_get() and gb_protocol_put() for a
connection, allowing the protocol to be looked up and the connection
structure to be inserted into its list.
Signed-off-by: default avatarAlex Elder <elder@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 7fba0079
...@@ -7,6 +7,7 @@ greybus-y := core.o \ ...@@ -7,6 +7,7 @@ greybus-y := core.o \
module.o \ module.o \
interface.o \ interface.o \
connection.o \ connection.o \
protocol.o \
operation.o \ operation.o \
i2c-gb.o \ i2c-gb.o \
gpio-gb.o \ gpio-gb.o \
......
...@@ -116,7 +116,7 @@ protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf) ...@@ -116,7 +116,7 @@ protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf)
{ {
struct gb_connection *connection = to_gb_connection(dev); struct gb_connection *connection = to_gb_connection(dev);
return sprintf(buf, "%d", connection->protocol_id); return sprintf(buf, "%d", connection->protocol->id);
} }
static DEVICE_ATTR_RO(protocol_id); static DEVICE_ATTR_RO(protocol_id);
...@@ -162,17 +162,23 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, ...@@ -162,17 +162,23 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface,
if (!connection) if (!connection)
return NULL; return NULL;
INIT_LIST_HEAD(&connection->protocol_links);
if (!gb_protocol_get(connection, protocol_id)) {
kfree(connection);
return NULL;
}
hd = interface->gmod->hd; hd = interface->gmod->hd;
connection->hd = hd; /* XXX refcount? */ connection->hd = hd; /* XXX refcount? */
if (!gb_connection_hd_cport_id_alloc(connection)) { if (!gb_connection_hd_cport_id_alloc(connection)) {
/* kref_put(connection->hd); */ /* kref_put(connection->hd); */
gb_protocol_put(connection);
kfree(connection); kfree(connection);
return NULL; return NULL;
} }
connection->interface = interface; connection->interface = interface;
connection->interface_cport_id = cport_id; connection->interface_cport_id = cport_id;
connection->protocol_id = protocol_id;
connection->state = GB_CONNECTION_STATE_DISABLED; connection->state = GB_CONNECTION_STATE_DISABLED;
connection->dev.parent = &interface->dev; connection->dev.parent = &interface->dev;
...@@ -188,6 +194,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, ...@@ -188,6 +194,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface,
if (retval) { if (retval) {
gb_connection_hd_cport_id_free(connection); gb_connection_hd_cport_id_free(connection);
/* kref_put(connection->hd); */ /* kref_put(connection->hd); */
gb_protocol_put(connection);
kfree(connection); kfree(connection);
return NULL; return NULL;
} }
...@@ -228,6 +235,8 @@ void gb_connection_destroy(struct gb_connection *connection) ...@@ -228,6 +235,8 @@ void gb_connection_destroy(struct gb_connection *connection)
spin_unlock_irq(&gb_connections_lock); spin_unlock_irq(&gb_connections_lock);
gb_connection_hd_cport_id_free(connection); gb_connection_hd_cport_id_free(connection);
/* kref_put(connection->hd); */
gb_protocol_put(connection);
device_del(&connection->dev); device_del(&connection->dev);
} }
...@@ -267,7 +276,7 @@ int gb_connection_init(struct gb_connection *connection) ...@@ -267,7 +276,7 @@ int gb_connection_init(struct gb_connection *connection)
/* Need to enable the connection to initialize it */ /* Need to enable the connection to initialize it */
connection->state = GB_CONNECTION_STATE_ENABLED; connection->state = GB_CONNECTION_STATE_ENABLED;
switch (connection->protocol_id) { switch (connection->protocol->id) {
case GREYBUS_PROTOCOL_I2C: case GREYBUS_PROTOCOL_I2C:
connection->handler = &gb_i2c_connection_handler; connection->handler = &gb_i2c_connection_handler;
break; break;
...@@ -287,7 +296,7 @@ int gb_connection_init(struct gb_connection *connection) ...@@ -287,7 +296,7 @@ int gb_connection_init(struct gb_connection *connection)
case GREYBUS_PROTOCOL_VENDOR: case GREYBUS_PROTOCOL_VENDOR:
default: default:
gb_connection_err(connection, "unimplemented protocol %hhu", gb_connection_err(connection, "unimplemented protocol %hhu",
connection->protocol_id); connection->protocol->id);
ret = -ENXIO; ret = -ENXIO;
break; break;
} }
......
...@@ -39,7 +39,9 @@ struct gb_connection { ...@@ -39,7 +39,9 @@ struct gb_connection {
struct rb_node hd_node; struct rb_node hd_node;
struct list_head interface_links; struct list_head interface_links;
u8 protocol_id;
struct gb_protocol *protocol;
struct list_head protocol_links;
enum gb_connection_state state; enum gb_connection_state state;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "module.h" #include "module.h"
#include "interface.h" #include "interface.h"
#include "connection.h" #include "connection.h"
#include "protocol.h"
#include "operation.h" #include "operation.h"
......
...@@ -200,7 +200,7 @@ static gb_operation_recv_handler gb_operation_recv_handlers[] = { ...@@ -200,7 +200,7 @@ static gb_operation_recv_handler gb_operation_recv_handlers[] = {
static void gb_operation_request_handle(struct gb_operation *operation) static void gb_operation_request_handle(struct gb_operation *operation)
{ {
u8 protocol_id = operation->connection->protocol_id; u8 protocol_id = operation->connection->protocol->id;
/* Subtract one from array size to stay within u8 range */ /* Subtract one from array size to stay within u8 range */
if (protocol_id <= (u8)(ARRAY_SIZE(gb_operation_recv_handlers) - 1)) { if (protocol_id <= (u8)(ARRAY_SIZE(gb_operation_recv_handlers) - 1)) {
......
/*
* Greybus protocol handling
*
* Copyright 2014 Google Inc.
*
* Released under the GPLv2 only.
*/
#include "greybus.h"
/* Global list of registered protocols */
static DEFINE_SPINLOCK(gb_protocols_lock);
static LIST_HEAD(gb_protocols);
/* Caller must hold gb_protocols_lock */
struct gb_protocol *_gb_protocol_find(u8 id)
{
struct gb_protocol *protocol;
list_for_each_entry(protocol, &gb_protocols, links)
if (protocol->id == id)
return protocol;
return NULL;
}
/* This is basically for debug */
static struct gb_protocol *gb_protocol_find(u8 id)
{
struct gb_protocol *protocol;
spin_lock_irq(&gb_protocols_lock);
protocol = _gb_protocol_find(id);
spin_unlock_irq(&gb_protocols_lock);
return protocol;
}
/* Returns true if protocol was succesfully registered, false otherwise */
bool gb_protocol_register(u8 id)
{
struct gb_protocol *protocol;
struct gb_protocol *existing;
/* Initialize it speculatively */
protocol = kzalloc(sizeof(*protocol), GFP_KERNEL);
if (!protocol)
return false;
protocol->id = id;
INIT_LIST_HEAD(&protocol->connections);
spin_lock_irq(&gb_protocols_lock);
existing = _gb_protocol_find(id);
if (!existing)
list_add(&protocol->links, &gb_protocols);
spin_unlock_irq(&gb_protocols_lock);
if (existing) {
kfree(protocol);
protocol = NULL;
}
return protocol != NULL;
}
/* Returns true if successful, false otherwise */
bool gb_protocol_deregister(struct gb_protocol *protocol)
{
spin_lock_irq(&gb_protocols_lock);
if (list_empty(&protocol->connections))
list_del(&protocol->links);
else
protocol = NULL; /* Protocol is still in use */
spin_unlock_irq(&gb_protocols_lock);
kfree(protocol);
return protocol != NULL;
}
/* Returns true if successful, false otherwise */
bool gb_protocol_get(struct gb_connection *connection, u8 id)
{
struct gb_protocol *protocol;
/* Sanity */
if (!list_empty(&connection->protocol_links) ||
!connection->protocol->id) {
gb_connection_err(connection,
"connection already has protocol");
return false;
}
spin_lock_irq(&gb_protocols_lock);
protocol = _gb_protocol_find(id);
if (protocol)
list_add(&connection->protocol_links, &protocol->connections);
spin_unlock_irq(&gb_protocols_lock);
connection->protocol = protocol;
return protocol != NULL;
}
void gb_protocol_put(struct gb_connection *connection)
{
struct gb_protocol *protocol = connection->protocol;
/* Sanity checks */
if (list_empty(&connection->protocol_links)) {
gb_connection_err(connection,
"connection protocol not recorded");
return;
}
if (!protocol || gb_protocol_find(protocol->id) != protocol) {
gb_connection_err(connection,
"connection has undefined protocol");
return;
}
spin_lock_irq(&gb_protocols_lock);
list_del(&connection->protocol_links);
connection->protocol = NULL;
spin_unlock_irq(&gb_protocols_lock);
}
/*
* Greybus protocol handling
*
* Copyright 2014 Google Inc.
*
* Released under the GPLv2 only.
*/
#ifndef __PROTOCOL_H
#define __PROTOCOL_H
#include "greybus.h"
struct gb_protocol {
u8 id;
struct list_head connections; /* protocol users */
struct list_head links; /* global list */
};
bool gb_protocol_register(u8 id);
bool gb_protocol_deregister(struct gb_protocol *protocol);
bool gb_protocol_get(struct gb_connection *connection, u8 id);
void gb_protocol_put(struct gb_connection *connection);
#endif /* __PROTOCOL_H */
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