mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
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:
parent
84d148b10e
commit
d90c25b0a2
3 changed files with 108 additions and 32 deletions
|
@ -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];
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue