greybus: Merge branch 'master' into vibrator-gb
diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile
index 198539c..b254516 100644
--- a/drivers/staging/greybus/Makefile
+++ b/drivers/staging/greybus/Makefile
@@ -1,5 +1,4 @@
 greybus-y :=	core.o		\
-		gbuf.o		\
 		sysfs.o		\
 		debugfs.o	\
 		ap.o		\
diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c
index cb6e2e1..258d96c 100644
--- a/drivers/staging/greybus/connection.c
+++ b/drivers/staging/greybus/connection.c
@@ -29,6 +29,21 @@
 	return connection;
 }
 
+void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
+			u8 *data, size_t length)
+{
+	struct gb_connection *connection;
+
+	connection = gb_hd_connection_find(hd, cport_id);
+	if (!connection) {
+		dev_err(hd->parent,
+			"nonexistent connection (%zu bytes dropped)\n", length);
+		return;
+	}
+	gb_connection_operation_recv(connection, data, length);
+}
+EXPORT_SYMBOL_GPL(greybus_cport_in);
+
 /*
  * Allocate an available CPort Id for use for the host side of the
  * given connection.  The lowest-available id is returned, so the
diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h
index 5e96967..bcaad47 100644
--- a/drivers/staging/greybus/connection.h
+++ b/drivers/staging/greybus/connection.h
@@ -53,6 +53,8 @@
 struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd,
 				u16 cport_id);
 
+void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
+			u8 *data, size_t length);
 __printf(2, 3)
 void gb_connection_err(struct gb_connection *connection, const char *fmt, ...);
 
diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c
index 26e4b44..588e624 100644
--- a/drivers/staging/greybus/core.c
+++ b/drivers/staging/greybus/core.c
@@ -226,12 +226,6 @@
 		goto error_ap;
 	}
 
-	retval = gb_gbuf_init();
-	if (retval) {
-		pr_err("gb_gbuf_init failed\n");
-		goto error_gbuf;
-	}
-
 	retval = gb_operation_init();
 	if (retval) {
 		pr_err("gb_operation_init failed\n");
@@ -250,8 +244,6 @@
 error_protocol:
 	gb_operation_exit();
 error_operation:
-	gb_gbuf_exit();
-error_gbuf:
 	gb_ap_exit();
 error_ap:
 	bus_unregister(&greybus_bus_type);
@@ -265,7 +257,6 @@
 {
 	gb_protocol_exit();
 	gb_operation_exit();
-	gb_gbuf_exit();
 	gb_ap_exit();
 	bus_unregister(&greybus_bus_type);
 	gb_debugfs_cleanup();
diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c
index e24012a..e276f0c 100644
--- a/drivers/staging/greybus/es1-ap-usb.c
+++ b/drivers/staging/greybus/es1-ap-usb.c
@@ -99,6 +99,9 @@
 	u32 cport_reserve = gbuf->dest_cport_id == CPORT_ID_BAD ? 0 : 1;
 	u8 *buffer;
 
+	if (gbuf->transfer_buffer)
+		return -EALREADY;
+
 	if (size > ES1_GBUF_MSG_SIZE) {
 		pr_err("guf was asked to be bigger than %ld!\n",
 		       ES1_GBUF_MSG_SIZE);
@@ -146,6 +149,7 @@
 	if (gbuf->dest_cport_id != CPORT_ID_BAD)
 		transfer_buffer--;	/* Back up to cport id */
 	kfree(transfer_buffer);
+	gbuf->transfer_buffer = NULL;
 }
 
 #define ES1_TIMEOUT	500	/* 500 ms for the SVC to do something */
@@ -214,6 +218,8 @@
 	struct urb *urb;
 
 	transfer_buffer = gbuf->transfer_buffer;
+	if (!transfer_buffer)
+		return -EINVAL;
 	buffer = &transfer_buffer[-1];	/* yes, we mean -1 */
 
 	/* Find a free urb */
diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c
deleted file mode 100644
index 6f8873af..0000000
--- a/drivers/staging/greybus/gbuf.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Greybus gbuf handling
- *
- * Copyright 2014 Google Inc.
- *
- * Released under the GPLv2 only.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/kref.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-
-#include "greybus.h"
-
-static struct kmem_cache *gbuf_head_cache;
-
-/**
- * greybus_alloc_gbuf - allocate a greybus buffer
- *
- * @gmod: greybus device that wants to allocate this
- * @cport: cport to send the data to
- * @complete: callback when the gbuf is finished with
- * @size: size of the buffer
- * @gfp_mask: allocation mask
- *
- * TODO: someday it will be nice to handle DMA, but for now, due to the
- * architecture we are stuck with, the greybus core has to allocate the buffer
- * that the driver can then fill up with the data to be sent out.  Curse
- * hardware designers for this issue...
- */
-struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd,
-				u16 dest_cport_id,
-				unsigned int size,
-				gfp_t gfp_mask)
-{
-	struct gbuf *gbuf;
-	int retval;
-
-	gbuf = kmem_cache_zalloc(gbuf_head_cache, gfp_mask);
-	if (!gbuf)
-		return NULL;
-
-	kref_init(&gbuf->kref);
-	gbuf->hd = hd;
-	gbuf->dest_cport_id = dest_cport_id;
-	gbuf->status = -EBADR;	/* Initial value--means "never set" */
-
-	/* Host controller specific allocation for the actual buffer */
-	retval = hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask);
-	if (retval) {
-		kmem_cache_free(gbuf_head_cache, gbuf);
-		return NULL;
-	}
-
-	return gbuf;
-}
-EXPORT_SYMBOL_GPL(greybus_alloc_gbuf);
-
-static DEFINE_MUTEX(gbuf_mutex);
-
-static void free_gbuf(struct kref *kref)
-{
-	struct gbuf *gbuf = container_of(kref, struct gbuf, kref);
-
-	gbuf->hd->driver->free_gbuf_data(gbuf);
-
-	kmem_cache_free(gbuf_head_cache, gbuf);
-	mutex_unlock(&gbuf_mutex);
-}
-
-void greybus_free_gbuf(struct gbuf *gbuf)
-{
-	/* drop the reference count and get out of here */
-	kref_put_mutex(&gbuf->kref, free_gbuf, &gbuf_mutex);
-}
-EXPORT_SYMBOL_GPL(greybus_free_gbuf);
-
-struct gbuf *greybus_get_gbuf(struct gbuf *gbuf)
-{
-	mutex_lock(&gbuf_mutex);
-	kref_get(&gbuf->kref);
-	mutex_unlock(&gbuf_mutex);
-	return gbuf;
-}
-EXPORT_SYMBOL_GPL(greybus_get_gbuf);
-
-int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask)
-{
-	gbuf->status = -EINPROGRESS;
-
-	return gbuf->hd->driver->submit_gbuf(gbuf, gfp_mask);
-}
-
-void greybus_kill_gbuf(struct gbuf *gbuf)
-{
-	if (gbuf->status != -EINPROGRESS)
-		return;
-
-	gbuf->hd->driver->kill_gbuf(gbuf);
-}
-
-void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
-			u8 *data, size_t length)
-{
-	struct gb_connection *connection;
-
-	connection = gb_hd_connection_find(hd, cport_id);
-	if (!connection) {
-		dev_err(hd->parent,
-			"nonexistent connection (%zu bytes dropped)\n", length);
-		return;
-	}
-	gb_connection_operation_recv(connection, data, length);
-}
-EXPORT_SYMBOL_GPL(greybus_cport_in);
-
-int gb_gbuf_init(void)
-{
-	gbuf_head_cache = kmem_cache_create("gbuf_head_cache",
-					    sizeof(struct gbuf), 0, 0, NULL);
-	return 0;
-}
-
-void gb_gbuf_exit(void)
-{
-	kmem_cache_destroy(gbuf_head_cache);
-	gbuf_head_cache = NULL;
-}
diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c
index 473df4d4..6f4609d 100644
--- a/drivers/staging/greybus/gpio-gb.c
+++ b/drivers/staging/greybus/gpio-gb.c
@@ -153,7 +153,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "version response %hhu",
 			response->status);
@@ -199,7 +199,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "line count response %hhu",
 			response->status);
@@ -234,7 +234,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 
 	/* Synchronous operation--no callback */
@@ -244,7 +244,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "activate response %hhu",
 			response->status);
@@ -278,7 +278,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 
 	/* Synchronous operation--no callback */
@@ -288,7 +288,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "deactivate response %hhu",
 			response->status);
@@ -320,7 +320,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 
 	/* Synchronous operation--no callback */
@@ -330,7 +330,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "get direction response %hhu",
 			response->status);
@@ -369,7 +369,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 
 	/* Synchronous operation--no callback */
@@ -379,7 +379,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "direction in response %hhu",
 			response->status);
@@ -412,7 +412,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 	request->value = value_high ? 1 : 0;
 
@@ -423,7 +423,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "direction out response %hhu",
 			response->status);
@@ -456,7 +456,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 
 	/* Synchronous operation--no callback */
@@ -466,7 +466,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "get value response %hhu",
 			response->status);
@@ -507,7 +507,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 	request->value = value_high ? 1 : 0;
 
@@ -518,7 +518,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "set value response %hhu",
 			response->status);
@@ -554,7 +554,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 	request->usec = cpu_to_le16(debounce_usec);
 
@@ -565,7 +565,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "set debounce response %hhu",
 			response->status);
diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h
index 90469bb..301bd45 100644
--- a/drivers/staging/greybus/greybus.h
+++ b/drivers/staging/greybus/greybus.h
@@ -56,82 +56,6 @@
 #define HOST_DEV_CPORT_ID_MAX	CONFIG_HOST_DEV_CPORT_ID_MAX
 #define CPORT_ID_BAD		U16_MAX		/* UniPro max id is 4095 */
 
-/*
-  gbuf
-
-  This is the "main" data structure to send / receive Greybus messages
-
-  There are two different "views" of a gbuf structure:
-    - a greybus driver
-    - a greybus host controller
-
-  A Greybus driver needs to worry about the following:
-    - creating a gbuf
-    - putting data into a gbuf
-    - sending a gbuf to a device
-    - receiving a gbuf from a device
-
-  Creating a gbuf:
-    A greybus driver calls greybus_alloc_gbuf()
-  Putting data into a gbuf:
-    copy data into gbuf->transfer_buffer
-  Send a gbuf:
-    A greybus driver calls greybus_submit_gbuf()
-    The completion function in a gbuf will be called if the gbuf is successful
-    or not.  That completion function runs in user context.  After the
-    completion function is called, the gbuf must not be touched again as the
-    greybus core "owns" it.  But, if a greybus driver wants to "hold on" to a
-    gbuf after the completion function has been called, a reference must be
-    grabbed on the gbuf with a call to greybus_get_gbuf().  When finished with
-    the gbuf, call greybus_free_gbuf() and when the last reference count is
-    dropped, it will be removed from the system.
-  Receive a gbuf:
-    A greybus driver calls gb_register_cport_complete() with a pointer to the
-    callback function to be called for when a gbuf is received from a specific
-    cport and device.  That callback will be made in user context with a gbuf
-    when it is received.  To stop receiving messages, call
-    gb_deregister_cport_complete() for a specific cport.
-
-
-  Greybus Host controller drivers need to provide
-    - a way to allocate the transfer buffer for a gbuf
-    - a way to free the transfer buffer for a gbuf when it is "finished"
-    - a way to submit gbuf for transmissions
-    - notify the core the gbuf is complete
-    - receive gbuf from the wire and submit them to the core
-    - a way to send and receive svc messages
-  Allocate a transfer buffer
-    the host controller function alloc_gbuf_data is called
-  Free a transfer buffer
-    the host controller function free_gbuf_data is called
-  Submit a gbuf to the hardware
-    the host controller function submit_gbuf is called
-  Notify the gbuf is complete
-    the host controller driver must call greybus_gbuf_finished()
-  Submit a SVC message to the hardware
-    the host controller function send_svc_msg is called
-  Receive gbuf messages
-    the host controller driver must call greybus_cport_in() with the data
-  Reveive SVC messages from the hardware
-    The host controller driver must call greybus_svc_in
-
-
-*/
-
-struct gbuf {
-	struct kref kref;
-
-	struct greybus_host_device *hd;
-	u16 dest_cport_id;		/* Destination CPort id */
-	int status;
-
-	void *transfer_buffer;
-	u32 transfer_buffer_length;
-
-	void *hcd_data;			/* for the HCD to track the gbuf */
-};
-
-
 /* For SP1 hardware, we are going to "hardcode" each device to have all logical
  * blocks in order to be able to address them as one unified "unit".  Then
  * higher up layers will then be able to talk to them as one logical block and
@@ -144,6 +68,7 @@
 
 struct greybus_host_device;
 struct svc_msg;
+struct gbuf;
 
 /* Greybus "Host driver" structure, needed by a host controller driver to be
  * able to handle both SVC control as well as "real" greybus messages
@@ -177,19 +102,6 @@
 struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd,
 					      struct device *parent);
 void greybus_remove_hd(struct greybus_host_device *hd);
-void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
-			u8 *data, size_t length);
-
-struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd,
-				u16 dest_cport_id, unsigned int size,
-				gfp_t gfp_mask);
-void greybus_free_gbuf(struct gbuf *gbuf);
-struct gbuf *greybus_get_gbuf(struct gbuf *gbuf);
-#define greybus_put_gbuf	greybus_free_gbuf
-
-int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags);
-void greybus_kill_gbuf(struct gbuf *gbuf);
-
 
 struct greybus_driver {
 	const char *name;
diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c
index c179078..3374173 100644
--- a/drivers/staging/greybus/i2c-gb.c
+++ b/drivers/staging/greybus/i2c-gb.c
@@ -118,7 +118,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "version response %hhu",
 			response->status);
@@ -170,7 +170,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "functionality response %hhu",
 			response->status);
@@ -198,7 +198,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->msec = cpu_to_le16(msec);
 
 	/* Synchronous operation--no callback */
@@ -208,7 +208,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "timeout response %hhu",
 			response->status);
@@ -235,7 +235,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->retries = retries;
 
 	/* Synchronous operation--no callback */
@@ -245,7 +245,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "retries response %hhu",
 			response->status);
@@ -321,7 +321,7 @@
 	if (!operation)
 		return NULL;
 
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->op_count = cpu_to_le16(op_count);
 	/* Fill in the ops array */
 	op = &request->ops[0];
@@ -380,7 +380,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		if (response->status == GB_OP_RETRY) {
 			ret = -EAGAIN;
diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c
index c0161a2..2239883 100644
--- a/drivers/staging/greybus/operation.c
+++ b/drivers/staging/greybus/operation.c
@@ -23,6 +23,7 @@
 
 /*
  * XXX This needs to be coordinated with host driver parameters
+ * XXX May need to reduce to allow for message header within a page
  */
 #define GB_OPERATION_MESSAGE_SIZE_MAX	4096
 
@@ -71,7 +72,7 @@
 	spin_unlock_irq(&gb_operations_lock);
 
 	/* Store the operation id in the request header */
-	header = operation->request->transfer_buffer;
+	header = operation->request.gbuf.transfer_buffer;
 	header->id = cpu_to_le16(operation->id);
 }
 
@@ -102,6 +103,20 @@
 	return found ? operation : NULL;
 }
 
