Commit 2eb585f8 authored by Alex Elder's avatar Alex Elder Committed by Greg Kroah-Hartman

greybus: move receive handling to operation layer

Create a work queue to do the bulk of processing of received
operation request or response messages.
Signed-off-by: default avatarAlex Elder <elder@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent d90c25b0
...@@ -284,6 +284,12 @@ static int __init gb_init(void) ...@@ -284,6 +284,12 @@ static int __init gb_init(void)
goto error_gbuf; goto error_gbuf;
} }
retval = gb_operation_init();
if (retval) {
pr_err("gb_operation_init failed\n");
goto error_operation;
}
retval = gb_tty_init(); retval = gb_tty_init();
if (retval) { if (retval) {
pr_err("gb_tty_init failed\n"); pr_err("gb_tty_init failed\n");
...@@ -293,6 +299,9 @@ static int __init gb_init(void) ...@@ -293,6 +299,9 @@ static int __init gb_init(void)
return 0; return 0;
error_tty: error_tty:
gb_operation_exit();
error_operation:
gb_gbuf_exit(); gb_gbuf_exit();
error_gbuf: error_gbuf:
...@@ -310,6 +319,7 @@ static int __init gb_init(void) ...@@ -310,6 +319,7 @@ static int __init gb_init(void)
static void __exit gb_exit(void) static void __exit gb_exit(void)
{ {
gb_tty_exit(); gb_tty_exit();
gb_operation_exit();
gb_gbuf_exit(); gb_gbuf_exit();
gb_ap_exit(); gb_ap_exit();
bus_unregister(&greybus_bus_type); bus_unregister(&greybus_bus_type);
......
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
*/ */
#define GB_OPERATION_MESSAGE_SIZE_MAX 4096 #define GB_OPERATION_MESSAGE_SIZE_MAX 4096
/* Workqueue to handle Greybus operation completions. */
static struct workqueue_struct *gb_operation_recv_workqueue;
/* /*
* 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
...@@ -126,16 +129,13 @@ gb_operation_find(struct gb_connection *connection, u16 id) ...@@ -126,16 +129,13 @@ 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);
} }
/* /* Wait for a submitted operation to complete */
* Wait for a submitted operatnoi to complete */
int gb_operation_wait(struct gb_operation *operation) int gb_operation_wait(struct gb_operation *operation)
{ {
int ret; int ret;
...@@ -148,46 +148,100 @@ int gb_operation_wait(struct gb_operation *operation) ...@@ -148,46 +148,100 @@ int gb_operation_wait(struct gb_operation *operation)
} }
typedef void (*gb_operation_recv_handler)(struct gb_operation *operation);
static gb_operation_recv_handler gb_operation_recv_handlers[] = {
[GREYBUS_PROTOCOL_CONTROL] = NULL,
[GREYBUS_PROTOCOL_AP] = NULL,
[GREYBUS_PROTOCOL_GPIO] = NULL,
[GREYBUS_PROTOCOL_I2C] = NULL,
[GREYBUS_PROTOCOL_UART] = NULL,
[GREYBUS_PROTOCOL_HID] = NULL,
[GREYBUS_PROTOCOL_VENDOR] = NULL,
};
static void gb_operation_request_handle(struct gb_operation *operation)
{
u8 protocol = operation->connection->protocol;
/* Subtract one from array size to stay within u8 range */
if (protocol <= (u8)(ARRAY_SIZE(gb_operation_recv_handlers) - 1)) {
gb_operation_recv_handler handler;
handler = gb_operation_recv_handlers[protocol];
if (handler) {
handler(operation); /* Handle the request */
return;
}
}
gb_connection_err(operation->connection, "unrecognized protocol %u\n",
(unsigned int)protocol);
operation->result = GB_OP_PROTOCOL_BAD;
gb_operation_complete(operation);
}
/* /*
* Called when an operation buffer completes. * Either this operation contains an incoming request, or its
* response has arrived. An incoming request will have a null
* response buffer pointer (it is the responsibility of the request
* handler to allocate and fill in the response buffer).
*/ */
static void gb_operation_gbuf_complete(struct gbuf *gbuf) static void gb_operation_recv_work(struct work_struct *recv_work)
{ {
/* Don't do anything */
struct gb_operation *operation; struct gb_operation *operation;
struct gb_operation_msg_hdr *header; bool incoming_request;
u16 id;
/* operation = container_of(recv_work, struct gb_operation, recv_work);
* This isn't right, but it keeps things balanced until we incoming_request = operation->response == NULL;
* can set up operation response handling. if (incoming_request)
*/ gb_operation_request_handle(operation);
header = gbuf->transfer_buffer; gb_operation_complete(operation);
id = le16_to_cpu(header->id);
operation = gb_operation_find(gbuf->connection, id); /* We're finished with the buffer we read into */
if (operation) if (incoming_request)
gb_operation_remove(operation); greybus_gbuf_finished(operation->request);
else else
gb_connection_err(gbuf->connection, "operation not found"); greybus_gbuf_finished(operation->response);
}
/*
* Buffer completion function. We get notified whenever any buffer
* completes. For outbound messages, this tells us that the message
* has been sent. For inbound messages, it means the data has
* landed in the buffer and is ready to be processed.
*
* Either way, we don't do anything. We don't really care when an
* outbound message has been sent, and for incoming messages we
* we'll be done with everything we need to do before we mark it
* finished.
*
* XXX We may want to record that a buffer is (or is no longer) in flight.
*/
static void gb_operation_gbuf_complete(struct gbuf *gbuf)
{
return;
} }
/* /*
* Allocate a buffer to be used for an operation request or response * Allocate a buffer to be used for an operation request or response
* message. Both types of message contain a header, which is filled * message. For outgoing messages, both types of message contain a
* in here. W * common header, which is filled in here. Incoming requests or
* responses also contain the same header, but there's no need to
* initialize it here (it'll be overwritten by the incoming
* message).
*/ */
struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation,
u8 type, size_t size, bool outbound) u8 type, size_t size, bool data_out)
{ {
struct gb_connection *connection = operation->connection; struct gb_connection *connection = operation->connection;
struct gb_operation_msg_hdr *header; struct gb_operation_msg_hdr *header;
struct gbuf *gbuf; struct gbuf *gbuf;
gfp_t gfp_flags = outbound ? GFP_KERNEL : GFP_ATOMIC; gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC;
/* Operation buffers hold a header in addition to their payload */
size += sizeof(*header); size += sizeof(*header);
gbuf = greybus_alloc_gbuf(connection, gb_operation_gbuf_complete, gbuf = greybus_alloc_gbuf(connection, gb_operation_gbuf_complete,
size, outbound, gfp_flags, operation); size, data_out, gfp_flags, operation);
if (!gbuf) if (!gbuf)
return NULL; return NULL;
...@@ -222,11 +276,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, ...@@ -222,11 +276,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection,
{ {
struct gb_operation *operation; struct gb_operation *operation;
gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC; gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC;
bool outgoing = response_size != 0;
if (!request_size) {
gb_connection_err(connection, "zero-sized request");
return NULL;
}
/* XXX Use a slab cache */ /* XXX Use a slab cache */
operation = kzalloc(sizeof(*operation), gfp_flags); operation = kzalloc(sizeof(*operation), gfp_flags);
...@@ -235,7 +285,8 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, ...@@ -235,7 +285,8 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection,
operation->connection = connection; /* XXX refcount? */ operation->connection = connection; /* XXX refcount? */
operation->request = gb_operation_gbuf_create(operation, type, operation->request = gb_operation_gbuf_create(operation, type,
request_size, true); request_size,
outgoing);
if (!operation->request) { if (!operation->request) {
kfree(operation); kfree(operation);
return NULL; return NULL;
...@@ -245,10 +296,11 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, ...@@ -245,10 +296,11 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection,
/* We always use the full request buffer */ /* We always use the full request buffer */
operation->request->actual_length = request_size; operation->request->actual_length = request_size;
if (response_size) { if (outgoing) {
type |= GB_OPERATION_TYPE_RESPONSE; type |= GB_OPERATION_TYPE_RESPONSE;
operation->response = gb_operation_gbuf_create(operation, operation->response = gb_operation_gbuf_create(operation,
type, response_size, false); type, response_size,
false);
if (!operation->response) { if (!operation->response) {
greybus_free_gbuf(operation->request); greybus_free_gbuf(operation->request);
kfree(operation); kfree(operation);
...@@ -259,6 +311,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, ...@@ -259,6 +311,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection,
sizeof(struct gb_operation_msg_hdr); sizeof(struct gb_operation_msg_hdr);
} }
INIT_WORK(&operation->recv_work, gb_operation_recv_work);
operation->callback = NULL; /* set at submit time */ operation->callback = NULL; /* set at submit time */
init_completion(&operation->completion); init_completion(&operation->completion);
...@@ -333,6 +386,11 @@ int gb_operation_response_send(struct gb_operation *operation) ...@@ -333,6 +386,11 @@ int gb_operation_response_send(struct gb_operation *operation)
return 0; return 0;
} }
/*
* Handle data arriving on a connection. This is called in
* interrupt context, so just copy the incoming data into a buffer
* and do remaining handling via a work queue.
*/
void gb_connection_operation_recv(struct gb_connection *connection, void gb_connection_operation_recv(struct gb_connection *connection,
void *data, size_t size) void *data, size_t size)
{ {
...@@ -354,7 +412,7 @@ void gb_connection_operation_recv(struct gb_connection *connection, ...@@ -354,7 +412,7 @@ void gb_connection_operation_recv(struct gb_connection *connection,
operation = gb_operation_find(connection, id); operation = gb_operation_find(connection, id);
if (!operation) { if (!operation) {
gb_connection_err(connection, "operation not found"); gb_connection_err(connection, "operation not found");
return; return;
} }
gb_operation_remove(operation); gb_operation_remove(operation);
gbuf = operation->response; gbuf = operation->response;
...@@ -376,5 +434,20 @@ void gb_connection_operation_recv(struct gb_connection *connection, ...@@ -376,5 +434,20 @@ void gb_connection_operation_recv(struct gb_connection *connection,
memcpy(gbuf->transfer_buffer, data, msg_size); memcpy(gbuf->transfer_buffer, data, msg_size);
gbuf->actual_length = msg_size; gbuf->actual_length = msg_size;
/* XXX And now we let a work queue handle the rest */ /* The rest will be handled in work queue context */
queue_work(gb_operation_recv_workqueue, &operation->recv_work);
}
int gb_operation_init(void)
{
gb_operation_recv_workqueue = alloc_workqueue("greybus_recv", 0, 1);
if (!gb_operation_recv_workqueue)
return -ENOMEM;
return 0;
}
void gb_operation_exit(void)
{
destroy_workqueue(gb_operation_recv_workqueue);
} }
...@@ -16,6 +16,7 @@ enum gb_operation_status { ...@@ -16,6 +16,7 @@ enum gb_operation_status {
GB_OP_INVALID = 1, GB_OP_INVALID = 1,
GB_OP_NO_MEMORY = 2, GB_OP_NO_MEMORY = 2,
GB_OP_INTERRUPTED = 3, GB_OP_INTERRUPTED = 3,
GB_OP_PROTOCOL_BAD = 4,
}; };
/* /*
...@@ -55,6 +56,7 @@ struct gb_operation { ...@@ -55,6 +56,7 @@ struct gb_operation {
u16 id; u16 id;
u8 result; u8 result;
struct work_struct recv_work;
gb_operation_callback callback; /* If asynchronous */ gb_operation_callback callback; /* If asynchronous */
struct completion completion; /* Used if no callback */ struct completion completion; /* Used if no callback */
...@@ -81,4 +83,7 @@ int gb_operation_response_send(struct gb_operation *operation); ...@@ -81,4 +83,7 @@ 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);
int gb_operation_init(void);
void gb_operation_exit(void);
#endif /* !__OPERATION_H */ #endif /* !__OPERATION_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment