{
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);
if (!connection)
return NULL;
+ INIT_LIST_HEAD(&connection->protocol_links);
+ if (!gb_protocol_get(connection, protocol_id)) {
+ kfree(connection);
+ return NULL;
+ }
+
hd = interface->gmod->hd;
connection->hd = hd; /* XXX refcount? */
if (!gb_connection_hd_cport_id_alloc(connection)) {
/* kref_put(connection->hd); */
+ gb_protocol_put(connection);
kfree(connection);
return NULL;
}
connection->interface = interface;
connection->interface_cport_id = cport_id;
- connection->protocol_id = protocol_id;
connection->state = GB_CONNECTION_STATE_DISABLED;
connection->dev.parent = &interface->dev;
if (retval) {
gb_connection_hd_cport_id_free(connection);
/* kref_put(connection->hd); */
+ gb_protocol_put(connection);
kfree(connection);
return NULL;
}
spin_unlock_irq(&gb_connections_lock);
gb_connection_hd_cport_id_free(connection);
+ /* kref_put(connection->hd); */
+ gb_protocol_put(connection);
device_del(&connection->dev);
}
/* Need to enable the connection to initialize it */
connection->state = GB_CONNECTION_STATE_ENABLED;
- switch (connection->protocol_id) {
+ switch (connection->protocol->id) {
case GREYBUS_PROTOCOL_I2C:
connection->handler = &gb_i2c_connection_handler;
break;
case GREYBUS_PROTOCOL_VENDOR:
default:
gb_connection_err(connection, "unimplemented protocol %hhu",
- connection->protocol_id);
+ connection->protocol->id);
ret = -ENXIO;
break;
}
--- /dev/null
+/*
+ * 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);
+}