greybus: let operation layer examine incoming data
authorAlex Elder <elder@linaro.org>
Thu, 16 Oct 2014 11:35:33 +0000 (06:35 -0500)
committerGreg Kroah-Hartman <greg@kroah.com>
Fri, 17 Oct 2014 16:13:15 +0000 (18:13 +0200)
Give the operation layer a chance to examine incoming data so that
it can handle it appropriately.

Treat the data as an operation message header.  If it's a response,
look up the operation it's associated with.  If it's not, create a
new operation.  Copy the incoming data into the request or response
buffer.  The next patch adds a work queue to pick up handling
the request or response from there.

Get rid of gb_operation_submit().  Instead, we have two functions,
one for sending an operation's request message, the other for
sending an operation's response message.

Not fully functional yet, still just filling things in...

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
drivers/staging/greybus/gbuf.c
drivers/staging/greybus/operation.c
drivers/staging/greybus/operation.h

index 6580deb..97b45e9 100644 (file)
@@ -167,6 +167,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
                        "nonexistent connection (%zu bytes dropped)\n", length);
                return;
        }
+       gb_connection_operation_recv(connection, data, length);
 
        /* first check to see if we have a cport handler for this cport */
        ch = &cport_handler[cport_id];
index b56a2b9..092ceb6 100644 (file)
  */
 #define GB_OPERATION_TYPE_RESPONSE     0x80
 
+/*
+ * XXX This needs to be coordinated with host driver parameters
+ */
+#define GB_OPERATION_MESSAGE_SIZE_MAX  4096
+
 /*
  * All operation messages (both requests and responses) begin with
  * a common header that encodes the size of the data (header
@@ -93,7 +98,7 @@ static void gb_operation_remove(struct gb_operation *operation)
 static struct gb_operation *
 gb_operation_find(struct gb_connection *connection, u16 id)
 {
-       struct gb_operation *operation;
+       struct gb_operation *operation = NULL;
        struct rb_node *node;
        bool found = false;
 
@@ -121,10 +126,12 @@ gb_operation_find(struct gb_connection *connection, u16 id)
  */
 void gb_operation_complete(struct gb_operation *operation)
 {
+       /* XXX Should probably report bad status if no callback */
        if (operation->callback)
                operation->callback(operation);
        else
                complete_all(&operation->completion);
+       gb_operation_destroy(operation);
 }
 
 /*
@@ -141,42 +148,12 @@ int gb_operation_wait(struct gb_operation *operation)
 
 }
 
-/*
- * Submit an outbound operation.  The caller has filled in any
- * payload so the request message is ready to go.  If non-null,
- * the callback function supplied will be called when the response
- * message has arrived indicating the operation is complete.  A null
- * callback function is used for a synchronous request; return from
- * this function won't occur until the operation is complete (or an
- * interrupt occurs).
- */
-int gb_operation_submit(struct gb_operation *operation,
-                       gb_operation_callback callback)
-{
-       int ret;
-
-       /*
-        * XXX
-        * I think the order of operations is going to be
-        * significant, and if so, we may need a mutex to surround
-        * setting the operation id and submitting the gbuf.
-        */
-       operation->callback = callback;
-       gb_operation_insert(operation);
-       ret = greybus_submit_gbuf(operation->request, GFP_KERNEL);
-       if (ret)
-               return ret;
-       if (!callback)
-               ret = gb_operation_wait(operation);
-
-       return ret;
-}
-
 /*
  * Called when an operation buffer completes.
  */
 static void gb_operation_gbuf_complete(struct gbuf *gbuf)
 {
+       /* Don't do anything */
        struct gb_operation *operation;
        struct gb_operation_msg_hdr *header;
        u16 id;
@@ -310,3 +287,94 @@ void gb_operation_destroy(struct gb_operation *operation)
 
        kfree(operation);
 }
+
+/*
+ * Send an operation request message.  The caller has filled in
+ * any payload so the request message is ready to go.  If non-null,
+ * the callback function supplied will be called when the response
+ * message has arrived indicating the operation is complete.  A null
+ * callback function is used for a synchronous request; return from
+ * this function won't occur until the operation is complete (or an
+ * interrupt occurs).
+ */
+int gb_operation_request_send(struct gb_operation *operation,
+                               gb_operation_callback callback)
+{
+       int ret;
+
+       /*
+        * XXX
+        * I think the order of operations is going to be
+        * significant, and if so, we may need a mutex to surround
+        * setting the operation id and submitting the gbuf.
+        */
+       operation->callback = callback;
+       gb_operation_insert(operation);
+       ret = greybus_submit_gbuf(operation->request, GFP_KERNEL);
+       if (ret)
+               return ret;
+       if (!callback)
+               ret = gb_operation_wait(operation);
+
+       return ret;
+}
+
+/*
+ * Send a response for an incoming operation request.
+ */
+int gb_operation_response_send(struct gb_operation *operation)
+{
+       /* XXX
+        * Caller needs to have set operation->response->actual_length
+        */
+       gb_operation_remove(operation);
+       gb_operation_destroy(operation);
+
+       return 0;
+}
+
+void gb_connection_operation_recv(struct gb_connection *connection,
+                               void *data, size_t size)
+{
+       struct gb_operation_msg_hdr *header;
+       struct gb_operation *operation;
+       struct gbuf *gbuf;
+       u16 msg_size;
+
+       if (size > GB_OPERATION_MESSAGE_SIZE_MAX) {
+               gb_connection_err(connection, "message too big");
+               return;
+       }
+
+       header = data;
+       msg_size = le16_to_cpu(header->size);
+       if (header->type & GB_OPERATION_TYPE_RESPONSE) {
+               u16 id = le16_to_cpu(header->id);
+
+               operation = gb_operation_find(connection, id);
+               if (!operation) {
+                       gb_connection_err(connection, "operation not found");
+                               return;
+               }
+               gb_operation_remove(operation);
+               gbuf = operation->response;
+               if (size > gbuf->transfer_buffer_length) {
+                       gb_connection_err(connection, "recv buffer too small");
+                       return;
+               }
+       } else {
+               WARN_ON(msg_size != size);
+               operation = gb_operation_create(connection, header->type,
+                                                       msg_size, 0);
+               if (!operation) {
+                       gb_connection_err(connection, "can't create operation");
+                       return;
+               }
+               gbuf = operation->request;
+       }
+
+       memcpy(gbuf->transfer_buffer, data, msg_size);
+       gbuf->actual_length = msg_size;
+
+       /* XXX And now we let a work queue handle the rest */
+}
index 5d863ed..2ed708a 100644 (file)
@@ -66,11 +66,18 @@ struct gb_operation {
        void                    *response_payload;
 };
 
+void gb_connection_operation_recv(struct gb_connection *connection,
+                                       void *data, size_t size);
+
 struct gb_operation *gb_operation_create(struct gb_connection *connection,
                                        u8 type, size_t request_size,
                                        size_t response_size);
 void gb_operation_destroy(struct gb_operation *operation);
 
+int gb_operation_request_send(struct gb_operation *operation,
+                               gb_operation_callback callback);
+int gb_operation_response_send(struct gb_operation *operation);
+
 int gb_operation_wait(struct gb_operation *operation);
 void gb_operation_complete(struct gb_operation *operation);