Blame view

drivers/greybus/operation.c 35 KB
eb50fd3a2   Greg Kroah-Hartman   staging: greybus:...
1
  // SPDX-License-Identifier: GPL-2.0
e88afa581   Alex Elder   greybus: introduc...
2
3
4
  /*
   * Greybus operations
   *
d3d2bea16   Alex Elder   greybus: clean up...
5
6
   * Copyright 2014-2015 Google Inc.
   * Copyright 2014-2015 Linaro Ltd.
e88afa581   Alex Elder   greybus: introduc...
7
8
9
10
11
   */
  
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/module.h>
fd7134a3c   Johan Hovold   greybus: operatio...
12
13
  #include <linux/sched.h>
  #include <linux/wait.h>
e88afa581   Alex Elder   greybus: introduc...
14
  #include <linux/workqueue.h>
ec0ad8681   Greg Kroah-Hartman   staging: greybus:...
15
  #include <linux/greybus.h>
e88afa581   Alex Elder   greybus: introduc...
16

5c8ad599b   Bryan O'Donoghue   greybus: operatio...
17
  #include "greybus_trace.h"
e88afa581   Alex Elder   greybus: introduc...
18

5b3db0dda   Alex Elder   greybus: create a...
19
  static struct kmem_cache *gb_operation_cache;
1e5613b4a   Johan Hovold   greybus: operatio...
20
  static struct kmem_cache *gb_message_cache;
5b3db0dda   Alex Elder   greybus: create a...
21

701615f82   Johan Hovold   greybus: operatio...
22
23
  /* Workqueue to handle Greybus operation completions. */
  static struct workqueue_struct *gb_operation_completion_wq;
fd7134a3c   Johan Hovold   greybus: operatio...
24
25
  /* Wait queue for synchronous cancellations. */
  static DECLARE_WAIT_QUEUE_HEAD(gb_operation_cancellation_queue);
d90c25b0a   Alex Elder   greybus: let oper...
26
  /*
008974cb5   Johan Hovold   greybus: operatio...
27
   * Protects updates to operation->errno.
82b5e3feb   Alex Elder   greybus: record t...
28
   */
e88afa581   Alex Elder   greybus: introduc...
29
  static DEFINE_SPINLOCK(gb_operations_lock);
abb722e79   Johan Hovold   greybus: operatio...
30
  static int gb_operation_response_send(struct gb_operation *operation,
8478c35a8   Cristian Sicilia   staging: greybus:...
31
  				      int errno);
abb722e79   Johan Hovold   greybus: operatio...
32

008974cb5   Johan Hovold   greybus: operatio...
33
34
35
36
37
38
39
  /*
   * 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)
3eeac7e37   Johan Hovold   greybus: operatio...
40
  {
008974cb5   Johan Hovold   greybus: operatio...
41
42
43
44
  	struct gb_connection *connection = operation->connection;
  	unsigned long flags;
  
  	spin_lock_irqsave(&connection->lock, flags);
77bbbcf6d   Johan Hovold   greybus: operatio...
45
46
47
48
49
50
51
  	switch (connection->state) {
  	case GB_CONNECTION_STATE_ENABLED:
  		break;
  	case GB_CONNECTION_STATE_ENABLED_TX:
  		if (gb_operation_is_incoming(operation))
  			goto err_unlock;
  		break;
3de5acfaf   Johan Hovold   greybus: connecti...
52
53
54
55
  	case GB_CONNECTION_STATE_DISCONNECTING:
  		if (!gb_operation_is_core(operation))
  			goto err_unlock;
  		break;
77bbbcf6d   Johan Hovold   greybus: operatio...
56
57
  	default:
  		goto err_unlock;
008974cb5   Johan Hovold   greybus: operatio...
58
59
60
61
  	}
  
  	if (operation->active++ == 0)
  		list_add_tail(&operation->links, &connection->operations);
f866e66f3   Alex Elder   greybus: add oper...
62
  	trace_gb_operation_get_active(operation);
008974cb5   Johan Hovold   greybus: operatio...
63
64
65
  	spin_unlock_irqrestore(&connection->lock, flags);
  
  	return 0;
77bbbcf6d   Johan Hovold   greybus: operatio...
66
67
68
69
70
  
  err_unlock:
  	spin_unlock_irqrestore(&connection->lock, flags);
  
  	return -ENOTCONN;
3eeac7e37   Johan Hovold   greybus: operatio...
71
72
73
  }
  
  /* Caller holds operation reference. */
008974cb5   Johan Hovold   greybus: operatio...
74
  static void gb_operation_put_active(struct gb_operation *operation)
3eeac7e37   Johan Hovold   greybus: operatio...
75
  {
008974cb5   Johan Hovold   greybus: operatio...
76
77
78
79
  	struct gb_connection *connection = operation->connection;
  	unsigned long flags;
  
  	spin_lock_irqsave(&connection->lock, flags);
f866e66f3   Alex Elder   greybus: add oper...
80

df732546c   Alex Elder   greybus: tracing:...
81
  	trace_gb_operation_put_active(operation);
f866e66f3   Alex Elder   greybus: add oper...
82

008974cb5   Johan Hovold   greybus: operatio...
83
84
  	if (--operation->active == 0) {
  		list_del(&operation->links);
fd7134a3c   Johan Hovold   greybus: operatio...
85
86
87
  		if (atomic_read(&operation->waiters))
  			wake_up(&gb_operation_cancellation_queue);
  	}
008974cb5   Johan Hovold   greybus: operatio...
88
  	spin_unlock_irqrestore(&connection->lock, flags);
fd7134a3c   Johan Hovold   greybus: operatio...
89
  }
008974cb5   Johan Hovold   greybus: operatio...
90
  static bool gb_operation_is_active(struct gb_operation *operation)
fd7134a3c   Johan Hovold   greybus: operatio...
91
  {
008974cb5   Johan Hovold   greybus: operatio...
92
93
94
95
96
97
98
99
100
  	struct gb_connection *connection = operation->connection;
  	unsigned long flags;
  	bool ret;
  
  	spin_lock_irqsave(&connection->lock, flags);
  	ret = operation->active;
  	spin_unlock_irqrestore(&connection->lock, flags);
  
  	return ret;
3eeac7e37   Johan Hovold   greybus: operatio...
101
  }
3deb37d4a   Alex Elder   greybus: use spec...
102
  /*
2fb2d2a73   Alex Elder   greybus: define -...
103
104
105
106
107
108
109
   * 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
d50628349   Johan Hovold   greybus: operatio...
110
   * response arrives.
3deb37d4a   Alex Elder   greybus: use spec...
111
112
113
114
115
   *
   * 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
2fb2d2a73   Alex Elder   greybus: define -...
116
117
118
119
120
121
122
123
124
125
   * 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.
3deb37d4a   Alex Elder   greybus: use spec...
126
   */
abe9a3006   Alex Elder   greybus: first op...
127
  static bool gb_operation_result_set(struct gb_operation *operation, int result)
ba986b5ab   Alex Elder   greybus: encapsul...
128
  {
184ab534d   Johan Hovold   greybus: operatio...
129
  	unsigned long flags;
894cbc313   Alex Elder   greybus: update o...
130
  	int prev;
3deb37d4a   Alex Elder   greybus: use spec...
131
  	if (result == -EINPROGRESS) {
2fb2d2a73   Alex Elder   greybus: define -...
132
133
134
135
136
137
138
  		/*
  		 * -EINPROGRESS is used to indicate the request is
  		 * in flight.  It should be the first result value
  		 * set after the initial -EBADR.  Issue a warning
  		 * and record an implementation error if it's
  		 * set at any other time.
  		 */
184ab534d   Johan Hovold   greybus: operatio...
139
  		spin_lock_irqsave(&gb_operations_lock, flags);
894cbc313   Alex Elder   greybus: update o...
140
141
142
  		prev = operation->errno;
  		if (prev == -EBADR)
  			operation->errno = result;
2fb2d2a73   Alex Elder   greybus: define -...
143
144
  		else
  			operation->errno = -EILSEQ;
184ab534d   Johan Hovold   greybus: operatio...
145
  		spin_unlock_irqrestore(&gb_operations_lock, flags);
2fb2d2a73   Alex Elder   greybus: define -...
146
  		WARN_ON(prev != -EBADR);
894cbc313   Alex Elder   greybus: update o...
147

2fb2d2a73   Alex Elder   greybus: define -...
148
  		return true;
3deb37d4a   Alex Elder   greybus: use spec...
149
  	}
3deb37d4a   Alex Elder   greybus: use spec...
150

2fb2d2a73   Alex Elder   greybus: define -...
151
152
153
154
155
156
157
158
159
160
161
  	/*
  	 * The first result value set after a request has been sent
  	 * will be the final result of the operation.  Subsequent
  	 * attempts to set the result are ignored.
  	 *
  	 * Note that -EBADR is a reserved "initial state" result
  	 * value.  Attempts to set this value result in a warning,
  	 * and the result code is set to -EILSEQ instead.
  	 */
  	if (WARN_ON(result == -EBADR))
  		result = -EILSEQ; /* Nobody should be setting -EBADR */
184ab534d   Johan Hovold   greybus: operatio...
162
  	spin_lock_irqsave(&gb_operations_lock, flags);
894cbc313   Alex Elder   greybus: update o...
163
164
  	prev = operation->errno;
  	if (prev == -EINPROGRESS)
2fb2d2a73   Alex Elder   greybus: define -...
165
  		operation->errno = result;	/* First and final result */
184ab534d   Johan Hovold   greybus: operatio...
166
  	spin_unlock_irqrestore(&gb_operations_lock, flags);
894cbc313   Alex Elder   greybus: update o...
167
168
  
  	return prev == -EINPROGRESS;
ba986b5ab   Alex Elder   greybus: encapsul...
169
170
171
172
  }
  
  int gb_operation_result(struct gb_operation *operation)
  {
3deb37d4a   Alex Elder   greybus: use spec...
173
  	int result = operation->errno;
2fb2d2a73   Alex Elder   greybus: define -...
174
  	WARN_ON(result == -EBADR);
3deb37d4a   Alex Elder   greybus: use spec...
175
176
177
  	WARN_ON(result == -EINPROGRESS);
  
  	return result;
ba986b5ab   Alex Elder   greybus: encapsul...
178
  }
1dad6b351   Johan Hovold   greybus: operatio...
179
  EXPORT_SYMBOL_GPL(gb_operation_result);
ba986b5ab   Alex Elder   greybus: encapsul...
180

0581f28ef   Johan Hovold   greybus: operatio...
181
  /*
048a7ffe2   Johan Hovold   greybus: operatio...
182
183
   * Looks up an outgoing operation on a connection and returns a refcounted
   * pointer if found, or NULL otherwise.
0581f28ef   Johan Hovold   greybus: operatio...
184
   */
84d148b10   Alex Elder   greybus: add gb_o...
185
  static struct gb_operation *
048a7ffe2   Johan Hovold   greybus: operatio...
186
  gb_operation_find_outgoing(struct gb_connection *connection, u16 operation_id)
84d148b10   Alex Elder   greybus: add gb_o...
187
  {
b8616da87   Alex Elder   greybus: simplify...
188
  	struct gb_operation *operation;
184ab534d   Johan Hovold   greybus: operatio...
189
  	unsigned long flags;
84d148b10   Alex Elder   greybus: add gb_o...
190
  	bool found = false;
008974cb5   Johan Hovold   greybus: operatio...
191
  	spin_lock_irqsave(&connection->lock, flags);
afb2e1342   Alex Elder   greybus: get rid ...
192
  	list_for_each_entry(operation, &connection->operations, links)
048a7ffe2   Johan Hovold   greybus: operatio...
193
  		if (operation->id == operation_id &&
8478c35a8   Cristian Sicilia   staging: greybus:...
194
  		    !gb_operation_is_incoming(operation)) {
0581f28ef   Johan Hovold   greybus: operatio...
195
  			gb_operation_get(operation);
84d148b10   Alex Elder   greybus: add gb_o...
196
  			found = true;
b8616da87   Alex Elder   greybus: simplify...
197
198
  			break;
  		}
008974cb5   Johan Hovold   greybus: operatio...
199
  	spin_unlock_irqrestore(&connection->lock, flags);
84d148b10   Alex Elder   greybus: add gb_o...
200
201
202
  
  	return found ? operation : NULL;
  }
a52c4352a   Johan Hovold   greybus: operatio...
203
  static int gb_message_send(struct gb_message *message, gfp_t gfp)
374e6a269   Alex Elder   greybus: kill off...
204
  {
3ed67aba9   Alex Elder   greybus: stop sto...
205
  	struct gb_connection *connection = message->operation->connection;
002fe66a7   Alex Elder   greybus: send mes...
206

5c8ad599b   Bryan O'Donoghue   greybus: operatio...
207
  	trace_gb_message_send(message);
3e136cc9e   Johan Hovold   greybus: operatio...
208
  	return connection->hd->driver->message_send(connection->hd,
0a9c4d70d   Alex Elder   greybus: switch c...
209
  					connection->hd_cport_id,
7cf7bca9e   Johan Hovold   greybus: pass mes...
210
  					message,
b84abdcb4   Johan Hovold   greybus: operatio...
211
  					gfp);
374e6a269   Alex Elder   greybus: kill off...
212
  }
6014718d4   Alex Elder   greybus: get rid ...
213
  /*
7cf7bca9e   Johan Hovold   greybus: pass mes...
214
   * Cancel a message we have passed to the host device layer to be sent.
6014718d4   Alex Elder   greybus: get rid ...
215
   */
35b1342bb   Alex Elder   greybus: cancel m...
216
  static void gb_message_cancel(struct gb_message *message)
374e6a269   Alex Elder   greybus: kill off...
217
  {
2537636ab   Johan Hovold   greybus: hd: rena...
218
  	struct gb_host_device *hd = message->operation->connection->hd;
374e6a269   Alex Elder   greybus: kill off...
219

3e136cc9e   Johan Hovold   greybus: operatio...
220
  	hd->driver->message_cancel(message);
374e6a269   Alex Elder   greybus: kill off...
221
  }
a9163b2c3   Alex Elder   greybus: cancel b...
222

2eb585f8d   Alex Elder   greybus: move rec...
223
224
  static void gb_operation_request_handle(struct gb_operation *operation)
  {
25cdd7aa5   Johan Hovold   greybus: operatio...
225
  	struct gb_connection *connection = operation->connection;
973ccfd62   Johan Hovold   greybus: operatio...
226
  	int status;
ff65be7a5   Johan Hovold   greybus: operatio...
227
  	int ret;
c3cf27853   Alex Elder   greybus: pass ope...
228

bfa9a5e2d   Johan Hovold   greybus: connecti...
229
230
  	if (connection->handler) {
  		status = connection->handler(operation);
973ccfd62   Johan Hovold   greybus: operatio...
231
  	} else {
25cdd7aa5   Johan Hovold   greybus: operatio...
232
  		dev_err(&connection->hd->dev,
2f3db927c   Viresh Kumar   greybus: don't us...
233
234
  			"%s: unexpected incoming request of type 0x%02x
  ",
25cdd7aa5   Johan Hovold   greybus: operatio...
235
  			connection->name, operation->type);
2eb585f8d   Alex Elder   greybus: move rec...
236

973ccfd62   Johan Hovold   greybus: operatio...
237
238
  		status = -EPROTONOSUPPORT;
  	}
ff65be7a5   Johan Hovold   greybus: operatio...
239

973ccfd62   Johan Hovold   greybus: operatio...
240
  	ret = gb_operation_response_send(operation, status);
ff65be7a5   Johan Hovold   greybus: operatio...
241
  	if (ret) {
25cdd7aa5   Johan Hovold   greybus: operatio...
242
  		dev_err(&connection->hd->dev,
2f3db927c   Viresh Kumar   greybus: don't us...
243
244
  			"%s: failed to send response %d for type 0x%02x: %d
  ",
25cdd7aa5   Johan Hovold   greybus: operatio...
245
  			connection->name, status, operation->type, ret);
c77bac08e   David Lin   greybus: operatio...
246
  		return;
ff65be7a5   Johan Hovold   greybus: operatio...
247
  	}
2eb585f8d   Alex Elder   greybus: move rec...
248
  }
e88afa581   Alex Elder   greybus: introduc...
249
  /*
c600e535a   Johan Hovold   greybus: operatio...
250
251
252
253
   * Process operation work.
   *
   * For incoming requests, call the protocol request handler. The operation
   * result should be -EINPROGRESS at this point.
d4a1ff674   Alex Elder   greybus: activate...
254
255
256
257
258
   *
   * 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.
e88afa581   Alex Elder   greybus: introduc...
259
   */
ee637a9b0   Alex Elder   greybus: abandon ...
260
  static void gb_operation_work(struct work_struct *work)
e88afa581   Alex Elder   greybus: introduc...
261
  {
84d148b10   Alex Elder   greybus: add gb_o...
262
  	struct gb_operation *operation;
dbec27298   Johan Hovold   staging: greybus:...
263
  	int ret;
84d148b10   Alex Elder   greybus: add gb_o...
264

ee637a9b0   Alex Elder   greybus: abandon ...
265
  	operation = container_of(work, struct gb_operation, work);
37754030b   Johan Hovold   greybus: operatio...
266

dbec27298   Johan Hovold   staging: greybus:...
267
  	if (gb_operation_is_incoming(operation)) {
c600e535a   Johan Hovold   greybus: operatio...
268
  		gb_operation_request_handle(operation);
dbec27298   Johan Hovold   staging: greybus:...
269
270
271
272
273
274
275
  	} else {
  		ret = del_timer_sync(&operation->timer);
  		if (!ret) {
  			/* Cancel request message if scheduled by timeout. */
  			if (gb_operation_result(operation) == -ETIMEDOUT)
  				gb_message_cancel(operation->request);
  		}
c600e535a   Johan Hovold   greybus: operatio...
276
  		operation->callback(operation);
dbec27298   Johan Hovold   staging: greybus:...
277
  	}
37754030b   Johan Hovold   greybus: operatio...
278

3eeac7e37   Johan Hovold   greybus: operatio...
279
  	gb_operation_put_active(operation);
10c693990   Alex Elder   greybus: rework s...
280
  	gb_operation_put(operation);
2eb585f8d   Alex Elder   greybus: move rec...
281
  }
e99e88a9d   Kees Cook   treewide: setup_t...
282
  static void gb_operation_timeout(struct timer_list *t)
dbec27298   Johan Hovold   staging: greybus:...
283
  {
e99e88a9d   Kees Cook   treewide: setup_t...
284
  	struct gb_operation *operation = from_timer(operation, t, timer);
dbec27298   Johan Hovold   staging: greybus:...
285
286
287
288
289
290
291
292
293
  
  	if (gb_operation_result_set(operation, -ETIMEDOUT)) {
  		/*
  		 * A stuck request message will be cancelled from the
  		 * workqueue.
  		 */
  		queue_work(gb_operation_completion_wq, &operation->work);
  	}
  }
2537636ab   Johan Hovold   greybus: hd: rena...
294
  static void gb_operation_message_init(struct gb_host_device *hd,
8478c35a8   Cristian Sicilia   staging: greybus:...
295
296
297
  				      struct gb_message *message,
  				      u16 operation_id,
  				      size_t payload_size, u8 type)
dc779229b   Alex Elder   greybus: introduc...
298
299
  {
  	struct gb_operation_msg_hdr *header;
dc779229b   Alex Elder   greybus: introduc...
300

24ef48539   Johan Hovold   greybus: drop hos...
301
  	header = message->buffer;
dc779229b   Alex Elder   greybus: introduc...
302
303
  
  	message->header = header;
746e0ef95   Alex Elder   greybus: use null...
304
  	message->payload = payload_size ? header + 1 : NULL;
7cfa69955   Alex Elder   greybus: only rec...
305
  	message->payload_size = payload_size;
dc779229b   Alex Elder   greybus: introduc...
306
307
308
  
  	/*
  	 * The type supplied for incoming message buffers will be
7adb32b42   Johan Hovold   greybus: operatio...
309
310
  	 * GB_REQUEST_TYPE_INVALID. Such buffers will be overwritten by
  	 * arriving data so there's no need to initialize the message header.
dc779229b   Alex Elder   greybus: introduc...
311
  	 */
7adb32b42   Johan Hovold   greybus: operatio...
312
  	if (type != GB_REQUEST_TYPE_INVALID) {
7cfa69955   Alex Elder   greybus: only rec...
313
  		u16 message_size = (u16)(sizeof(*header) + payload_size);
dc779229b   Alex Elder   greybus: introduc...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  		/*
  		 * For a request, the operation id gets filled in
  		 * when the message is sent.  For a response, it
  		 * will be copied from the request by the caller.
  		 *
  		 * The result field in a request message must be
  		 * zero.  It will be set just prior to sending for
  		 * a response.
  		 */
  		header->size = cpu_to_le16(message_size);
  		header->operation_id = 0;
  		header->type = type;
  		header->result = 0;
  	}
  }
d98b52b04   Alex Elder   greybus: define g...
329
  /*
ea64cd9a5   Alex Elder   greybus: use oper...
330
331
332
333
334
335
336
337
   * 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.
87d208feb   Alex Elder   greybus: embed me...
338
   *
1e5613b4a   Johan Hovold   greybus: operatio...
339
   * Our message buffers have the following layout:
87d208feb   Alex Elder   greybus: embed me...
340
341
   *	message header  \_ these combined are
   *	message payload /  the message size
22b320f40   Alex Elder   greybus: add resp...
342
   */
c08b1ddae   Alex Elder   greybus: dynamica...
343
  static struct gb_message *
2537636ab   Johan Hovold   greybus: hd: rena...
344
  gb_operation_message_alloc(struct gb_host_device *hd, u8 type,
8478c35a8   Cristian Sicilia   staging: greybus:...
345
  			   size_t payload_size, gfp_t gfp_flags)
22b320f40   Alex Elder   greybus: add resp...
346
  {
c7f82d5dc   Alex Elder   greybus: start us...
347
  	struct gb_message *message;
22b320f40   Alex Elder   greybus: add resp...
348
  	struct gb_operation_msg_hdr *header;
87d208feb   Alex Elder   greybus: embed me...
349
  	size_t message_size = payload_size + sizeof(*header);
22b320f40   Alex Elder   greybus: add resp...
350

1e5613b4a   Johan Hovold   greybus: operatio...
351
  	if (message_size > hd->buffer_size_max) {
b427572eb   Johan Hovold   greybus: core: ad...
352
353
  		dev_warn(&hd->dev, "requested message size too big (%zu > %zu)
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
354
  			 message_size, hd->buffer_size_max);
1e5613b4a   Johan Hovold   greybus: operatio...
355
  		return NULL;
0cffcac30   Alex Elder   greybus: create a...
356
  	}
1e5613b4a   Johan Hovold   greybus: operatio...
357
358
359
  
  	/* Allocate the message structure and buffer. */
  	message = kmem_cache_zalloc(gb_message_cache, gfp_flags);
c08b1ddae   Alex Elder   greybus: dynamica...
360
361
  	if (!message)
  		return NULL;
22b320f40   Alex Elder   greybus: add resp...
362

24ef48539   Johan Hovold   greybus: drop hos...
363
  	message->buffer = kzalloc(message_size, gfp_flags);
1e5613b4a   Johan Hovold   greybus: operatio...
364
365
  	if (!message->buffer)
  		goto err_free_message;
dc779229b   Alex Elder   greybus: introduc...
366
  	/* Initialize the message.  Operation id is filled in later. */
7cfa69955   Alex Elder   greybus: only rec...
367
  	gb_operation_message_init(hd, message, 0, payload_size, type);
ea64cd9a5   Alex Elder   greybus: use oper...
368

c08b1ddae   Alex Elder   greybus: dynamica...
369
  	return message;
1e5613b4a   Johan Hovold   greybus: operatio...
370
371
372
373
374
  
  err_free_message:
  	kmem_cache_free(gb_message_cache, message);
  
  	return NULL;
c7f82d5dc   Alex Elder   greybus: start us...
375
  }
c08b1ddae   Alex Elder   greybus: dynamica...
376
  static void gb_operation_message_free(struct gb_message *message)
c7f82d5dc   Alex Elder   greybus: start us...
377
  {
1e5613b4a   Johan Hovold   greybus: operatio...
378
379
  	kfree(message->buffer);
  	kmem_cache_free(gb_message_cache, message);
22b320f40   Alex Elder   greybus: add resp...
380
381
382
  }
  
  /*
696e0ccab   Viresh Kumar   greybus: Random s...
383
384
   * Map an enum gb_operation_status value (which is represented in a
   * message as a single byte) to an appropriate Linux negative errno.
bc717fcbf   Alex Elder   greybus: define g...
385
   */
0c90fff4e   Alex Elder   greybus: introduc...
386
  static int gb_operation_status_map(u8 status)
bc717fcbf   Alex Elder   greybus: define g...
387
388
389
390
  {
  	switch (status) {
  	case GB_OP_SUCCESS:
  		return 0;
bc717fcbf   Alex Elder   greybus: define g...
391
392
  	case GB_OP_INTERRUPTED:
  		return -EINTR;
57248face   Alex Elder   greybus: renumber...
393
394
395
396
  	case GB_OP_TIMEOUT:
  		return -ETIMEDOUT;
  	case GB_OP_NO_MEMORY:
  		return -ENOMEM;
bc717fcbf   Alex Elder   greybus: define g...
397
398
399
  	case GB_OP_PROTOCOL_BAD:
  		return -EPROTONOSUPPORT;
  	case GB_OP_OVERFLOW:
1a365154c   Alex Elder   greybus: fix some...
400
  		return -EMSGSIZE;
57248face   Alex Elder   greybus: renumber...
401
402
403
404
  	case GB_OP_INVALID:
  		return -EINVAL;
  	case GB_OP_RETRY:
  		return -EAGAIN;
aa26351d0   Alex Elder   greybus: define G...
405
406
  	case GB_OP_NONEXISTENT:
  		return -ENODEV;
57248face   Alex Elder   greybus: renumber...
407
408
409
  	case GB_OP_MALFUNCTION:
  		return -EILSEQ;
  	case GB_OP_UNKNOWN_ERROR:
bc717fcbf   Alex Elder   greybus: define g...
410
411
412
413
414
415
  	default:
  		return -EIO;
  	}
  }
  
  /*
0c90fff4e   Alex Elder   greybus: introduc...
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
   * 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)
  {
  	switch (errno) {
  	case 0:
  		return GB_OP_SUCCESS;
  	case -EINTR:
  		return GB_OP_INTERRUPTED;
  	case -ETIMEDOUT:
  		return GB_OP_TIMEOUT;
  	case -ENOMEM:
  		return GB_OP_NO_MEMORY;
  	case -EPROTONOSUPPORT:
  		return GB_OP_PROTOCOL_BAD;
  	case -EMSGSIZE:
  		return GB_OP_OVERFLOW;	/* Could be underflow too */
  	case -EINVAL:
  		return GB_OP_INVALID;
  	case -EAGAIN:
  		return GB_OP_RETRY;
  	case -EILSEQ:
  		return GB_OP_MALFUNCTION;
aa26351d0   Alex Elder   greybus: define G...
442
443
  	case -ENODEV:
  		return GB_OP_NONEXISTENT;
0c90fff4e   Alex Elder   greybus: introduc...
444
445
446
447
448
  	case -EIO:
  	default:
  		return GB_OP_UNKNOWN_ERROR;
  	}
  }
82e26f73b   Alex Elder   greybus: send ope...
449
  bool gb_operation_response_alloc(struct gb_operation *operation,
8478c35a8   Cristian Sicilia   staging: greybus:...
450
  				 size_t response_size, gfp_t gfp)
82e26f73b   Alex Elder   greybus: send ope...
451
  {
2537636ab   Johan Hovold   greybus: hd: rena...
452
  	struct gb_host_device *hd = operation->connection->hd;
82e26f73b   Alex Elder   greybus: send ope...
453
454
455
  	struct gb_operation_msg_hdr *request_header;
  	struct gb_message *response;
  	u8 type;
6d653370c   Alex Elder   greybus: eliminat...
456
  	type = operation->type | GB_MESSAGE_TYPE_RESPONSE;
1c7658cf5   Johan Hovold   greybus: operatio...
457
  	response = gb_operation_message_alloc(hd, type, response_size, gfp);
82e26f73b   Alex Elder   greybus: send ope...
458
459
460
461
462
463
464
465
466
467
  	if (!response)
  		return false;
  	response->operation = operation;
  
  	/*
  	 * Size and type get initialized when the message is
  	 * allocated.  The errno will be set before sending.  All
  	 * that's left is the operation id, which we copy from the
  	 * request message header (as-is, in little-endian order).
  	 */
82b5e3feb   Alex Elder   greybus: record t...
468
  	request_header = operation->request->header;
82e26f73b   Alex Elder   greybus: send ope...
469
470
471
472
473
  	response->header->operation_id = request_header->operation_id;
  	operation->response = response;
  
  	return true;
  }
