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
|
@ -167,6 +167,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id,
|
||||||
"nonexistent connection (%zu bytes dropped)\n", length);
|
"nonexistent connection (%zu bytes dropped)\n", length);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
gb_connection_operation_recv(connection, data, length);
|
||||||
|
|
||||||
/* first check to see if we have a cport handler for this cport */
|
/* first check to see if we have a cport handler for this cport */
|
||||||
ch = &cport_handler[cport_id];
|
ch = &cport_handler[cport_id];
|
||||||
|
|
|
@ -19,6 +19,11 @@
|
||||||
*/
|
*/
|
||||||
#define GB_OPERATION_TYPE_RESPONSE 0x80
|
#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
|
* All operation messages (both requests and responses) begin with
|
||||||
* a common header that encodes the size of the data (header
|
* 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 *
|
static struct gb_operation *
|
||||||
gb_operation_find(struct gb_connection *connection, u16 id)
|
gb_operation_find(struct gb_connection *connection, u16 id)
|
||||||
{
|
{
|
||||||
struct gb_operation *operation;
|
struct gb_operation *operation = NULL;
|
||||||
struct rb_node *node;
|
struct rb_node *node;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
|
@ -121,10 +126,12 @@ gb_operation_find(struct gb_connection *connection, u16 id)
|
||||||
*/
|
*/
|
||||||
void gb_operation_complete(struct gb_operation *operation)
|
void gb_operation_complete(struct gb_operation *operation)
|
||||||
{
|
{
|
||||||
|
/* XXX Should probably report bad status if no callback */
|
||||||
if (operation->callback)
|
if (operation->callback)
|
||||||
operation->callback(operation);
|
operation->callback(operation);
|
||||||
else
|
else
|
||||||
complete_all(&operation->completion);
|
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.
|
* Called when an operation buffer completes.
|
||||||
*/
|
*/
|
||||||
static void gb_operation_gbuf_complete(struct gbuf *gbuf)
|
static void gb_operation_gbuf_complete(struct gbuf *gbuf)
|
||||||
{
|
{
|
||||||
|
/* Don't do anything */
|
||||||
struct gb_operation *operation;
|
struct gb_operation *operation;
|
||||||
struct gb_operation_msg_hdr *header;
|
struct gb_operation_msg_hdr *header;
|
||||||
u16 id;
|
u16 id;
|
||||||
|
@ -310,3 +287,94 @@ void gb_operation_destroy(struct gb_operation *operation)
|
||||||
|
|
||||||
kfree(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 *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,
|
struct gb_operation *gb_operation_create(struct gb_connection *connection,
|
||||||
u8 type, size_t request_size,
|
u8 type, size_t request_size,
|
||||||
size_t response_size);
|
size_t response_size);
|
||||||
void gb_operation_destroy(struct gb_operation *operation);
|
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);
|
int gb_operation_wait(struct gb_operation *operation);
|
||||||
void gb_operation_complete(struct gb_operation *operation);
|
void gb_operation_complete(struct gb_operation *operation);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue