1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Greybus connections |
4 | * |
5 | * Copyright 2014 Google Inc. |
6 | * Copyright 2014 Linaro Ltd. |
7 | */ |
8 | |
9 | #include <linux/workqueue.h> |
10 | #include <linux/greybus.h> |
11 | |
12 | #include "greybus_trace.h" |
13 | |
14 | #define GB_CONNECTION_CPORT_QUIESCE_TIMEOUT 1000 |
15 | |
16 | static void gb_connection_kref_release(struct kref *kref); |
17 | |
18 | static DEFINE_SPINLOCK(gb_connections_lock); |
19 | static DEFINE_MUTEX(gb_connection_mutex); |
20 | |
21 | /* Caller holds gb_connection_mutex. */ |
22 | static bool gb_connection_cport_in_use(struct gb_interface *intf, u16 cport_id) |
23 | { |
24 | struct gb_host_device *hd = intf->hd; |
25 | struct gb_connection *connection; |
26 | |
27 | list_for_each_entry(connection, &hd->connections, hd_links) { |
28 | if (connection->intf == intf && |
29 | connection->intf_cport_id == cport_id) |
30 | return true; |
31 | } |
32 | |
33 | return false; |
34 | } |
35 | |
36 | static void gb_connection_get(struct gb_connection *connection) |
37 | { |
38 | kref_get(kref: &connection->kref); |
39 | |
40 | trace_gb_connection_get(connection); |
41 | } |
42 | |
43 | static void gb_connection_put(struct gb_connection *connection) |
44 | { |
45 | trace_gb_connection_put(connection); |
46 | |
47 | kref_put(kref: &connection->kref, release: gb_connection_kref_release); |
48 | } |
49 | |
50 | /* |
51 | * Returns a reference-counted pointer to the connection if found. |
52 | */ |
53 | static struct gb_connection * |
54 | gb_connection_hd_find(struct gb_host_device *hd, u16 cport_id) |
55 | { |
56 | struct gb_connection *connection; |
57 | unsigned long flags; |
58 | |
59 | spin_lock_irqsave(&gb_connections_lock, flags); |
60 | list_for_each_entry(connection, &hd->connections, hd_links) |
61 | if (connection->hd_cport_id == cport_id) { |
62 | gb_connection_get(connection); |
63 | goto found; |
64 | } |
65 | connection = NULL; |
66 | found: |
67 | spin_unlock_irqrestore(lock: &gb_connections_lock, flags); |
68 | |
69 | return connection; |
70 | } |
71 | |
72 | /* |
73 | * Callback from the host driver to let us know that data has been |
74 | * received on the bundle. |
75 | */ |
76 | void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, |
77 | u8 *data, size_t length) |
78 | { |
79 | struct gb_connection *connection; |
80 | |
81 | trace_gb_hd_in(hd); |
82 | |
83 | connection = gb_connection_hd_find(hd, cport_id); |
84 | if (!connection) { |
85 | dev_err(&hd->dev, |
86 | "nonexistent connection (%zu bytes dropped)\n" , length); |
87 | return; |
88 | } |
89 | gb_connection_recv(connection, data, size: length); |
90 | gb_connection_put(connection); |
91 | } |
92 | EXPORT_SYMBOL_GPL(greybus_data_rcvd); |
93 | |
94 | static void gb_connection_kref_release(struct kref *kref) |
95 | { |
96 | struct gb_connection *connection; |
97 | |
98 | connection = container_of(kref, struct gb_connection, kref); |
99 | |
100 | trace_gb_connection_release(connection); |
101 | |
102 | kfree(objp: connection); |
103 | } |
104 | |
105 | static void gb_connection_init_name(struct gb_connection *connection) |
106 | { |
107 | u16 hd_cport_id = connection->hd_cport_id; |
108 | u16 cport_id = 0; |
109 | u8 intf_id = 0; |
110 | |
111 | if (connection->intf) { |
112 | intf_id = connection->intf->interface_id; |
113 | cport_id = connection->intf_cport_id; |
114 | } |
115 | |
116 | snprintf(buf: connection->name, size: sizeof(connection->name), |
117 | fmt: "%u/%u:%u" , hd_cport_id, intf_id, cport_id); |
118 | } |
119 | |
120 | /* |
121 | * _gb_connection_create() - create a Greybus connection |
122 | * @hd: host device of the connection |
123 | * @hd_cport_id: host-device cport id, or -1 for dynamic allocation |
124 | * @intf: remote interface, or NULL for static connections |
125 | * @bundle: remote-interface bundle (may be NULL) |
126 | * @cport_id: remote-interface cport id, or 0 for static connections |
127 | * @handler: request handler (may be NULL) |
128 | * @flags: connection flags |
129 | * |
130 | * Create a Greybus connection, representing the bidirectional link |
131 | * between a CPort on a (local) Greybus host device and a CPort on |
132 | * another Greybus interface. |
133 | * |
134 | * A connection also maintains the state of operations sent over the |
135 | * connection. |
136 | * |
137 | * Serialised against concurrent create and destroy using the |
138 | * gb_connection_mutex. |
139 | * |
140 | * Return: A pointer to the new connection if successful, or an ERR_PTR |
141 | * otherwise. |
142 | */ |
143 | static struct gb_connection * |
144 | _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, |
145 | struct gb_interface *intf, |
146 | struct gb_bundle *bundle, int cport_id, |
147 | gb_request_handler_t handler, |
148 | unsigned long flags) |
149 | { |
150 | struct gb_connection *connection; |
151 | int ret; |
152 | |
153 | mutex_lock(&gb_connection_mutex); |
154 | |
155 | if (intf && gb_connection_cport_in_use(intf, cport_id)) { |
156 | dev_err(&intf->dev, "cport %u already in use\n" , cport_id); |
157 | ret = -EBUSY; |
158 | goto err_unlock; |
159 | } |
160 | |
161 | ret = gb_hd_cport_allocate(hd, cport_id: hd_cport_id, flags); |
162 | if (ret < 0) { |
163 | dev_err(&hd->dev, "failed to allocate cport: %d\n" , ret); |
164 | goto err_unlock; |
165 | } |
166 | hd_cport_id = ret; |
167 | |
168 | connection = kzalloc(size: sizeof(*connection), GFP_KERNEL); |
169 | if (!connection) { |
170 | ret = -ENOMEM; |
171 | goto err_hd_cport_release; |
172 | } |
173 | |
174 | connection->hd_cport_id = hd_cport_id; |
175 | connection->intf_cport_id = cport_id; |
176 | connection->hd = hd; |
177 | connection->intf = intf; |
178 | connection->bundle = bundle; |
179 | connection->handler = handler; |
180 | connection->flags = flags; |
181 | if (intf && (intf->quirks & GB_INTERFACE_QUIRK_NO_CPORT_FEATURES)) |
182 | connection->flags |= GB_CONNECTION_FLAG_NO_FLOWCTRL; |
183 | connection->state = GB_CONNECTION_STATE_DISABLED; |
184 | |
185 | atomic_set(v: &connection->op_cycle, i: 0); |
186 | mutex_init(&connection->mutex); |
187 | spin_lock_init(&connection->lock); |
188 | INIT_LIST_HEAD(list: &connection->operations); |
189 | |
190 | connection->wq = alloc_ordered_workqueue("%s:%d" , 0, dev_name(&hd->dev), |
191 | hd_cport_id); |
192 | if (!connection->wq) { |
193 | ret = -ENOMEM; |
194 | goto err_free_connection; |
195 | } |
196 | |
197 | kref_init(kref: &connection->kref); |
198 | |
199 | gb_connection_init_name(connection); |
200 | |
201 | spin_lock_irq(lock: &gb_connections_lock); |
202 | list_add(new: &connection->hd_links, head: &hd->connections); |
203 | |
204 | if (bundle) |
205 | list_add(new: &connection->bundle_links, head: &bundle->connections); |
206 | else |
207 | INIT_LIST_HEAD(list: &connection->bundle_links); |
208 | |
209 | spin_unlock_irq(lock: &gb_connections_lock); |
210 | |
211 | mutex_unlock(lock: &gb_connection_mutex); |
212 | |
213 | trace_gb_connection_create(connection); |
214 | |
215 | return connection; |
216 | |
217 | err_free_connection: |
218 | kfree(objp: connection); |
219 | err_hd_cport_release: |
220 | gb_hd_cport_release(hd, cport_id: hd_cport_id); |
221 | err_unlock: |
222 | mutex_unlock(lock: &gb_connection_mutex); |
223 | |
224 | return ERR_PTR(error: ret); |
225 | } |
226 | |
227 | struct gb_connection * |
228 | gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id, |
229 | gb_request_handler_t handler) |
230 | { |
231 | return _gb_connection_create(hd, hd_cport_id, NULL, NULL, cport_id: 0, handler, |
232 | GB_CONNECTION_FLAG_HIGH_PRIO); |
233 | } |
234 | |
235 | struct gb_connection * |
236 | gb_connection_create_control(struct gb_interface *intf) |
237 | { |
238 | return _gb_connection_create(hd: intf->hd, hd_cport_id: -1, intf, NULL, cport_id: 0, NULL, |
239 | GB_CONNECTION_FLAG_CONTROL | |
240 | GB_CONNECTION_FLAG_HIGH_PRIO); |
241 | } |
242 | |
243 | struct gb_connection * |
244 | gb_connection_create(struct gb_bundle *bundle, u16 cport_id, |
245 | gb_request_handler_t handler) |
246 | { |
247 | struct gb_interface *intf = bundle->intf; |
248 | |
249 | return _gb_connection_create(hd: intf->hd, hd_cport_id: -1, intf, bundle, cport_id, |
250 | handler, flags: 0); |
251 | } |
252 | EXPORT_SYMBOL_GPL(gb_connection_create); |
253 | |
254 | struct gb_connection * |
255 | gb_connection_create_flags(struct gb_bundle *bundle, u16 cport_id, |
256 | gb_request_handler_t handler, |
257 | unsigned long flags) |
258 | { |
259 | struct gb_interface *intf = bundle->intf; |
260 | |
261 | if (WARN_ON_ONCE(flags & GB_CONNECTION_FLAG_CORE_MASK)) |
262 | flags &= ~GB_CONNECTION_FLAG_CORE_MASK; |
263 | |
264 | return _gb_connection_create(hd: intf->hd, hd_cport_id: -1, intf, bundle, cport_id, |
265 | handler, flags); |
266 | } |
267 | EXPORT_SYMBOL_GPL(gb_connection_create_flags); |
268 | |
269 | struct gb_connection * |
270 | gb_connection_create_offloaded(struct gb_bundle *bundle, u16 cport_id, |
271 | unsigned long flags) |
272 | { |
273 | flags |= GB_CONNECTION_FLAG_OFFLOADED; |
274 | |
275 | return gb_connection_create_flags(bundle, cport_id, NULL, flags); |
276 | } |
277 | EXPORT_SYMBOL_GPL(gb_connection_create_offloaded); |
278 | |
279 | static int gb_connection_hd_cport_enable(struct gb_connection *connection) |
280 | { |
281 | struct gb_host_device *hd = connection->hd; |
282 | int ret; |
283 | |
284 | if (!hd->driver->cport_enable) |
285 | return 0; |
286 | |
287 | ret = hd->driver->cport_enable(hd, connection->hd_cport_id, |
288 | connection->flags); |
289 | if (ret) { |
290 | dev_err(&hd->dev, "%s: failed to enable host cport: %d\n" , |
291 | connection->name, ret); |
292 | return ret; |
293 | } |
294 | |
295 | return 0; |
296 | } |
297 | |
298 | static void gb_connection_hd_cport_disable(struct gb_connection *connection) |
299 | { |
300 | struct gb_host_device *hd = connection->hd; |
301 | int ret; |
302 | |
303 | if (!hd->driver->cport_disable) |
304 | return; |
305 | |
306 | ret = hd->driver->cport_disable(hd, connection->hd_cport_id); |
307 | if (ret) { |
308 | dev_err(&hd->dev, "%s: failed to disable host cport: %d\n" , |
309 | connection->name, ret); |
310 | } |
311 | } |
312 | |
313 | static int gb_connection_hd_cport_connected(struct gb_connection *connection) |
314 | { |
315 | struct gb_host_device *hd = connection->hd; |
316 | int ret; |
317 | |
318 | if (!hd->driver->cport_connected) |
319 | return 0; |
320 | |
321 | ret = hd->driver->cport_connected(hd, connection->hd_cport_id); |
322 | if (ret) { |
323 | dev_err(&hd->dev, "%s: failed to set connected state: %d\n" , |
324 | connection->name, ret); |
325 | return ret; |
326 | } |
327 | |
328 | return 0; |
329 | } |
330 | |
331 | static int gb_connection_hd_cport_flush(struct gb_connection *connection) |
332 | { |
333 | struct gb_host_device *hd = connection->hd; |
334 | int ret; |
335 | |
336 | if (!hd->driver->cport_flush) |
337 | return 0; |
338 | |
339 | ret = hd->driver->cport_flush(hd, connection->hd_cport_id); |
340 | if (ret) { |
341 | dev_err(&hd->dev, "%s: failed to flush host cport: %d\n" , |
342 | connection->name, ret); |
343 | return ret; |
344 | } |
345 | |
346 | return 0; |
347 | } |
348 | |
349 | static int gb_connection_hd_cport_quiesce(struct gb_connection *connection) |
350 | { |
351 | struct gb_host_device *hd = connection->hd; |
352 | size_t peer_space; |
353 | int ret; |
354 | |
355 | if (!hd->driver->cport_quiesce) |
356 | return 0; |
357 | |
358 | peer_space = sizeof(struct gb_operation_msg_hdr) + |
359 | sizeof(struct gb_cport_shutdown_request); |
360 | |
361 | if (connection->mode_switch) |
362 | peer_space += sizeof(struct gb_operation_msg_hdr); |
363 | |
364 | ret = hd->driver->cport_quiesce(hd, connection->hd_cport_id, |
365 | peer_space, |
366 | GB_CONNECTION_CPORT_QUIESCE_TIMEOUT); |
367 | if (ret) { |
368 | dev_err(&hd->dev, "%s: failed to quiesce host cport: %d\n" , |
369 | connection->name, ret); |
370 | return ret; |
371 | } |
372 | |
373 | return 0; |
374 | } |
375 | |
376 | static int gb_connection_hd_cport_clear(struct gb_connection *connection) |
377 | { |
378 | struct gb_host_device *hd = connection->hd; |
379 | int ret; |
380 | |
381 | if (!hd->driver->cport_clear) |
382 | return 0; |
383 | |
384 | ret = hd->driver->cport_clear(hd, connection->hd_cport_id); |
385 | if (ret) { |
386 | dev_err(&hd->dev, "%s: failed to clear host cport: %d\n" , |
387 | connection->name, ret); |
388 | return ret; |
389 | } |
390 | |
391 | return 0; |
392 | } |
393 | |
394 | /* |
395 | * Request the SVC to create a connection from AP's cport to interface's |
396 | * cport. |
397 | */ |
398 | static int |
399 | gb_connection_svc_connection_create(struct gb_connection *connection) |
400 | { |
401 | struct gb_host_device *hd = connection->hd; |
402 | struct gb_interface *intf; |
403 | u8 cport_flags; |
404 | int ret; |
405 | |
406 | if (gb_connection_is_static(connection)) |
407 | return 0; |
408 | |
409 | intf = connection->intf; |
410 | |
411 | /* |
412 | * Enable either E2EFC or CSD, unless no flow control is requested. |
413 | */ |
414 | cport_flags = GB_SVC_CPORT_FLAG_CSV_N; |
415 | if (gb_connection_flow_control_disabled(connection)) { |
416 | cport_flags |= GB_SVC_CPORT_FLAG_CSD_N; |
417 | } else if (gb_connection_e2efc_enabled(connection)) { |
418 | cport_flags |= GB_SVC_CPORT_FLAG_CSD_N | |
419 | GB_SVC_CPORT_FLAG_E2EFC; |
420 | } |
421 | |
422 | ret = gb_svc_connection_create(svc: hd->svc, |
423 | intf1_id: hd->svc->ap_intf_id, |
424 | cport1_id: connection->hd_cport_id, |
425 | intf2_id: intf->interface_id, |
426 | cport2_id: connection->intf_cport_id, |
427 | cport_flags); |
428 | if (ret) { |
429 | dev_err(&connection->hd->dev, |
430 | "%s: failed to create svc connection: %d\n" , |
431 | connection->name, ret); |
432 | return ret; |
433 | } |
434 | |
435 | return 0; |
436 | } |
437 | |
438 | static void |
439 | gb_connection_svc_connection_destroy(struct gb_connection *connection) |
440 | { |
441 | if (gb_connection_is_static(connection)) |
442 | return; |
443 | |
444 | gb_svc_connection_destroy(svc: connection->hd->svc, |
445 | intf1_id: connection->hd->svc->ap_intf_id, |
446 | cport1_id: connection->hd_cport_id, |
447 | intf2_id: connection->intf->interface_id, |
448 | cport2_id: connection->intf_cport_id); |
449 | } |
450 | |
451 | /* Inform Interface about active CPorts */ |
452 | static int gb_connection_control_connected(struct gb_connection *connection) |
453 | { |
454 | struct gb_control *control; |
455 | u16 cport_id = connection->intf_cport_id; |
456 | int ret; |
457 | |
458 | if (gb_connection_is_static(connection)) |
459 | return 0; |
460 | |
461 | if (gb_connection_is_control(connection)) |
462 | return 0; |
463 | |
464 | control = connection->intf->control; |
465 | |
466 | ret = gb_control_connected_operation(control, cport_id); |
467 | if (ret) { |
468 | dev_err(&connection->bundle->dev, |
469 | "failed to connect cport: %d\n" , ret); |
470 | return ret; |
471 | } |
472 | |
473 | return 0; |
474 | } |
475 | |
476 | static void |
477 | gb_connection_control_disconnecting(struct gb_connection *connection) |
478 | { |
479 | struct gb_control *control; |
480 | u16 cport_id = connection->intf_cport_id; |
481 | int ret; |
482 | |
483 | if (gb_connection_is_static(connection)) |
484 | return; |
485 | |
486 | control = connection->intf->control; |
487 | |
488 | ret = gb_control_disconnecting_operation(control, cport_id); |
489 | if (ret) { |
490 | dev_err(&connection->hd->dev, |
491 | "%s: failed to send disconnecting: %d\n" , |
492 | connection->name, ret); |
493 | } |
494 | } |
495 | |
496 | static void |
497 | gb_connection_control_disconnected(struct gb_connection *connection) |
498 | { |
499 | struct gb_control *control; |
500 | u16 cport_id = connection->intf_cport_id; |
501 | int ret; |
502 | |
503 | if (gb_connection_is_static(connection)) |
504 | return; |
505 | |
506 | control = connection->intf->control; |
507 | |
508 | if (gb_connection_is_control(connection)) { |
509 | if (connection->mode_switch) { |
510 | ret = gb_control_mode_switch_operation(control); |
511 | if (ret) { |
512 | /* |
513 | * Allow mode switch to time out waiting for |
514 | * mailbox event. |
515 | */ |
516 | return; |
517 | } |
518 | } |
519 | |
520 | return; |
521 | } |
522 | |
523 | ret = gb_control_disconnected_operation(control, cport_id); |
524 | if (ret) { |
525 | dev_warn(&connection->bundle->dev, |
526 | "failed to disconnect cport: %d\n" , ret); |
527 | } |
528 | } |
529 | |
530 | static int gb_connection_shutdown_operation(struct gb_connection *connection, |
531 | u8 phase) |
532 | { |
533 | struct gb_cport_shutdown_request *req; |
534 | struct gb_operation *operation; |
535 | int ret; |
536 | |
537 | operation = gb_operation_create_core(connection, |
538 | GB_REQUEST_TYPE_CPORT_SHUTDOWN, |
539 | request_size: sizeof(*req), response_size: 0, flags: 0, |
540 | GFP_KERNEL); |
541 | if (!operation) |
542 | return -ENOMEM; |
543 | |
544 | req = operation->request->payload; |
545 | req->phase = phase; |
546 | |
547 | ret = gb_operation_request_send_sync(operation); |
548 | |
549 | gb_operation_put(operation); |
550 | |
551 | return ret; |
552 | } |
553 | |
554 | static int gb_connection_cport_shutdown(struct gb_connection *connection, |
555 | u8 phase) |
556 | { |
557 | struct gb_host_device *hd = connection->hd; |
558 | const struct gb_hd_driver *drv = hd->driver; |
559 | int ret; |
560 | |
561 | if (gb_connection_is_static(connection)) |
562 | return 0; |
563 | |
564 | if (gb_connection_is_offloaded(connection)) { |
565 | if (!drv->cport_shutdown) |
566 | return 0; |
567 | |
568 | ret = drv->cport_shutdown(hd, connection->hd_cport_id, phase, |
569 | GB_OPERATION_TIMEOUT_DEFAULT); |
570 | } else { |
571 | ret = gb_connection_shutdown_operation(connection, phase); |
572 | } |
573 | |
574 | if (ret) { |
575 | dev_err(&hd->dev, "%s: failed to send cport shutdown (phase %d): %d\n" , |
576 | connection->name, phase, ret); |
577 | return ret; |
578 | } |
579 | |
580 | return 0; |
581 | } |
582 | |
583 | static int |
584 | gb_connection_cport_shutdown_phase_1(struct gb_connection *connection) |
585 | { |
586 | return gb_connection_cport_shutdown(connection, phase: 1); |
587 | } |
588 | |
589 | static int |
590 | gb_connection_cport_shutdown_phase_2(struct gb_connection *connection) |
591 | { |
592 | return gb_connection_cport_shutdown(connection, phase: 2); |
593 | } |
594 | |
595 | /* |
596 | * Cancel all active operations on a connection. |
597 | * |
598 | * Locking: Called with connection lock held and state set to DISABLED or |
599 | * DISCONNECTING. |
600 | */ |
601 | static void gb_connection_cancel_operations(struct gb_connection *connection, |
602 | int errno) |
603 | __must_hold(&connection->lock) |
604 | { |
605 | struct gb_operation *operation; |
606 | |
607 | while (!list_empty(head: &connection->operations)) { |
608 | operation = list_last_entry(&connection->operations, |
609 | struct gb_operation, links); |
610 | gb_operation_get(operation); |
611 | spin_unlock_irq(lock: &connection->lock); |
612 | |
613 | if (gb_operation_is_incoming(operation)) |
614 | gb_operation_cancel_incoming(operation, errno); |
615 | else |
616 | gb_operation_cancel(operation, errno); |
617 | |
618 | gb_operation_put(operation); |
619 | |
620 | spin_lock_irq(lock: &connection->lock); |
621 | } |
622 | } |
623 | |
624 | /* |
625 | * Cancel all active incoming operations on a connection. |
626 | * |
627 | * Locking: Called with connection lock held and state set to ENABLED_TX. |
628 | */ |
629 | static void |
630 | gb_connection_flush_incoming_operations(struct gb_connection *connection, |
631 | int errno) |
632 | __must_hold(&connection->lock) |
633 | { |
634 | struct gb_operation *operation; |
635 | bool incoming; |
636 | |
637 | while (!list_empty(head: &connection->operations)) { |
638 | incoming = false; |
639 | list_for_each_entry(operation, &connection->operations, |
640 | links) { |
641 | if (gb_operation_is_incoming(operation)) { |
642 | gb_operation_get(operation); |
643 | incoming = true; |
644 | break; |
645 | } |
646 | } |
647 | |
648 | if (!incoming) |
649 | break; |
650 | |
651 | spin_unlock_irq(lock: &connection->lock); |
652 | |
653 | /* FIXME: flush, not cancel? */ |
654 | gb_operation_cancel_incoming(operation, errno); |
655 | gb_operation_put(operation); |
656 | |
657 | spin_lock_irq(lock: &connection->lock); |
658 | } |
659 | } |
660 | |
661 | /* |
662 | * _gb_connection_enable() - enable a connection |
663 | * @connection: connection to enable |
664 | * @rx: whether to enable incoming requests |
665 | * |
666 | * Connection-enable helper for DISABLED->ENABLED, DISABLED->ENABLED_TX, and |
667 | * ENABLED_TX->ENABLED state transitions. |
668 | * |
669 | * Locking: Caller holds connection->mutex. |
670 | */ |
671 | static int _gb_connection_enable(struct gb_connection *connection, bool rx) |
672 | { |
673 | int ret; |
674 | |
675 | /* Handle ENABLED_TX -> ENABLED transitions. */ |
676 | if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) { |
677 | if (!(connection->handler && rx)) |
678 | return 0; |
679 | |
680 | spin_lock_irq(lock: &connection->lock); |
681 | connection->state = GB_CONNECTION_STATE_ENABLED; |
682 | spin_unlock_irq(lock: &connection->lock); |
683 | |
684 | return 0; |
685 | } |
686 | |
687 | ret = gb_connection_hd_cport_enable(connection); |
688 | if (ret) |
689 | return ret; |
690 | |
691 | ret = gb_connection_svc_connection_create(connection); |
692 | if (ret) |
693 | goto err_hd_cport_clear; |
694 | |
695 | ret = gb_connection_hd_cport_connected(connection); |
696 | if (ret) |
697 | goto err_svc_connection_destroy; |
698 | |
699 | spin_lock_irq(lock: &connection->lock); |
700 | if (connection->handler && rx) |
701 | connection->state = GB_CONNECTION_STATE_ENABLED; |
702 | else |
703 | connection->state = GB_CONNECTION_STATE_ENABLED_TX; |
704 | spin_unlock_irq(lock: &connection->lock); |
705 | |
706 | ret = gb_connection_control_connected(connection); |
707 | if (ret) |
708 | goto err_control_disconnecting; |
709 | |
710 | return 0; |
711 | |
712 | err_control_disconnecting: |
713 | spin_lock_irq(lock: &connection->lock); |
714 | connection->state = GB_CONNECTION_STATE_DISCONNECTING; |
715 | gb_connection_cancel_operations(connection, errno: -ESHUTDOWN); |
716 | spin_unlock_irq(lock: &connection->lock); |
717 | |
718 | /* Transmit queue should already be empty. */ |
719 | gb_connection_hd_cport_flush(connection); |
720 | |
721 | gb_connection_control_disconnecting(connection); |
722 | gb_connection_cport_shutdown_phase_1(connection); |
723 | gb_connection_hd_cport_quiesce(connection); |
724 | gb_connection_cport_shutdown_phase_2(connection); |
725 | gb_connection_control_disconnected(connection); |
726 | connection->state = GB_CONNECTION_STATE_DISABLED; |
727 | err_svc_connection_destroy: |
728 | gb_connection_svc_connection_destroy(connection); |
729 | err_hd_cport_clear: |
730 | gb_connection_hd_cport_clear(connection); |
731 | |
732 | gb_connection_hd_cport_disable(connection); |
733 | |
734 | return ret; |
735 | } |
736 | |
737 | int gb_connection_enable(struct gb_connection *connection) |
738 | { |
739 | int ret = 0; |
740 | |
741 | mutex_lock(&connection->mutex); |
742 | |
743 | if (connection->state == GB_CONNECTION_STATE_ENABLED) |
744 | goto out_unlock; |
745 | |
746 | ret = _gb_connection_enable(connection, rx: true); |
747 | if (!ret) |
748 | trace_gb_connection_enable(connection); |
749 | |
750 | out_unlock: |
751 | mutex_unlock(lock: &connection->mutex); |
752 | |
753 | return ret; |
754 | } |
755 | EXPORT_SYMBOL_GPL(gb_connection_enable); |
756 | |
757 | int gb_connection_enable_tx(struct gb_connection *connection) |
758 | { |
759 | int ret = 0; |
760 | |
761 | mutex_lock(&connection->mutex); |
762 | |
763 | if (connection->state == GB_CONNECTION_STATE_ENABLED) { |
764 | ret = -EINVAL; |
765 | goto out_unlock; |
766 | } |
767 | |
768 | if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) |
769 | goto out_unlock; |
770 | |
771 | ret = _gb_connection_enable(connection, rx: false); |
772 | if (!ret) |
773 | trace_gb_connection_enable(connection); |
774 | |
775 | out_unlock: |
776 | mutex_unlock(lock: &connection->mutex); |
777 | |
778 | return ret; |
779 | } |
780 | EXPORT_SYMBOL_GPL(gb_connection_enable_tx); |
781 | |
782 | void gb_connection_disable_rx(struct gb_connection *connection) |
783 | { |
784 | mutex_lock(&connection->mutex); |
785 | |
786 | spin_lock_irq(lock: &connection->lock); |
787 | if (connection->state != GB_CONNECTION_STATE_ENABLED) { |
788 | spin_unlock_irq(lock: &connection->lock); |
789 | goto out_unlock; |
790 | } |
791 | connection->state = GB_CONNECTION_STATE_ENABLED_TX; |
792 | gb_connection_flush_incoming_operations(connection, errno: -ESHUTDOWN); |
793 | spin_unlock_irq(lock: &connection->lock); |
794 | |
795 | trace_gb_connection_disable(connection); |
796 | |
797 | out_unlock: |
798 | mutex_unlock(lock: &connection->mutex); |
799 | } |
800 | EXPORT_SYMBOL_GPL(gb_connection_disable_rx); |
801 | |
802 | void gb_connection_mode_switch_prepare(struct gb_connection *connection) |
803 | { |
804 | connection->mode_switch = true; |
805 | } |
806 | |
807 | void gb_connection_mode_switch_complete(struct gb_connection *connection) |
808 | { |
809 | gb_connection_svc_connection_destroy(connection); |
810 | gb_connection_hd_cport_clear(connection); |
811 | |
812 | gb_connection_hd_cport_disable(connection); |
813 | |
814 | connection->mode_switch = false; |
815 | } |
816 | |
817 | void gb_connection_disable(struct gb_connection *connection) |
818 | { |
819 | mutex_lock(&connection->mutex); |
820 | |
821 | if (connection->state == GB_CONNECTION_STATE_DISABLED) |
822 | goto out_unlock; |
823 | |
824 | trace_gb_connection_disable(connection); |
825 | |
826 | spin_lock_irq(lock: &connection->lock); |
827 | connection->state = GB_CONNECTION_STATE_DISCONNECTING; |
828 | gb_connection_cancel_operations(connection, errno: -ESHUTDOWN); |
829 | spin_unlock_irq(lock: &connection->lock); |
830 | |
831 | gb_connection_hd_cport_flush(connection); |
832 | |
833 | gb_connection_control_disconnecting(connection); |
834 | gb_connection_cport_shutdown_phase_1(connection); |
835 | gb_connection_hd_cport_quiesce(connection); |
836 | gb_connection_cport_shutdown_phase_2(connection); |
837 | gb_connection_control_disconnected(connection); |
838 | |
839 | connection->state = GB_CONNECTION_STATE_DISABLED; |
840 | |
841 | /* control-connection tear down is deferred when mode switching */ |
842 | if (!connection->mode_switch) { |
843 | gb_connection_svc_connection_destroy(connection); |
844 | gb_connection_hd_cport_clear(connection); |
845 | |
846 | gb_connection_hd_cport_disable(connection); |
847 | } |
848 | |
849 | out_unlock: |
850 | mutex_unlock(lock: &connection->mutex); |
851 | } |
852 | EXPORT_SYMBOL_GPL(gb_connection_disable); |
853 | |
854 | /* Disable a connection without communicating with the remote end. */ |
855 | void gb_connection_disable_forced(struct gb_connection *connection) |
856 | { |
857 | mutex_lock(&connection->mutex); |
858 | |
859 | if (connection->state == GB_CONNECTION_STATE_DISABLED) |
860 | goto out_unlock; |
861 | |
862 | trace_gb_connection_disable(connection); |
863 | |
864 | spin_lock_irq(lock: &connection->lock); |
865 | connection->state = GB_CONNECTION_STATE_DISABLED; |
866 | gb_connection_cancel_operations(connection, errno: -ESHUTDOWN); |
867 | spin_unlock_irq(lock: &connection->lock); |
868 | |
869 | gb_connection_hd_cport_flush(connection); |
870 | |
871 | gb_connection_svc_connection_destroy(connection); |
872 | gb_connection_hd_cport_clear(connection); |
873 | |
874 | gb_connection_hd_cport_disable(connection); |
875 | out_unlock: |
876 | mutex_unlock(lock: &connection->mutex); |
877 | } |
878 | EXPORT_SYMBOL_GPL(gb_connection_disable_forced); |
879 | |
880 | /* Caller must have disabled the connection before destroying it. */ |
881 | void gb_connection_destroy(struct gb_connection *connection) |
882 | { |
883 | if (!connection) |
884 | return; |
885 | |
886 | if (WARN_ON(connection->state != GB_CONNECTION_STATE_DISABLED)) |
887 | gb_connection_disable(connection); |
888 | |
889 | mutex_lock(&gb_connection_mutex); |
890 | |
891 | spin_lock_irq(lock: &gb_connections_lock); |
892 | list_del(entry: &connection->bundle_links); |
893 | list_del(entry: &connection->hd_links); |
894 | spin_unlock_irq(lock: &gb_connections_lock); |
895 | |
896 | destroy_workqueue(wq: connection->wq); |
897 | |
898 | gb_hd_cport_release(hd: connection->hd, cport_id: connection->hd_cport_id); |
899 | connection->hd_cport_id = CPORT_ID_BAD; |
900 | |
901 | mutex_unlock(lock: &gb_connection_mutex); |
902 | |
903 | gb_connection_put(connection); |
904 | } |
905 | EXPORT_SYMBOL_GPL(gb_connection_destroy); |
906 | |
907 | void gb_connection_latency_tag_enable(struct gb_connection *connection) |
908 | { |
909 | struct gb_host_device *hd = connection->hd; |
910 | int ret; |
911 | |
912 | if (!hd->driver->latency_tag_enable) |
913 | return; |
914 | |
915 | ret = hd->driver->latency_tag_enable(hd, connection->hd_cport_id); |
916 | if (ret) { |
917 | dev_err(&connection->hd->dev, |
918 | "%s: failed to enable latency tag: %d\n" , |
919 | connection->name, ret); |
920 | } |
921 | } |
922 | EXPORT_SYMBOL_GPL(gb_connection_latency_tag_enable); |
923 | |
924 | void gb_connection_latency_tag_disable(struct gb_connection *connection) |
925 | { |
926 | struct gb_host_device *hd = connection->hd; |
927 | int ret; |
928 | |
929 | if (!hd->driver->latency_tag_disable) |
930 | return; |
931 | |
932 | ret = hd->driver->latency_tag_disable(hd, connection->hd_cport_id); |
933 | if (ret) { |
934 | dev_err(&connection->hd->dev, |
935 | "%s: failed to disable latency tag: %d\n" , |
936 | connection->name, ret); |
937 | } |
938 | } |
939 | EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable); |
940 | |