greybus: register preallocated protocols

Set up protocol structures as static objects in each protocol source
file.  Pass the address of that in--rather than the protocol id and
version information--to the protocol registration routine.  Call a
central routine to register all our pre-defined protocols.

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c
index 28c0d0b..457daf70 100644
--- a/drivers/staging/greybus/battery-gb.c
+++ b/drivers/staging/greybus/battery-gb.c
@@ -404,3 +404,19 @@
 	.connection_init	= gb_battery_connection_init,
 	.connection_exit	= gb_battery_connection_exit,
 };
+
+static struct gb_protocol battery_protocol = {
+	.id			= GREYBUS_PROTOCOL_BATTERY,
+	.major			= 0,
+	.minor			= 1,
+};
+
+bool gb_battery_protocol_init(void)
+{
+	return gb_protocol_register(&battery_protocol);
+}
+
+void gb_battery_protocol_exit(void)
+{
+	gb_protocol_deregister(&battery_protocol);
+}
diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c
index 252d131..1d05c35 100644
--- a/drivers/staging/greybus/core.c
+++ b/drivers/staging/greybus/core.c
@@ -277,17 +277,23 @@
 		goto error_operation;
 	}
 
-	return 0;
+	if (!gb_protocol_init()) {
+		/* This only fails for duplicate protocol registration */
+		retval = -EEXIST;
+		pr_err("gb_protocol_init failed\n");
+		goto error_protocol;
+	}
 
+	return 0;	/* Success */
+
+error_protocol:
+	gb_operation_exit();
 error_operation:
 	gb_gbuf_exit();
-
 error_gbuf:
 	gb_ap_exit();
-
 error_ap:
 	bus_unregister(&greybus_bus_type);
-
 error_bus:
 	gb_debugfs_cleanup();
 
diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c
index df7dbae..242b91a 100644
--- a/drivers/staging/greybus/gpio-gb.c
+++ b/drivers/staging/greybus/gpio-gb.c
@@ -796,3 +796,19 @@
 	.connection_init	= gb_gpio_connection_init,
 	.connection_exit	= gb_gpio_connection_exit,
 };
+
+static struct gb_protocol gpio_protocol = {
+	.id			= GREYBUS_PROTOCOL_GPIO,
+	.major			= 0,
+	.minor			= 1,
+};
+
+bool gb_gpio_protocol_init(void)
+{
+	return gb_protocol_register(&gpio_protocol);
+}
+
+void gb_gpio_protocol_exit(void)
+{
+	gb_protocol_deregister(&gpio_protocol);
+}
diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h
index c6988ce..844ab8a 100644
--- a/drivers/staging/greybus/greybus_manifest.h
+++ b/drivers/staging/greybus/greybus_manifest.h
@@ -30,6 +30,7 @@
 	GREYBUS_PROTOCOL_I2C		= 0x03,
 	GREYBUS_PROTOCOL_UART		= 0x04,
 	GREYBUS_PROTOCOL_HID		= 0x05,
+	GREYBUS_PROTOCOL_SDIO		= 0x06,
 	GREYBUS_PROTOCOL_BATTERY	= 0x08,
 	GREYBUS_PROTOCOL_LED		= 0x0e,
 		/* ... */
diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c
index b26464a..c8fae17 100644
--- a/drivers/staging/greybus/i2c-gb.c
+++ b/drivers/staging/greybus/i2c-gb.c
@@ -522,3 +522,19 @@
 	.connection_init	= gb_i2c_connection_init,
 	.connection_exit	= gb_i2c_connection_exit,
 };
+
+static struct gb_protocol i2c_protocol = {
+	.id			= GREYBUS_PROTOCOL_I2C,
+	.major			= 0,
+	.minor			= 1,
+};
+
+bool gb_i2c_protocol_init(void)
+{
+	return gb_protocol_register(&i2c_protocol);
+}
+
+void gb_i2c_protocol_exit(void)
+{
+	gb_protocol_deregister(&i2c_protocol);
+}
diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c
index 6fec32e..93e0af3 100644
--- a/drivers/staging/greybus/protocol.c
+++ b/drivers/staging/greybus/protocol.c
@@ -39,18 +39,12 @@
 }
 
 /* Returns true if protocol was succesfully registered, false otherwise */
-bool gb_protocol_register(u8 id, u8 major, u8 minor)
+bool gb_protocol_register(struct gb_protocol *protocol)
 {
-	struct gb_protocol *protocol;
 	struct gb_protocol *existing;
-
-	/* Initialize it speculatively */
-	protocol = kzalloc(sizeof(*protocol), GFP_KERNEL);
-	if (!protocol)
-		return false;
-	protocol->id = id;
-	protocol->major = major;
-	protocol->minor = minor;
+	u8 id = protocol->id;
+	u8 major = protocol->major;
+	u8 minor = protocol->minor;
 
 	/*
 	 * The protocols list is sorted first by protocol id (low to
@@ -79,7 +73,6 @@
 
 		/* A matching protocol has already been registered */
 		spin_unlock_irq(&gb_protocols_lock);
-		kfree(protocol);
 
 		return false;
 	}
