greybus: let operation layer examine incoming data

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>
This commit is contained in:
Alex Elder 2014-10-16 06:35:33 -05:00 committed by Greg Kroah-Hartman
parent 84d148b10e
commit d90c25b0a2
3 changed files with 108 additions and 32 deletions

View 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];

View File

@ -19,6 +19,11 @@
*/
#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 */
}

View 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);