linux/drivers/greybus/operation.c

// 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)
{}