@@ -94,7 +87,17 @@
 	return true;
 }
 
-/* Returns true if successful, false otherwise */
+/*
+ * De-register a previously registered protocol.
+ *
+ * XXX Currently this fails (and reports an error to the caller) if
+ * XXX the protocol is currently in use.  We may want to forcefully
+ * XXX kill off a protocol and all its active users at some point.
+ * XXX But I think that's better handled by quescing modules that
+ * XXX have users and having those users drop their reference.
+ *
+ * Returns true if successful, false otherwise.
+ */
 bool gb_protocol_deregister(struct gb_protocol *protocol)
 {
 	u8 protocol_count = 0;
@@ -108,7 +111,6 @@
 			list_del(&protocol->links);
 	}
 	spin_unlock_irq(&gb_protocols_lock);
-	kfree(protocol);
 
 	return protocol && !protocol_count;
 }
@@ -158,3 +160,39 @@
 		pr_err("protocol id %hhu version %hhu.%hhu not found\n",
 			protocol->id, major, minor);
 }
+
+bool gb_protocol_init(void)
+{
+	bool ret = true;
+
+	if (!gb_battery_protocol_init()) {
+		pr_err("error initializing battery protocol\n");
+		ret = false;
+	}
+	if (!gb_gpio_protocol_init()) {
+		pr_err("error initializing gpio protocol\n");
+		ret = false;
+	}
+	if (!gb_i2c_protocol_init()) {
+		pr_err("error initializing i2c protocol\n");
+		ret = false;
+	}
+	if (!gb_uart_protocol_init()) {
+		pr_err("error initializing uart protocol\n");
+		ret = false;
+	}
+	if (!gb_sdio_protocol_init()) {
+		pr_err("error initializing sdio protocol\n");
+		ret = false;
+	}
+	return ret;
+}
+
+void gb_protocol_exit(void)
+{
+	gb_sdio_protocol_exit();
+	gb_uart_protocol_exit();
+	gb_i2c_protocol_exit();
+	gb_gpio_protocol_exit();
+	gb_battery_protocol_exit();
+}
diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h
index aa7b554..c2adfdc 100644
--- a/drivers/staging/greybus/protocol.h
+++ b/drivers/staging/greybus/protocol.h
@@ -25,10 +25,33 @@
 	struct list_head	links;		/* global list */
 };
 
-bool gb_protocol_register(u8 id, u8 major, u8 minor);
+bool gb_protocol_register(struct gb_protocol *protocol);
 bool gb_protocol_deregister(struct gb_protocol *protocol);
 
 struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor);
 void gb_protocol_put(struct gb_protocol *protocol);
 
+/*
+ * These are defined in their respective protocol source files.
+ * Declared here for now.  They could be added via modules, or maybe
+ * just use initcalls (which level?).
+ */
+extern bool gb_battery_protocol_init(void);
+extern void gb_battery_protocol_exit(void);
+
+extern bool gb_gpio_protocol_init(void);
+extern void gb_gpio_protocol_exit(void);
+
+extern bool gb_i2c_protocol_init(void);
+extern void gb_i2c_protocol_exit(void);
+
+extern bool gb_uart_protocol_init(void);
+extern void gb_uart_protocol_exit(void);
+
+extern bool gb_sdio_protocol_init(void);
+extern void gb_sdio_protocol_exit(void);
+
+bool gb_protocol_init(void);
+void gb_protocol_exit(void);
+
 #endif /* __PROTOCOL_H */
diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c
index 30caba8..8fbfbca 100644
--- a/drivers/staging/greybus/sdio-gb.c
+++ b/drivers/staging/greybus/sdio-gb.c
@@ -81,3 +81,19 @@
 	.connection_init	= gb_sdio_connection_init,
 	.connection_exit	= gb_sdio_connection_exit,
 };
+
+static struct gb_protocol sdio_protocol = {
+	.id			= GREYBUS_PROTOCOL_SDIO,
+	.major			= 0,
+	.minor			= 1,
+};
+
+bool gb_sdio_protocol_init(void)
+{
+	return gb_protocol_register(&sdio_protocol);
+}
+
+void gb_sdio_protocol_exit(void)
+{
+	gb_protocol_deregister(&sdio_protocol);
+}
diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c
index b52d9e1..5596644 100644
--- a/drivers/staging/greybus/uart-gb.c
+++ b/drivers/staging/greybus/uart-gb.c
@@ -524,3 +524,19 @@
 	.connection_init	= gb_uart_connection_init,
 	.connection_exit	= gb_uart_connection_exit,
 };
+
+static struct gb_protocol uart_protocol = {
+	.id			= GREYBUS_PROTOCOL_UART,
+	.major			= 0,
+	.minor			= 1,
+};
+
+bool gb_uart_protocol_init(void)
+{
+	return gb_protocol_register(&uart_protocol);
+}
+
+void gb_uart_protocol_exit(void)
+{
+	gb_protocol_deregister(&uart_protocol);
+}