From e88afa5811c741facff3fa695d340133ac8a1be1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:15 -0500 Subject: [PATCH] greybus: introduce an operation abstraction This patch defines a new "operation" abstraction. An operation is a request from by one end of a connection to the function (or AP) on the other, coupled with a matching response returned to the requestor. The request indicates some action to be performed by the target of the request (such as "read some data"). Once the action has completed the target sends back an operation response message. Additional data can be supplied by the sender with its request, and/or by the target with its resposne message. Each request message has a unique id, generated by the sender. The sender recognizes the matching response by the presence of this id value. Each end of a connection is responsible for creating unique ids for the requests it sends. An operation also has a type, whose interpretation is dependent on the function type on the end of the connection opposite the sender. It is up to the creator of an operation to fill in the data (if any) to be sent with the request. Note that not all requests are initiated by the AP. Incoming data on a module function can result in a request message being sent from that function to the AP to notify of the data's arrival. Once the AP has processed this, it sends a response to the sender. Every operation response contains a status byte. If it's value is 0, the operation was successful. Any other value indicates an error. Add a defintion of U16_MAX to "kernel_ver.h". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/connection.c | 14 +++ drivers/staging/greybus/connection.h | 5 + drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/kernel_ver.h | 4 + drivers/staging/greybus/operation.c | 171 +++++++++++++++++++++++++++ drivers/staging/greybus/operation.h | 69 +++++++++++ 7 files changed, 265 insertions(+) create mode 100644 drivers/staging/greybus/operation.c create mode 100644 drivers/staging/greybus/operation.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index ed39a5c6b6fd..d6c4cc3c89ff 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -7,6 +7,7 @@ greybus-y := core.o \ interface.o \ function.o \ connection.o \ + operation.o \ i2c-gb.o \ gpio-gb.o \ sdio-gb.o \ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 113c98542858..fa5ab5d30ae7 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -6,6 +6,9 @@ * Released under the GPLv2 only. */ +#include + +#include "kernel_ver.h" #include "greybus.h" /* @@ -13,6 +16,9 @@ * between a CPort on a (local) Greybus host device and a CPort on * another Greybus module. * + * A connection also maintains the state of operations sent over the + * connection. + * * Returns a pointer to the new connection if successful, or a null * pointer otherwise. */ @@ -28,6 +34,8 @@ struct gb_connection *gb_connection_create(struct greybus_host_device *hd, connection->hd = hd; /* XXX refcount? */ connection->cport_id = cport_id; connection->function = function; /* XXX refcount? */ + INIT_LIST_HEAD(&connection->operations); + atomic_set(&connection->op_cycle, 0); return connection; } @@ -41,8 +49,14 @@ void gb_connection_destroy(struct gb_connection *connection) return; /* XXX Need to wait for any outstanding requests to complete */ + WARN_ON(!list_empty(&connection->operations)); /* kref_put(function); */ /* kref_put(hd); */ kfree(connection); } + +u16 gb_connection_op_id(struct gb_connection *connection) +{ + return (u16)(atomic_inc_return(&connection->op_cycle) % U16_MAX); +} diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 79b3b07f94c4..c653c95d2834 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -20,10 +20,15 @@ struct gb_connection { u16 cport_id; /* Host side */ struct list_head host_links; + + struct list_head operations; + atomic_t op_cycle; }; bool gb_connection_setup(struct greybus_host_device *hd, u16 cport_id, struct gb_function *function); void gb_connection_teardown(struct gb_connection *connection); +u16 gb_connection_op_id(struct gb_connection *connection); + #endif /* __CONNECTION_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 732cc5e51dc4..9a66fd1e60b0 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -24,6 +24,7 @@ #include "interface.h" #include "function.h" #include "connection.h" +#include "operation.h" /* Matches up with the Greybus Protocol specification document */ diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 4aa5b83bff60..c9ea7a94f4e6 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -22,4 +22,8 @@ #define U8_MAX ((u8)~0U) #endif /* ! U8_MAX */ +#ifndef U16_MAX +#define U16_MAX ((u16)(~0U)) +#endif /* !U16_MAX */ + #endif /* __GREYBUS_KERNEL_VER_H */ diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c new file mode 100644 index 000000000000..5cb23aa3867a --- /dev/null +++ b/drivers/staging/greybus/operation.c @@ -0,0 +1,171 @@ +/* + * Greybus operations + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include + +#include "greybus.h" + +/* + * All operation messages (both requests and responses) begin with + * a common header that encodes the size of the data (header + * included). This header also contains a unique identifier, which + * is used to keep track of in-flight operations. Finally, the + * header contains a operation type field, whose interpretation is + * dependent on what type of device lies on the other end of the + * connection. Response messages are distinguished from request + * messages by setting the high bit (0x80) in the operation type + * value. + * + * The wire format for all numeric fields in the header is little + * endian. Any operation-specific data begins immediately after the + * header, and is 64-bit aligned. + */ +struct gb_operation_msg_hdr { + __le16 size; /* Size in bytes of header + payload */ + __le16 id; /* Operation unique id */ + __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ + /* 3 bytes pad, must be zero (ignore when read) */ +} __aligned(sizeof(u64)); + +/* XXX Could be per-host device, per-module, or even per-connection */ +static DEFINE_SPINLOCK(gb_operations_lock); + +/* + * An operations's response message has arrived. If no callback was + * supplied it was submitted for asynchronous completion, so we notify + * any waiters. Otherwise we assume calling the completion is enough + * and nobody else will be waiting. + */ +void gb_operation_complete(struct gb_operation *operation) +{ + if (operation->callback) + operation->callback(operation); + else + complete_all(&operation->completion); +} + +/* + * Wait for a submitted operatnoi to complete */ +int gb_operation_wait(struct gb_operation *operation) +{ + int ret; + + ret = wait_for_completion_interruptible(&operation->completion); + /* If interrupted, cancel the in-flight buffer */ + if (ret < 0) + ret = greybus_kill_gbuf(operation->gbuf); + return ret; + +} + +/* + * 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 + * gfp is probably GFP_ATOMIC but really I think + * the gfp mask should go away. + */ + operation->callback = callback; + ret = greybus_submit_gbuf(operation->gbuf, GFP_KERNEL); + if (ret) + return ret; + if (!callback) + ret = gb_operation_wait(operation); + + return ret; +} + +/* + * Called when a greybus request message has actually been sent. + */ +static void gbuf_out_callback(struct gbuf *gbuf) +{ + /* Record it's been submitted; need response now */ +} + +/* + * Create a Greybus operation having a buffer big enough for an + * outgoing payload of the given size to be sent over the given + * connection. + * + * Returns a pointer to the new operation or a null pointer if a + * failure occurs due to memory exhaustion. + */ +struct gb_operation *gb_operation_create(struct gb_connection *connection, + size_t size) +{ + struct gb_operation *operation; + struct gb_operation_msg_hdr *header; + struct gbuf *gbuf; + + /* XXX Use a slab cache */ + operation = kzalloc(sizeof(*operation), GFP_KERNEL); + if (!operation) + return NULL; + + /* Our buffer holds a header in addition to the requested payload */ + size += sizeof(*header); + gbuf = greybus_alloc_gbuf(connection->function->interface->gmod, + connection->cport_id, + gbuf_out_callback, size, + GFP_KERNEL, operation); + if (gbuf) { + kfree(operation); + return NULL; + } + + operation->connection = connection; /* XXX refcount? */ + + /* Fill in the header structure and payload pointer */ + operation->gbuf = gbuf; + header = (struct gb_operation_msg_hdr *)&gbuf->transfer_buffer; + header->id = 0; + header->size = size; + operation->payload = (char *)header + sizeof(*header); + + operation->callback = NULL; /* set at submit time */ + init_completion(&operation->completion); + + spin_lock_irq(&gb_operations_lock); + list_add_tail(&operation->links, &connection->operations); + spin_unlock_irq(&gb_operations_lock); + + return operation; +} + +/* + * Destroy a previously created operation. + */ +void gb_operation_destroy(struct gb_operation *operation) +{ + if (WARN_ON(!operation)) + return; + + /* XXX Make sure it's not in flight */ + spin_lock_irq(&gb_operations_lock); + list_del(&operation->links); + spin_unlock_irq(&gb_operations_lock); + + greybus_free_gbuf(operation->gbuf); + + kfree(operation); +} diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h new file mode 100644 index 000000000000..96a7a0fcba56 --- /dev/null +++ b/drivers/staging/greybus/operation.h @@ -0,0 +1,69 @@ +/* + * Greybus operations + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __OPERATION_H +#define __OPERATION_H + +#include + +enum gb_operation_status { + GB_OP_SUCCESS = 0, + GB_OP_INVALID = 1, + GB_OP_NO_MEMORY = 2, + GB_OP_INTERRUPTED = 3, +}; + +/* + * A Greybus operation is a remote procedure call performed over a + * connection between the AP and a function on Greybus module. + * Every operation consists of a request message sent to the other + * end of the connection coupled with a reply returned to the + * sender. + * + * The state for managing active requests on a connection is held in + * the connection structure. + * + * YADA YADA + * + * submitting each request and providing its matching response to + * the caller when it arrives. Operations normally complete + * asynchronously, and when an operation's response arrives its + * callback function is executed. The callback pointer is supplied + * at the time the operation is submitted; a null callback pointer + * causes synchronous operation--the caller is blocked until + * the response arrives. In addition, it is possible to await + * the completion of a submitted asynchronous operation. + * + * A Greybus device operation includes a Greybus buffer to hold the + * data sent to the device. The only field within a Greybus + * operation that should be used by a caller is the payload pointer, + * which should be used to populate the request data. This pointer + * is guaranteed to be 64-bit aligned. + * XXX and callback? + */ +struct gb_operation; +typedef void (*gb_operation_callback)(struct gb_operation *); +struct gb_operation { + struct gb_connection *connection; + struct gbuf *gbuf; + void *payload; /* sender data */ + gb_operation_callback callback; /* If asynchronous */ + struct completion completion; /* Used if no callback */ + u8 result; + + struct list_head links; /* connection->operations */ +}; + +struct gb_operation *gb_operation_create(struct gb_connection *connection, + size_t size); +void gb_operation_destroy(struct gb_operation *operation); + +int gb_operation_wait(struct gb_operation *operation); +void gb_operation_complete(struct gb_operation *operation); + +#endif /* !__OPERATION_H */ -- 2.39.2