1dad6b351   Johan Hovold   greybus: operatio...
474
  EXPORT_SYMBOL_GPL(gb_operation_response_alloc);
82e26f73b   Alex Elder   greybus: send ope...
475

0c90fff4e   Alex Elder   greybus: introduc...
476
  /*
22b320f40   Alex Elder   greybus: add resp...
477
   * Create a Greybus operation to be sent over the given connection.
696e0ccab   Viresh Kumar   greybus: Random s...
478
   * The request buffer will be big enough for a payload of the given
ea64cd9a5   Alex Elder   greybus: use oper...
479
480
481
482
483
484
485
486
   * 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.
22b320f40   Alex Elder   greybus: add resp...
487
   *
ea64cd9a5   Alex Elder   greybus: use oper...
488
489
490
491
492
493
   * 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.
e88afa581   Alex Elder   greybus: introduc...
494
   *
22b320f40   Alex Elder   greybus: add resp...
495
496
   * Returns a pointer to the new operation or a null pointer if an
   * error occurs.
e88afa581   Alex Elder   greybus: introduc...
497
   */
30a2964f8   Alex Elder   greybus: distingu...
498
  static struct gb_operation *
ea64cd9a5   Alex Elder   greybus: use oper...
499
  gb_operation_create_common(struct gb_connection *connection, u8 type,
8478c35a8   Cristian Sicilia   staging: greybus:...
500
501
  			   size_t request_size, size_t response_size,
  			   unsigned long op_flags, gfp_t gfp_flags)
e88afa581   Alex Elder   greybus: introduc...
502
  {
2537636ab   Johan Hovold   greybus: hd: rena...
503
  	struct gb_host_device *hd = connection->hd;
e88afa581   Alex Elder   greybus: introduc...
504
  	struct gb_operation *operation;
e88afa581   Alex Elder   greybus: introduc...
505

5b3db0dda   Alex Elder   greybus: create a...
506
  	operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags);
e88afa581   Alex Elder   greybus: introduc...
507
508
  	if (!operation)
  		return NULL;
6507cced6   Greg Kroah-Hartman   greybus: FIXME/XX...
509
  	operation->connection = connection;
e88afa581   Alex Elder   greybus: introduc...
510

c08b1ddae   Alex Elder   greybus: dynamica...
511
512
513
  	operation->request = gb_operation_message_alloc(hd, type, request_size,
  							gfp_flags);
  	if (!operation->request)
5b3db0dda   Alex Elder   greybus: create a...
514
  		goto err_cache;
c08b1ddae   Alex Elder   greybus: dynamica...
515
  	operation->request->operation = operation;
22b320f40   Alex Elder   greybus: add resp...
516

ea64cd9a5   Alex Elder   greybus: use oper...
517
  	/* Allocate the response buffer for outgoing operations */
710067e2e   Johan Hovold   greybus: operatio...
518
  	if (!(op_flags & GB_OPERATION_FLAG_INCOMING)) {
1c7658cf5   Johan Hovold   greybus: operatio...
519
520
  		if (!gb_operation_response_alloc(operation, response_size,
  						 gfp_flags)) {
5b3db0dda   Alex Elder   greybus: create a...
521
  			goto err_request;
1c7658cf5   Johan Hovold   greybus: operatio...
522
  		}
dbec27298   Johan Hovold   staging: greybus:...
523

e99e88a9d   Kees Cook   treewide: setup_t...
524
  		timer_setup(&operation->timer, gb_operation_timeout, 0);
82b5e3feb   Alex Elder   greybus: record t...
525
  	}
710067e2e   Johan Hovold   greybus: operatio...
526
527
528
  
  	operation->flags = op_flags;
  	operation->type = type;
3deb37d4a   Alex Elder   greybus: use spec...
529
  	operation->errno = -EBADR;  /* Initial value--means "never set" */
e88afa581   Alex Elder   greybus: introduc...
530

ee637a9b0   Alex Elder   greybus: abandon ...
531
  	INIT_WORK(&operation->work, gb_operation_work);
e88afa581   Alex Elder   greybus: introduc...
532
  	init_completion(&operation->completion);
c7d0f258f   Alex Elder   greybus: referenc...
533
  	kref_init(&operation->kref);
fd7134a3c   Johan Hovold   greybus: operatio...
534
  	atomic_set(&operation->waiters, 0);
e88afa581   Alex Elder   greybus: introduc...
535

e88afa581   Alex Elder   greybus: introduc...
536
  	return operation;
5b3db0dda   Alex Elder   greybus: create a...
537
538
  
  err_request:
c08b1ddae   Alex Elder   greybus: dynamica...
539
  	gb_operation_message_free(operation->request);
5b3db0dda   Alex Elder   greybus: create a...
540
541
542
543
  err_cache:
  	kmem_cache_free(gb_operation_cache, operation);
  
  	return NULL;
e88afa581   Alex Elder   greybus: introduc...
544
  }
55f66a88d   Alex Elder   greybus: enforce ...
545
546
547
548
549
550
551
552
  /*
   * 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.
   */
7e43e337a   Johan Hovold   greybus: operatio...
553
554
  struct gb_operation *
  gb_operation_create_flags(struct gb_connection *connection,
8478c35a8   Cristian Sicilia   staging: greybus:...
555
556
557
  			  u8 type, size_t request_size,
  			  size_t response_size, unsigned long flags,
  			  gfp_t gfp)
30a2964f8   Alex Elder   greybus: distingu...
558
  {
f866e66f3   Alex Elder   greybus: add oper...
559
  	struct gb_operation *operation;
7adb32b42   Johan Hovold   greybus: operatio...
560
  	if (WARN_ON_ONCE(type == GB_REQUEST_TYPE_INVALID))
55f66a88d   Alex Elder   greybus: enforce ...
561
  		return NULL;
6d653370c   Alex Elder   greybus: eliminat...
562
563
  	if (WARN_ON_ONCE(type & GB_MESSAGE_TYPE_RESPONSE))
  		type &= ~GB_MESSAGE_TYPE_RESPONSE;
55f66a88d   Alex Elder   greybus: enforce ...
564

7e43e337a   Johan Hovold   greybus: operatio...
565
566
  	if (WARN_ON_ONCE(flags & ~GB_OPERATION_FLAG_USER_MASK))
  		flags &= GB_OPERATION_FLAG_USER_MASK;
f866e66f3   Alex Elder   greybus: add oper...
567
  	operation = gb_operation_create_common(connection, type,
8478c35a8   Cristian Sicilia   staging: greybus:...
568
569
  					       request_size, response_size,
  					       flags, gfp);
f866e66f3   Alex Elder   greybus: add oper...
570
571
572
573
  	if (operation)
  		trace_gb_operation_create(operation);
  
  	return operation;
30a2964f8   Alex Elder   greybus: distingu...
574
  }
7e43e337a   Johan Hovold   greybus: operatio...
575
  EXPORT_SYMBOL_GPL(gb_operation_create_flags);
30a2964f8   Alex Elder   greybus: distingu...
576

18079ece8   Johan Hovold   greybus: operatio...
577
578
  struct gb_operation *
  gb_operation_create_core(struct gb_connection *connection,
8478c35a8   Cristian Sicilia   staging: greybus:...
579
580
581
  			 u8 type, size_t request_size,
  			 size_t response_size, unsigned long flags,
  			 gfp_t gfp)
18079ece8   Johan Hovold   greybus: operatio...
582
583
584
585
586
587
  {
  	struct gb_operation *operation;
  
  	flags |= GB_OPERATION_FLAG_CORE;
  
  	operation = gb_operation_create_common(connection, type,
8478c35a8   Cristian Sicilia   staging: greybus:...
588
589
  					       request_size, response_size,
  					       flags, gfp);
18079ece8   Johan Hovold   greybus: operatio...
590
591
592
593
594
  	if (operation)
  		trace_gb_operation_create_core(operation);
  
  	return operation;
  }
8478c35a8   Cristian Sicilia   staging: greybus:...
595

18079ece8   Johan Hovold   greybus: operatio...
596
  /* Do not export this function. */
d52b35f6b   Johan Hovold   greybus: operatio...
597
598
  size_t gb_operation_get_payload_size_max(struct gb_connection *connection)
  {
2537636ab   Johan Hovold   greybus: hd: rena...
599
  	struct gb_host_device *hd = connection->hd;
d52b35f6b   Johan Hovold   greybus: operatio...
600
601
602
603
  
  	return hd->buffer_size_max - sizeof(struct gb_operation_msg_hdr);
  }
  EXPORT_SYMBOL_GPL(gb_operation_get_payload_size_max);
30a2964f8   Alex Elder   greybus: distingu...
604
  static struct gb_operation *
ea64cd9a5   Alex Elder   greybus: use oper...
605
  gb_operation_create_incoming(struct gb_connection *connection, u16 id,
8478c35a8   Cristian Sicilia   staging: greybus:...
606
  			     u8 type, void *data, size_t size)
30a2964f8   Alex Elder   greybus: distingu...
607
  {
34db1f91e   Alex Elder   greybus: move cop...
608
  	struct gb_operation *operation;
cfa79699c   Johan Hovold   greybus: operatio...
609
  	size_t request_size;
710067e2e   Johan Hovold   greybus: operatio...
610
  	unsigned long flags = GB_OPERATION_FLAG_INCOMING;
cfa79699c   Johan Hovold   greybus: operatio...
611
612
613
  
  	/* Caller has made sure we at least have a message header. */
  	request_size = size - sizeof(struct gb_operation_msg_hdr);
34db1f91e   Alex Elder   greybus: move cop...
614

e3398811c   Johan Hovold   greybus: operatio...
615
616
  	if (!id)
  		flags |= GB_OPERATION_FLAG_UNIDIRECTIONAL;
710067e2e   Johan Hovold   greybus: operatio...
617
  	operation = gb_operation_create_common(connection, type,
8478c35a8   Cristian Sicilia   staging: greybus:...
618
619
620
  					       request_size,
  					       GB_REQUEST_TYPE_INVALID,
  					       flags, GFP_ATOMIC);
9a586bd2b   Johan Hovold   greybus: operatio...
621
622
623
624
625
  	if (!operation)
  		return NULL;
  
  	operation->id = id;
  	memcpy(operation->request->header, data, size);
f866e66f3   Alex Elder   greybus: add oper...
626
  	trace_gb_operation_create_incoming(operation);
34db1f91e   Alex Elder   greybus: move cop...
627
628
  
  	return operation;
30a2964f8   Alex Elder   greybus: distingu...
629
  }
e88afa581   Alex Elder   greybus: introduc...
630
  /*
deb4b9efb   Alex Elder   greybus: add a re...
631
632
633
634
635
636
   * Get an additional reference on an operation.
   */
  void gb_operation_get(struct gb_operation *operation)
  {
  	kref_get(&operation->kref);
  }
1dad6b351   Johan Hovold   greybus: operatio...
637
  EXPORT_SYMBOL_GPL(gb_operation_get);
deb4b9efb   Alex Elder   greybus: add a re...
638
639
  
  /*
e88afa581   Alex Elder   greybus: introduc...
640
641
   * Destroy a previously created operation.
   */
c7d0f258f   Alex Elder   greybus: referenc...
642
  static void _gb_operation_destroy(struct kref *kref)
e88afa581   Alex Elder   greybus: introduc...
643
  {
c7d0f258f   Alex Elder   greybus: referenc...
644
645
646
  	struct gb_operation *operation;
  
  	operation = container_of(kref, struct gb_operation, kref);
e88afa581   Alex Elder   greybus: introduc...
647

f866e66f3   Alex Elder   greybus: add oper...
648
  	trace_gb_operation_destroy(operation);
948966768   Johan Hovold   greybus: operatio...
649
650
  	if (operation->response)
  		gb_operation_message_free(operation->response);
c08b1ddae   Alex Elder   greybus: dynamica...
651
  	gb_operation_message_free(operation->request);
e88afa581   Alex Elder   greybus: introduc...
652

5b3db0dda   Alex Elder   greybus: create a...
653
  	kmem_cache_free(gb_operation_cache, operation);
e88afa581   Alex Elder   greybus: introduc...
654
  }
d90c25b0a   Alex Elder   greybus: let oper...
655

deb4b9efb   Alex Elder   greybus: add a re...
656
657
658
659
  /*
   * Drop a reference on an operation, and destroy it when the last
   * one is gone.
   */
c7d0f258f   Alex Elder   greybus: referenc...
660
661
  void gb_operation_put(struct gb_operation *operation)
  {
85109f7dd   Johan Hovold   greybus: operatio...
662
663
  	if (WARN_ON(!operation))
  		return;
008974cb5   Johan Hovold   greybus: operatio...
664
  	kref_put(&operation->kref, _gb_operation_destroy);
c7d0f258f   Alex Elder   greybus: referenc...
665
  }
df469a942   Greg Kroah-Hartman   greybus: export n...
666
  EXPORT_SYMBOL_GPL(gb_operation_put);
c7d0f258f   Alex Elder   greybus: referenc...
667

10c693990   Alex Elder   greybus: rework s...
668
669
670
671
672
  /* Tell the requester we're done */
  static void gb_operation_sync_callback(struct gb_operation *operation)
  {
  	complete(&operation->completion);
  }
613c15e86   Johan Hovold   greybus: operatio...
673
674
675
676
  /**
   * gb_operation_request_send() - send an operation request message
   * @operation:	the operation to initiate
   * @callback:	the operation completion callback
dbec27298   Johan Hovold   staging: greybus:...
677
   * @timeout:	operation timeout in milliseconds, or zero for no timeout
613c15e86   Johan Hovold   greybus: operatio...
678
679
680
681
   * @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
3e2ee2c1e   Johan Hovold   greybus: operatio...
682
683
684
685
   * 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.
613c15e86   Johan Hovold   greybus: operatio...
686
687
688
   *
   * Return: 0 if the request was successfully queued in the host-driver queues,
   * or a negative errno.
d90c25b0a   Alex Elder   greybus: let oper...
689
690
   */
  int gb_operation_request_send(struct gb_operation *operation,
8478c35a8   Cristian Sicilia   staging: greybus:...
691
692
693
  			      gb_operation_callback callback,
  			      unsigned int timeout,
  			      gfp_t gfp)
d90c25b0a   Alex Elder   greybus: let oper...
694
  {
afb2e1342   Alex Elder   greybus: get rid ...
695
696
  	struct gb_connection *connection = operation->connection;
  	struct gb_operation_msg_hdr *header;
4afb7fd01   Alex Elder   greybus: make op_...
697
  	unsigned int cycle;
ea2c2ee80   Johan Hovold   greybus: operatio...
698
  	int ret;
d90c25b0a   Alex Elder   greybus: let oper...
699

ca1f8f809   Johan Hovold   greybus: connecti...
700
701
  	if (gb_connection_is_offloaded(connection))
  		return -EBUSY;
37754030b   Johan Hovold   greybus: operatio...
702
703
  	if (!callback)
  		return -EINVAL;
3e2ee2c1e   Johan Hovold   greybus: operatio...
704

c25572ca9   Alex Elder   greybus: introduc...
705
706
707
708
709
710
  	/*
  	 * Record the callback function, which is executed in
  	 * non-atomic (workqueue) context when the final result
  	 * of an operation has been set.
  	 */
  	operation->callback = callback;
afb2e1342   Alex Elder   greybus: get rid ...
711
712
713
  
  	/*
  	 * Assign the operation's id, and store it in the request header.
3e2ee2c1e   Johan Hovold   greybus: operatio...
714
  	 * Zero is a reserved operation id for unidirectional operations.
afb2e1342   Alex Elder   greybus: get rid ...
715
  	 */
3e2ee2c1e   Johan Hovold   greybus: operatio...
716
717
718
719
720
721
  	if (gb_operation_is_unidirectional(operation)) {
  		operation->id = 0;
  	} else {
  		cycle = (unsigned int)atomic_inc_return(&connection->op_cycle);
  		operation->id = (u16)(cycle % U16_MAX + 1);
  	}
afb2e1342   Alex Elder   greybus: get rid ...
722
723
  	header = operation->request->header;
  	header->operation_id = cpu_to_le16(operation->id);
e8b48d158   Alex Elder   greybus: fix a ti...
724

3deb37d4a   Alex Elder   greybus: use spec...
725
  	gb_operation_result_set(operation, -EINPROGRESS);
c25572ca9   Alex Elder   greybus: introduc...
726

3325a4ad7   Johan Hovold   greybus: operatio...
727
728
729
730
731
732
733
734
  	/*
  	 * Get an extra reference on the operation. It'll be dropped when the
  	 * operation completes.
  	 */
  	gb_operation_get(operation);
  	ret = gb_operation_get_active(operation);
  	if (ret)
  		goto err_put;
a52c4352a   Johan Hovold   greybus: operatio...
735
  	ret = gb_message_send(operation->request, gfp);
008974cb5   Johan Hovold   greybus: operatio...
736
737
  	if (ret)
  		goto err_put_active;
dbec27298   Johan Hovold   staging: greybus:...
738
739
740
741
  	if (timeout) {
  		operation->timer.expires = jiffies + msecs_to_jiffies(timeout);
  		add_timer(&operation->timer);
  	}
008974cb5   Johan Hovold   greybus: operatio...
742
743
744
745
746
747
  	return 0;
  
  err_put_active:
  	gb_operation_put_active(operation);
  err_put:
  	gb_operation_put(operation);
ea2c2ee80   Johan Hovold   greybus: operatio...
748
749
  
  	return ret;
c25572ca9   Alex Elder   greybus: introduc...
750
  }
1dad6b351   Johan Hovold   greybus: operatio...
751
  EXPORT_SYMBOL_GPL(gb_operation_request_send);
c25572ca9   Alex Elder   greybus: introduc...
752
753
754
755
756
757
758
  
  /*
   * 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.
   */
4f2c08aba   Johan Hovold   greybus: operatio...
759
  int gb_operation_request_send_sync_timeout(struct gb_operation *operation,
8478c35a8   Cristian Sicilia   staging: greybus:...
760
  					   unsigned int timeout)
c25572ca9   Alex Elder   greybus: introduc...
761
762
  {
  	int ret;
a52c4352a   Johan Hovold   greybus: operatio...
763
  	ret = gb_operation_request_send(operation, gb_operation_sync_callback,
dbec27298   Johan Hovold   staging: greybus:...
764
  					timeout, GFP_KERNEL);
c25572ca9   Alex Elder   greybus: introduc...
765
  	if (ret)
d90c25b0a   Alex Elder   greybus: let oper...
766
  		return ret;
8350e7a01   Alex Elder   greybus: move tim...
767

dbec27298   Johan Hovold   staging: greybus:...
768
  	ret = wait_for_completion_interruptible(&operation->completion);
7bad4e85b   Perry Hung   greybus: gb_opera...
769
770
  	if (ret < 0) {
  		/* Cancel the operation if interrupted */
1a365154c   Alex Elder   greybus: fix some...
771
  		gb_operation_cancel(operation, -ECANCELED);
7bad4e85b   Perry Hung   greybus: gb_opera...
772
  	}
2cf72a233   Alex Elder   greybus: kill gb_...
773

ba986b5ab   Alex Elder   greybus: encapsul...
774
  	return gb_operation_result(operation);
d90c25b0a   Alex Elder   greybus: let oper...
775
  }
4f2c08aba   Johan Hovold   greybus: operatio...
776
  EXPORT_SYMBOL_GPL(gb_operation_request_send_sync_timeout);
d90c25b0a   Alex Elder   greybus: let oper...
777
778
  
  /*
82e26f73b   Alex Elder   greybus: send ope...
779
780
781
782
783
784
785
   * 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.
d90c25b0a   Alex Elder   greybus: let oper...
786
   */
abb722e79   Johan Hovold   greybus: operatio...
787
  static int gb_operation_response_send(struct gb_operation *operation,
8478c35a8   Cristian Sicilia   staging: greybus:...
788
  				      int errno)
d90c25b0a   Alex Elder   greybus: let oper...
789
  {
e1baa3f0a   Johan Hovold   greybus: operatio...
790
  	struct gb_connection *connection = operation->connection;
0fb5acc40   Johan Hovold   greybus: operatio...
791
  	int ret;
fde7382b4   Johan Hovold   greybus: operatio...
792
  	if (!operation->response &&
8478c35a8   Cristian Sicilia   staging: greybus:...
793
  	    !gb_operation_is_unidirectional(operation)) {
1c7658cf5   Johan Hovold   greybus: operatio...
794
  		if (!gb_operation_response_alloc(operation, 0, GFP_KERNEL))
fde7382b4   Johan Hovold   greybus: operatio...
795
796
  			return -ENOMEM;
  	}
d2d2c0fe7   Alex Elder   greybus: set resu...
797
798
  	/* Record the result */
  	if (!gb_operation_result_set(operation, errno)) {
25cdd7aa5   Johan Hovold   greybus: operatio...
799
800
  		dev_err(&connection->hd->dev, "request result already set
  ");
d2d2c0fe7   Alex Elder   greybus: set resu...
801
802
  		return -EIO;	/* Shouldn't happen */
  	}
d90c25b0a   Alex Elder   greybus: let oper...
803

1d771fe41   Johan Hovold   greybus: operatio...
804
  	/* Sender of request does not care about response. */
e3398811c   Johan Hovold   greybus: operatio...
805
  	if (gb_operation_is_unidirectional(operation))
1d771fe41   Johan Hovold   greybus: operatio...
806
  		return 0;
0fb5acc40   Johan Hovold   greybus: operatio...
807
808
  	/* Reference will be dropped when message has been sent. */
  	gb_operation_get(operation);
008974cb5   Johan Hovold   greybus: operatio...
809
810
811
  	ret = gb_operation_get_active(operation);
  	if (ret)
  		goto err_put;
0fb5acc40   Johan Hovold   greybus: operatio...
812

82e26f73b   Alex Elder   greybus: send ope...
813
814
  	/* Fill in the response header and send it */
  	operation->response->header->result = gb_operation_errno_map(errno);
a52c4352a   Johan Hovold   greybus: operatio...
815
  	ret = gb_message_send(operation->response, GFP_KERNEL);
008974cb5   Johan Hovold   greybus: operatio...
816
817
818
819
820
821
822
823
824
  	if (ret)
  		goto err_put_active;
  
  	return 0;
  
  err_put_active:
  	gb_operation_put_active(operation);
  err_put:
  	gb_operation_put(operation);
0fb5acc40   Johan Hovold   greybus: operatio...
825
826
  
  	return ret;
d90c25b0a   Alex Elder   greybus: let oper...
827
  }
2eb585f8d   Alex Elder   greybus: move rec...
828
  /*
7cf7bca9e   Johan Hovold   greybus: pass mes...
829
   * This function is called when a message send request has completed.
d98b52b04   Alex Elder   greybus: define g...
830
   */
2537636ab   Johan Hovold   greybus: hd: rena...
831
  void greybus_message_sent(struct gb_host_device *hd,
8478c35a8   Cristian Sicilia   staging: greybus:...
832
  			  struct gb_message *message, int status)
d98b52b04   Alex Elder   greybus: define g...
833
  {
a4e08469e   Johan Hovold   greybus: operatio...
834
835
  	struct gb_operation *operation = message->operation;
  	struct gb_connection *connection = operation->connection;
d98b52b04   Alex Elder   greybus: define g...
836

d4a1ff674   Alex Elder   greybus: activate...
837
838
839
840
841
  	/*
  	 * If the message was a response, we just need to drop our
  	 * reference to the operation.  If an error occurred, report
  	 * it.
  	 *
3e2ee2c1e   Johan Hovold   greybus: operatio...
842
843
844
845
846
  	 * For requests, if there's no error and the operation in not
  	 * unidirectional, there's nothing more to do until the response
  	 * arrives. If an error occurred attempting to send it, or if the
  	 * operation is unidrectional, record the result of the operation and
  	 * schedule its completion.
d4a1ff674   Alex Elder   greybus: activate...
847
  	 */
d4a1ff674   Alex Elder   greybus: activate...
848
  	if (message == operation->response) {
e1baa3f0a   Johan Hovold   greybus: operatio...
849
  		if (status) {
25cdd7aa5   Johan Hovold   greybus: operatio...
850
  			dev_err(&connection->hd->dev,
2f3db927c   Viresh Kumar   greybus: don't us...
851
852
  				"%s: error sending response 0x%02x: %d
  ",
25cdd7aa5   Johan Hovold   greybus: operatio...
853
  				connection->name, operation->type, status);
e1baa3f0a   Johan Hovold   greybus: operatio...
854
  		}
3e2ee2c1e   Johan Hovold   greybus: operatio...
855

3eeac7e37   Johan Hovold   greybus: operatio...
856
  		gb_operation_put_active(operation);
d4a1ff674   Alex Elder   greybus: activate...
857
  		gb_operation_put(operation);
3e2ee2c1e   Johan Hovold   greybus: operatio...
858
  	} else if (status || gb_operation_is_unidirectional(operation)) {
701615f82   Johan Hovold   greybus: operatio...
859
860
  		if (gb_operation_result_set(operation, status)) {
  			queue_work(gb_operation_completion_wq,
8478c35a8   Cristian Sicilia   staging: greybus:...
861
  				   &operation->work);
701615f82   Johan Hovold   greybus: operatio...
862
  		}
d4a1ff674   Alex Elder   greybus: activate...
863
  	}
d98b52b04   Alex Elder   greybus: define g...
864
  }
7cf7bca9e   Johan Hovold   greybus: pass mes...
865
  EXPORT_SYMBOL_GPL(greybus_message_sent);
d98b52b04   Alex Elder   greybus: define g...
866
867
  
  /*
d37b1db13   Alex Elder   greybus: refactor...
868
869
   * We've received data on a connection, and it doesn't look like a
   * response, so we assume it's a request.
78496db01   Alex Elder   greybus: clean up...
870
871
   *
   * This is called in interrupt context, so just copy the incoming
d37b1db13   Alex Elder   greybus: refactor...
872
873
   * data into the request buffer and handle the rest via workqueue.
   */
85a044289   Greg Kroah-Hartman   greybus: operatio...
874
  static void gb_connection_recv_request(struct gb_connection *connection,
2321f049a   Johan Hovold   greybus: operatio...
875
876
  				const struct gb_operation_msg_hdr *header,
  				void *data, size_t size)
d37b1db13   Alex Elder   greybus: refactor...
877
878
  {
  	struct gb_operation *operation;
2321f049a   Johan Hovold   greybus: operatio...
879
880
  	u16 operation_id;
  	u8 type;
008974cb5   Johan Hovold   greybus: operatio...
881
  	int ret;
d37b1db13   Alex Elder   greybus: refactor...
882

2321f049a   Johan Hovold   greybus: operatio...
883
884
  	operation_id = le16_to_cpu(header->operation_id);
  	type = header->type;
34db1f91e   Alex Elder   greybus: move cop...
885
  	operation = gb_operation_create_incoming(connection, operation_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
886
  						 type, data, size);
d37b1db13   Alex Elder   greybus: refactor...
887
  	if (!operation) {
25cdd7aa5   Johan Hovold   greybus: operatio...
888
889
890
891
  		dev_err(&connection->hd->dev,
  			"%s: can't create incoming operation
  ",
  			connection->name);
ff65e20ee   Johan Hovold   greybus: operatio...
892
  		return;
d37b1db13   Alex Elder   greybus: refactor...
893
  	}
d37b1db13   Alex Elder   greybus: refactor...
894

008974cb5   Johan Hovold   greybus: operatio...
895
896
897
898
899
  	ret = gb_operation_get_active(operation);
  	if (ret) {
  		gb_operation_put(operation);
  		return;
  	}
5c8ad599b   Bryan O'Donoghue   greybus: operatio...
900
  	trace_gb_message_recv_request(operation->request);
3eeac7e37   Johan Hovold   greybus: operatio...
901

d4a1ff674   Alex Elder   greybus: activate...
902
  	/*
c600e535a   Johan Hovold   greybus: operatio...
903
904
  	 * The initial reference to the operation will be dropped when the
  	 * request handler returns.
d4a1ff674   Alex Elder   greybus: activate...
905
  	 */
d4a1ff674   Alex Elder   greybus: activate...
906
  	if (gb_operation_result_set(operation, -EINPROGRESS))
5a5bc354c   Johan Hovold   greybus: operatio...
907
  		queue_work(connection->wq, &operation->work);
d37b1db13   Alex Elder   greybus: refactor...
908
909
910
911
912
  }
  
  /*
   * We've received data that appears to be an operation response
   * message.  Look up the operation, and record that we've received
696e0ccab   Viresh Kumar   greybus: Random s...
913
   * its response.
78496db01   Alex Elder   greybus: clean up...
914
   *
d37b1db13   Alex Elder   greybus: refactor...
915
916
917
918
   * 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,
dfcba8626   Johan Hovold   greybus: operatio...
919
920
  				const struct gb_operation_msg_hdr *header,
  				void *data, size_t size)
d37b1db13   Alex Elder   greybus: refactor...
921
922
923
  {
  	struct gb_operation *operation;
  	struct gb_message *message;
7cfa69955   Alex Elder   greybus: only rec...
924
  	size_t message_size;
dfcba8626   Johan Hovold   greybus: operatio...
925
926
927
928
  	u16 operation_id;
  	int errno;
  
  	operation_id = le16_to_cpu(header->operation_id);
d37b1db13   Alex Elder   greybus: refactor...
929

3e2ee2c1e   Johan Hovold   greybus: operatio...
930
  	if (!operation_id) {
b0e97bce1   Eli Sennesh   greybus: operatio...
931
  		dev_err_ratelimited(&connection->hd->dev,
8478c35a8   Cristian Sicilia   staging: greybus:...
932
933
934
  				    "%s: invalid response id 0 received
  ",
  				    connection->name);
3e2ee2c1e   Johan Hovold   greybus: operatio...
935
936
  		return;
  	}
048a7ffe2   Johan Hovold   greybus: operatio...
937
  	operation = gb_operation_find_outgoing(connection, operation_id);
d37b1db13   Alex Elder   greybus: refactor...
938
  	if (!operation) {
b0e97bce1   Eli Sennesh   greybus: operatio...
939
  		dev_err_ratelimited(&connection->hd->dev,
8478c35a8   Cristian Sicilia   staging: greybus:...
940
941
942
  				    "%s: unexpected response id 0x%04x received
  ",
  				    connection->name, operation_id);
d37b1db13   Alex Elder   greybus: refactor...
943
944
  		return;
  	}
dfcba8626   Johan Hovold   greybus: operatio...
945
  	errno = gb_operation_status_map(header->result);
c08b1ddae   Alex Elder   greybus: dynamica...
946
  	message = operation->response;
34804efb0   Johan Hovold   greybus: operatio...
947
  	message_size = sizeof(*header) + message->payload_size;
7e43e337a   Johan Hovold   greybus: operatio...
948
  	if (!errno && size > message_size) {
b0e97bce1   Eli Sennesh   greybus: operatio...
949
  		dev_err_ratelimited(&connection->hd->dev,
8478c35a8   Cristian Sicilia   staging: greybus:...
950
951
952
953
  				    "%s: malformed response 0x%02x received (%zu > %zu)
  ",
  				    connection->name, header->type,
  				    size, message_size);
64ce39a34   Alex Elder   greybus: pass res...
954
  		errno = -EMSGSIZE;
7e43e337a   Johan Hovold   greybus: operatio...
955
956
957
958
  	} else if (!errno && size < message_size) {
  		if (gb_operation_short_response_allowed(operation)) {
  			message->payload_size = size - sizeof(*header);
  		} else {
b0e97bce1   Eli Sennesh   greybus: operatio...
959
  			dev_err_ratelimited(&connection->hd->dev,
8478c35a8   Cristian Sicilia   staging: greybus:...
960
961
962
963
  					    "%s: short response 0x%02x received (%zu < %zu)
  ",
  					    connection->name, header->type,
  					    size, message_size);
7e43e337a   Johan Hovold   greybus: operatio...
964
965
  			errno = -EMSGSIZE;
  		}
d37b1db13   Alex Elder   greybus: refactor...
966
  	}
d37b1db13   Alex Elder   greybus: refactor...
967

25d0f81a0   Alex Elder   greybus: remove s...
968
  	/* We must ignore the payload if a bad status is returned */
64ce39a34   Alex Elder   greybus: pass res...
969
  	if (errno)
34804efb0   Johan Hovold   greybus: operatio...
970
  		size = sizeof(*header);
d37b1db13   Alex Elder   greybus: refactor...
971
972
  
  	/* The rest will be handled in work queue context */
e4340b13f   Johan Hovold   greybus: operatio...
973
  	if (gb_operation_result_set(operation, errno)) {
dfcba8626   Johan Hovold   greybus: operatio...
974
  		memcpy(message->buffer, data, size);
112f563e1   Johan Hovold   greybus: operatio...
975
976
  
  		trace_gb_message_recv_response(message);
701615f82   Johan Hovold   greybus: operatio...
977
  		queue_work(gb_operation_completion_wq, &operation->work);
e4340b13f   Johan Hovold   greybus: operatio...
978
  	}
0581f28ef   Johan Hovold   greybus: operatio...
979
980
  
  	gb_operation_put(operation);
d37b1db13   Alex Elder   greybus: refactor...
981
982
983
984
985
986
  }
  
  /*
   * 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).
2eb585f8d   Alex Elder   greybus: move rec...
987
   */
