greybus: define greybus function abstraction

Define new source files "function.h" and "function.c" to contain the
definitions of the Greybus function abstraction.  A Greybus function
represents an active entity connected to a CPort implemented by a
Greybus interface.  A Greybus function has a type, which defines the
protocol to be used to interact with the function.  A Greybus
interface normally has at least two functions, but potentially many
more.

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile
index 21f6358..0efb695 100644
--- a/drivers/staging/greybus/Makefile
+++ b/drivers/staging/greybus/Makefile
@@ -5,6 +5,7 @@
 		ap.o		\
 		module.o	\
 		interface.o	\
+		function.o	\
 		i2c-gb.o	\
 		gpio-gb.o	\
 		sdio-gb.o	\
diff --git a/drivers/staging/greybus/function.c b/drivers/staging/greybus/function.c
new file mode 100644
index 0000000..b06265c
--- /dev/null
+++ b/drivers/staging/greybus/function.c
@@ -0,0 +1,58 @@
+/*
+ * Greybus functions
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include "greybus.h"
+
+/* XXX This could be per-host device or per-module or per-interface */
+static DEFINE_SPINLOCK(gb_functions_lock);
+
+/*
+ * A Greybus function generically defines an entity associated with
+ * a CPort within a module.  Each function has a type (e.g. i2c,
+ * GPIO, etc.) that defines how it behaves and how the AP interacts
+ * with it.
+ *
+ * Create a gb_function structure to represent a discovered
+ * function.  Returns a pointer to the new function or a null
+ * pointer if a failure occurs due to memory exhaustion.
+ */
+struct gb_function *gb_function_create(struct gb_interface *interface,
+			u16 cport_id, enum greybus_function_type type)
+{
+	struct gb_function *function;
+
+	function = kzalloc(sizeof(*function), GFP_KERNEL);
+	if (!function)
+		return NULL;
+
+	function->interface = interface;	/* XXX refcount? */
+	function->cport_id = cport_id;
+	function->type = type;
+
+	spin_lock_irq(&gb_functions_lock);
+	list_add_tail(&function->links, &interface->functions);
+	spin_unlock_irq(&gb_functions_lock);
+
+	return function;
+}
+
+/*
+ * Tear down a previously set up function.
+ */
+void gb_function_destroy(struct gb_function *function)
+{
+	if (WARN_ON(!function))
+		return;
+
+	spin_lock_irq(&gb_functions_lock);
+	list_del(&function->links);
+	spin_unlock_irq(&gb_functions_lock);
+
+	/* kref_put(gmod); */
+	kfree(function);
+}
diff --git a/drivers/staging/greybus/function.h b/drivers/staging/greybus/function.h
new file mode 100644
index 0000000..379ffcd
--- /dev/null
+++ b/drivers/staging/greybus/function.h
@@ -0,0 +1,25 @@
+/*
+ * Greybus functions
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef __FUNCTION_H
+#define __FUNCTION_H
+
+struct gb_function {
+	struct gb_interface		*interface;
+	u16				cport_id;
+	enum greybus_function_type	type;
+
+	struct list_head		links;	/* interface->functions */
+};
+
+struct gb_function *gb_function_create(struct gb_interface *interface,
+				u16 cport_id,
+				enum greybus_function_type function_type);
+void gb_function_destroy(struct gb_function *function);
+
+#endif /* __FUNCTION_H */
diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h
index c82c630..4eb70af 100644
--- a/drivers/staging/greybus/greybus.h
+++ b/drivers/staging/greybus/greybus.h
@@ -22,6 +22,7 @@
 #include "greybus_manifest.h"
 #include "module.h"
 #include "interface.h"
+#include "function.h"
 
 
 /* Matches up with the Greybus Protocol specification document */
diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c
index a6e6e0e..dc7d009 100644
--- a/drivers/staging/greybus/interface.c
+++ b/drivers/staging/greybus/interface.c
@@ -33,6 +33,7 @@
 
 	interface->gmod = gmod;		/* XXX refcount? */
 	interface->interface_id = interface_id;
+	INIT_LIST_HEAD(&interface->functions);
 
 	spin_lock_irq(&gb_interfaces_lock);
 	list_add_tail(&interface->links, &gmod->interfaces);
diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h
index 63ae762..7c3feb7 100644
--- a/drivers/staging/greybus/interface.h
+++ b/drivers/staging/greybus/interface.h
@@ -14,6 +14,7 @@
 struct gb_interface {
 	struct gb_module	*gmod;
 	u8			interface_id;
+	struct list_head	functions;
 
 	struct list_head	links;	/* module->interfaces */
 };