blob: fe2f1a7137e5544eacd295b59c8545244e18c190 [file] [log] [blame]
Alex Eldere88afa52014-10-01 21:54:15 -05001/*
2 * Greybus operations
3 *
4 * Copyright 2014 Google Inc.
5 *
6 * Released under the GPLv2 only.
7 */
8
9#include <linux/kernel.h>
10#include <linux/slab.h>
11#include <linux/module.h>
12#include <linux/workqueue.h>
13
14#include "greybus.h"
15
16/*
17 * All operation messages (both requests and responses) begin with
18 * a common header that encodes the size of the data (header
19 * included). This header also contains a unique identifier, which
20 * is used to keep track of in-flight operations. Finally, the
21 * header contains a operation type field, whose interpretation is
22 * dependent on what type of device lies on the other end of the
23 * connection. Response messages are distinguished from request
24 * messages by setting the high bit (0x80) in the operation type
25 * value.
26 *
27 * The wire format for all numeric fields in the header is little
28 * endian. Any operation-specific data begins immediately after the
29 * header, and is 64-bit aligned.
30 */
31struct gb_operation_msg_hdr {
32 __le16 size; /* Size in bytes of header + payload */
33 __le16 id; /* Operation unique id */
34 __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */
35 /* 3 bytes pad, must be zero (ignore when read) */
36} __aligned(sizeof(u64));
37
38/* XXX Could be per-host device, per-module, or even per-connection */
39static DEFINE_SPINLOCK(gb_operations_lock);
40
41/*
42 * An operations's response message has arrived. If no callback was
43 * supplied it was submitted for asynchronous completion, so we notify
44 * any waiters. Otherwise we assume calling the completion is enough
45 * and nobody else will be waiting.
46 */
47void gb_operation_complete(struct gb_operation *operation)
48{
49 if (operation->callback)
50 operation->callback(operation);
51 else
52 complete_all(&operation->completion);
53}
54
55/*
56 * Wait for a submitted operatnoi to complete */
57int gb_operation_wait(struct gb_operation *operation)
58{
59 int ret;
60
61 ret = wait_for_completion_interruptible(&operation->completion);
62 /* If interrupted, cancel the in-flight buffer */
63 if (ret < 0)
64 ret = greybus_kill_gbuf(operation->gbuf);
65 return ret;
66
67}
68
69/*
70 * Submit an outbound operation. The caller has filled in any
71 * payload so the request message is ready to go. If non-null,
72 * the callback function supplied will be called when the response
73 * message has arrived indicating the operation is complete. A null
74 * callback function is used for a synchronous request; return from
75 * this function won't occur until the operation is complete (or an
76 * interrupt occurs).
77 */
78int gb_operation_submit(struct gb_operation *operation,
79 gb_operation_callback callback)
80{
81 int ret;
82
83 /* XXX
84 * gfp is probably GFP_ATOMIC but really I think
85 * the gfp mask should go away.
86 */
87 operation->callback = callback;
88 ret = greybus_submit_gbuf(operation->gbuf, GFP_KERNEL);
89 if (ret)
90 return ret;
91 if (!callback)
92 ret = gb_operation_wait(operation);
93
94 return ret;
95}
96
97/*
98 * Called when a greybus request message has actually been sent.
99 */
100static void gbuf_out_callback(struct gbuf *gbuf)
101{
102 /* Record it's been submitted; need response now */
103}
104
105/*
106 * Create a Greybus operation having a buffer big enough for an
107 * outgoing payload of the given size to be sent over the given
108 * connection.
109 *
110 * Returns a pointer to the new operation or a null pointer if a
111 * failure occurs due to memory exhaustion.
112 */
113struct gb_operation *gb_operation_create(struct gb_connection *connection,
114 size_t size)
115{
116 struct gb_operation *operation;
117 struct gb_operation_msg_hdr *header;
118 struct gbuf *gbuf;
119
120 /* XXX Use a slab cache */
121 operation = kzalloc(sizeof(*operation), GFP_KERNEL);
122 if (!operation)
123 return NULL;
124
125 /* Our buffer holds a header in addition to the requested payload */
126 size += sizeof(*header);
Alex Eldercd345072014-10-02 12:30:05 -0500127 gbuf = greybus_alloc_gbuf(connection->interface->gmod,
128 connection->hd_cport_id,
Alex Eldere88afa52014-10-01 21:54:15 -0500129 gbuf_out_callback, size,
130 GFP_KERNEL, operation);
131 if (gbuf) {
132 kfree(operation);
133 return NULL;
134 }
135
136 operation->connection = connection; /* XXX refcount? */
137
138 /* Fill in the header structure and payload pointer */
139 operation->gbuf = gbuf;
140 header = (struct gb_operation_msg_hdr *)&gbuf->transfer_buffer;
141 header->id = 0;
Greg Kroah-Hartman322543a2014-10-02 21:25:21 -0700142 header->size = cpu_to_le16(size);
Alex Eldere88afa52014-10-01 21:54:15 -0500143 operation->payload = (char *)header + sizeof(*header);
144
145 operation->callback = NULL; /* set at submit time */
146 init_completion(&operation->completion);
147
148 spin_lock_irq(&gb_operations_lock);
149 list_add_tail(&operation->links, &connection->operations);
150 spin_unlock_irq(&gb_operations_lock);
151
152 return operation;
153}
154
155/*
156 * Destroy a previously created operation.
157 */
158void gb_operation_destroy(struct gb_operation *operation)
159{
160 if (WARN_ON(!operation))
161 return;
162
163 /* XXX Make sure it's not in flight */
164 spin_lock_irq(&gb_operations_lock);
165 list_del(&operation->links);
166 spin_unlock_irq(&gb_operations_lock);
167
168 greybus_free_gbuf(operation->gbuf);
169
170 kfree(operation);
171}