61089e89e   Alex Elder   greybus: rework r...
988
  void gb_connection_recv(struct gb_connection *connection,
8478c35a8   Cristian Sicilia   staging: greybus:...
989
  			void *data, size_t size)
d90c25b0a   Alex Elder   greybus: let oper...
990
  {
564c72b1c   Johan Hovold   greybus: operatio...
991
  	struct gb_operation_msg_hdr header;
25cdd7aa5   Johan Hovold   greybus: operatio...
992
  	struct device *dev = &connection->hd->dev;
d37b1db13   Alex Elder   greybus: refactor...
993
  	size_t msg_size;
d90c25b0a   Alex Elder   greybus: let oper...
994

8890f9571   Johan Hovold   greybus: operatio...
995
  	if (connection->state == GB_CONNECTION_STATE_DISABLED ||
8478c35a8   Cristian Sicilia   staging: greybus:...
996
  	    gb_connection_is_offloaded(connection)) {
b0e97bce1   Eli Sennesh   greybus: operatio...
997
998
  		dev_warn_ratelimited(dev, "%s: dropping %zu received bytes
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
999
  				     connection->name, size);
36561f23a   Alex Elder   greybus: define c...
1000
  		return;
d37b1db13   Alex Elder   greybus: refactor...
1001
  	}
36561f23a   Alex Elder   greybus: define c...
1002

564c72b1c   Johan Hovold   greybus: operatio...
1003
  	if (size < sizeof(header)) {
b0e97bce1   Eli Sennesh   greybus: operatio...
1004
1005
  		dev_err_ratelimited(dev, "%s: short message received
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
1006
  				    connection->name);
d90c25b0a   Alex Elder   greybus: let oper...
1007
1008
  		return;
  	}
564c72b1c   Johan Hovold   greybus: operatio...
1009
1010
1011
  	/* Use memcpy as data may be unaligned */
  	memcpy(&header, data, sizeof(header));
  	msg_size = le16_to_cpu(header.size);
0150bd7f2   Johan Hovold   greybus: operatio...
1012
  	if (size < msg_size) {
b0e97bce1   Eli Sennesh   greybus: operatio...
1013
  		dev_err_ratelimited(dev,
8478c35a8   Cristian Sicilia   staging: greybus:...
1014
1015
1016
1017
1018
  				    "%s: incomplete message 0x%04x of type 0x%02x received (%zu < %zu)
  ",
  				    connection->name,
  				    le16_to_cpu(header.operation_id),
  				    header.type, size, msg_size);
d37b1db13   Alex Elder   greybus: refactor...
1019
  		return;		/* XXX Should still complete operation */
d90c25b0a   Alex Elder   greybus: let oper...
1020
  	}
2321f049a   Johan Hovold   greybus: operatio...
1021
  	if (header.type & GB_MESSAGE_TYPE_RESPONSE) {
dfcba8626   Johan Hovold   greybus: operatio...
1022
  		gb_connection_recv_response(connection,	&header, data,
8478c35a8   Cristian Sicilia   staging: greybus:...
1023
  					    msg_size);
2321f049a   Johan Hovold   greybus: operatio...
1024
1025
  	} else {
  		gb_connection_recv_request(connection, &header, data,
8478c35a8   Cristian Sicilia   staging: greybus:...
1026
  					   msg_size);
2321f049a   Johan Hovold   greybus: operatio...
1027
  	}
2eb585f8d   Alex Elder   greybus: move rec...
1028
  }
e1158df06   Alex Elder   greybus: define o...
1029
  /*
5a3be769e   Johan Hovold   greybus: operatio...
1030
1031
   * Cancel an outgoing operation synchronously, and record the given error to
   * indicate why.
e1158df06   Alex Elder   greybus: define o...
1032
   */
f68c05c02   Alex Elder   greybus: cancel o...
1033
  void gb_operation_cancel(struct gb_operation *operation, int errno)
