// SPDX-License-Identifier: GPL-2.0 /* * Greybus operations * * Copyright 2014-2015 Google Inc. * Copyright 2014-2015 Linaro Ltd. */ #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 <linux/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) { … } /* Caller holds operation reference. */ static void gb_operation_put_active(struct gb_operation *operation) { … } static bool gb_operation_is_active(struct gb_operation *operation) { … } /* * 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) { … } int gb_operation_result(struct gb_operation *operation) { … } EXPORT_SYMBOL_GPL(…); /* * 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) { … } static int gb_message_send(struct gb_message *message, gfp_t gfp) { … } /* * Cancel a message we have passed to the host device layer to be sent. */ static void gb_message_cancel(struct gb_message *message) { … } static void gb_operation_request_handle(struct gb_operation *operation) { … } /* * 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) { … } static void gb_operation_timeout(struct timer_list *t) { … } static void gb_operation_message_init(struct gb_host_device *hd, struct gb_message *message, u16 operation_id, size_t payload_size, u8 type) { … } /* * 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) { … } static void gb_operation_message_free(struct gb_message *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) { … } /* * 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) { … } bool gb_operation_response_alloc(struct gb_operation *operation, size_t response_size, gfp_t gfp) { … } EXPORT_SYMBOL_GPL(…); /* * 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) { … } /* * 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) { … } EXPORT_SYMBOL_GPL(…); 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) { … } /* Do not export this function. */ size_t gb_operation_get_payload_size_max(struct gb_connection *connection) { … } EXPORT_SYMBOL_GPL(…); static struct gb_operation * gb_operation_create_incoming(struct gb_connection *connection, u16 id, u8 type, void *data, size_t size) { … } /* * Get an additional reference on an operation. */ void gb_operation_get(struct gb_operation *operation) { … } EXPORT_SYMBOL_GPL(…); /* * Destroy a previously created operation. */ static void _gb_operation_destroy(struct kref *kref) { … } /* * Drop a reference on an operation, and destroy it when the last * one is gone. */ void gb_operation_put(struct gb_operation *operation) { … } EXPORT_SYMBOL_GPL(…); /* Tell the requester we're done */ static void gb_operation_sync_callback(struct gb_operation *operation) { … } /** * gb_operation_request_send() - send an operation request message * @operation: the operation to initiate * @callback: the operation completion callback * @timeout: operation timeout in milliseconds, or zero for no timeout * @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, unsigned int timeout, gfp_t gfp) { … } EXPORT_SYMBOL_GPL(…); /* * 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) { … } EXPORT_SYMBOL_GPL(…); /* * 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) { … } /* * 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) { … } EXPORT_SYMBOL_GPL(…); /* * 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) { … } /* * 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) { … } /* * 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) { … } /* * Cancel an outgoing operation synchronously, and record the given error to * indicate why. */ void gb_operation_cancel(struct gb_operation *operation, int errno) { … } EXPORT_SYMBOL_GPL(…); /* * Cancel an incoming operation synchronously. Called during connection tear * down. */ void gb_operation_cancel_incoming(struct gb_operation *operation, int errno) { … } /** * 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) { … } EXPORT_SYMBOL_GPL(…); /** * 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) { … } EXPORT_SYMBOL_GPL(…); int __init gb_operation_init(void) { … } void gb_operation_exit(void) { … }