1
0
Fork 0

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 <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
hifive-unleashed-5.1
Alex Elder 2014-10-01 21:54:15 -05:00 committed by Greg Kroah-Hartman
parent c68adb2f2c
commit e88afa5811
7 changed files with 265 additions and 0 deletions

View File

@ -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 \

View File

@ -6,6 +6,9 @@
* Released under the GPLv2 only.
*/
#include <linux/atomic.h>
#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);
}

View File

@ -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 */

View File

@ -24,6 +24,7 @@
#include "interface.h"
#include "function.h"
#include "connection.h"
#include "operation.h"
/* Matches up with the Greybus Protocol specification document */

View File

@ -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 */

View File

@ -0,0 +1,171 @@
/*
* Greybus operations
*
* Copyright 2014 Google Inc.
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#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);
}

View File

@ -0,0 +1,69 @@
/*
* Greybus operations
*
* Copyright 2014 Google Inc.
*
* Released under the GPLv2 only.
*/
#ifndef __OPERATION_H
#define __OPERATION_H
#include <linux/completion.h>
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 */