+static int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask)
+{
+	gbuf->status = -EINPROGRESS;
+
+	return gbuf->hd->driver->submit_gbuf(gbuf, gfp_mask);
+}
+
+static void greybus_kill_gbuf(struct gbuf *gbuf)
+{
+	if (gbuf->status != -EINPROGRESS)
+		return;
+
+	gbuf->hd->driver->kill_gbuf(gbuf);
+}
 /*
  * An operations's response message has arrived.  If no callback was
  * supplied it was submitted for asynchronous completion, so we notify
@@ -124,7 +139,7 @@
 	ret = wait_for_completion_interruptible(&operation->completion);
 	/* If interrupted, cancel the in-flight buffer */
 	if (ret < 0)
-		greybus_kill_gbuf(operation->request);
+		greybus_kill_gbuf(&operation->request.gbuf);
 	return ret;
 
 }
@@ -134,7 +149,7 @@
 	struct gb_protocol *protocol = operation->connection->protocol;
 	struct gb_operation_msg_hdr *header;
 
-	header = operation->request->transfer_buffer;
+	header = operation->request.gbuf.transfer_buffer;
 
 	/*
 	 * If the protocol has no incoming request handler, report
@@ -164,7 +179,7 @@
 	bool incoming_request;
 
 	operation = container_of(recv_work, struct gb_operation, recv_work);
-	incoming_request = operation->response == NULL;
+	incoming_request = operation->response.gbuf.transfer_buffer == NULL;
 	if (incoming_request)
 		gb_operation_request_handle(operation);
 	gb_operation_complete(operation);
@@ -196,28 +211,42 @@
  * initialize it here (it'll be overwritten by the incoming
  * message).
  */
-static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation,
-					     u8 type, size_t size,
-					     bool data_out)
+static int gb_operation_message_init(struct gb_operation *operation,
+					u8 type, size_t size,
+					bool request, bool data_out)
 {
 	struct gb_connection *connection = operation->connection;
+	struct greybus_host_device *hd = connection->hd;
+	struct gb_message *message;
 	struct gb_operation_msg_hdr *header;
 	struct gbuf *gbuf;
 	gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC;
 	u16 dest_cport_id;
+	int ret;
 
 	if (size > GB_OPERATION_MESSAGE_SIZE_MAX)
-		return NULL;	/* Message too big */
+		return -E2BIG;
+	size += sizeof(*header);
+
+	if (request) {
+		message = &operation->request;
+	} else {
+		message = &operation->response;
+		type |= GB_OPERATION_TYPE_RESPONSE;
+	}
+	gbuf = &message->gbuf;
 
 	if (data_out)
 		dest_cport_id = connection->interface_cport_id;
 	else
 		dest_cport_id = CPORT_ID_BAD;
-	size += sizeof(*header);
-	gbuf = greybus_alloc_gbuf(connection->hd, dest_cport_id,
-					size, gfp_flags);
-	if (!gbuf)
-		return NULL;
+
+	gbuf->hd = hd;
+	gbuf->dest_cport_id = dest_cport_id;
+	gbuf->status = -EBADR;	/* Initial value--means "never set" */
+	ret = hd->driver->alloc_gbuf_data(gbuf, size, gfp_flags);
+	if (ret)
+		return ret;
 
 	/* Fill in the header structure */
 	header = (struct gb_operation_msg_hdr *)gbuf->transfer_buffer;
@@ -225,7 +254,17 @@
 	header->id = 0;		/* Filled in when submitted */
 	header->type = type;
 
-	return gbuf;
+	message->payload = header + 1;
+	message->operation = operation;
+
+	return 0;
+}
+
+static void gb_operation_message_exit(struct gb_message *message)
+{
+	message->operation = NULL;
+	message->payload = NULL;
+	message->gbuf.hd->driver->free_gbuf_data(&message->gbuf);
 }
 
 /*
@@ -251,30 +290,23 @@
 	struct gb_operation *operation;
 	gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC;
 	bool outgoing = response_size != 0;
+	int ret;
 
 	operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags);
 	if (!operation)
 		return NULL;
 	operation->connection = connection;
 
-	operation->request = gb_operation_gbuf_create(operation, type,
-							request_size,
-							outgoing);
-	if (!operation->request)
+	ret = gb_operation_message_init(operation, type, request_size,
+						true, outgoing);
+	if (ret)
 		goto err_cache;
-	operation->request_payload = operation->request->transfer_buffer +
-					sizeof(struct gb_operation_msg_hdr);
 
 	if (outgoing) {
-		type |= GB_OPERATION_TYPE_RESPONSE;
-		operation->response = gb_operation_gbuf_create(operation,
-						type, response_size,
-						false);
-		if (!operation->response)
+		ret = gb_operation_message_init(operation, type, response_size,
+						false, false);
+		if (ret)
 			goto err_request;
-		operation->response_payload =
-				operation->response->transfer_buffer +
-				sizeof(struct gb_operation_msg_hdr);
 	}
 
 	INIT_WORK(&operation->recv_work, gb_operation_recv_work);
@@ -290,7 +322,7 @@
 	return operation;
 
 err_request:
-	greybus_free_gbuf(operation->request);
+	gb_operation_message_exit(&operation->request);
 err_cache:
 	kmem_cache_free(gb_operation_cache, operation);
 
@@ -311,8 +343,8 @@
 	list_del(&operation->links);
 	spin_unlock_irq(&gb_operations_lock);
 
-	greybus_free_gbuf(operation->response);
-	greybus_free_gbuf(operation->request);
+	gb_operation_message_exit(&operation->response);
+	gb_operation_message_exit(&operation->request);
 
 	kmem_cache_free(gb_operation_cache, operation);
 }
@@ -349,7 +381,7 @@
 	 */
 	operation->callback = callback;
 	gb_pending_operation_insert(operation);
-	ret = greybus_submit_gbuf(operation->request, GFP_KERNEL);
+	ret = greybus_submit_gbuf(&operation->request.gbuf, GFP_KERNEL);
 	if (ret)
 		return ret;
 
@@ -414,7 +446,7 @@
 		}
 		cancel_delayed_work(&operation->timeout_work);
 		gb_pending_operation_remove(operation);
-		gbuf = operation->response;
+		gbuf = &operation->response.gbuf;
 		if (size > gbuf->transfer_buffer_length) {
 			operation->result = GB_OP_OVERFLOW;
 			gb_connection_err(connection, "recv buffer too small");
@@ -429,7 +461,7 @@
 			gb_connection_err(connection, "can't create operation");
 			return;
 		}
-		gbuf = operation->request;
+		gbuf = &operation->request.gbuf;
 	}
 
 	memcpy(gbuf->transfer_buffer, data, msg_size);
@@ -444,9 +476,9 @@
 void gb_operation_cancel(struct gb_operation *operation)
 {
 	operation->canceled = true;
-	greybus_kill_gbuf(operation->request);
-	if (operation->response)
-		greybus_kill_gbuf(operation->response);
+	greybus_kill_gbuf(&operation->request.gbuf);
+	if (operation->response.gbuf.transfer_buffer)
+		greybus_kill_gbuf(&operation->response.gbuf);
 }
 
 int gb_operation_init(void)
diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h
index dc15c2f..f43531d 100644
--- a/drivers/staging/greybus/operation.h
+++ b/drivers/staging/greybus/operation.h
@@ -11,6 +11,8 @@
 
 #include <linux/completion.h>
 
+struct gb_operation;
+
 enum gb_operation_status {
 	GB_OP_SUCCESS		= 0,
 	GB_OP_INVALID		= 1,
@@ -22,6 +24,23 @@
 	GB_OP_TIMEOUT		= 0xff,
 };
 
+struct gbuf {
+	struct greybus_host_device *hd;
+	u16 dest_cport_id;		/* Destination CPort id */
+	int status;
+
+	void *transfer_buffer;
+	u32 transfer_buffer_length;
+
+	void *hcd_data;			/* for the HCD to track the gbuf */
+};
+
+struct gb_message {
+	void			*payload;
+	struct gb_operation	*operation;
+	struct gbuf		gbuf;
+};
+
 /*
  * A Greybus operation is a remote procedure call performed over a
  * connection between the AP and a function on Greybus module.
@@ -50,12 +69,11 @@
  * is guaranteed to be 64-bit aligned.
  * XXX and callback?
  */
-struct gb_operation;
 typedef void (*gb_operation_callback)(struct gb_operation *);
 struct gb_operation {
 	struct gb_connection	*connection;
-	struct gbuf		*request;
-	struct gbuf		*response;
+	struct gb_message	request;
+	struct gb_message	response;
 	u16			id;
 	bool			canceled;
 
@@ -67,10 +85,6 @@
 
 	struct kref		kref;
 	struct list_head	links;	/* connection->{operations,pending} */
-
-	/* These are what's used by caller */
-	void			*request_payload;
-	void			*response_payload;
 };
 
 void gb_connection_operation_recv(struct gb_connection *connection,
diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c
index 44a4f64..0f46552 100644
--- a/drivers/staging/greybus/pwm-gb.c
+++ b/drivers/staging/greybus/pwm-gb.c
@@ -110,7 +110,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "version response %hhu",
 				  response->status);
@@ -151,7 +151,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "pwm count response %hhu",
 				  response->status);
@@ -181,7 +181,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 
 	/* Synchronous operation--no callback */
@@ -191,7 +191,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "activate response %hhu",
 				  response->status);
@@ -220,7 +220,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 
 	/* Synchronous operation--no callback */
@@ -230,7 +230,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "deactivate response %hhu",
 				  response->status);
@@ -258,7 +258,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 	request->duty = duty;
 	request->period = period;
@@ -270,7 +270,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "config response %hhu",
 				  response->status);
@@ -299,7 +299,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 	request->polarity = polarity;
 
@@ -310,7 +310,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "set polarity response %hhu",
 				  response->status);
@@ -339,7 +339,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 
 	/* Synchronous operation--no callback */
@@ -349,7 +349,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "enable response %hhu",
 				  response->status);
@@ -378,7 +378,7 @@
 					sizeof(*request), sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->which = which;
 
 	/* Synchronous operation--no callback */
@@ -388,7 +388,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "disable response %hhu",
 				  response->status);
diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c
index a4f745a..a8c342e 100644
--- a/drivers/staging/greybus/uart-gb.c
+++ b/drivers/staging/greybus/uart-gb.c
@@ -229,7 +229,7 @@
 					sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->size = cpu_to_le16(size);
 	memcpy(&request->data[0], data, size);
 
@@ -241,7 +241,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "send data response %hhu",
 				  response->status);
@@ -267,7 +267,7 @@
 					sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	memcpy(&request->line_coding, line_coding, sizeof(*line_coding));
 
 	/* Synchronous operation--no callback */
@@ -278,7 +278,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "send line coding response %hhu",
 				  response->status);
@@ -304,7 +304,7 @@
 					sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->control = cpu_to_le16(control);
 
 	/* Synchronous operation--no callback */
@@ -315,7 +315,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "send control response %hhu",
 				  response->status);
@@ -345,7 +345,7 @@
 					sizeof(*response));
 	if (!operation)
 		return -ENOMEM;
-	request = operation->request_payload;
+	request = operation->request.payload;
 	request->state = state;
 
 	/* Synchronous operation--no callback */
@@ -356,7 +356,7 @@
 		goto out;
 	}
 
-	response = operation->response_payload;
+	response = operation->response.payload;
 	if (response->status) {
 		gb_connection_err(connection, "send break response %hhu",
 				  response->status);