From 82e26f73b2a8ee4acc2507494430aa5774da2b74 Mon Sep 17 00:00:00 2001
From: Alex Elder <elder@linaro.org>
Date: Tue, 2 Dec 2014 08:30:39 -0600
Subject: [PATCH] greybus: send operation response messages

Define a helper function gb_operation_response_alloc() and use it
to allocate the response buffer for outgoing operations in
gb_operation_create_common(.

Use it also in  gb_operation_response_send() if the caller has not
allocated a response buffer.

Once a response buffer is allocated, fill in its result code and
send it.

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
---
 drivers/staging/greybus/operation.c | 60 +++++++++++++++++++++++------
 drivers/staging/greybus/operation.h |  3 ++
 2 files changed, 52 insertions(+), 11 deletions(-)

diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c
index f3246818d7770..1409d31f48c0f 100644
--- a/drivers/staging/greybus/operation.c
+++ b/drivers/staging/greybus/operation.c
@@ -471,6 +471,34 @@ static u8 gb_operation_errno_map(int errno)
 	}
 }
 
+bool gb_operation_response_alloc(struct gb_operation *operation,
+					size_t response_size)
+{
+	struct greybus_host_device *hd = operation->connection->hd;
+	struct gb_operation_msg_hdr *request_header;
+	struct gb_message *response;
+	u8 type;
+
+	request_header = operation->request->header;
+	type = request_header->type | GB_OPERATION_TYPE_RESPONSE;
+	response = gb_operation_message_alloc(hd, type, response_size,
+						GFP_KERNEL);
+	if (!response)
+		return false;
+	response->operation = operation;
+
+	/*
+	 * Size and type get initialized when the message is
+	 * allocated.  The errno will be set before sending.  All
+	 * that's left is the operation id, which we copy from the
+	 * request message header (as-is, in little-endian order).
+	 */
+	response->header->operation_id = request_header->operation_id;
+	operation->response = response;
+
+	return true;
+}
+
 /*
  * Create a Greybus operation to be sent over the given connection.
  * The request buffer will be big enough for a payload of the given
@@ -513,14 +541,9 @@ gb_operation_create_common(struct gb_connection *connection, u8 type,
 	operation->request->operation = operation;
 
 	/* Allocate the response buffer for outgoing operations */
-	if (type) {
-		type |= GB_OPERATION_TYPE_RESPONSE;
-		operation->response = gb_operation_message_alloc(hd, type,
-						response_size, GFP_KERNEL);
-		if (!operation->response)
+	if (type)
+		if (!gb_operation_response_alloc(operation, response_size))
 			goto err_request;
-		operation->response->operation = operation;
-	}
 	operation->errno = -EBADR;  /* Initial value--means "never set" */
 
 	INIT_WORK(&operation->work, gb_operation_work);
@@ -680,7 +703,13 @@ int gb_operation_request_send(struct gb_operation *operation,
 }
 
 /*
- * Send a response for an incoming operation request.
+ * Send a response for an incoming operation request.  A non-zero
+ * errno indicates a failed operation.
+ *
+ * If there is any response payload, the incoming request handler is
+ * responsible for allocating the response message.  Otherwise the
+ * it can simply supply the result errno; this function will
+ * allocate the response message if necessary.
  */
 int gb_operation_response_send(struct gb_operation *operation, int errno)
 {
@@ -689,10 +718,19 @@ int gb_operation_response_send(struct gb_operation *operation, int errno)
 		pr_err("request result already set\n");
 		return -EIO;	/* Shouldn't happen */
 	}
-	(void)gb_operation_errno_map;	/* avoid a build warning */
-	gb_operation_destroy(operation);
 
-	return 0;
+	if (!operation->response) {
+		if (!gb_operation_response_alloc(operation, 0)) {
+			pr_err("error allocating response\n");
+			/* XXX Respond with pre-allocated -ENOMEM? */
+			return -ENOMEM;
+		}
+	}
+
+	/* Fill in the response header and send it */
+	operation->response->header->result = gb_operation_errno_map(errno);
+
+	return gb_message_send(operation->response);
 }
 
 /*
diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h
index adaec7c43ea94..c60decb405399 100644
--- a/drivers/staging/greybus/operation.h
+++ b/drivers/staging/greybus/operation.h
@@ -98,6 +98,9 @@ static inline void gb_operation_destroy(struct gb_operation *operation)
 	gb_operation_put(operation);
 }
 
+bool gb_operation_response_alloc(struct gb_operation *operation,
+					size_t response_size);
+
 int gb_operation_request_send(struct gb_operation *operation,
 				gb_operation_callback callback);
 int gb_operation_response_send(struct gb_operation *operation, int errno);
-- 
2.30.2