]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/operation.c
greybus: gbuf: have caller set actual_length
[karo-tx-linux.git] / drivers / staging / greybus / operation.c
1 /*
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  */
31 struct 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 */
39 static 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  */
47 void 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 */
57 int 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  */
78 int 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  */
100 static 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  */
113 struct 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);
127         gbuf = greybus_alloc_gbuf(connection->interface->gmod,
128                                 connection->hd_cport_id,
129                                 gbuf_out_callback, size,
130                                 GFP_KERNEL, operation);
131         if (gbuf) {
132                 kfree(operation);
133                 return NULL;
134         }
135         gbuf->actual_length = size;     /* Record what we'll use */
136
137         operation->connection = connection;             /* XXX refcount? */
138
139         /* Fill in the header structure and payload pointer */
140         operation->gbuf = gbuf;
141         header = (struct gb_operation_msg_hdr *)&gbuf->transfer_buffer;
142         header->id = 0;
143         header->size = cpu_to_le16(size);
144         operation->payload = (char *)header + sizeof(*header);
145
146         operation->callback = NULL;     /* set at submit time */
147         init_completion(&operation->completion);
148
149         spin_lock_irq(&gb_operations_lock);
150         list_add_tail(&operation->links, &connection->operations);
151         spin_unlock_irq(&gb_operations_lock);
152
153         return operation;
154 }
155
156 /*
157  * Destroy a previously created operation.
158  */
159 void gb_operation_destroy(struct gb_operation *operation)
160 {
161         if (WARN_ON(!operation))
162                 return;
163
164         /* XXX Make sure it's not in flight */
165         spin_lock_irq(&gb_operations_lock);
166         list_del(&operation->links);
167         spin_unlock_irq(&gb_operations_lock);
168
169         greybus_free_gbuf(operation->gbuf);
170
171         kfree(operation);
172 }