diff options
Diffstat (limited to 'greybus_operations.patch')
| -rw-r--r-- | greybus_operations.patch | 1460 |
1 files changed, 1460 insertions, 0 deletions
diff --git a/greybus_operations.patch b/greybus_operations.patch new file mode 100644 index 00000000000000..6398656528a23f --- /dev/null +++ b/greybus_operations.patch @@ -0,0 +1,1460 @@ +--- + drivers/greybus/operation.c | 1239 ++++++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/operation.h | 210 +++++++ + 2 files changed, 1449 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/operation.c +@@ -0,0 +1,1239 @@ ++/* ++ * Greybus operations ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/sched.h> ++#include <linux/wait.h> ++#include <linux/workqueue.h> ++ ++#include "greybus.h" ++#include "greybus_trace.h" ++ ++static struct kmem_cache *gb_operation_cache; ++static struct kmem_cache *gb_message_cache; ++ ++/* Workqueue to handle Greybus operation completions. */ ++static struct workqueue_struct *gb_operation_completion_wq; ++ ++/* Wait queue for synchronous cancellations. */ ++static DECLARE_WAIT_QUEUE_HEAD(gb_operation_cancellation_queue); ++ ++/* ++ * Protects updates to operation->errno. ++ */ ++static DEFINE_SPINLOCK(gb_operations_lock); ++ ++static int gb_operation_response_send(struct gb_operation *operation, ++ int errno); ++ ++/* ++ * Increment operation active count and add to connection list unless the ++ * connection is going away. ++ * ++ * Caller holds operation reference. ++ */ ++static int gb_operation_get_active(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&connection->lock, flags); ++ switch (connection->state) { ++ case GB_CONNECTION_STATE_ENABLED: ++ break; ++ case GB_CONNECTION_STATE_ENABLED_TX: ++ if (gb_operation_is_incoming(operation)) ++ goto err_unlock; ++ break; ++ case GB_CONNECTION_STATE_DISCONNECTING: ++ if (!gb_operation_is_core(operation)) ++ goto err_unlock; ++ break; ++ default: ++ goto err_unlock; ++ } ++ ++ if (operation->active++ == 0) ++ list_add_tail(&operation->links, &connection->operations); ++ ++ trace_gb_operation_get_active(operation); ++ ++ spin_unlock_irqrestore(&connection->lock, flags); ++ ++ return 0; ++ ++err_unlock: ++ spin_unlock_irqrestore(&connection->lock, flags); ++ ++ return -ENOTCONN; ++} ++ ++/* Caller holds operation reference. */ ++static void gb_operation_put_active(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&connection->lock, flags); ++ ++ trace_gb_operation_put_active(operation); ++ ++ if (--operation->active == 0) { ++ list_del(&operation->links); ++ if (atomic_read(&operation->waiters)) ++ wake_up(&gb_operation_cancellation_queue); ++ } ++ spin_unlock_irqrestore(&connection->lock, flags); ++} ++ ++static bool gb_operation_is_active(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ unsigned long flags; ++ bool ret; ++ ++ spin_lock_irqsave(&connection->lock, flags); ++ ret = operation->active; ++ spin_unlock_irqrestore(&connection->lock, flags); ++ ++ return ret; ++} ++ ++/* ++ * Set an operation's result. ++ * ++ * Initially an outgoing operation's errno value is -EBADR. ++ * If no error occurs before sending the request message the only ++ * valid value operation->errno can be set to is -EINPROGRESS, ++ * indicating the request has been (or rather is about to be) sent. ++ * At that point nobody should be looking at the result until the ++ * response arrives. ++ * ++ * The first time the result gets set after the request has been ++ * sent, that result "sticks." That is, if two concurrent threads ++ * race to set the result, the first one wins. The return value ++ * tells the caller whether its result was recorded; if not the ++ * caller has nothing more to do. ++ * ++ * The result value -EILSEQ is reserved to signal an implementation ++ * error; if it's ever observed, the code performing the request has ++ * done something fundamentally wrong. It is an error to try to set ++ * the result to -EBADR, and attempts to do so result in a warning, ++ * and -EILSEQ is used instead. Similarly, the only valid result ++ * value to set for an operation in initial state is -EINPROGRESS. ++ * Attempts to do otherwise will also record a (successful) -EILSEQ ++ * operation result. ++ */ ++static bool gb_operation_result_set(struct gb_operation *operation, int result) ++{ ++ unsigned long flags; ++ int prev; ++ ++ if (result == -EINPROGRESS) { ++ /* ++ * -EINPROGRESS is used to indicate the request is ++ * in flight. It should be the first result value ++ * set after the initial -EBADR. Issue a warning ++ * and record an implementation error if it's ++ * set at any other time. ++ */ ++ spin_lock_irqsave(&gb_operations_lock, flags); ++ prev = operation->errno; ++ if (prev == -EBADR) ++ operation->errno = result; ++ else ++ operation->errno = -EILSEQ; ++ spin_unlock_irqrestore(&gb_operations_lock, flags); ++ WARN_ON(prev != -EBADR); ++ ++ return true; ++ } ++ ++ /* ++ * The first result value set after a request has been sent ++ * will be the final result of the operation. Subsequent ++ * attempts to set the result are ignored. ++ * ++ * Note that -EBADR is a reserved "initial state" result ++ * value. Attempts to set this value result in a warning, ++ * and the result code is set to -EILSEQ instead. ++ */ ++ if (WARN_ON(result == -EBADR)) ++ result = -EILSEQ; /* Nobody should be setting -EBADR */ ++ ++ spin_lock_irqsave(&gb_operations_lock, flags); ++ prev = operation->errno; ++ if (prev == -EINPROGRESS) ++ operation->errno = result; /* First and final result */ ++ spin_unlock_irqrestore(&gb_operations_lock, flags); ++ ++ return prev == -EINPROGRESS; ++} ++ ++int gb_operation_result(struct gb_operation *operation) ++{ ++ int result = operation->errno; ++ ++ WARN_ON(result == -EBADR); ++ WARN_ON(result == -EINPROGRESS); ++ ++ return result; ++} ++EXPORT_SYMBOL_GPL(gb_operation_result); ++ ++/* ++ * Looks up an outgoing operation on a connection and returns a refcounted ++ * pointer if found, or NULL otherwise. ++ */ ++static struct gb_operation * ++gb_operation_find_outgoing(struct gb_connection *connection, u16 operation_id) ++{ ++ struct gb_operation *operation; ++ unsigned long flags; ++ bool found = false; ++ ++ spin_lock_irqsave(&connection->lock, flags); ++ list_for_each_entry(operation, &connection->operations, links) ++ if (operation->id == operation_id && ++ !gb_operation_is_incoming(operation)) { ++ gb_operation_get(operation); ++ found = true; ++ break; ++ } ++ spin_unlock_irqrestore(&connection->lock, flags); ++ ++ return found ? operation : NULL; ++} ++ ++static int gb_message_send(struct gb_message *message, gfp_t gfp) ++{ ++ struct gb_connection *connection = message->operation->connection; ++ ++ trace_gb_message_send(message); ++ return connection->hd->driver->message_send(connection->hd, ++ connection->hd_cport_id, ++ message, ++ gfp); ++} ++ ++/* ++ * Cancel a message we have passed to the host device layer to be sent. ++ */ ++static void gb_message_cancel(struct gb_message *message) ++{ ++ struct gb_host_device *hd = message->operation->connection->hd; ++ ++ hd->driver->message_cancel(message); ++} ++ ++static void gb_operation_request_handle(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ int status; ++ int ret; ++ ++ if (connection->handler) { ++ status = connection->handler(operation); ++ } else { ++ dev_err(&connection->hd->dev, ++ "%s: unexpected incoming request of type 0x%02x\n", ++ connection->name, operation->type); ++ ++ status = -EPROTONOSUPPORT; ++ } ++ ++ ret = gb_operation_response_send(operation, status); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: failed to send response %d for type 0x%02x: %d\n", ++ connection->name, status, operation->type, ret); ++ return; ++ } ++} ++ ++/* ++ * Process operation work. ++ * ++ * For incoming requests, call the protocol request handler. The operation ++ * result should be -EINPROGRESS at this point. ++ * ++ * For outgoing requests, the operation result value should have ++ * been set before queueing this. The operation callback function ++ * allows the original requester to know the request has completed ++ * and its result is available. ++ */ ++static void gb_operation_work(struct work_struct *work) ++{ ++ struct gb_operation *operation; ++ ++ operation = container_of(work, struct gb_operation, work); ++ ++ if (gb_operation_is_incoming(operation)) ++ gb_operation_request_handle(operation); ++ else ++ operation->callback(operation); ++ ++ gb_operation_put_active(operation); ++ gb_operation_put(operation); ++} ++ ++static void gb_operation_message_init(struct gb_host_device *hd, ++ struct gb_message *message, u16 operation_id, ++ size_t payload_size, u8 type) ++{ ++ struct gb_operation_msg_hdr *header; ++ ++ header = message->buffer; ++ ++ message->header = header; ++ message->payload = payload_size ? header + 1 : NULL; ++ message->payload_size = payload_size; ++ ++ /* ++ * The type supplied for incoming message buffers will be ++ * GB_REQUEST_TYPE_INVALID. Such buffers will be overwritten by ++ * arriving data so there's no need to initialize the message header. ++ */ ++ if (type != GB_REQUEST_TYPE_INVALID) { ++ u16 message_size = (u16)(sizeof(*header) + payload_size); ++ ++ /* ++ * For a request, the operation id gets filled in ++ * when the message is sent. For a response, it ++ * will be copied from the request by the caller. ++ * ++ * The result field in a request message must be ++ * zero. It will be set just prior to sending for ++ * a response. ++ */ ++ header->size = cpu_to_le16(message_size); ++ header->operation_id = 0; ++ header->type = type; ++ header->result = 0; ++ } ++} ++ ++/* ++ * Allocate a message to be used for an operation request or response. ++ * Both types of message contain a common header. The request message ++ * for an outgoing operation is outbound, as is the response message ++ * for an incoming operation. The message header for an outbound ++ * message is partially initialized here. ++ * ++ * The headers for inbound messages don't need to be initialized; ++ * they'll be filled in by arriving data. ++ * ++ * Our message buffers have the following layout: ++ * message header \_ these combined are ++ * message payload / the message size ++ */ ++static struct gb_message * ++gb_operation_message_alloc(struct gb_host_device *hd, u8 type, ++ size_t payload_size, gfp_t gfp_flags) ++{ ++ struct gb_message *message; ++ struct gb_operation_msg_hdr *header; ++ size_t message_size = payload_size + sizeof(*header); ++ ++ if (message_size > hd->buffer_size_max) { ++ dev_warn(&hd->dev, "requested message size too big (%zu > %zu)\n", ++ message_size, hd->buffer_size_max); ++ return NULL; ++ } ++ ++ /* Allocate the message structure and buffer. */ ++ message = kmem_cache_zalloc(gb_message_cache, gfp_flags); ++ if (!message) ++ return NULL; ++ ++ message->buffer = kzalloc(message_size, gfp_flags); ++ if (!message->buffer) ++ goto err_free_message; ++ ++ /* Initialize the message. Operation id is filled in later. */ ++ gb_operation_message_init(hd, message, 0, payload_size, type); ++ ++ return message; ++ ++err_free_message: ++ kmem_cache_free(gb_message_cache, message); ++ ++ return NULL; ++} ++ ++static void gb_operation_message_free(struct gb_message *message) ++{ ++ kfree(message->buffer); ++ kmem_cache_free(gb_message_cache, message); ++} ++ ++/* ++ * Map an enum gb_operation_status value (which is represented in a ++ * message as a single byte) to an appropriate Linux negative errno. ++ */ ++static int gb_operation_status_map(u8 status) ++{ ++ switch (status) { ++ case GB_OP_SUCCESS: ++ return 0; ++ case GB_OP_INTERRUPTED: ++ return -EINTR; ++ case GB_OP_TIMEOUT: ++ return -ETIMEDOUT; ++ case GB_OP_NO_MEMORY: ++ return -ENOMEM; ++ case GB_OP_PROTOCOL_BAD: ++ return -EPROTONOSUPPORT; ++ case GB_OP_OVERFLOW: ++ return -EMSGSIZE; ++ case GB_OP_INVALID: ++ return -EINVAL; ++ case GB_OP_RETRY: ++ return -EAGAIN; ++ case GB_OP_NONEXISTENT: ++ return -ENODEV; ++ case GB_OP_MALFUNCTION: ++ return -EILSEQ; ++ case GB_OP_UNKNOWN_ERROR: ++ default: ++ return -EIO; ++ } ++} ++ ++/* ++ * Map a Linux errno value (from operation->errno) into the value ++ * that should represent it in a response message status sent ++ * over the wire. Returns an enum gb_operation_status value (which ++ * is represented in a message as a single byte). ++ */ ++static u8 gb_operation_errno_map(int errno) ++{ ++ switch (errno) { ++ case 0: ++ return GB_OP_SUCCESS; ++ case -EINTR: ++ return GB_OP_INTERRUPTED; ++ case -ETIMEDOUT: ++ return GB_OP_TIMEOUT; ++ case -ENOMEM: ++ return GB_OP_NO_MEMORY; ++ case -EPROTONOSUPPORT: ++ return GB_OP_PROTOCOL_BAD; ++ case -EMSGSIZE: ++ return GB_OP_OVERFLOW; /* Could be underflow too */ ++ case -EINVAL: ++ return GB_OP_INVALID; ++ case -EAGAIN: ++ return GB_OP_RETRY; ++ case -EILSEQ: ++ return GB_OP_MALFUNCTION; ++ case -ENODEV: ++ return GB_OP_NONEXISTENT; ++ case -EIO: ++ default: ++ return GB_OP_UNKNOWN_ERROR; ++ } ++} ++ ++bool gb_operation_response_alloc(struct gb_operation *operation, ++ size_t response_size, gfp_t gfp) ++{ ++ struct gb_host_device *hd = operation->connection->hd; ++ struct gb_operation_msg_hdr *request_header; ++ struct gb_message *response; ++ u8 type; ++ ++ type = operation->type | GB_MESSAGE_TYPE_RESPONSE; ++ response = gb_operation_message_alloc(hd, type, response_size, gfp); ++ if (!response) ++ return false; ++ response->operation = operation; ++ ++ /* ++ * Size and type get initialized when the message is ++ * allocated. The errno will be set before sending. All ++ * that's left is the operation id, which we copy from the ++ * request message header (as-is, in little-endian order). ++ */ ++ request_header = operation->request->header; ++ response->header->operation_id = request_header->operation_id; ++ operation->response = response; ++ ++ return true; ++} ++EXPORT_SYMBOL_GPL(gb_operation_response_alloc); ++ ++/* ++ * Create a Greybus operation to be sent over the given connection. ++ * The request buffer will be big enough for a payload of the given ++ * size. ++ * ++ * For outgoing requests, the request message's header will be ++ * initialized with the type of the request and the message size. ++ * Outgoing operations must also specify the response buffer size, ++ * which must be sufficient to hold all expected response data. The ++ * response message header will eventually be overwritten, so there's ++ * no need to initialize it here. ++ * ++ * Request messages for incoming operations can arrive in interrupt ++ * context, so they must be allocated with GFP_ATOMIC. In this case ++ * the request buffer will be immediately overwritten, so there is ++ * no need to initialize the message header. Responsibility for ++ * allocating a response buffer lies with the incoming request ++ * handler for a protocol. So we don't allocate that here. ++ * ++ * Returns a pointer to the new operation or a null pointer if an ++ * error occurs. ++ */ ++static struct gb_operation * ++gb_operation_create_common(struct gb_connection *connection, u8 type, ++ size_t request_size, size_t response_size, ++ unsigned long op_flags, gfp_t gfp_flags) ++{ ++ struct gb_host_device *hd = connection->hd; ++ struct gb_operation *operation; ++ ++ operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); ++ if (!operation) ++ return NULL; ++ operation->connection = connection; ++ ++ operation->request = gb_operation_message_alloc(hd, type, request_size, ++ gfp_flags); ++ if (!operation->request) ++ goto err_cache; ++ operation->request->operation = operation; ++ ++ /* Allocate the response buffer for outgoing operations */ ++ if (!(op_flags & GB_OPERATION_FLAG_INCOMING)) { ++ if (!gb_operation_response_alloc(operation, response_size, ++ gfp_flags)) { ++ goto err_request; ++ } ++ } ++ ++ operation->flags = op_flags; ++ operation->type = type; ++ operation->errno = -EBADR; /* Initial value--means "never set" */ ++ ++ INIT_WORK(&operation->work, gb_operation_work); ++ init_completion(&operation->completion); ++ kref_init(&operation->kref); ++ atomic_set(&operation->waiters, 0); ++ ++ return operation; ++ ++err_request: ++ gb_operation_message_free(operation->request); ++err_cache: ++ kmem_cache_free(gb_operation_cache, operation); ++ ++ return NULL; ++} ++ ++/* ++ * Create a new operation associated with the given connection. The ++ * request and response sizes provided are the number of bytes ++ * required to hold the request/response payload only. Both of ++ * these are allowed to be 0. Note that 0x00 is reserved as an ++ * invalid operation type for all protocols, and this is enforced ++ * here. ++ */ ++struct gb_operation * ++gb_operation_create_flags(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, unsigned long flags, ++ gfp_t gfp) ++{ ++ struct gb_operation *operation; ++ ++ if (WARN_ON_ONCE(type == GB_REQUEST_TYPE_INVALID)) ++ return NULL; ++ if (WARN_ON_ONCE(type & GB_MESSAGE_TYPE_RESPONSE)) ++ type &= ~GB_MESSAGE_TYPE_RESPONSE; ++ ++ if (WARN_ON_ONCE(flags & ~GB_OPERATION_FLAG_USER_MASK)) ++ flags &= GB_OPERATION_FLAG_USER_MASK; ++ ++ operation = gb_operation_create_common(connection, type, ++ request_size, response_size, ++ flags, gfp); ++ if (operation) ++ trace_gb_operation_create(operation); ++ ++ return operation; ++} ++EXPORT_SYMBOL_GPL(gb_operation_create_flags); ++ ++struct gb_operation * ++gb_operation_create_core(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, unsigned long flags, ++ gfp_t gfp) ++{ ++ struct gb_operation *operation; ++ ++ flags |= GB_OPERATION_FLAG_CORE; ++ ++ operation = gb_operation_create_common(connection, type, ++ request_size, response_size, ++ flags, gfp); ++ if (operation) ++ trace_gb_operation_create_core(operation); ++ ++ return operation; ++} ++/* Do not export this function. */ ++ ++size_t gb_operation_get_payload_size_max(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ ++ return hd->buffer_size_max - sizeof(struct gb_operation_msg_hdr); ++} ++EXPORT_SYMBOL_GPL(gb_operation_get_payload_size_max); ++ ++static struct gb_operation * ++gb_operation_create_incoming(struct gb_connection *connection, u16 id, ++ u8 type, void *data, size_t size) ++{ ++ struct gb_operation *operation; ++ size_t request_size; ++ unsigned long flags = GB_OPERATION_FLAG_INCOMING; ++ ++ /* Caller has made sure we at least have a message header. */ ++ request_size = size - sizeof(struct gb_operation_msg_hdr); ++ ++ if (!id) ++ flags |= GB_OPERATION_FLAG_UNIDIRECTIONAL; ++ ++ operation = gb_operation_create_common(connection, type, ++ request_size, ++ GB_REQUEST_TYPE_INVALID, ++ flags, GFP_ATOMIC); ++ if (!operation) ++ return NULL; ++ ++ operation->id = id; ++ memcpy(operation->request->header, data, size); ++ trace_gb_operation_create_incoming(operation); ++ ++ return operation; ++} ++ ++/* ++ * Get an additional reference on an operation. ++ */ ++void gb_operation_get(struct gb_operation *operation) ++{ ++ kref_get(&operation->kref); ++} ++EXPORT_SYMBOL_GPL(gb_operation_get); ++ ++/* ++ * Destroy a previously created operation. ++ */ ++static void _gb_operation_destroy(struct kref *kref) ++{ ++ struct gb_operation *operation; ++ ++ operation = container_of(kref, struct gb_operation, kref); ++ ++ trace_gb_operation_destroy(operation); ++ ++ if (operation->response) ++ gb_operation_message_free(operation->response); ++ gb_operation_message_free(operation->request); ++ ++ kmem_cache_free(gb_operation_cache, operation); ++} ++ ++/* ++ * Drop a reference on an operation, and destroy it when the last ++ * one is gone. ++ */ ++void gb_operation_put(struct gb_operation *operation) ++{ ++ if (WARN_ON(!operation)) ++ return; ++ ++ kref_put(&operation->kref, _gb_operation_destroy); ++} ++EXPORT_SYMBOL_GPL(gb_operation_put); ++ ++/* Tell the requester we're done */ ++static void gb_operation_sync_callback(struct gb_operation *operation) ++{ ++ complete(&operation->completion); ++} ++ ++/** ++ * gb_operation_request_send() - send an operation request message ++ * @operation: the operation to initiate ++ * @callback: the operation completion callback ++ * @gfp: the memory flags to use for any allocations ++ * ++ * The caller has filled in any payload so the request message is ready to go. ++ * The callback function supplied will be called when the response message has ++ * arrived, a unidirectional request has been sent, or the operation is ++ * cancelled, indicating that the operation is complete. The callback function ++ * can fetch the result of the operation using gb_operation_result() if ++ * desired. ++ * ++ * Return: 0 if the request was successfully queued in the host-driver queues, ++ * or a negative errno. ++ */ ++int gb_operation_request_send(struct gb_operation *operation, ++ gb_operation_callback callback, ++ gfp_t gfp) ++{ ++ struct gb_connection *connection = operation->connection; ++ struct gb_operation_msg_hdr *header; ++ unsigned int cycle; ++ int ret; ++ ++ if (gb_connection_is_offloaded(connection)) ++ return -EBUSY; ++ ++ if (!callback) ++ return -EINVAL; ++ ++ /* ++ * Record the callback function, which is executed in ++ * non-atomic (workqueue) context when the final result ++ * of an operation has been set. ++ */ ++ operation->callback = callback; ++ ++ /* ++ * Assign the operation's id, and store it in the request header. ++ * Zero is a reserved operation id for unidirectional operations. ++ */ ++ if (gb_operation_is_unidirectional(operation)) { ++ operation->id = 0; ++ } else { ++ cycle = (unsigned int)atomic_inc_return(&connection->op_cycle); ++ operation->id = (u16)(cycle % U16_MAX + 1); ++ } ++ ++ header = operation->request->header; ++ header->operation_id = cpu_to_le16(operation->id); ++ ++ gb_operation_result_set(operation, -EINPROGRESS); ++ ++ /* ++ * Get an extra reference on the operation. It'll be dropped when the ++ * operation completes. ++ */ ++ gb_operation_get(operation); ++ ret = gb_operation_get_active(operation); ++ if (ret) ++ goto err_put; ++ ++ ret = gb_message_send(operation->request, gfp); ++ if (ret) ++ goto err_put_active; ++ ++ return 0; ++ ++err_put_active: ++ gb_operation_put_active(operation); ++err_put: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_operation_request_send); ++ ++/* ++ * Send a synchronous operation. This function is expected to ++ * block, returning only when the response has arrived, (or when an ++ * error is detected. The return value is the result of the ++ * operation. ++ */ ++int gb_operation_request_send_sync_timeout(struct gb_operation *operation, ++ unsigned int timeout) ++{ ++ int ret; ++ unsigned long timeout_jiffies; ++ ++ ret = gb_operation_request_send(operation, gb_operation_sync_callback, ++ GFP_KERNEL); ++ if (ret) ++ return ret; ++ ++ if (timeout) ++ timeout_jiffies = msecs_to_jiffies(timeout); ++ else ++ timeout_jiffies = MAX_SCHEDULE_TIMEOUT; ++ ++ ret = wait_for_completion_interruptible_timeout(&operation->completion, ++ timeout_jiffies); ++ if (ret < 0) { ++ /* Cancel the operation if interrupted */ ++ gb_operation_cancel(operation, -ECANCELED); ++ } else if (ret == 0) { ++ /* Cancel the operation if op timed out */ ++ gb_operation_cancel(operation, -ETIMEDOUT); ++ } ++ ++ return gb_operation_result(operation); ++} ++EXPORT_SYMBOL_GPL(gb_operation_request_send_sync_timeout); ++ ++/* ++ * Send a response for an incoming operation request. A non-zero ++ * errno indicates a failed operation. ++ * ++ * If there is any response payload, the incoming request handler is ++ * responsible for allocating the response message. Otherwise the ++ * it can simply supply the result errno; this function will ++ * allocate the response message if necessary. ++ */ ++static int gb_operation_response_send(struct gb_operation *operation, ++ int errno) ++{ ++ struct gb_connection *connection = operation->connection; ++ int ret; ++ ++ if (!operation->response && ++ !gb_operation_is_unidirectional(operation)) { ++ if (!gb_operation_response_alloc(operation, 0, GFP_KERNEL)) ++ return -ENOMEM; ++ } ++ ++ /* Record the result */ ++ if (!gb_operation_result_set(operation, errno)) { ++ dev_err(&connection->hd->dev, "request result already set\n"); ++ return -EIO; /* Shouldn't happen */ ++ } ++ ++ /* Sender of request does not care about response. */ ++ if (gb_operation_is_unidirectional(operation)) ++ return 0; ++ ++ /* Reference will be dropped when message has been sent. */ ++ gb_operation_get(operation); ++ ret = gb_operation_get_active(operation); ++ if (ret) ++ goto err_put; ++ ++ /* Fill in the response header and send it */ ++ operation->response->header->result = gb_operation_errno_map(errno); ++ ++ ret = gb_message_send(operation->response, GFP_KERNEL); ++ if (ret) ++ goto err_put_active; ++ ++ return 0; ++ ++err_put_active: ++ gb_operation_put_active(operation); ++err_put: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++/* ++ * This function is called when a message send request has completed. ++ */ ++void greybus_message_sent(struct gb_host_device *hd, ++ struct gb_message *message, int status) ++{ ++ struct gb_operation *operation = message->operation; ++ struct gb_connection *connection = operation->connection; ++ ++ /* ++ * If the message was a response, we just need to drop our ++ * reference to the operation. If an error occurred, report ++ * it. ++ * ++ * For requests, if there's no error and the operation in not ++ * unidirectional, there's nothing more to do until the response ++ * arrives. If an error occurred attempting to send it, or if the ++ * operation is unidrectional, record the result of the operation and ++ * schedule its completion. ++ */ ++ if (message == operation->response) { ++ if (status) { ++ dev_err(&connection->hd->dev, ++ "%s: error sending response 0x%02x: %d\n", ++ connection->name, operation->type, status); ++ } ++ ++ gb_operation_put_active(operation); ++ gb_operation_put(operation); ++ } else if (status || gb_operation_is_unidirectional(operation)) { ++ if (gb_operation_result_set(operation, status)) { ++ queue_work(gb_operation_completion_wq, ++ &operation->work); ++ } ++ } ++} ++EXPORT_SYMBOL_GPL(greybus_message_sent); ++ ++/* ++ * We've received data on a connection, and it doesn't look like a ++ * response, so we assume it's a request. ++ * ++ * This is called in interrupt context, so just copy the incoming ++ * data into the request buffer and handle the rest via workqueue. ++ */ ++static void gb_connection_recv_request(struct gb_connection *connection, ++ const struct gb_operation_msg_hdr *header, ++ void *data, size_t size) ++{ ++ struct gb_operation *operation; ++ u16 operation_id; ++ u8 type; ++ int ret; ++ ++ operation_id = le16_to_cpu(header->operation_id); ++ type = header->type; ++ ++ operation = gb_operation_create_incoming(connection, operation_id, ++ type, data, size); ++ if (!operation) { ++ dev_err(&connection->hd->dev, ++ "%s: can't create incoming operation\n", ++ connection->name); ++ return; ++ } ++ ++ ret = gb_operation_get_active(operation); ++ if (ret) { ++ gb_operation_put(operation); ++ return; ++ } ++ trace_gb_message_recv_request(operation->request); ++ ++ /* ++ * The initial reference to the operation will be dropped when the ++ * request handler returns. ++ */ ++ if (gb_operation_result_set(operation, -EINPROGRESS)) ++ queue_work(connection->wq, &operation->work); ++} ++ ++/* ++ * We've received data that appears to be an operation response ++ * message. Look up the operation, and record that we've received ++ * its response. ++ * ++ * This is called in interrupt context, so just copy the incoming ++ * data into the response buffer and handle the rest via workqueue. ++ */ ++static void gb_connection_recv_response(struct gb_connection *connection, ++ const struct gb_operation_msg_hdr *header, ++ void *data, size_t size) ++{ ++ struct gb_operation *operation; ++ struct gb_message *message; ++ size_t message_size; ++ u16 operation_id; ++ int errno; ++ ++ operation_id = le16_to_cpu(header->operation_id); ++ ++ if (!operation_id) { ++ dev_err_ratelimited(&connection->hd->dev, ++ "%s: invalid response id 0 received\n", ++ connection->name); ++ return; ++ } ++ ++ operation = gb_operation_find_outgoing(connection, operation_id); ++ if (!operation) { ++ dev_err_ratelimited(&connection->hd->dev, ++ "%s: unexpected response id 0x%04x received\n", ++ connection->name, operation_id); ++ return; ++ } ++ ++ errno = gb_operation_status_map(header->result); ++ message = operation->response; ++ message_size = sizeof(*header) + message->payload_size; ++ if (!errno && size > message_size) { ++ dev_err_ratelimited(&connection->hd->dev, ++ "%s: malformed response 0x%02x received (%zu > %zu)\n", ++ connection->name, header->type, ++ size, message_size); ++ errno = -EMSGSIZE; ++ } else if (!errno && size < message_size) { ++ if (gb_operation_short_response_allowed(operation)) { ++ message->payload_size = size - sizeof(*header); ++ } else { ++ dev_err_ratelimited(&connection->hd->dev, ++ "%s: short response 0x%02x received (%zu < %zu)\n", ++ connection->name, header->type, ++ size, message_size); ++ errno = -EMSGSIZE; ++ } ++ } ++ ++ /* We must ignore the payload if a bad status is returned */ ++ if (errno) ++ size = sizeof(*header); ++ ++ /* The rest will be handled in work queue context */ ++ if (gb_operation_result_set(operation, errno)) { ++ memcpy(message->buffer, data, size); ++ ++ trace_gb_message_recv_response(message); ++ ++ queue_work(gb_operation_completion_wq, &operation->work); ++ } ++ ++ gb_operation_put(operation); ++} ++ ++/* ++ * Handle data arriving on a connection. As soon as we return the ++ * supplied data buffer will be reused (so unless we do something ++ * with, it's effectively dropped). ++ */ ++void gb_connection_recv(struct gb_connection *connection, ++ void *data, size_t size) ++{ ++ struct gb_operation_msg_hdr header; ++ struct device *dev = &connection->hd->dev; ++ size_t msg_size; ++ ++ if (connection->state == GB_CONNECTION_STATE_DISABLED || ++ gb_connection_is_offloaded(connection)) { ++ dev_warn_ratelimited(dev, "%s: dropping %zu received bytes\n", ++ connection->name, size); ++ return; ++ } ++ ++ if (size < sizeof(header)) { ++ dev_err_ratelimited(dev, "%s: short message received\n", ++ connection->name); ++ return; ++ } ++ ++ /* Use memcpy as data may be unaligned */ ++ memcpy(&header, data, sizeof(header)); ++ msg_size = le16_to_cpu(header.size); ++ if (size < msg_size) { ++ dev_err_ratelimited(dev, ++ "%s: incomplete message 0x%04x of type 0x%02x received (%zu < %zu)\n", ++ connection->name, ++ le16_to_cpu(header.operation_id), ++ header.type, size, msg_size); ++ return; /* XXX Should still complete operation */ ++ } ++ ++ if (header.type & GB_MESSAGE_TYPE_RESPONSE) { ++ gb_connection_recv_response(connection, &header, data, ++ msg_size); ++ } else { ++ gb_connection_recv_request(connection, &header, data, ++ msg_size); ++ } ++} ++ ++/* ++ * Cancel an outgoing operation synchronously, and record the given error to ++ * indicate why. ++ */ ++void gb_operation_cancel(struct gb_operation *operation, int errno) ++{ ++ if (WARN_ON(gb_operation_is_incoming(operation))) ++ return; ++ ++ if (gb_operation_result_set(operation, errno)) { ++ gb_message_cancel(operation->request); ++ queue_work(gb_operation_completion_wq, &operation->work); ++ } ++ trace_gb_message_cancel_outgoing(operation->request); ++ ++ atomic_inc(&operation->waiters); ++ wait_event(gb_operation_cancellation_queue, ++ !gb_operation_is_active(operation)); ++ atomic_dec(&operation->waiters); ++} ++EXPORT_SYMBOL_GPL(gb_operation_cancel); ++ ++/* ++ * Cancel an incoming operation synchronously. Called during connection tear ++ * down. ++ */ ++void gb_operation_cancel_incoming(struct gb_operation *operation, int errno) ++{ ++ if (WARN_ON(!gb_operation_is_incoming(operation))) ++ return; ++ ++ if (!gb_operation_is_unidirectional(operation)) { ++ /* ++ * Make sure the request handler has submitted the response ++ * before cancelling it. ++ */ ++ flush_work(&operation->work); ++ if (!gb_operation_result_set(operation, errno)) ++ gb_message_cancel(operation->response); ++ } ++ trace_gb_message_cancel_incoming(operation->response); ++ ++ atomic_inc(&operation->waiters); ++ wait_event(gb_operation_cancellation_queue, ++ !gb_operation_is_active(operation)); ++ atomic_dec(&operation->waiters); ++} ++ ++/** ++ * gb_operation_sync_timeout() - implement a "simple" synchronous operation ++ * @connection: the Greybus connection to send this to ++ * @type: the type of operation to send ++ * @request: pointer to a memory buffer to copy the request from ++ * @request_size: size of @request ++ * @response: pointer to a memory buffer to copy the response to ++ * @response_size: the size of @response. ++ * @timeout: operation timeout in milliseconds ++ * ++ * This function implements a simple synchronous Greybus operation. It sends ++ * the provided operation request and waits (sleeps) until the corresponding ++ * operation response message has been successfully received, or an error ++ * occurs. @request and @response are buffers to hold the request and response ++ * data respectively, and if they are not NULL, their size must be specified in ++ * @request_size and @response_size. ++ * ++ * If a response payload is to come back, and @response is not NULL, ++ * @response_size number of bytes will be copied into @response if the operation ++ * is successful. ++ * ++ * If there is an error, the response buffer is left alone. ++ */ ++int gb_operation_sync_timeout(struct gb_connection *connection, int type, ++ void *request, int request_size, ++ void *response, int response_size, ++ unsigned int timeout) ++{ ++ struct gb_operation *operation; ++ int ret; ++ ++ if ((response_size && !response) || ++ (request_size && !request)) ++ return -EINVAL; ++ ++ operation = gb_operation_create(connection, type, ++ request_size, response_size, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ if (request_size) ++ memcpy(operation->request->payload, request, request_size); ++ ++ ret = gb_operation_request_send_sync_timeout(operation, timeout); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: synchronous operation id 0x%04x of type 0x%02x failed: %d\n", ++ connection->name, operation->id, type, ret); ++ } else { ++ if (response_size) { ++ memcpy(response, operation->response->payload, ++ response_size); ++ } ++ } ++ ++ gb_operation_put(operation); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_operation_sync_timeout); ++ ++/** ++ * gb_operation_unidirectional_timeout() - initiate a unidirectional operation ++ * @connection: connection to use ++ * @type: type of operation to send ++ * @request: memory buffer to copy the request from ++ * @request_size: size of @request ++ * @timeout: send timeout in milliseconds ++ * ++ * Initiate a unidirectional operation by sending a request message and ++ * waiting for it to be acknowledged as sent by the host device. ++ * ++ * Note that successful send of a unidirectional operation does not imply that ++ * the request as actually reached the remote end of the connection. ++ */ ++int gb_operation_unidirectional_timeout(struct gb_connection *connection, ++ int type, void *request, int request_size, ++ unsigned int timeout) ++{ ++ struct gb_operation *operation; ++ int ret; ++ ++ if (request_size && !request) ++ return -EINVAL; ++ ++ operation = gb_operation_create_flags(connection, type, ++ request_size, 0, ++ GB_OPERATION_FLAG_UNIDIRECTIONAL, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ if (request_size) ++ memcpy(operation->request->payload, request, request_size); ++ ++ ret = gb_operation_request_send_sync_timeout(operation, timeout); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: unidirectional operation of type 0x%02x failed: %d\n", ++ connection->name, type, ret); ++ } ++ ++ gb_operation_put(operation); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_operation_unidirectional_timeout); ++ ++int __init gb_operation_init(void) ++{ ++ gb_message_cache = kmem_cache_create("gb_message_cache", ++ sizeof(struct gb_message), 0, 0, NULL); ++ if (!gb_message_cache) ++ return -ENOMEM; ++ ++ gb_operation_cache = kmem_cache_create("gb_operation_cache", ++ sizeof(struct gb_operation), 0, 0, NULL); ++ if (!gb_operation_cache) ++ goto err_destroy_message_cache; ++ ++ gb_operation_completion_wq = alloc_workqueue("greybus_completion", ++ 0, 0); ++ if (!gb_operation_completion_wq) ++ goto err_destroy_operation_cache; ++ ++ return 0; ++ ++err_destroy_operation_cache: ++ kmem_cache_destroy(gb_operation_cache); ++ gb_operation_cache = NULL; ++err_destroy_message_cache: ++ kmem_cache_destroy(gb_message_cache); ++ gb_message_cache = NULL; ++ ++ return -ENOMEM; ++} ++ ++void gb_operation_exit(void) ++{ ++ destroy_workqueue(gb_operation_completion_wq); ++ gb_operation_completion_wq = NULL; ++ kmem_cache_destroy(gb_operation_cache); ++ gb_operation_cache = NULL; ++ kmem_cache_destroy(gb_message_cache); ++ gb_message_cache = NULL; ++} +--- /dev/null ++++ b/drivers/greybus/operation.h +@@ -0,0 +1,210 @@ ++/* ++ * Greybus operations ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __OPERATION_H ++#define __OPERATION_H ++ ++#include <linux/completion.h> ++ ++struct gb_operation; ++ ++/* The default amount of time a request is given to complete */ ++#define GB_OPERATION_TIMEOUT_DEFAULT 1000 /* milliseconds */ ++ ++/* ++ * The top bit of the type in an operation message header indicates ++ * whether the message is a request (bit clear) or response (bit set) ++ */ ++#define GB_MESSAGE_TYPE_RESPONSE ((u8)0x80) ++ ++enum gb_operation_result { ++ GB_OP_SUCCESS = 0x00, ++ GB_OP_INTERRUPTED = 0x01, ++ GB_OP_TIMEOUT = 0x02, ++ GB_OP_NO_MEMORY = 0x03, ++ GB_OP_PROTOCOL_BAD = 0x04, ++ GB_OP_OVERFLOW = 0x05, ++ GB_OP_INVALID = 0x06, ++ GB_OP_RETRY = 0x07, ++ GB_OP_NONEXISTENT = 0x08, ++ GB_OP_UNKNOWN_ERROR = 0xfe, ++ GB_OP_MALFUNCTION = 0xff, ++}; ++ ++#define GB_OPERATION_MESSAGE_SIZE_MIN sizeof(struct gb_operation_msg_hdr) ++#define GB_OPERATION_MESSAGE_SIZE_MAX U16_MAX ++ ++/* ++ * Protocol code should only examine the payload and payload_size fields, and ++ * host-controller drivers may use the hcpriv field. All other fields are ++ * intended to be private to the operations core code. ++ */ ++struct gb_message { ++ struct gb_operation *operation; ++ struct gb_operation_msg_hdr *header; ++ ++ void *payload; ++ size_t payload_size; ++ ++ void *buffer; ++ ++ void *hcpriv; ++}; ++ ++#define GB_OPERATION_FLAG_INCOMING BIT(0) ++#define GB_OPERATION_FLAG_UNIDIRECTIONAL BIT(1) ++#define GB_OPERATION_FLAG_SHORT_RESPONSE BIT(2) ++#define GB_OPERATION_FLAG_CORE BIT(3) ++ ++#define GB_OPERATION_FLAG_USER_MASK (GB_OPERATION_FLAG_SHORT_RESPONSE | \ ++ GB_OPERATION_FLAG_UNIDIRECTIONAL) ++ ++/* ++ * A Greybus operation is a remote procedure call performed over a ++ * connection between two UniPro interfaces. ++ * ++ * Every operation consists of a request message sent to the other ++ * end of the connection coupled with a reply message returned to ++ * the sender. Every operation has a type, whose interpretation is ++ * dependent on the protocol associated with the connection. ++ * ++ * Only four things in an operation structure are intended to be ++ * directly usable by protocol handlers: the operation's connection ++ * pointer; the operation type; the request message payload (and ++ * size); and the response message payload (and size). Note that a ++ * message with a 0-byte payload has a null message payload pointer. ++ * ++ * In addition, every operation has a result, which is an errno ++ * value. Protocol handlers access the operation result using ++ * gb_operation_result(). ++ */ ++typedef void (*gb_operation_callback)(struct gb_operation *); ++struct gb_operation { ++ struct gb_connection *connection; ++ struct gb_message *request; ++ struct gb_message *response; ++ ++ unsigned long flags; ++ u8 type; ++ u16 id; ++ int errno; /* Operation result */ ++ ++ struct work_struct work; ++ gb_operation_callback callback; ++ struct completion completion; ++ ++ struct kref kref; ++ atomic_t waiters; ++ ++ int active; ++ struct list_head links; /* connection->operations */ ++}; ++ ++static inline bool ++gb_operation_is_incoming(struct gb_operation *operation) ++{ ++ return operation->flags & GB_OPERATION_FLAG_INCOMING; ++} ++ ++static inline bool ++gb_operation_is_unidirectional(struct gb_operation *operation) ++{ ++ return operation->flags & GB_OPERATION_FLAG_UNIDIRECTIONAL; ++} ++ ++static inline bool ++gb_operation_short_response_allowed(struct gb_operation *operation) ++{ ++ return operation->flags & GB_OPERATION_FLAG_SHORT_RESPONSE; ++} ++ ++static inline bool gb_operation_is_core(struct gb_operation *operation) ++{ ++ return operation->flags & GB_OPERATION_FLAG_CORE; ++} ++ ++void gb_connection_recv(struct gb_connection *connection, ++ void *data, size_t size); ++ ++int gb_operation_result(struct gb_operation *operation); ++ ++size_t gb_operation_get_payload_size_max(struct gb_connection *connection); ++struct gb_operation * ++gb_operation_create_flags(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, unsigned long flags, ++ gfp_t gfp); ++ ++static inline struct gb_operation * ++gb_operation_create(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, gfp_t gfp) ++{ ++ return gb_operation_create_flags(connection, type, request_size, ++ response_size, 0, gfp); ++} ++ ++struct gb_operation * ++gb_operation_create_core(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, unsigned long flags, ++ gfp_t gfp); ++ ++void gb_operation_get(struct gb_operation *operation); ++void gb_operation_put(struct gb_operation *operation); ++ ++bool gb_operation_response_alloc(struct gb_operation *operation, ++ size_t response_size, gfp_t gfp); ++ ++int gb_operation_request_send(struct gb_operation *operation, ++ gb_operation_callback callback, ++ gfp_t gfp); ++int gb_operation_request_send_sync_timeout(struct gb_operation *operation, ++ unsigned int timeout); ++static inline int ++gb_operation_request_send_sync(struct gb_operation *operation) ++{ ++ return gb_operation_request_send_sync_timeout(operation, ++ GB_OPERATION_TIMEOUT_DEFAULT); ++} ++ ++void gb_operation_cancel(struct gb_operation *operation, int errno); ++void gb_operation_cancel_incoming(struct gb_operation *operation, int errno); ++ ++void greybus_message_sent(struct gb_host_device *hd, ++ struct gb_message *message, int status); ++ ++int gb_operation_sync_timeout(struct gb_connection *connection, int type, ++ void *request, int request_size, ++ void *response, int response_size, ++ unsigned int timeout); ++int gb_operation_unidirectional_timeout(struct gb_connection *connection, ++ int type, void *request, int request_size, ++ unsigned int timeout); ++ ++static inline int gb_operation_sync(struct gb_connection *connection, int type, ++ void *request, int request_size, ++ void *response, int response_size) ++{ ++ return gb_operation_sync_timeout(connection, type, ++ request, request_size, response, response_size, ++ GB_OPERATION_TIMEOUT_DEFAULT); ++} ++ ++static inline int gb_operation_unidirectional(struct gb_connection *connection, ++ int type, void *request, int request_size) ++{ ++ return gb_operation_unidirectional_timeout(connection, type, ++ request, request_size, GB_OPERATION_TIMEOUT_DEFAULT); ++} ++ ++int gb_operation_init(void); ++void gb_operation_exit(void); ++ ++#endif /* !__OPERATION_H */ |