e1158df06   Alex Elder   greybus: define o...
1034
  {
5a3be769e   Johan Hovold   greybus: operatio...
1035
1036
1037
1038
1039
  	if (WARN_ON(gb_operation_is_incoming(operation)))
  		return;
  
  	if (gb_operation_result_set(operation, errno)) {
  		gb_message_cancel(operation->request);
701615f82   Johan Hovold   greybus: operatio...
1040
  		queue_work(gb_operation_completion_wq, &operation->work);
abe9a3006   Alex Elder   greybus: first op...
1041
  	}
5c8ad599b   Bryan O'Donoghue   greybus: operatio...
1042
  	trace_gb_message_cancel_outgoing(operation->request);
fd7134a3c   Johan Hovold   greybus: operatio...
1043
1044
1045
  
  	atomic_inc(&operation->waiters);
  	wait_event(gb_operation_cancellation_queue,
8478c35a8   Cristian Sicilia   staging: greybus:...
1046
  		   !gb_operation_is_active(operation));
fd7134a3c   Johan Hovold   greybus: operatio...
1047
  	atomic_dec(&operation->waiters);
e1158df06   Alex Elder   greybus: define o...
1048
  }
1dad6b351   Johan Hovold   greybus: operatio...
1049
  EXPORT_SYMBOL_GPL(gb_operation_cancel);
e1158df06   Alex Elder   greybus: define o...
1050

5a3be769e   Johan Hovold   greybus: operatio...
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
  /*
   * Cancel an incoming operation synchronously. Called during connection tear
   * down.
   */
  void gb_operation_cancel_incoming(struct gb_operation *operation, int errno)
  {
  	if (WARN_ON(!gb_operation_is_incoming(operation)))
  		return;
  
  	if (!gb_operation_is_unidirectional(operation)) {
  		/*
  		 * Make sure the request handler has submitted the response
  		 * before cancelling it.
  		 */
  		flush_work(&operation->work);
  		if (!gb_operation_result_set(operation, errno))
  			gb_message_cancel(operation->response);
  	}
5c8ad599b   Bryan O'Donoghue   greybus: operatio...
1069
  	trace_gb_message_cancel_incoming(operation->response);
5a3be769e   Johan Hovold   greybus: operatio...
1070
1071
1072
  
  	atomic_inc(&operation->waiters);
  	wait_event(gb_operation_cancellation_queue,
8478c35a8   Cristian Sicilia   staging: greybus:...
1073
  		   !gb_operation_is_active(operation));
5a3be769e   Johan Hovold   greybus: operatio...
1074
1075
  	atomic_dec(&operation->waiters);
  }
10aa801d3   Greg Kroah-Hartman   greybus: operatio...
1076
  /**
410abddb0   Johan Hovold   greybus: fix outd...
1077
   * gb_operation_sync_timeout() - implement a "simple" synchronous operation
10aa801d3   Greg Kroah-Hartman   greybus: operatio...
1078
1079
1080
1081
1082
1083
   * @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.
129a06f54   Johan Hovold   greybus: operatio...
1084
   * @timeout: operation timeout in milliseconds
10aa801d3   Greg Kroah-Hartman   greybus: operatio...
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
   *
   * 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.
   */
129a06f54   Johan Hovold   greybus: operatio...
1099
  int gb_operation_sync_timeout(struct gb_connection *connection, int type,
8478c35a8   Cristian Sicilia   staging: greybus:...
1100
1101
1102
  			      void *request, int request_size,
  			      void *response, int response_size,
  			      unsigned int timeout)
10aa801d3   Greg Kroah-Hartman   greybus: operatio...
1103
1104
1105
1106
1107
1108
1109
1110
1111
  {
  	struct gb_operation *operation;
  	int ret;
  
  	if ((response_size && !response) ||
  	    (request_size && !request))
  		return -EINVAL;
  
  	operation = gb_operation_create(connection, type,
e420721b4   Johan Hovold   greybus: operatio...
1112
1113
  					request_size, response_size,
  					GFP_KERNEL);
10aa801d3   Greg Kroah-Hartman   greybus: operatio...
1114
1115
1116
1117
  	if (!operation)
  		return -ENOMEM;
  
  	if (request_size)
6cd6ec55f   Alex Elder   greybus: fix a bu...
1118
  		memcpy(operation->request->payload, request, request_size);
10aa801d3   Greg Kroah-Hartman   greybus: operatio...
1119

129a06f54   Johan Hovold   greybus: operatio...
1120
  	ret = gb_operation_request_send_sync_timeout(operation, timeout);
ee8f81b09   Johan Hovold   greybus: operatio...
1121
  	if (ret) {
05e309556   Viresh Kumar   greybus: Revert "...
1122
  		dev_err(&connection->hd->dev,
e514dec73   David Lin   greybus: operatio...
1123
1124
1125
  			"%s: synchronous operation id 0x%04x of type 0x%02x failed: %d
  ",
  			connection->name, operation->id, type, ret);
ee8f81b09   Johan Hovold   greybus: operatio...
1126
1127
  	} else {
  		if (response_size) {
10aa801d3   Greg Kroah-Hartman   greybus: operatio...
1128
1129
  			memcpy(response, operation->response->payload,
  			       response_size);
ee8f81b09   Johan Hovold   greybus: operatio...
1130
1131
  		}
  	}
6ab1ce4d5   Johan Hovold   greybus: operatio...
1132
1133
  
  	gb_operation_put(operation);
10aa801d3   Greg Kroah-Hartman   greybus: operatio...
1134
1135
1136
  
  	return ret;
  }
129a06f54   Johan Hovold   greybus: operatio...
1137
  EXPORT_SYMBOL_GPL(gb_operation_sync_timeout);
10aa801d3   Greg Kroah-Hartman   greybus: operatio...
1138

5fdc027d5   Johan Hovold   greybus: operatio...
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
  /**
   * 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,
8478c35a8   Cristian Sicilia   staging: greybus:...
1154
1155
1156
  					int type, void *request,
  					int request_size,
  					unsigned int timeout)
5fdc027d5   Johan Hovold   greybus: operatio...
1157
1158
1159
1160
1161
1162
1163
1164
  {
  	struct gb_operation *operation;
  	int ret;
  
  	if (request_size && !request)
  		return -EINVAL;
  
  	operation = gb_operation_create_flags(connection, type,
8478c35a8   Cristian Sicilia   staging: greybus:...
1165
1166
1167
  					      request_size, 0,
  					      GB_OPERATION_FLAG_UNIDIRECTIONAL,
  					      GFP_KERNEL);
5fdc027d5   Johan Hovold   greybus: operatio...
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
  	if (!operation)
  		return -ENOMEM;
  
  	if (request_size)
  		memcpy(operation->request->payload, request, request_size);
  
  	ret = gb_operation_request_send_sync_timeout(operation, timeout);
  	if (ret) {
  		dev_err(&connection->hd->dev,
  			"%s: unidirectional operation of type 0x%02x failed: %d
  ",
  			connection->name, type, ret);
  	}
  
  	gb_operation_put(operation);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(gb_operation_unidirectional_timeout);
47ed2c924   Alex Elder   greybus: tag core...
1187
  int __init gb_operation_init(void)
2eb585f8d   Alex Elder   greybus: move rec...
1188
  {
1e5613b4a   Johan Hovold   greybus: operatio...
1189
  	gb_message_cache = kmem_cache_create("gb_message_cache",
8478c35a8   Cristian Sicilia   staging: greybus:...
1190
1191
  					     sizeof(struct gb_message), 0, 0,
  					     NULL);
1e5613b4a   Johan Hovold   greybus: operatio...
1192
  	if (!gb_message_cache)
0cffcac30   Alex Elder   greybus: create a...
1193
  		return -ENOMEM;
5b3db0dda   Alex Elder   greybus: create a...
1194
  	gb_operation_cache = kmem_cache_create("gb_operation_cache",
8478c35a8   Cristian Sicilia   staging: greybus:...
1195
1196
  					       sizeof(struct gb_operation), 0,
  					       0, NULL);
5b3db0dda   Alex Elder   greybus: create a...
1197
  	if (!gb_operation_cache)
1e5613b4a   Johan Hovold   greybus: operatio...
1198
  		goto err_destroy_message_cache;
5b3db0dda   Alex Elder   greybus: create a...
1199

701615f82   Johan Hovold   greybus: operatio...
1200
  	gb_operation_completion_wq = alloc_workqueue("greybus_completion",
8478c35a8   Cristian Sicilia   staging: greybus:...
1201
  						     0, 0);
701615f82   Johan Hovold   greybus: operatio...
1202
1203
  	if (!gb_operation_completion_wq)
  		goto err_destroy_operation_cache;
2eb585f8d   Alex Elder   greybus: move rec...
1204
  	return 0;
5a5bc354c   Johan Hovold   greybus: operatio...
1205

701615f82   Johan Hovold   greybus: operatio...
1206
1207
1208
  err_destroy_operation_cache:
  	kmem_cache_destroy(gb_operation_cache);
  	gb_operation_cache = NULL;
1e5613b4a   Johan Hovold   greybus: operatio...
1209
1210
1211
  err_destroy_message_cache:
  	kmem_cache_destroy(gb_message_cache);
  	gb_message_cache = NULL;
0cffcac30   Alex Elder   greybus: create a...
1212
1213
  
  	return -ENOMEM;
2eb585f8d   Alex Elder   greybus: move rec...
1214
  }
f35ab903e   Alex Elder   greybus: endo: de...
1215
  void gb_operation_exit(void)
2eb585f8d   Alex Elder   greybus: move rec...
1216
  {
701615f82   Johan Hovold   greybus: operatio...
1217
1218
  	destroy_workqueue(gb_operation_completion_wq);
  	gb_operation_completion_wq = NULL;
837b3b7c0   Viresh Kumar   greybus: operatio...
1219
1220
  	kmem_cache_destroy(gb_operation_cache);
  	gb_operation_cache = NULL;
1e5613b4a   Johan Hovold   greybus: operatio...
1221
1222
  	kmem_cache_destroy(gb_message_cache);
  	gb_message_cache = NULL;
d90c25b0a   Alex Elder   greybus: let oper...
1223
  }