1 /**
2 * Copyright (c) 2010-2012 Broadcom. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions, and the following disclaimer,
9 * without modification.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The names of the above-listed copyright holders may not be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * ALTERNATIVELY, this software may be distributed under the terms of the
18 * GNU General Public License ("GPL") version 2, as published by the Free
19 * Software Foundation.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "vchiq_core.h"
35 #include "vchiq_killable.h"
36
37 #define VCHIQ_SLOT_HANDLER_STACK 8192
38
39 #define HANDLE_STATE_SHIFT 12
40
41 #define SLOT_INFO_FROM_INDEX(state, index) (state->slot_info + (index))
42 #define SLOT_DATA_FROM_INDEX(state, index) (state->slot_data + (index))
43 #define SLOT_INDEX_FROM_DATA(state, data) \
44 (((unsigned int)((char *)data - (char *)state->slot_data)) / \
45 VCHIQ_SLOT_SIZE)
46 #define SLOT_INDEX_FROM_INFO(state, info) \
47 ((unsigned int)(info - state->slot_info))
48 #define SLOT_QUEUE_INDEX_FROM_POS(pos) \
49 ((int)((unsigned int)(pos) / VCHIQ_SLOT_SIZE))
50
51 #define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1))
52
53 #define SRVTRACE_LEVEL(srv) \
54 (((srv) && (srv)->trace) ? VCHIQ_LOG_TRACE : vchiq_core_msg_log_level)
55 #define SRVTRACE_ENABLED(srv, lev) \
56 (((srv) && (srv)->trace) || (vchiq_core_msg_log_level >= (lev)))
57
58 struct vchiq_open_payload {
59 int fourcc;
60 int client_id;
61 short version;
62 short version_min;
63 };
64
65 struct vchiq_openack_payload {
66 short version;
67 };
68
69 enum
70 {
71 QMFLAGS_IS_BLOCKING = (1 << 0),
72 QMFLAGS_NO_MUTEX_LOCK = (1 << 1),
73 QMFLAGS_NO_MUTEX_UNLOCK = (1 << 2)
74 };
75
76 /* we require this for consistency between endpoints */
77 vchiq_static_assert(sizeof(VCHIQ_HEADER_T) == 8);
78 vchiq_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T)));
79 vchiq_static_assert(IS_POW2(VCHIQ_NUM_CURRENT_BULKS));
80 vchiq_static_assert(IS_POW2(VCHIQ_NUM_SERVICE_BULKS));
81 vchiq_static_assert(IS_POW2(VCHIQ_MAX_SERVICES));
82 vchiq_static_assert(VCHIQ_VERSION >= VCHIQ_VERSION_MIN);
83
84 /* Run time control of log level, based on KERN_XXX level. */
85 int vchiq_core_log_level = VCHIQ_LOG_DEFAULT;
86 int vchiq_core_msg_log_level = VCHIQ_LOG_DEFAULT;
87 int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT;
88
89 static atomic_t pause_bulks_count = ATOMIC_INIT(0);
90
91 static DEFINE_SPINLOCK(service_spinlock);
92 DEFINE_SPINLOCK(bulk_waiter_spinlock);
93 DEFINE_SPINLOCK(quota_spinlock);
94
95 void
vchiq_core_initialize(void)96 vchiq_core_initialize(void)
97 {
98 spin_lock_init(&service_spinlock);
99 spin_lock_init(&bulk_waiter_spinlock);
100 spin_lock_init("a_spinlock);
101 }
102
103 VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES];
104 static unsigned int handle_seq;
105
106 static const char *const srvstate_names[] = {
107 "FREE",
108 "HIDDEN",
109 "LISTENING",
110 "OPENING",
111 "OPEN",
112 "OPENSYNC",
113 "CLOSESENT",
114 "CLOSERECVD",
115 "CLOSEWAIT",
116 "CLOSED"
117 };
118
119 static const char *const reason_names[] = {
120 "SERVICE_OPENED",
121 "SERVICE_CLOSED",
122 "MESSAGE_AVAILABLE",
123 "BULK_TRANSMIT_DONE",
124 "BULK_RECEIVE_DONE",
125 "BULK_TRANSMIT_ABORTED",
126 "BULK_RECEIVE_ABORTED"
127 };
128
129 static const char *const conn_state_names[] = {
130 "DISCONNECTED",
131 "CONNECTING",
132 "CONNECTED",
133 "PAUSING",
134 "PAUSE_SENT",
135 "PAUSED",
136 "RESUMING",
137 "PAUSE_TIMEOUT",
138 "RESUME_TIMEOUT"
139 };
140
141
142 static void
143 release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header);
144
msg_type_str(unsigned int msg_type)145 static const char *msg_type_str(unsigned int msg_type)
146 {
147 switch (msg_type) {
148 case VCHIQ_MSG_PADDING: return "PADDING";
149 case VCHIQ_MSG_CONNECT: return "CONNECT";
150 case VCHIQ_MSG_OPEN: return "OPEN";
151 case VCHIQ_MSG_OPENACK: return "OPENACK";
152 case VCHIQ_MSG_CLOSE: return "CLOSE";
153 case VCHIQ_MSG_DATA: return "DATA";
154 case VCHIQ_MSG_BULK_RX: return "BULK_RX";
155 case VCHIQ_MSG_BULK_TX: return "BULK_TX";
156 case VCHIQ_MSG_BULK_RX_DONE: return "BULK_RX_DONE";
157 case VCHIQ_MSG_BULK_TX_DONE: return "BULK_TX_DONE";
158 case VCHIQ_MSG_PAUSE: return "PAUSE";
159 case VCHIQ_MSG_RESUME: return "RESUME";
160 case VCHIQ_MSG_REMOTE_USE: return "REMOTE_USE";
161 case VCHIQ_MSG_REMOTE_RELEASE: return "REMOTE_RELEASE";
162 case VCHIQ_MSG_REMOTE_USE_ACTIVE: return "REMOTE_USE_ACTIVE";
163 }
164 return "???";
165 }
166
167 static inline void
vchiq_set_service_state(VCHIQ_SERVICE_T * service,int newstate)168 vchiq_set_service_state(VCHIQ_SERVICE_T *service, int newstate)
169 {
170 vchiq_log_info(vchiq_core_log_level, "%d: srv:%d %s->%s",
171 service->state->id, service->localport,
172 srvstate_names[service->srvstate],
173 srvstate_names[newstate]);
174 service->srvstate = newstate;
175 }
176
177 VCHIQ_SERVICE_T *
find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle)178 find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle)
179 {
180 VCHIQ_SERVICE_T *service;
181
182 spin_lock(&service_spinlock);
183 service = handle_to_service(handle);
184 if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) &&
185 (service->handle == handle)) {
186 BUG_ON(service->ref_count == 0);
187 service->ref_count++;
188 } else
189 service = NULL;
190 spin_unlock(&service_spinlock);
191
192 if (!service)
193 vchiq_log_info(vchiq_core_log_level,
194 "Invalid service handle 0x%x", handle);
195
196 return service;
197 }
198
199 VCHIQ_SERVICE_T *
find_service_by_port(VCHIQ_STATE_T * state,int localport)200 find_service_by_port(VCHIQ_STATE_T *state, int localport)
201 {
202 VCHIQ_SERVICE_T *service = NULL;
203 if ((unsigned int)localport <= VCHIQ_PORT_MAX) {
204 spin_lock(&service_spinlock);
205 service = state->services[localport];
206 if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE)) {
207 BUG_ON(service->ref_count == 0);
208 service->ref_count++;
209 } else
210 service = NULL;
211 spin_unlock(&service_spinlock);
212 }
213
214 if (!service)
215 vchiq_log_info(vchiq_core_log_level,
216 "Invalid port %d", localport);
217
218 return service;
219 }
220
221 VCHIQ_SERVICE_T *
find_service_for_instance(VCHIQ_INSTANCE_T instance,VCHIQ_SERVICE_HANDLE_T handle)222 find_service_for_instance(VCHIQ_INSTANCE_T instance,
223 VCHIQ_SERVICE_HANDLE_T handle) {
224 VCHIQ_SERVICE_T *service;
225
226 spin_lock(&service_spinlock);
227 service = handle_to_service(handle);
228 if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) &&
229 (service->handle == handle) &&
230 (service->instance == instance)) {
231 BUG_ON(service->ref_count == 0);
232 service->ref_count++;
233 } else
234 service = NULL;
235 spin_unlock(&service_spinlock);
236
237 if (!service)
238 vchiq_log_info(vchiq_core_log_level,
239 "Invalid service handle 0x%x", handle);
240
241 return service;
242 }
243
244 VCHIQ_SERVICE_T *
find_closed_service_for_instance(VCHIQ_INSTANCE_T instance,VCHIQ_SERVICE_HANDLE_T handle)245 find_closed_service_for_instance(VCHIQ_INSTANCE_T instance,
246 VCHIQ_SERVICE_HANDLE_T handle) {
247 VCHIQ_SERVICE_T *service;
248
249 spin_lock(&service_spinlock);
250 service = handle_to_service(handle);
251 if (service &&
252 ((service->srvstate == VCHIQ_SRVSTATE_FREE) ||
253 (service->srvstate == VCHIQ_SRVSTATE_CLOSED)) &&
254 (service->handle == handle) &&
255 (service->instance == instance)) {
256 BUG_ON(service->ref_count == 0);
257 service->ref_count++;
258 } else
259 service = NULL;
260 spin_unlock(&service_spinlock);
261
262 if (!service)
263 vchiq_log_info(vchiq_core_log_level,
264 "Invalid service handle 0x%x", handle);
265
266 return service;
267 }
268
269 VCHIQ_SERVICE_T *
next_service_by_instance(VCHIQ_STATE_T * state,VCHIQ_INSTANCE_T instance,int * pidx)270 next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance,
271 int *pidx)
272 {
273 VCHIQ_SERVICE_T *service = NULL;
274 int idx = *pidx;
275
276 spin_lock(&service_spinlock);
277 while (idx < state->unused_service) {
278 VCHIQ_SERVICE_T *srv = state->services[idx++];
279 if (srv && (srv->srvstate != VCHIQ_SRVSTATE_FREE) &&
280 (srv->instance == instance)) {
281 service = srv;
282 BUG_ON(service->ref_count == 0);
283 service->ref_count++;
284 break;
285 }
286 }
287 spin_unlock(&service_spinlock);
288
289 *pidx = idx;
290
291 return service;
292 }
293
294 void
lock_service(VCHIQ_SERVICE_T * service)295 lock_service(VCHIQ_SERVICE_T *service)
296 {
297 spin_lock(&service_spinlock);
298 BUG_ON(!service || (service->ref_count == 0));
299 if (service)
300 service->ref_count++;
301 spin_unlock(&service_spinlock);
302 }
303
304 void
unlock_service(VCHIQ_SERVICE_T * service)305 unlock_service(VCHIQ_SERVICE_T *service)
306 {
307 VCHIQ_STATE_T *state = service->state;
308 spin_lock(&service_spinlock);
309 BUG_ON(!service || (service->ref_count == 0));
310 if (service && service->ref_count) {
311 service->ref_count--;
312 if (!service->ref_count) {
313 BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE);
314 state->services[service->localport] = NULL;
315
316 _sema_destroy(&service->remove_event);
317 _sema_destroy(&service->bulk_remove_event);
318 lmutex_destroy(&service->bulk_mutex);
319 } else
320 service = NULL;
321 }
322 spin_unlock(&service_spinlock);
323
324 if (service && service->userdata_term)
325 service->userdata_term(service->base.userdata);
326
327 kfree(service);
328 }
329
330 int
vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle)331 vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle)
332 {
333 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
334 int id;
335
336 id = service ? service->client_id : 0;
337 if (service)
338 unlock_service(service);
339
340 return id;
341 }
342
343 void *
vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle)344 vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle)
345 {
346 VCHIQ_SERVICE_T *service = handle_to_service(handle);
347
348 return service ? service->base.userdata : NULL;
349 }
350
351 int
vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle)352 vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle)
353 {
354 VCHIQ_SERVICE_T *service = handle_to_service(handle);
355
356 return service ? service->base.fourcc : 0;
357 }
358
359 static void
mark_service_closing_internal(VCHIQ_SERVICE_T * service,int sh_thread)360 mark_service_closing_internal(VCHIQ_SERVICE_T *service, int sh_thread)
361 {
362 VCHIQ_STATE_T *state = service->state;
363 VCHIQ_SERVICE_QUOTA_T *service_quota;
364
365 service->closing = 1;
366
367 /* Synchronise with other threads. */
368 lmutex_lock(&state->recycle_mutex);
369 lmutex_unlock(&state->recycle_mutex);
370 if (!sh_thread || (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT)) {
371 /* If we're pausing then the slot_mutex is held until resume
372 * by the slot handler. Therefore don't try to acquire this
373 * mutex if we're the slot handler and in the pause sent state.
374 * We don't need to in this case anyway. */
375 lmutex_lock(&state->slot_mutex);
376 lmutex_unlock(&state->slot_mutex);
377 }
378
379 /* Unblock any sending thread. */
380 service_quota = &state->service_quotas[service->localport];
381 up(&service_quota->quota_event);
382 }
383
384 static void
mark_service_closing(VCHIQ_SERVICE_T * service)385 mark_service_closing(VCHIQ_SERVICE_T *service)
386 {
387 mark_service_closing_internal(service, 0);
388 }
389
390 static inline VCHIQ_STATUS_T
make_service_callback(VCHIQ_SERVICE_T * service,VCHIQ_REASON_T reason,VCHIQ_HEADER_T * header,void * bulk_userdata)391 make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason,
392 VCHIQ_HEADER_T *header, void *bulk_userdata)
393 {
394 VCHIQ_STATUS_T status;
395 vchiq_log_trace(vchiq_core_log_level, "%d: callback:%d (%s, %p, %p)",
396 service->state->id, service->localport, reason_names[reason],
397 header, bulk_userdata);
398 status = service->base.callback(reason, header, service->handle,
399 bulk_userdata);
400 if (status == VCHIQ_ERROR) {
401 vchiq_log_warning(vchiq_core_log_level,
402 "%d: ignoring ERROR from callback to service %x",
403 service->state->id, service->handle);
404 status = VCHIQ_SUCCESS;
405 }
406 return status;
407 }
408
409 inline void
vchiq_set_conn_state(VCHIQ_STATE_T * state,VCHIQ_CONNSTATE_T newstate)410 vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate)
411 {
412 VCHIQ_CONNSTATE_T oldstate = state->conn_state;
413 vchiq_log_info(vchiq_core_log_level, "%d: %s->%s", state->id,
414 conn_state_names[oldstate],
415 conn_state_names[newstate]);
416 state->conn_state = newstate;
417 vchiq_platform_conn_state_changed(state, oldstate, newstate);
418 }
419
420 #define ACTUAL_EVENT_SEM_ADDR(ref,offset)\
421 ((struct semaphore *)(((size_t) ref) + ((size_t) offset)))
422 static inline void
remote_event_create(VCHIQ_STATE_T * ref,REMOTE_EVENT_T * event)423 remote_event_create(VCHIQ_STATE_T *ref, REMOTE_EVENT_T *event)
424 {
425 event->armed = 0;
426 /* Don't clear the 'fired' flag because it may already have been set
427 ** by the other side. */
428 _sema_init(ACTUAL_EVENT_SEM_ADDR(ref,event->event), 0);
429 }
430
431 __unused static inline void
remote_event_destroy(REMOTE_EVENT_T * event)432 remote_event_destroy(REMOTE_EVENT_T *event)
433 {
434 (void)event;
435 }
436
437 static inline int
remote_event_wait(VCHIQ_STATE_T * ref,REMOTE_EVENT_T * event)438 remote_event_wait(VCHIQ_STATE_T *ref, REMOTE_EVENT_T *event)
439 {
440 if (!event->fired) {
441 event->armed = 1;
442 #if defined(__aarch64__)
443 dsb(sy);
444 #else
445 dsb();
446 #endif
447
448 if (!event->fired) {
449 if (down_interruptible(ACTUAL_EVENT_SEM_ADDR(ref,event->event)) != 0) {
450 event->armed = 0;
451 return 0;
452 }
453 }
454 event->armed = 0;
455 wmb();
456 }
457
458 event->fired = 0;
459 return 1;
460 }
461
462 static inline void
remote_event_signal_local(VCHIQ_STATE_T * ref,REMOTE_EVENT_T * event)463 remote_event_signal_local(VCHIQ_STATE_T *ref, REMOTE_EVENT_T *event)
464 {
465 /*
466 * Mirror
467 * https://github.com/raspberrypi/linux/commit/a50c4c9a65779ca835746b5fd79d3d5278afbdbe
468 * for extra safety
469 */
470 event->fired = 1;
471 event->armed = 0;
472 up(ACTUAL_EVENT_SEM_ADDR(ref,event->event));
473 }
474
475 static inline void
remote_event_poll(VCHIQ_STATE_T * ref,REMOTE_EVENT_T * event)476 remote_event_poll(VCHIQ_STATE_T *ref, REMOTE_EVENT_T *event)
477 {
478 if (event->fired && event->armed)
479 remote_event_signal_local(ref,event);
480 }
481
482 void
remote_event_pollall(VCHIQ_STATE_T * state)483 remote_event_pollall(VCHIQ_STATE_T *state)
484 {
485 remote_event_poll(state , &state->local->sync_trigger);
486 remote_event_poll(state , &state->local->sync_release);
487 remote_event_poll(state , &state->local->trigger);
488 remote_event_poll(state , &state->local->recycle);
489 }
490
491 /* Round up message sizes so that any space at the end of a slot is always big
492 ** enough for a header. This relies on header size being a power of two, which
493 ** has been verified earlier by a static assertion. */
494
495 static inline unsigned int
calc_stride(unsigned int size)496 calc_stride(unsigned int size)
497 {
498 /* Allow room for the header */
499 size += sizeof(VCHIQ_HEADER_T);
500
501 /* Round up */
502 return (size + sizeof(VCHIQ_HEADER_T) - 1) & ~(sizeof(VCHIQ_HEADER_T)
503 - 1);
504 }
505
506 /* Called by the slot handler thread */
507 static VCHIQ_SERVICE_T *
get_listening_service(VCHIQ_STATE_T * state,int fourcc)508 get_listening_service(VCHIQ_STATE_T *state, int fourcc)
509 {
510 int i;
511
512 WARN_ON(fourcc == VCHIQ_FOURCC_INVALID);
513
514 for (i = 0; i < state->unused_service; i++) {
515 VCHIQ_SERVICE_T *service = state->services[i];
516 if (service &&
517 (service->public_fourcc == fourcc) &&
518 ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) ||
519 ((service->srvstate == VCHIQ_SRVSTATE_OPEN) &&
520 (service->remoteport == VCHIQ_PORT_FREE)))) {
521 lock_service(service);
522 return service;
523 }
524 }
525
526 return NULL;
527 }
528
529 /* Called by the slot handler thread */
530 static VCHIQ_SERVICE_T *
get_connected_service(VCHIQ_STATE_T * state,unsigned int port)531 get_connected_service(VCHIQ_STATE_T *state, unsigned int port)
532 {
533 int i;
534 for (i = 0; i < state->unused_service; i++) {
535 VCHIQ_SERVICE_T *service = state->services[i];
536 if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN)
537 && (service->remoteport == port)) {
538 lock_service(service);
539 return service;
540 }
541 }
542 return NULL;
543 }
544
545 inline void
request_poll(VCHIQ_STATE_T * state,VCHIQ_SERVICE_T * service,int poll_type)546 request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type)
547 {
548 uint32_t value;
549
550 if (service) {
551 do {
552 value = atomic_read(&service->poll_flags);
553 } while (atomic_cmpxchg(&service->poll_flags, value,
554 value | (1 << poll_type)) != value);
555
556 do {
557 value = atomic_read(&state->poll_services[
558 service->localport>>5]);
559 } while (atomic_cmpxchg(
560 &state->poll_services[service->localport>>5],
561 value, value | (1 << (service->localport & 0x1f)))
562 != value);
563 }
564
565 state->poll_needed = 1;
566 wmb();
567
568 /* ... and ensure the slot handler runs. */
569 remote_event_signal_local(state, &state->local->trigger);
570 }
571
572 /* Called from queue_message, by the slot handler and application threads,
573 ** with slot_mutex held */
574 static VCHIQ_HEADER_T *
reserve_space(VCHIQ_STATE_T * state,int space,int is_blocking)575 reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking)
576 {
577 VCHIQ_SHARED_STATE_T *local = state->local;
578 int tx_pos = state->local_tx_pos;
579 int slot_space = VCHIQ_SLOT_SIZE - (tx_pos & VCHIQ_SLOT_MASK);
580
581 if (space > slot_space) {
582 VCHIQ_HEADER_T *header;
583 /* Fill the remaining space with padding */
584 WARN_ON(state->tx_data == NULL);
585 header = (VCHIQ_HEADER_T *)
586 (state->tx_data + (tx_pos & VCHIQ_SLOT_MASK));
587 header->msgid = VCHIQ_MSGID_PADDING;
588 header->size = slot_space - sizeof(VCHIQ_HEADER_T);
589
590 tx_pos += slot_space;
591 }
592
593 /* If necessary, get the next slot. */
594 if ((tx_pos & VCHIQ_SLOT_MASK) == 0) {
595 int slot_index;
596
597 /* If there is no free slot... */
598
599 if (down_trylock(&state->slot_available_event) != 0) {
600 /* ...wait for one. */
601
602 VCHIQ_STATS_INC(state, slot_stalls);
603
604 /* But first, flush through the last slot. */
605 state->local_tx_pos = tx_pos;
606 local->tx_pos = tx_pos;
607 remote_event_signal(&state->remote->trigger);
608
609 if (!is_blocking ||
610 (down_interruptible(
611 &state->slot_available_event) != 0))
612 return NULL; /* No space available */
613 }
614
615 BUG_ON(tx_pos ==
616 (state->slot_queue_available * VCHIQ_SLOT_SIZE));
617
618 slot_index = local->slot_queue[
619 SLOT_QUEUE_INDEX_FROM_POS(tx_pos) &
620 VCHIQ_SLOT_QUEUE_MASK];
621 state->tx_data =
622 (char *)SLOT_DATA_FROM_INDEX(state, slot_index);
623 }
624
625 state->local_tx_pos = tx_pos + space;
626
627 return (VCHIQ_HEADER_T *)(state->tx_data + (tx_pos & VCHIQ_SLOT_MASK));
628 }
629
630 /* Called by the recycle thread. */
631 static void
process_free_queue(VCHIQ_STATE_T * state)632 process_free_queue(VCHIQ_STATE_T *state)
633 {
634 VCHIQ_SHARED_STATE_T *local = state->local;
635 VCHI_BITSET_T service_found[VCHI_BITSET_SIZE(VCHIQ_MAX_SERVICES)];
636 int slot_queue_available;
637
638 /* Find slots which have been freed by the other side, and return them
639 ** to the available queue. */
640 slot_queue_available = state->slot_queue_available;
641
642 /* Use a memory barrier to ensure that any state that may have been
643 ** modified by another thread is not masked by stale prefetched
644 ** values. */
645 mb();
646
647 while (slot_queue_available != local->slot_queue_recycle) {
648 unsigned int pos;
649 int slot_index = local->slot_queue[slot_queue_available++ &
650 VCHIQ_SLOT_QUEUE_MASK];
651 char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index);
652 int data_found = 0;
653
654 rmb();
655
656 vchiq_log_trace(vchiq_core_log_level, "%d: pfq %d=%p %x %x",
657 state->id, slot_index, data,
658 local->slot_queue_recycle, slot_queue_available);
659
660 /* Initialise the bitmask for services which have used this
661 ** slot */
662 VCHI_BITSET_ZERO(service_found);
663
664 pos = 0;
665
666 while (pos < VCHIQ_SLOT_SIZE) {
667 VCHIQ_HEADER_T *header =
668 (VCHIQ_HEADER_T *)(data + pos);
669 int msgid = header->msgid;
670 if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA) {
671 int port = VCHIQ_MSG_SRCPORT(msgid);
672 VCHIQ_SERVICE_QUOTA_T *service_quota =
673 &state->service_quotas[port];
674 int count;
675 spin_lock("a_spinlock);
676 count = service_quota->message_use_count;
677 if (count > 0)
678 service_quota->message_use_count =
679 count - 1;
680 spin_unlock("a_spinlock);
681
682 if (count == service_quota->message_quota)
683 /* Signal the service that it
684 ** has dropped below its quota
685 */
686 up(&service_quota->quota_event);
687 else if (count == 0) {
688 vchiq_log_error(vchiq_core_log_level,
689 "service %d "
690 "message_use_count=%d "
691 "(header %p, msgid %x, "
692 "header->msgid %x, "
693 "header->size %x)",
694 port,
695 service_quota->
696 message_use_count,
697 header, msgid,
698 header->msgid,
699 header->size);
700 WARN(1, "invalid message use count\n");
701 }
702 if (!VCHI_BITSET_IS_SET(service_found, port)) {
703 /* Set the found bit for this service */
704 VCHI_BITSET_SET(service_found, port);
705
706 spin_lock("a_spinlock);
707 count = service_quota->slot_use_count;
708 if (count > 0)
709 service_quota->slot_use_count =
710 count - 1;
711 spin_unlock("a_spinlock);
712
713 if (count > 0) {
714 /* Signal the service in case
715 ** it has dropped below its
716 ** quota */
717 up(&service_quota->quota_event);
718 vchiq_log_trace(
719 vchiq_core_log_level,
720 "%d: pfq:%d %x@%p - "
721 "slot_use->%d",
722 state->id, port,
723 header->size,
724 header,
725 count - 1);
726 } else {
727 vchiq_log_error(
728 vchiq_core_log_level,
729 "service %d "
730 "slot_use_count"
731 "=%d (header %p"
732 ", msgid %x, "
733 "header->msgid"
734 " %x, header->"
735 "size %x)",
736 port, count,
737 header,
738 msgid,
739 header->msgid,
740 header->size);
741 WARN(1, "bad slot use count\n");
742 }
743 }
744
745 data_found = 1;
746 }
747
748 pos += calc_stride(header->size);
749 if (pos > VCHIQ_SLOT_SIZE) {
750 vchiq_log_error(vchiq_core_log_level,
751 "pfq - pos %x: header %p, msgid %x, "
752 "header->msgid %x, header->size %x",
753 pos, header, msgid,
754 header->msgid, header->size);
755 WARN(1, "invalid slot position\n");
756 }
757 }
758
759 if (data_found) {
760 int count;
761 spin_lock("a_spinlock);
762 count = state->data_use_count;
763 if (count > 0)
764 state->data_use_count =
765 count - 1;
766 spin_unlock("a_spinlock);
767 if (count == state->data_quota)
768 up(&state->data_quota_event);
769 }
770
771 mb();
772
773 state->slot_queue_available = slot_queue_available;
774 up(&state->slot_available_event);
775 }
776 }
777
778 /* Called by the slot handler and application threads */
779 static VCHIQ_STATUS_T
queue_message(VCHIQ_STATE_T * state,VCHIQ_SERVICE_T * service,int msgid,const VCHIQ_ELEMENT_T * elements,int count,int size,int flags)780 queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
781 int msgid, const VCHIQ_ELEMENT_T *elements,
782 int count, int size, int flags)
783 {
784 VCHIQ_SHARED_STATE_T *local;
785 VCHIQ_SERVICE_QUOTA_T *service_quota = NULL;
786 VCHIQ_HEADER_T *header;
787 int type = VCHIQ_MSG_TYPE(msgid);
788
789 unsigned int stride;
790
791 local = state->local;
792
793 stride = calc_stride(size);
794
795 WARN_ON(!(stride <= VCHIQ_SLOT_SIZE));
796
797 if (!(flags & QMFLAGS_NO_MUTEX_LOCK) &&
798 (lmutex_lock_interruptible(&state->slot_mutex) != 0))
799 return VCHIQ_RETRY;
800
801 if (type == VCHIQ_MSG_DATA) {
802 int tx_end_index;
803
804 BUG_ON(!service);
805 BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK |
806 QMFLAGS_NO_MUTEX_UNLOCK)) != 0);
807
808 if (service->closing) {
809 /* The service has been closed */
810 lmutex_unlock(&state->slot_mutex);
811 return VCHIQ_ERROR;
812 }
813
814 service_quota = &state->service_quotas[service->localport];
815
816 spin_lock("a_spinlock);
817
818 /* Ensure this service doesn't use more than its quota of
819 ** messages or slots */
820 tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(
821 state->local_tx_pos + stride - 1);
822
823 /* Ensure data messages don't use more than their quota of
824 ** slots */
825 while ((tx_end_index != state->previous_data_index) &&
826 (state->data_use_count == state->data_quota)) {
827 VCHIQ_STATS_INC(state, data_stalls);
828 spin_unlock("a_spinlock);
829 lmutex_unlock(&state->slot_mutex);
830
831 if (down_interruptible(&state->data_quota_event)
832 != 0)
833 return VCHIQ_RETRY;
834
835 lmutex_lock(&state->slot_mutex);
836 spin_lock("a_spinlock);
837 tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(
838 state->local_tx_pos + stride - 1);
839 if ((tx_end_index == state->previous_data_index) ||
840 (state->data_use_count < state->data_quota)) {
841 /* Pass the signal on to other waiters */
842 up(&state->data_quota_event);
843 break;
844 }
845 }
846
847 while ((service_quota->message_use_count ==
848 service_quota->message_quota) ||
849 ((tx_end_index != service_quota->previous_tx_index) &&
850 (service_quota->slot_use_count ==
851 service_quota->slot_quota))) {
852 spin_unlock("a_spinlock);
853 vchiq_log_trace(vchiq_core_log_level,
854 "%d: qm:%d %s,%x - quota stall "
855 "(msg %d, slot %d)",
856 state->id, service->localport,
857 msg_type_str(type), size,
858 service_quota->message_use_count,
859 service_quota->slot_use_count);
860 VCHIQ_SERVICE_STATS_INC(service, quota_stalls);
861 lmutex_unlock(&state->slot_mutex);
862 if (down_interruptible(&service_quota->quota_event)
863 != 0)
864 return VCHIQ_RETRY;
865 if (service->closing)
866 return VCHIQ_ERROR;
867 if (lmutex_lock_interruptible(&state->slot_mutex) != 0)
868 return VCHIQ_RETRY;
869 if (service->srvstate != VCHIQ_SRVSTATE_OPEN) {
870 /* The service has been closed */
871 lmutex_unlock(&state->slot_mutex);
872 return VCHIQ_ERROR;
873 }
874 spin_lock("a_spinlock);
875 tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(
876 state->local_tx_pos + stride - 1);
877 }
878
879 spin_unlock("a_spinlock);
880 }
881
882 header = reserve_space(state, stride, flags & QMFLAGS_IS_BLOCKING);
883
884 if (!header) {
885 if (service)
886 VCHIQ_SERVICE_STATS_INC(service, slot_stalls);
887 /* In the event of a failure, return the mutex to the
888 state it was in */
889 if (!(flags & QMFLAGS_NO_MUTEX_LOCK))
890 lmutex_unlock(&state->slot_mutex);
891
892 return VCHIQ_RETRY;
893 }
894
895 if (type == VCHIQ_MSG_DATA) {
896 int i, pos;
897 int tx_end_index;
898 int slot_use_count;
899
900 vchiq_log_info(vchiq_core_log_level,
901 "%d: qm %s@%p,%x (%d->%d)",
902 state->id,
903 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
904 header, size,
905 VCHIQ_MSG_SRCPORT(msgid),
906 VCHIQ_MSG_DSTPORT(msgid));
907
908 BUG_ON(!service);
909 BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK |
910 QMFLAGS_NO_MUTEX_UNLOCK)) != 0);
911 for (i = 0, pos = 0; i < (unsigned int)count;
912 pos += elements[i++].size)
913 if (elements[i].size) {
914 if (vchiq_copy_from_user
915 (header->data + pos, elements[i].data,
916 (size_t) elements[i].size) !=
917 VCHIQ_SUCCESS) {
918 lmutex_unlock(&state->slot_mutex);
919 VCHIQ_SERVICE_STATS_INC(service,
920 error_count);
921 return VCHIQ_ERROR;
922 }
923 }
924
925 if (SRVTRACE_ENABLED(service,
926 VCHIQ_LOG_INFO))
927 vchiq_log_dump_mem("Sent", 0,
928 header->data,
929 min(16, pos));
930
931 spin_lock("a_spinlock);
932 service_quota->message_use_count++;
933
934 tx_end_index =
935 SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos - 1);
936
937 /* If this transmission can't fit in the last slot used by any
938 ** service, the data_use_count must be increased. */
939 if (tx_end_index != state->previous_data_index) {
940 state->previous_data_index = tx_end_index;
941 state->data_use_count++;
942 }
943
944 /* If this isn't the same slot last used by this service,
945 ** the service's slot_use_count must be increased. */
946 if (tx_end_index != service_quota->previous_tx_index) {
947 service_quota->previous_tx_index = tx_end_index;
948 slot_use_count = ++service_quota->slot_use_count;
949 } else {
950 slot_use_count = 0;
951 }
952
953 spin_unlock("a_spinlock);
954
955 if (slot_use_count)
956 vchiq_log_trace(vchiq_core_log_level,
957 "%d: qm:%d %s,%x - slot_use->%d (hdr %p)",
958 state->id, service->localport,
959 msg_type_str(VCHIQ_MSG_TYPE(msgid)), size,
960 slot_use_count, header);
961
962 VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count);
963 VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size);
964 } else {
965 vchiq_log_info(vchiq_core_log_level,
966 "%d: qm %s@%p,%x (%d->%d)", state->id,
967 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
968 header, size,
969 VCHIQ_MSG_SRCPORT(msgid),
970 VCHIQ_MSG_DSTPORT(msgid));
971 if (size != 0) {
972 WARN_ON(!((count == 1) && (size == elements[0].size)));
973 memcpy(header->data, elements[0].data,
974 elements[0].size);
975 }
976 VCHIQ_STATS_INC(state, ctrl_tx_count);
977 }
978
979 header->msgid = msgid;
980 header->size = size;
981
982 {
983 int svc_fourcc;
984
985 svc_fourcc = service
986 ? service->base.fourcc
987 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
988
989 vchiq_log_info(SRVTRACE_LEVEL(service),
990 "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d",
991 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
992 VCHIQ_MSG_TYPE(msgid),
993 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
994 VCHIQ_MSG_SRCPORT(msgid),
995 VCHIQ_MSG_DSTPORT(msgid),
996 size);
997 }
998
999 /* Make sure the new header is visible to the peer. */
1000 wmb();
1001
1002 /* Make the new tx_pos visible to the peer. */
1003 local->tx_pos = state->local_tx_pos;
1004 wmb();
1005
1006 if (service && (type == VCHIQ_MSG_CLOSE))
1007 vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT);
1008
1009 if (!(flags & QMFLAGS_NO_MUTEX_UNLOCK))
1010 lmutex_unlock(&state->slot_mutex);
1011
1012 remote_event_signal(&state->remote->trigger);
1013
1014 return VCHIQ_SUCCESS;
1015 }
1016
1017 /* Called by the slot handler and application threads */
1018 static VCHIQ_STATUS_T
queue_message_sync(VCHIQ_STATE_T * state,VCHIQ_SERVICE_T * service,int msgid,const VCHIQ_ELEMENT_T * elements,int count,int size,int is_blocking)1019 queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
1020 int msgid, const VCHIQ_ELEMENT_T *elements,
1021 int count, int size, int is_blocking)
1022 {
1023 VCHIQ_SHARED_STATE_T *local;
1024 VCHIQ_HEADER_T *header;
1025
1026 local = state->local;
1027
1028 if ((VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_RESUME) &&
1029 (lmutex_lock_interruptible(&state->sync_mutex) != 0))
1030 return VCHIQ_RETRY;
1031
1032 remote_event_wait(state, &local->sync_release);
1033
1034 rmb();
1035
1036 header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state,
1037 local->slot_sync);
1038
1039 {
1040 int oldmsgid = header->msgid;
1041 if (oldmsgid != VCHIQ_MSGID_PADDING)
1042 vchiq_log_error(vchiq_core_log_level,
1043 "%d: qms - msgid %x, not PADDING",
1044 state->id, oldmsgid);
1045 }
1046
1047 if (service) {
1048 int i, pos;
1049
1050 vchiq_log_info(vchiq_sync_log_level,
1051 "%d: qms %s@%p,%x (%d->%d)", state->id,
1052 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
1053 header, size,
1054 VCHIQ_MSG_SRCPORT(msgid),
1055 VCHIQ_MSG_DSTPORT(msgid));
1056
1057 for (i = 0, pos = 0; i < (unsigned int)count;
1058 pos += elements[i++].size)
1059 if (elements[i].size) {
1060 if (vchiq_copy_from_user
1061 (header->data + pos, elements[i].data,
1062 (size_t) elements[i].size) !=
1063 VCHIQ_SUCCESS) {
1064 lmutex_unlock(&state->sync_mutex);
1065 VCHIQ_SERVICE_STATS_INC(service,
1066 error_count);
1067 return VCHIQ_ERROR;
1068 }
1069 }
1070
1071 if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE)
1072 vchiq_log_dump_mem("Sent Sync",
1073 0, header->data,
1074 min(16, pos));
1075
1076 VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count);
1077 VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size);
1078 } else {
1079 vchiq_log_info(vchiq_sync_log_level,
1080 "%d: qms %s@%p,%x (%d->%d)", state->id,
1081 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
1082 header, size,
1083 VCHIQ_MSG_SRCPORT(msgid),
1084 VCHIQ_MSG_DSTPORT(msgid));
1085 if (size != 0) {
1086 WARN_ON(!((count == 1) && (size == elements[0].size)));
1087 memcpy(header->data, elements[0].data,
1088 elements[0].size);
1089 }
1090 VCHIQ_STATS_INC(state, ctrl_tx_count);
1091 }
1092
1093 header->size = size;
1094 header->msgid = msgid;
1095
1096 if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) {
1097 int svc_fourcc;
1098
1099 svc_fourcc = service
1100 ? service->base.fourcc
1101 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
1102
1103 vchiq_log_trace(vchiq_sync_log_level,
1104 "Sent Sync Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d",
1105 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
1106 VCHIQ_MSG_TYPE(msgid),
1107 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
1108 VCHIQ_MSG_SRCPORT(msgid),
1109 VCHIQ_MSG_DSTPORT(msgid),
1110 size);
1111 }
1112
1113 remote_event_signal(&state->remote->sync_trigger);
1114
1115 if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE)
1116 lmutex_unlock(&state->sync_mutex);
1117
1118 return VCHIQ_SUCCESS;
1119 }
1120
1121 static inline void
claim_slot(VCHIQ_SLOT_INFO_T * slot)1122 claim_slot(VCHIQ_SLOT_INFO_T *slot)
1123 {
1124 slot->use_count++;
1125 }
1126
1127 static void
release_slot(VCHIQ_STATE_T * state,VCHIQ_SLOT_INFO_T * slot_info,VCHIQ_HEADER_T * header,VCHIQ_SERVICE_T * service)1128 release_slot(VCHIQ_STATE_T *state, VCHIQ_SLOT_INFO_T *slot_info,
1129 VCHIQ_HEADER_T *header, VCHIQ_SERVICE_T *service)
1130 {
1131 int release_count;
1132
1133 lmutex_lock(&state->recycle_mutex);
1134
1135 if (header) {
1136 int msgid = header->msgid;
1137 if (((msgid & VCHIQ_MSGID_CLAIMED) == 0) ||
1138 (service && service->closing)) {
1139 lmutex_unlock(&state->recycle_mutex);
1140 return;
1141 }
1142
1143 /* Rewrite the message header to prevent a double
1144 ** release */
1145 header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED;
1146 }
1147
1148 release_count = slot_info->release_count;
1149 slot_info->release_count = ++release_count;
1150
1151 if (release_count == slot_info->use_count) {
1152 int slot_queue_recycle;
1153 /* Add to the freed queue */
1154
1155 /* A read barrier is necessary here to prevent speculative
1156 ** fetches of remote->slot_queue_recycle from overtaking the
1157 ** mutex. */
1158 rmb();
1159
1160 slot_queue_recycle = state->remote->slot_queue_recycle;
1161 state->remote->slot_queue[slot_queue_recycle &
1162 VCHIQ_SLOT_QUEUE_MASK] =
1163 SLOT_INDEX_FROM_INFO(state, slot_info);
1164 state->remote->slot_queue_recycle = slot_queue_recycle + 1;
1165 vchiq_log_info(vchiq_core_log_level,
1166 "%d: release_slot %d - recycle->%x",
1167 state->id, SLOT_INDEX_FROM_INFO(state, slot_info),
1168 state->remote->slot_queue_recycle);
1169
1170 /* A write barrier is necessary, but remote_event_signal
1171 ** contains one. */
1172 remote_event_signal(&state->remote->recycle);
1173 }
1174
1175 lmutex_unlock(&state->recycle_mutex);
1176 }
1177
1178 /* Called by the slot handler - don't hold the bulk mutex */
1179 static VCHIQ_STATUS_T
notify_bulks(VCHIQ_SERVICE_T * service,VCHIQ_BULK_QUEUE_T * queue,int retry_poll)1180 notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue,
1181 int retry_poll)
1182 {
1183 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
1184
1185 vchiq_log_trace(vchiq_core_log_level,
1186 "%d: nb:%d %cx - p=%x rn=%x r=%x",
1187 service->state->id, service->localport,
1188 (queue == &service->bulk_tx) ? 't' : 'r',
1189 queue->process, queue->remote_notify, queue->remove);
1190
1191 if (service->state->is_master) {
1192 while (queue->remote_notify != queue->process) {
1193 VCHIQ_BULK_T *bulk =
1194 &queue->bulks[BULK_INDEX(queue->remote_notify)];
1195 int msgtype = (bulk->dir == VCHIQ_BULK_TRANSMIT) ?
1196 VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE;
1197 int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport,
1198 service->remoteport);
1199 VCHIQ_ELEMENT_T element = { &bulk->actual, 4 };
1200 /* Only reply to non-dummy bulk requests */
1201 if (bulk->remote_data) {
1202 status = queue_message(service->state, NULL,
1203 msgid, &element, 1, 4, 0);
1204 if (status != VCHIQ_SUCCESS)
1205 break;
1206 }
1207 queue->remote_notify++;
1208 }
1209 } else {
1210 queue->remote_notify = queue->process;
1211 }
1212
1213 if (status == VCHIQ_SUCCESS) {
1214 while (queue->remove != queue->remote_notify) {
1215 VCHIQ_BULK_T *bulk =
1216 &queue->bulks[BULK_INDEX(queue->remove)];
1217
1218 /* Only generate callbacks for non-dummy bulk
1219 ** requests, and non-terminated services */
1220 if (bulk->data && service->instance) {
1221 if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) {
1222 if (bulk->dir == VCHIQ_BULK_TRANSMIT) {
1223 VCHIQ_SERVICE_STATS_INC(service,
1224 bulk_tx_count);
1225 VCHIQ_SERVICE_STATS_ADD(service,
1226 bulk_tx_bytes,
1227 bulk->actual);
1228 } else {
1229 VCHIQ_SERVICE_STATS_INC(service,
1230 bulk_rx_count);
1231 VCHIQ_SERVICE_STATS_ADD(service,
1232 bulk_rx_bytes,
1233 bulk->actual);
1234 }
1235 } else {
1236 VCHIQ_SERVICE_STATS_INC(service,
1237 bulk_aborted_count);
1238 }
1239 if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) {
1240 struct bulk_waiter *waiter;
1241 spin_lock(&bulk_waiter_spinlock);
1242 waiter = bulk->userdata;
1243 if (waiter) {
1244 waiter->actual = bulk->actual;
1245 up(&waiter->event);
1246 }
1247 spin_unlock(&bulk_waiter_spinlock);
1248 } else if (bulk->mode ==
1249 VCHIQ_BULK_MODE_CALLBACK) {
1250 VCHIQ_REASON_T reason = (bulk->dir ==
1251 VCHIQ_BULK_TRANSMIT) ?
1252 ((bulk->actual ==
1253 VCHIQ_BULK_ACTUAL_ABORTED) ?
1254 VCHIQ_BULK_TRANSMIT_ABORTED :
1255 VCHIQ_BULK_TRANSMIT_DONE) :
1256 ((bulk->actual ==
1257 VCHIQ_BULK_ACTUAL_ABORTED) ?
1258 VCHIQ_BULK_RECEIVE_ABORTED :
1259 VCHIQ_BULK_RECEIVE_DONE);
1260 status = make_service_callback(service,
1261 reason, NULL, bulk->userdata);
1262 if (status == VCHIQ_RETRY)
1263 break;
1264 }
1265 }
1266
1267 queue->remove++;
1268 up(&service->bulk_remove_event);
1269 }
1270 if (!retry_poll)
1271 status = VCHIQ_SUCCESS;
1272 }
1273
1274 if (status == VCHIQ_RETRY)
1275 request_poll(service->state, service,
1276 (queue == &service->bulk_tx) ?
1277 VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY);
1278
1279 return status;
1280 }
1281
1282 /* Called by the slot handler thread */
1283 static void
poll_services(VCHIQ_STATE_T * state)1284 poll_services(VCHIQ_STATE_T *state)
1285 {
1286 int group, i;
1287
1288 for (group = 0; group < VCHI_BITSET_SIZE(state->unused_service); group++) {
1289 uint32_t flags;
1290 flags = atomic_xchg(&state->poll_services[group], 0);
1291 for (i = 0; flags; i++) {
1292 if (flags & (1 << i)) {
1293 VCHIQ_SERVICE_T *service =
1294 find_service_by_port(state,
1295 (group<<5) + i);
1296 uint32_t service_flags;
1297 flags &= ~(1 << i);
1298 if (!service)
1299 continue;
1300 service_flags =
1301 atomic_xchg(&service->poll_flags, 0);
1302 if (service_flags &
1303 (1 << VCHIQ_POLL_REMOVE)) {
1304 vchiq_log_info(vchiq_core_log_level,
1305 "%d: ps - remove %d<->%d",
1306 state->id, service->localport,
1307 service->remoteport);
1308
1309 /* Make it look like a client, because
1310 it must be removed and not left in
1311 the LISTENING state. */
1312 service->public_fourcc =
1313 VCHIQ_FOURCC_INVALID;
1314
1315 if (vchiq_close_service_internal(
1316 service, 0/*!close_recvd*/) !=
1317 VCHIQ_SUCCESS)
1318 request_poll(state, service,
1319 VCHIQ_POLL_REMOVE);
1320 } else if (service_flags &
1321 (1 << VCHIQ_POLL_TERMINATE)) {
1322 vchiq_log_info(vchiq_core_log_level,
1323 "%d: ps - terminate %d<->%d",
1324 state->id, service->localport,
1325 service->remoteport);
1326 if (vchiq_close_service_internal(
1327 service, 0/*!close_recvd*/) !=
1328 VCHIQ_SUCCESS)
1329 request_poll(state, service,
1330 VCHIQ_POLL_TERMINATE);
1331 }
1332 if (service_flags & (1 << VCHIQ_POLL_TXNOTIFY))
1333 notify_bulks(service,
1334 &service->bulk_tx,
1335 1/*retry_poll*/);
1336 if (service_flags & (1 << VCHIQ_POLL_RXNOTIFY))
1337 notify_bulks(service,
1338 &service->bulk_rx,
1339 1/*retry_poll*/);
1340 unlock_service(service);
1341 }
1342 }
1343 }
1344 }
1345
1346 /* Called by the slot handler or application threads, holding the bulk mutex. */
1347 static int
resolve_bulks(VCHIQ_SERVICE_T * service,VCHIQ_BULK_QUEUE_T * queue)1348 resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue)
1349 {
1350 VCHIQ_STATE_T *state = service->state;
1351 int resolved = 0;
1352 int rc;
1353
1354 while ((queue->process != queue->local_insert) &&
1355 (queue->process != queue->remote_insert)) {
1356 VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)];
1357
1358 vchiq_log_trace(vchiq_core_log_level,
1359 "%d: rb:%d %cx - li=%x ri=%x p=%x",
1360 state->id, service->localport,
1361 (queue == &service->bulk_tx) ? 't' : 'r',
1362 queue->local_insert, queue->remote_insert,
1363 queue->process);
1364
1365 WARN_ON(!((int)(queue->local_insert - queue->process) > 0));
1366 WARN_ON(!((int)(queue->remote_insert - queue->process) > 0));
1367
1368 rc = lmutex_lock_interruptible(&state->bulk_transfer_mutex);
1369 if (rc != 0)
1370 break;
1371
1372 vchiq_transfer_bulk(bulk);
1373 lmutex_unlock(&state->bulk_transfer_mutex);
1374
1375 if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) {
1376 const char *header = (queue == &service->bulk_tx) ?
1377 "Send Bulk to" : "Recv Bulk from";
1378 if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED)
1379 vchiq_log_info(SRVTRACE_LEVEL(service),
1380 "%s %c%c%c%c d:%d len:%d %p<->%p",
1381 header,
1382 VCHIQ_FOURCC_AS_4CHARS(
1383 service->base.fourcc),
1384 service->remoteport,
1385 bulk->size,
1386 bulk->data,
1387 bulk->remote_data);
1388 else
1389 vchiq_log_info(SRVTRACE_LEVEL(service),
1390 "%s %c%c%c%c d:%d ABORTED - tx len:%d,"
1391 " rx len:%d %p<->%p",
1392 header,
1393 VCHIQ_FOURCC_AS_4CHARS(
1394 service->base.fourcc),
1395 service->remoteport,
1396 bulk->size,
1397 bulk->remote_size,
1398 bulk->data,
1399 bulk->remote_data);
1400 }
1401
1402 vchiq_complete_bulk(bulk);
1403 queue->process++;
1404 resolved++;
1405 }
1406 return resolved;
1407 }
1408
1409 /* Called with the bulk_mutex held */
1410 static void
abort_outstanding_bulks(VCHIQ_SERVICE_T * service,VCHIQ_BULK_QUEUE_T * queue)1411 abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue)
1412 {
1413 int is_tx = (queue == &service->bulk_tx);
1414 vchiq_log_trace(vchiq_core_log_level,
1415 "%d: aob:%d %cx - li=%x ri=%x p=%x",
1416 service->state->id, service->localport, is_tx ? 't' : 'r',
1417 queue->local_insert, queue->remote_insert, queue->process);
1418
1419 WARN_ON(!((int)(queue->local_insert - queue->process) >= 0));
1420 WARN_ON(!((int)(queue->remote_insert - queue->process) >= 0));
1421
1422 while ((queue->process != queue->local_insert) ||
1423 (queue->process != queue->remote_insert)) {
1424 VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)];
1425
1426 if (queue->process == queue->remote_insert) {
1427 /* fabricate a matching dummy bulk */
1428 bulk->remote_data = NULL;
1429 bulk->remote_size = 0;
1430 queue->remote_insert++;
1431 }
1432
1433 if (queue->process != queue->local_insert) {
1434 vchiq_complete_bulk(bulk);
1435
1436 vchiq_log_info(SRVTRACE_LEVEL(service),
1437 "%s %c%c%c%c d:%d ABORTED - tx len:%d, "
1438 "rx len:%d",
1439 is_tx ? "Send Bulk to" : "Recv Bulk from",
1440 VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
1441 service->remoteport,
1442 bulk->size,
1443 bulk->remote_size);
1444 } else {
1445 /* fabricate a matching dummy bulk */
1446 bulk->data = NULL;
1447 bulk->size = 0;
1448 bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED;
1449 bulk->dir = is_tx ? VCHIQ_BULK_TRANSMIT :
1450 VCHIQ_BULK_RECEIVE;
1451 queue->local_insert++;
1452 }
1453
1454 queue->process++;
1455 }
1456 }
1457
1458 /* Called from the slot handler thread */
1459 static void
pause_bulks(VCHIQ_STATE_T * state)1460 pause_bulks(VCHIQ_STATE_T *state)
1461 {
1462 if (unlikely(atomic_inc_return(&pause_bulks_count) != 1)) {
1463 WARN_ON_ONCE(1);
1464 atomic_set(&pause_bulks_count, 1);
1465 return;
1466 }
1467
1468 /* Block bulk transfers from all services */
1469 lmutex_lock(&state->bulk_transfer_mutex);
1470 }
1471
1472 /* Called from the slot handler thread */
1473 static void
resume_bulks(VCHIQ_STATE_T * state)1474 resume_bulks(VCHIQ_STATE_T *state)
1475 {
1476 int i;
1477 if (unlikely(atomic_dec_return(&pause_bulks_count) != 0)) {
1478 WARN_ON_ONCE(1);
1479 atomic_set(&pause_bulks_count, 0);
1480 return;
1481 }
1482
1483 /* Allow bulk transfers from all services */
1484 lmutex_unlock(&state->bulk_transfer_mutex);
1485
1486 if (state->deferred_bulks == 0)
1487 return;
1488
1489 /* Deal with any bulks which had to be deferred due to being in
1490 * paused state. Don't try to match up to number of deferred bulks
1491 * in case we've had something come and close the service in the
1492 * interim - just process all bulk queues for all services */
1493 vchiq_log_info(vchiq_core_log_level, "%s: processing %d deferred bulks",
1494 __func__, state->deferred_bulks);
1495
1496 for (i = 0; i < state->unused_service; i++) {
1497 VCHIQ_SERVICE_T *service = state->services[i];
1498 int resolved_rx = 0;
1499 int resolved_tx = 0;
1500 if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN))
1501 continue;
1502
1503 lmutex_lock(&service->bulk_mutex);
1504 resolved_rx = resolve_bulks(service, &service->bulk_rx);
1505 resolved_tx = resolve_bulks(service, &service->bulk_tx);
1506 lmutex_unlock(&service->bulk_mutex);
1507 if (resolved_rx)
1508 notify_bulks(service, &service->bulk_rx, 1);
1509 if (resolved_tx)
1510 notify_bulks(service, &service->bulk_tx, 1);
1511 }
1512 state->deferred_bulks = 0;
1513 }
1514
1515 static int
parse_open(VCHIQ_STATE_T * state,VCHIQ_HEADER_T * header)1516 parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header)
1517 {
1518 VCHIQ_SERVICE_T *service = NULL;
1519 int msgid, size;
1520 unsigned int localport, remoteport;
1521
1522 msgid = header->msgid;
1523 size = header->size;
1524 //int type = VCHIQ_MSG_TYPE(msgid);
1525 localport = VCHIQ_MSG_DSTPORT(msgid);
1526 remoteport = VCHIQ_MSG_SRCPORT(msgid);
1527 if (size >= sizeof(struct vchiq_open_payload)) {
1528 const struct vchiq_open_payload *payload =
1529 (struct vchiq_open_payload *)header->data;
1530 unsigned int fourcc;
1531
1532 fourcc = payload->fourcc;
1533 vchiq_log_info(vchiq_core_log_level,
1534 "%d: prs OPEN@%p (%d->'%c%c%c%c')",
1535 state->id, header,
1536 localport,
1537 VCHIQ_FOURCC_AS_4CHARS(fourcc));
1538
1539 service = get_listening_service(state, fourcc);
1540
1541 if (service) {
1542 /* A matching service exists */
1543 short version = payload->version;
1544 short version_min = payload->version_min;
1545 if ((service->version < version_min) ||
1546 (version < service->version_min)) {
1547 /* Version mismatch */
1548 vchiq_loud_error_header();
1549 vchiq_loud_error("%d: service %d (%c%c%c%c) "
1550 "version mismatch - local (%d, min %d)"
1551 " vs. remote (%d, min %d)",
1552 state->id, service->localport,
1553 VCHIQ_FOURCC_AS_4CHARS(fourcc),
1554 service->version, service->version_min,
1555 version, version_min);
1556 vchiq_loud_error_footer();
1557 unlock_service(service);
1558 service = NULL;
1559 goto fail_open;
1560 }
1561 service->peer_version = version;
1562
1563 if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) {
1564 struct vchiq_openack_payload ack_payload = {
1565 service->version
1566 };
1567 VCHIQ_ELEMENT_T body = {
1568 &ack_payload,
1569 sizeof(ack_payload)
1570 };
1571
1572 if (state->version_common <
1573 VCHIQ_VERSION_SYNCHRONOUS_MODE)
1574 service->sync = 0;
1575
1576 /* Acknowledge the OPEN */
1577 if (service->sync &&
1578 (state->version_common >=
1579 VCHIQ_VERSION_SYNCHRONOUS_MODE)) {
1580 if (queue_message_sync(state, NULL,
1581 VCHIQ_MAKE_MSG(
1582 VCHIQ_MSG_OPENACK,
1583 service->localport,
1584 remoteport),
1585 &body, 1, sizeof(ack_payload),
1586 0) == VCHIQ_RETRY)
1587 goto bail_not_ready;
1588 } else {
1589 if (queue_message(state, NULL,
1590 VCHIQ_MAKE_MSG(
1591 VCHIQ_MSG_OPENACK,
1592 service->localport,
1593 remoteport),
1594 &body, 1, sizeof(ack_payload),
1595 0) == VCHIQ_RETRY)
1596 goto bail_not_ready;
1597 }
1598
1599 /* The service is now open */
1600 vchiq_set_service_state(service,
1601 service->sync ? VCHIQ_SRVSTATE_OPENSYNC
1602 : VCHIQ_SRVSTATE_OPEN);
1603 }
1604
1605 service->remoteport = remoteport;
1606 service->client_id = ((int *)header->data)[1];
1607 if (make_service_callback(service, VCHIQ_SERVICE_OPENED,
1608 NULL, NULL) == VCHIQ_RETRY) {
1609 /* Bail out if not ready */
1610 service->remoteport = VCHIQ_PORT_FREE;
1611 goto bail_not_ready;
1612 }
1613
1614 /* Success - the message has been dealt with */
1615 unlock_service(service);
1616 return 1;
1617 }
1618 }
1619
1620 fail_open:
1621 /* No available service, or an invalid request - send a CLOSE */
1622 if (queue_message(state, NULL,
1623 VCHIQ_MAKE_MSG(VCHIQ_MSG_CLOSE, 0, VCHIQ_MSG_SRCPORT(msgid)),
1624 NULL, 0, 0, 0) == VCHIQ_RETRY)
1625 goto bail_not_ready;
1626
1627 return 1;
1628
1629 bail_not_ready:
1630 if (service)
1631 unlock_service(service);
1632
1633 return 0;
1634 }
1635
1636 /* Called by the slot handler thread */
1637 static void
parse_rx_slots(VCHIQ_STATE_T * state)1638 parse_rx_slots(VCHIQ_STATE_T *state)
1639 {
1640 VCHIQ_SHARED_STATE_T *remote = state->remote;
1641 VCHIQ_SERVICE_T *service = NULL;
1642 int tx_pos;
1643 DEBUG_INITIALISE(state->local)
1644
1645 tx_pos = remote->tx_pos;
1646
1647 while (state->rx_pos != tx_pos) {
1648 VCHIQ_HEADER_T *header;
1649 int msgid, size;
1650 int type;
1651 unsigned int localport, remoteport;
1652
1653 DEBUG_TRACE(PARSE_LINE);
1654 if (!state->rx_data) {
1655 int rx_index;
1656 WARN_ON(!((state->rx_pos & VCHIQ_SLOT_MASK) == 0));
1657 rx_index = remote->slot_queue[
1658 SLOT_QUEUE_INDEX_FROM_POS(state->rx_pos) &
1659 VCHIQ_SLOT_QUEUE_MASK];
1660 state->rx_data = (char *)SLOT_DATA_FROM_INDEX(state,
1661 rx_index);
1662 state->rx_info = SLOT_INFO_FROM_INDEX(state, rx_index);
1663
1664 /* Initialise use_count to one, and increment
1665 ** release_count at the end of the slot to avoid
1666 ** releasing the slot prematurely. */
1667 state->rx_info->use_count = 1;
1668 state->rx_info->release_count = 0;
1669 }
1670
1671 header = (VCHIQ_HEADER_T *)(state->rx_data +
1672 (state->rx_pos & VCHIQ_SLOT_MASK));
1673 DEBUG_VALUE(PARSE_HEADER, (size_t)header);
1674 msgid = header->msgid;
1675 DEBUG_VALUE(PARSE_MSGID, msgid);
1676 size = header->size;
1677 type = VCHIQ_MSG_TYPE(msgid);
1678 localport = VCHIQ_MSG_DSTPORT(msgid);
1679 remoteport = VCHIQ_MSG_SRCPORT(msgid);
1680
1681 if (type != VCHIQ_MSG_DATA)
1682 VCHIQ_STATS_INC(state, ctrl_rx_count);
1683
1684 switch (type) {
1685 case VCHIQ_MSG_OPENACK:
1686 case VCHIQ_MSG_CLOSE:
1687 case VCHIQ_MSG_DATA:
1688 case VCHIQ_MSG_BULK_RX:
1689 case VCHIQ_MSG_BULK_TX:
1690 case VCHIQ_MSG_BULK_RX_DONE:
1691 case VCHIQ_MSG_BULK_TX_DONE:
1692 service = find_service_by_port(state, localport);
1693 if ((!service ||
1694 ((service->remoteport != remoteport) &&
1695 (service->remoteport != VCHIQ_PORT_FREE))) &&
1696 (localport == 0) &&
1697 (type == VCHIQ_MSG_CLOSE)) {
1698 /* This could be a CLOSE from a client which
1699 hadn't yet received the OPENACK - look for
1700 the connected service */
1701 if (service)
1702 unlock_service(service);
1703 service = get_connected_service(state,
1704 remoteport);
1705 if (service)
1706 vchiq_log_warning(vchiq_core_log_level,
1707 "%d: prs %s@%p (%d->%d) - "
1708 "found connected service %d",
1709 state->id, msg_type_str(type),
1710 header,
1711 remoteport, localport,
1712 service->localport);
1713 }
1714
1715 if (!service) {
1716 vchiq_log_error(vchiq_core_log_level,
1717 "%d: prs %s@%p (%d->%d) - " /* XXX */
1718 "invalid/closed service %d",
1719 state->id, msg_type_str(type),
1720 header,
1721 remoteport, localport, localport);
1722 goto skip_message;
1723 }
1724 break;
1725 default:
1726 break;
1727 }
1728
1729 if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) {
1730 int svc_fourcc;
1731
1732 svc_fourcc = service
1733 ? service->base.fourcc
1734 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
1735 vchiq_log_info(SRVTRACE_LEVEL(service),
1736 "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d "
1737 "len:%d",
1738 msg_type_str(type), type,
1739 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
1740 remoteport, localport, size);
1741 if (size > 0)
1742 vchiq_log_dump_mem("Rcvd", 0, header->data,
1743 min(16, size));
1744 }
1745
1746 if (((size_t)header & VCHIQ_SLOT_MASK) + calc_stride(size)
1747 > VCHIQ_SLOT_SIZE) {
1748 vchiq_log_error(vchiq_core_log_level,
1749 "header %p (msgid %x) - size %x too big for "
1750 "slot",
1751 header, (unsigned int)msgid,
1752 (unsigned int)size);
1753 WARN(1, "oversized for slot\n");
1754 }
1755
1756 switch (type) {
1757 case VCHIQ_MSG_OPEN:
1758 WARN_ON(!(VCHIQ_MSG_DSTPORT(msgid) == 0));
1759 if (!parse_open(state, header))
1760 goto bail_not_ready;
1761 break;
1762 case VCHIQ_MSG_OPENACK:
1763 if (size >= sizeof(struct vchiq_openack_payload)) {
1764 const struct vchiq_openack_payload *payload =
1765 (struct vchiq_openack_payload *)
1766 header->data;
1767 service->peer_version = payload->version;
1768 }
1769 vchiq_log_info(vchiq_core_log_level,
1770 "%d: prs OPENACK@%p,%x (%d->%d) v:%d",
1771 state->id, header, size,
1772 remoteport, localport, service->peer_version);
1773 if (service->srvstate ==
1774 VCHIQ_SRVSTATE_OPENING) {
1775 service->remoteport = remoteport;
1776 vchiq_set_service_state(service,
1777 VCHIQ_SRVSTATE_OPEN);
1778 up(&service->remove_event);
1779 } else
1780 vchiq_log_error(vchiq_core_log_level,
1781 "OPENACK received in state %s",
1782 srvstate_names[service->srvstate]);
1783 break;
1784 case VCHIQ_MSG_CLOSE:
1785 WARN_ON(size != 0); /* There should be no data */
1786
1787 vchiq_log_info(vchiq_core_log_level,
1788 "%d: prs CLOSE@%p (%d->%d)",
1789 state->id, header,
1790 remoteport, localport);
1791
1792 mark_service_closing_internal(service, 1);
1793
1794 if (vchiq_close_service_internal(service,
1795 1/*close_recvd*/) == VCHIQ_RETRY)
1796 goto bail_not_ready;
1797
1798 vchiq_log_info(vchiq_core_log_level,
1799 "Close Service %c%c%c%c s:%u d:%d",
1800 VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
1801 service->localport,
1802 service->remoteport);
1803 break;
1804 case VCHIQ_MSG_DATA:
1805 vchiq_log_info(vchiq_core_log_level,
1806 "%d: prs DATA@%p,%x (%d->%d)",
1807 state->id, header, size,
1808 remoteport, localport);
1809
1810 if ((service->remoteport == remoteport)
1811 && (service->srvstate ==
1812 VCHIQ_SRVSTATE_OPEN)) {
1813 header->msgid = msgid | VCHIQ_MSGID_CLAIMED;
1814 claim_slot(state->rx_info);
1815 DEBUG_TRACE(PARSE_LINE);
1816 if (make_service_callback(service,
1817 VCHIQ_MESSAGE_AVAILABLE, header,
1818 NULL) == VCHIQ_RETRY) {
1819 DEBUG_TRACE(PARSE_LINE);
1820 goto bail_not_ready;
1821 }
1822 VCHIQ_SERVICE_STATS_INC(service, ctrl_rx_count);
1823 VCHIQ_SERVICE_STATS_ADD(service, ctrl_rx_bytes,
1824 size);
1825 } else {
1826 VCHIQ_STATS_INC(state, error_count);
1827 }
1828 break;
1829 case VCHIQ_MSG_CONNECT:
1830 vchiq_log_info(vchiq_core_log_level,
1831 "%d: prs CONNECT@%p",
1832 state->id, header);
1833 state->version_common = ((VCHIQ_SLOT_ZERO_T *)
1834 state->slot_data)->version;
1835 up(&state->connect);
1836 break;
1837 /*
1838 * XXXMDC Apparently nothing uses this
1839 * https://github.com/raspberrypi/linux/commit/14f4d72fb799a9b3170a45ab80d4a3ddad541960
1840 * but taking out the master bits is a whole new job
1841 */
1842 case VCHIQ_MSG_BULK_RX:
1843 case VCHIQ_MSG_BULK_TX:
1844 WARN_ON(1);
1845 break;
1846 #if 0
1847 {
1848 VCHIQ_BULK_QUEUE_T *queue;
1849 WARN_ON(!state->is_master);
1850 queue = (type == VCHIQ_MSG_BULK_RX) ?
1851 &service->bulk_tx : &service->bulk_rx;
1852 if ((service->remoteport == remoteport)
1853 && (service->srvstate ==
1854 VCHIQ_SRVSTATE_OPEN)) {
1855 VCHIQ_BULK_T *bulk;
1856 int resolved = 0;
1857
1858 DEBUG_TRACE(PARSE_LINE);
1859 if (lmutex_lock_interruptible(
1860 &service->bulk_mutex) != 0) {
1861 DEBUG_TRACE(PARSE_LINE);
1862 goto bail_not_ready;
1863 }
1864
1865 WARN_ON(!(queue->remote_insert < queue->remove +
1866 VCHIQ_NUM_SERVICE_BULKS));
1867 bulk = &queue->bulks[
1868 BULK_INDEX(queue->remote_insert)];
1869 bulk->remote_data =
1870 (void *)((int *)header->data)[0];
1871 bulk->remote_size = ((int *)header->data)[1];
1872 wmb();
1873
1874 vchiq_log_info(vchiq_core_log_level,
1875 "%d: prs %s@%p (%d->%d) %x@%p",
1876 state->id, msg_type_str(type),
1877 header,
1878 remoteport, localport,
1879 bulk->remote_size,
1880 bulk->remote_data);
1881
1882 queue->remote_insert++;
1883
1884 if (atomic_read(&pause_bulks_count)) {
1885 state->deferred_bulks++;
1886 vchiq_log_info(vchiq_core_log_level,
1887 "%s: deferring bulk (%d)",
1888 __func__,
1889 state->deferred_bulks);
1890 if (state->conn_state !=
1891 VCHIQ_CONNSTATE_PAUSE_SENT)
1892 vchiq_log_error(
1893 vchiq_core_log_level,
1894 "%s: bulks paused in "
1895 "unexpected state %s",
1896 __func__,
1897 conn_state_names[
1898 state->conn_state]);
1899 } else if (state->conn_state ==
1900 VCHIQ_CONNSTATE_CONNECTED) {
1901 DEBUG_TRACE(PARSE_LINE);
1902 resolved = resolve_bulks(service,
1903 queue);
1904 }
1905
1906 lmutex_unlock(&service->bulk_mutex);
1907 if (resolved)
1908 notify_bulks(service, queue,
1909 1//retry_poll
1910 );
1911 }
1912 }
1913 #endif
1914 case VCHIQ_MSG_BULK_RX_DONE:
1915 case VCHIQ_MSG_BULK_TX_DONE:
1916 WARN_ON(state->is_master);
1917 if ((service->remoteport == remoteport)
1918 && (service->srvstate !=
1919 VCHIQ_SRVSTATE_FREE)) {
1920 VCHIQ_BULK_QUEUE_T *queue;
1921 VCHIQ_BULK_T *bulk;
1922
1923 queue = (type == VCHIQ_MSG_BULK_RX_DONE) ?
1924 &service->bulk_rx : &service->bulk_tx;
1925
1926 DEBUG_TRACE(PARSE_LINE);
1927 if (lmutex_lock_interruptible(
1928 &service->bulk_mutex) != 0) {
1929 DEBUG_TRACE(PARSE_LINE);
1930 goto bail_not_ready;
1931 }
1932 if ((int)(queue->remote_insert -
1933 queue->local_insert) >= 0) {
1934 vchiq_log_error(vchiq_core_log_level,
1935 "%d: prs %s@%p (%d->%d) "
1936 "unexpected (ri=%d,li=%d)",
1937 state->id, msg_type_str(type),
1938 header,
1939 remoteport, localport,
1940 queue->remote_insert,
1941 queue->local_insert);
1942 lmutex_unlock(&service->bulk_mutex);
1943 break;
1944 }
1945
1946 BUG_ON(queue->process == queue->local_insert);
1947 BUG_ON(queue->process != queue->remote_insert);
1948
1949 bulk = &queue->bulks[
1950 BULK_INDEX(queue->remote_insert)];
1951 bulk->actual = *(int *)header->data;
1952 queue->remote_insert++;
1953
1954 vchiq_log_info(vchiq_core_log_level,
1955 "%d: prs %s@%p (%d->%d) %x@%p",
1956 state->id, msg_type_str(type),
1957 header,
1958 remoteport, localport,
1959 bulk->actual, bulk->data);
1960
1961 vchiq_log_trace(vchiq_core_log_level,
1962 "%d: prs:%d %cx li=%x ri=%x p=%x",
1963 state->id, localport,
1964 (type == VCHIQ_MSG_BULK_RX_DONE) ?
1965 'r' : 't',
1966 queue->local_insert,
1967 queue->remote_insert, queue->process);
1968
1969 DEBUG_TRACE(PARSE_LINE);
1970 WARN_ON(queue->process == queue->local_insert);
1971 vchiq_complete_bulk(bulk);
1972 queue->process++;
1973 lmutex_unlock(&service->bulk_mutex);
1974 DEBUG_TRACE(PARSE_LINE);
1975 notify_bulks(service, queue, 1/*retry_poll*/);
1976 DEBUG_TRACE(PARSE_LINE);
1977 }
1978 break;
1979 case VCHIQ_MSG_PADDING:
1980 vchiq_log_trace(vchiq_core_log_level,
1981 "%d: prs PADDING@%p,%x",
1982 state->id, header, size);
1983 break;
1984 case VCHIQ_MSG_PAUSE:
1985 /* If initiated, signal the application thread */
1986 vchiq_log_trace(vchiq_core_log_level,
1987 "%d: prs PAUSE@%p,%x",
1988 state->id, header, size);
1989 if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) {
1990 vchiq_log_error(vchiq_core_log_level,
1991 "%d: PAUSE received in state PAUSED",
1992 state->id);
1993 break;
1994 }
1995 if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT) {
1996 /* Send a PAUSE in response */
1997 if (queue_message(state, NULL,
1998 VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0),
1999 NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK)
2000 == VCHIQ_RETRY)
2001 goto bail_not_ready;
2002 if (state->is_master)
2003 pause_bulks(state);
2004 }
2005 /* At this point slot_mutex is held */
2006 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED);
2007 vchiq_platform_paused(state);
2008 break;
2009 case VCHIQ_MSG_RESUME:
2010 vchiq_log_trace(vchiq_core_log_level,
2011 "%d: prs RESUME@%p,%x",
2012 state->id, header, size);
2013 /* Release the slot mutex */
2014 lmutex_unlock(&state->slot_mutex);
2015 if (state->is_master)
2016 resume_bulks(state);
2017 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED);
2018 vchiq_platform_resumed(state);
2019 break;
2020
2021 case VCHIQ_MSG_REMOTE_USE:
2022 vchiq_on_remote_use(state);
2023 break;
2024 case VCHIQ_MSG_REMOTE_RELEASE:
2025 vchiq_on_remote_release(state);
2026 break;
2027 case VCHIQ_MSG_REMOTE_USE_ACTIVE:
2028 vchiq_on_remote_use_active(state);
2029 break;
2030
2031 default:
2032 vchiq_log_error(vchiq_core_log_level,
2033 "%d: prs invalid msgid %x@%p,%x",
2034 state->id, msgid, header, size);
2035 WARN(1, "invalid message\n");
2036 break;
2037 }
2038
2039 skip_message:
2040 if (service) {
2041 unlock_service(service);
2042 service = NULL;
2043 }
2044
2045 state->rx_pos += calc_stride(size);
2046
2047 DEBUG_TRACE(PARSE_LINE);
2048 /* Perform some housekeeping when the end of the slot is
2049 ** reached. */
2050 if ((state->rx_pos & VCHIQ_SLOT_MASK) == 0) {
2051 /* Remove the extra reference count. */
2052 release_slot(state, state->rx_info, NULL, NULL);
2053 state->rx_data = NULL;
2054 }
2055 }
2056
2057 bail_not_ready:
2058 if (service)
2059 unlock_service(service);
2060 }
2061
2062 /* Called by the slot handler thread */
2063 int slot_handler_func(void *v);
2064 int
slot_handler_func(void * v)2065 slot_handler_func(void *v)
2066 {
2067 VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
2068 VCHIQ_SHARED_STATE_T *local = state->local;
2069 DEBUG_INITIALISE(local)
2070
2071 while (1) {
2072 DEBUG_COUNT(SLOT_HANDLER_COUNT);
2073 DEBUG_TRACE(SLOT_HANDLER_LINE);
2074 remote_event_wait(state, &local->trigger);
2075
2076 rmb();
2077
2078 DEBUG_TRACE(SLOT_HANDLER_LINE);
2079 if (state->poll_needed) {
2080 /* Check if we need to suspend - may change our
2081 * conn_state */
2082 vchiq_platform_check_suspend(state);
2083
2084 state->poll_needed = 0;
2085
2086 /* Handle service polling and other rare conditions here
2087 ** out of the mainline code */
2088 switch (state->conn_state) {
2089 case VCHIQ_CONNSTATE_CONNECTED:
2090 /* Poll the services as requested */
2091 poll_services(state);
2092 break;
2093
2094 case VCHIQ_CONNSTATE_PAUSING:
2095 if (state->is_master)
2096 pause_bulks(state);
2097 if (queue_message(state, NULL,
2098 VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0),
2099 NULL, 0, 0,
2100 QMFLAGS_NO_MUTEX_UNLOCK)
2101 != VCHIQ_RETRY) {
2102 vchiq_set_conn_state(state,
2103 VCHIQ_CONNSTATE_PAUSE_SENT);
2104 } else {
2105 if (state->is_master)
2106 resume_bulks(state);
2107 /* Retry later */
2108 state->poll_needed = 1;
2109 }
2110 break;
2111
2112 case VCHIQ_CONNSTATE_PAUSED:
2113 vchiq_platform_resume(state);
2114 break;
2115
2116 case VCHIQ_CONNSTATE_RESUMING:
2117 if (queue_message(state, NULL,
2118 VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0),
2119 NULL, 0, 0, QMFLAGS_NO_MUTEX_LOCK)
2120 != VCHIQ_RETRY) {
2121 if (state->is_master)
2122 resume_bulks(state);
2123 vchiq_set_conn_state(state,
2124 VCHIQ_CONNSTATE_CONNECTED);
2125 vchiq_platform_resumed(state);
2126 } else {
2127 /* This should really be impossible,
2128 ** since the PAUSE should have flushed
2129 ** through outstanding messages. */
2130 vchiq_log_error(vchiq_core_log_level,
2131 "Failed to send RESUME "
2132 "message");
2133 BUG();
2134 }
2135 break;
2136
2137 case VCHIQ_CONNSTATE_PAUSE_TIMEOUT:
2138 case VCHIQ_CONNSTATE_RESUME_TIMEOUT:
2139 vchiq_platform_handle_timeout(state);
2140 break;
2141 default:
2142 break;
2143 }
2144
2145
2146 }
2147
2148 DEBUG_TRACE(SLOT_HANDLER_LINE);
2149 parse_rx_slots(state);
2150 }
2151 return 0;
2152 }
2153
2154
2155 /* Called by the recycle thread */
2156 int recycle_func(void *v);
2157 int
recycle_func(void * v)2158 recycle_func(void *v)
2159 {
2160 VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
2161 VCHIQ_SHARED_STATE_T *local = state->local;
2162
2163 while (1) {
2164 remote_event_wait(state, &local->recycle);
2165 process_free_queue(state);
2166 }
2167 return 0;
2168 }
2169
2170
2171 /* Called by the sync thread */
2172 int sync_func(void *v);
2173 int
sync_func(void * v)2174 sync_func(void *v)
2175 {
2176 VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
2177 VCHIQ_SHARED_STATE_T *local = state->local;
2178 VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state,
2179 state->remote->slot_sync);
2180
2181 while (1) {
2182 VCHIQ_SERVICE_T *service;
2183 int msgid, size;
2184 int type;
2185 unsigned int localport, remoteport;
2186
2187 remote_event_wait(state, &local->sync_trigger);
2188
2189 rmb();
2190
2191 msgid = header->msgid;
2192 size = header->size;
2193 type = VCHIQ_MSG_TYPE(msgid);
2194 localport = VCHIQ_MSG_DSTPORT(msgid);
2195 remoteport = VCHIQ_MSG_SRCPORT(msgid);
2196
2197 service = find_service_by_port(state, localport);
2198
2199 if (!service) {
2200 vchiq_log_error(vchiq_sync_log_level,
2201 "%d: sf %s@%p (%d->%d) - "
2202 "invalid/closed service %d",
2203 state->id, msg_type_str(type),
2204 header,
2205 remoteport, localport, localport);
2206 release_message_sync(state, header);
2207 continue;
2208 }
2209
2210 if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) {
2211 int svc_fourcc;
2212
2213 svc_fourcc = service
2214 ? service->base.fourcc
2215 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
2216 vchiq_log_trace(vchiq_sync_log_level,
2217 "Rcvd Msg %s from %c%c%c%c s:%d d:%d len:%d",
2218 msg_type_str(type),
2219 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
2220 remoteport, localport, size);
2221 if (size > 0)
2222 vchiq_log_dump_mem("Rcvd", 0, header->data,
2223 min(16, size));
2224 }
2225
2226 switch (type) {
2227 case VCHIQ_MSG_OPENACK:
2228 if (size >= sizeof(struct vchiq_openack_payload)) {
2229 const struct vchiq_openack_payload *payload =
2230 (struct vchiq_openack_payload *)
2231 header->data;
2232 service->peer_version = payload->version;
2233 }
2234 vchiq_log_info(vchiq_sync_log_level,
2235 "%d: sf OPENACK@%p,%x (%d->%d) v:%d",
2236 state->id, header, size,
2237 remoteport, localport, service->peer_version);
2238 if (service->srvstate == VCHIQ_SRVSTATE_OPENING) {
2239 service->remoteport = remoteport;
2240 vchiq_set_service_state(service,
2241 VCHIQ_SRVSTATE_OPENSYNC);
2242 service->sync = 1;
2243 up(&service->remove_event);
2244 }
2245 release_message_sync(state, header);
2246 break;
2247
2248 case VCHIQ_MSG_DATA:
2249 vchiq_log_trace(vchiq_sync_log_level,
2250 "%d: sf DATA@%p,%x (%d->%d)",
2251 state->id, header, size,
2252 remoteport, localport);
2253
2254 if ((service->remoteport == remoteport) &&
2255 (service->srvstate ==
2256 VCHIQ_SRVSTATE_OPENSYNC)) {
2257 if (make_service_callback(service,
2258 VCHIQ_MESSAGE_AVAILABLE, header,
2259 NULL) == VCHIQ_RETRY)
2260 vchiq_log_error(vchiq_sync_log_level,
2261 "synchronous callback to "
2262 "service %d returns "
2263 "VCHIQ_RETRY",
2264 localport);
2265 }
2266 break;
2267
2268 default:
2269 vchiq_log_error(vchiq_sync_log_level,
2270 "%d: sf unexpected msgid %x@%p,%x",
2271 state->id, msgid, header, size);
2272 release_message_sync(state, header);
2273 break;
2274 }
2275
2276 unlock_service(service);
2277 }
2278
2279 return 0;
2280 }
2281
2282
2283 static void
init_bulk_queue(VCHIQ_BULK_QUEUE_T * queue)2284 init_bulk_queue(VCHIQ_BULK_QUEUE_T *queue)
2285 {
2286 queue->local_insert = 0;
2287 queue->remote_insert = 0;
2288 queue->process = 0;
2289 queue->remote_notify = 0;
2290 queue->remove = 0;
2291 }
2292
2293
2294 inline const char *
get_conn_state_name(VCHIQ_CONNSTATE_T conn_state)2295 get_conn_state_name(VCHIQ_CONNSTATE_T conn_state)
2296 {
2297 return conn_state_names[conn_state];
2298 }
2299
2300
2301 VCHIQ_SLOT_ZERO_T *
vchiq_init_slots(void * mem_base,int mem_size)2302 vchiq_init_slots(void *mem_base, int mem_size)
2303 {
2304 int mem_align = (int)((VCHIQ_SLOT_SIZE - (long)mem_base) & VCHIQ_SLOT_MASK);
2305 VCHIQ_SLOT_ZERO_T *slot_zero =
2306 (VCHIQ_SLOT_ZERO_T *)((char *)mem_base + mem_align);
2307 int num_slots = (mem_size - mem_align)/VCHIQ_SLOT_SIZE;
2308 int first_data_slot = VCHIQ_SLOT_ZERO_SLOTS;
2309
2310 /* Ensure there is enough memory to run an absolutely minimum system */
2311 num_slots -= first_data_slot;
2312
2313 if (num_slots < 4) {
2314 vchiq_log_error(vchiq_core_log_level,
2315 "vchiq_init_slots - insufficient memory %x bytes",
2316 mem_size);
2317 return NULL;
2318 }
2319
2320 memset(slot_zero, 0, sizeof(VCHIQ_SLOT_ZERO_T));
2321
2322 slot_zero->magic = VCHIQ_MAGIC;
2323 slot_zero->version = VCHIQ_VERSION;
2324 slot_zero->version_min = VCHIQ_VERSION_MIN;
2325 slot_zero->slot_zero_size = sizeof(VCHIQ_SLOT_ZERO_T);
2326 slot_zero->slot_size = VCHIQ_SLOT_SIZE;
2327 slot_zero->max_slots = VCHIQ_MAX_SLOTS;
2328 slot_zero->max_slots_per_side = VCHIQ_MAX_SLOTS_PER_SIDE;
2329
2330 slot_zero->master.slot_sync = first_data_slot;
2331 slot_zero->master.slot_first = first_data_slot + 1;
2332 slot_zero->master.slot_last = first_data_slot + (num_slots/2) - 1;
2333 slot_zero->slave.slot_sync = first_data_slot + (num_slots/2);
2334 slot_zero->slave.slot_first = first_data_slot + (num_slots/2) + 1;
2335 slot_zero->slave.slot_last = first_data_slot + num_slots - 1;
2336
2337 return slot_zero;
2338 }
2339
2340 VCHIQ_STATUS_T
vchiq_init_state(VCHIQ_STATE_T * state,VCHIQ_SLOT_ZERO_T * slot_zero,int is_master)2341 vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero,
2342 int is_master)
2343 {
2344 VCHIQ_SHARED_STATE_T *local;
2345 VCHIQ_SHARED_STATE_T *remote;
2346 VCHIQ_STATUS_T status;
2347 char threadname[10];
2348 static int id;
2349 int i;
2350
2351 /* Check the input configuration */
2352
2353 if (slot_zero->magic != VCHIQ_MAGIC) {
2354 vchiq_loud_error_header();
2355 vchiq_loud_error("Invalid VCHIQ magic value found.");
2356 vchiq_loud_error("slot_zero=%p: magic=%x (expected %x)",
2357 slot_zero, slot_zero->magic, VCHIQ_MAGIC);
2358 vchiq_loud_error_footer();
2359 return VCHIQ_ERROR;
2360 }
2361
2362 vchiq_log_warning(vchiq_core_log_level,
2363 "local ver %d (min %d), remote ver %d.",
2364 VCHIQ_VERSION, VCHIQ_VERSION_MIN,
2365 slot_zero->version);
2366
2367 if (slot_zero->version < VCHIQ_VERSION_MIN) {
2368 vchiq_loud_error_header();
2369 vchiq_loud_error("Incompatible VCHIQ versions found.");
2370 vchiq_loud_error("slot_zero=%p: VideoCore version=%d "
2371 "(minimum %d)",
2372 slot_zero, slot_zero->version,
2373 VCHIQ_VERSION_MIN);
2374 vchiq_loud_error("Restart with a newer VideoCore image.");
2375 vchiq_loud_error_footer();
2376 return VCHIQ_ERROR;
2377 }
2378
2379 if (VCHIQ_VERSION < slot_zero->version_min) {
2380 vchiq_loud_error_header();
2381 vchiq_loud_error("Incompatible VCHIQ versions found.");
2382 vchiq_loud_error("slot_zero=%p: version=%d (VideoCore "
2383 "minimum %d)",
2384 slot_zero, VCHIQ_VERSION,
2385 slot_zero->version_min);
2386 vchiq_loud_error("Restart with a newer kernel.");
2387 vchiq_loud_error_footer();
2388 return VCHIQ_ERROR;
2389 }
2390
2391 if ((slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) ||
2392 (slot_zero->slot_size != VCHIQ_SLOT_SIZE) ||
2393 (slot_zero->max_slots != VCHIQ_MAX_SLOTS) ||
2394 (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE)) {
2395 vchiq_loud_error_header();
2396 if (slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T))
2397 vchiq_loud_error("slot_zero=%p: slot_zero_size=%x "
2398 "(expected %zx)",
2399 slot_zero,
2400 slot_zero->slot_zero_size,
2401 sizeof(VCHIQ_SLOT_ZERO_T));
2402 if (slot_zero->slot_size != VCHIQ_SLOT_SIZE)
2403 vchiq_loud_error("slot_zero=%p: slot_size=%d "
2404 "(expected %d",
2405 slot_zero, slot_zero->slot_size,
2406 VCHIQ_SLOT_SIZE);
2407 if (slot_zero->max_slots != VCHIQ_MAX_SLOTS)
2408 vchiq_loud_error("slot_zero=%p: max_slots=%d "
2409 "(expected %d)",
2410 slot_zero, slot_zero->max_slots,
2411 VCHIQ_MAX_SLOTS);
2412 if (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE)
2413 vchiq_loud_error("slot_zero=%p: max_slots_per_side=%d "
2414 "(expected %d)",
2415 slot_zero,
2416 slot_zero->max_slots_per_side,
2417 VCHIQ_MAX_SLOTS_PER_SIDE);
2418 vchiq_loud_error_footer();
2419 return VCHIQ_ERROR;
2420 }
2421
2422 if (VCHIQ_VERSION < slot_zero->version)
2423 slot_zero->version = VCHIQ_VERSION;
2424
2425 if (is_master) {
2426 local = &slot_zero->master;
2427 remote = &slot_zero->slave;
2428 } else {
2429 local = &slot_zero->slave;
2430 remote = &slot_zero->master;
2431 }
2432
2433 if (local->initialised) {
2434 vchiq_loud_error_header();
2435 if (remote->initialised)
2436 vchiq_loud_error("local state has already been "
2437 "initialised");
2438 else
2439 vchiq_loud_error("master/slave mismatch - two %ss",
2440 is_master ? "master" : "slave");
2441 vchiq_loud_error_footer();
2442 return VCHIQ_ERROR;
2443 }
2444
2445 memset(state, 0, sizeof(VCHIQ_STATE_T));
2446
2447 state->id = id++;
2448 state->is_master = is_master;
2449
2450 /*
2451 initialize shared state pointers
2452 */
2453
2454 state->local = local;
2455 state->remote = remote;
2456 state->slot_data = (VCHIQ_SLOT_T *)slot_zero;
2457
2458 /*
2459 initialize events and mutexes
2460 */
2461
2462 _sema_init(&state->connect, 0);
2463 lmutex_init(&state->mutex);
2464 _sema_init(&state->trigger_event, 0);
2465 _sema_init(&state->recycle_event, 0);
2466 _sema_init(&state->sync_trigger_event, 0);
2467 _sema_init(&state->sync_release_event, 0);
2468
2469 lmutex_init(&state->slot_mutex);
2470 lmutex_init(&state->recycle_mutex);
2471 lmutex_init(&state->sync_mutex);
2472 lmutex_init(&state->bulk_transfer_mutex);
2473
2474 _sema_init(&state->slot_available_event, 0);
2475 _sema_init(&state->slot_remove_event, 0);
2476 _sema_init(&state->data_quota_event, 0);
2477
2478 state->slot_queue_available = 0;
2479
2480 for (i = 0; i < VCHIQ_MAX_SERVICES; i++) {
2481 VCHIQ_SERVICE_QUOTA_T *service_quota =
2482 &state->service_quotas[i];
2483 _sema_init(&service_quota->quota_event, 0);
2484 }
2485
2486 for (i = local->slot_first; i <= local->slot_last; i++) {
2487 local->slot_queue[state->slot_queue_available++] = i;
2488 up(&state->slot_available_event);
2489 }
2490
2491 state->default_slot_quota = state->slot_queue_available/2;
2492 state->default_message_quota =
2493 min((unsigned short)(state->default_slot_quota * 256),
2494 (unsigned short)~0);
2495
2496 state->previous_data_index = -1;
2497 state->data_use_count = 0;
2498 state->data_quota = state->slot_queue_available - 1;
2499
2500 local->trigger.event = offsetof(VCHIQ_STATE_T, trigger_event);
2501 remote_event_create(state, &local->trigger);
2502 local->tx_pos = 0;
2503
2504 local->recycle.event = offsetof(VCHIQ_STATE_T, recycle_event);
2505 remote_event_create(state, &local->recycle);
2506 local->slot_queue_recycle = state->slot_queue_available;
2507
2508 local->sync_trigger.event = offsetof(VCHIQ_STATE_T, sync_trigger_event);
2509 remote_event_create(state, &local->sync_trigger);
2510
2511 local->sync_release.event = offsetof(VCHIQ_STATE_T, sync_release_event);
2512 remote_event_create(state, &local->sync_release);
2513
2514 /* At start-of-day, the slot is empty and available */
2515 ((VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, local->slot_sync))->msgid
2516 = VCHIQ_MSGID_PADDING;
2517 remote_event_signal_local(state, &local->sync_release);
2518
2519 local->debug[DEBUG_ENTRIES] = DEBUG_MAX;
2520
2521 status = vchiq_platform_init_state(state);
2522
2523 /*
2524 bring up slot handler thread
2525 */
2526 snprintf(threadname, sizeof(threadname), "VCHIQ-%d", state->id);
2527 state->slot_handler_thread = vchiq_thread_create(&slot_handler_func,
2528 (void *)state,
2529 threadname);
2530
2531 if (state->slot_handler_thread == NULL) {
2532 vchiq_loud_error_header();
2533 vchiq_loud_error("couldn't create thread %s", threadname);
2534 vchiq_loud_error_footer();
2535 return VCHIQ_ERROR;
2536 }
2537 set_user_nice(state->slot_handler_thread, -19);
2538 wake_up_process(state->slot_handler_thread);
2539
2540 snprintf(threadname, sizeof(threadname), "VCHIQr-%d", state->id);
2541 state->recycle_thread = vchiq_thread_create(&recycle_func,
2542 (void *)state,
2543 threadname);
2544 if (state->recycle_thread == NULL) {
2545 vchiq_loud_error_header();
2546 vchiq_loud_error("couldn't create thread %s", threadname);
2547 vchiq_loud_error_footer();
2548 return VCHIQ_ERROR;
2549 }
2550 set_user_nice(state->recycle_thread, -19);
2551 wake_up_process(state->recycle_thread);
2552
2553 snprintf(threadname, sizeof(threadname), "VCHIQs-%d", state->id);
2554 state->sync_thread = vchiq_thread_create(&sync_func,
2555 (void *)state,
2556 threadname);
2557 if (state->sync_thread == NULL) {
2558 vchiq_loud_error_header();
2559 vchiq_loud_error("couldn't create thread %s", threadname);
2560 vchiq_loud_error_footer();
2561 return VCHIQ_ERROR;
2562 }
2563 set_user_nice(state->sync_thread, -20);
2564 wake_up_process(state->sync_thread);
2565
2566 BUG_ON(state->id >= VCHIQ_MAX_STATES);
2567 vchiq_states[state->id] = state;
2568
2569 /* Indicate readiness to the other side */
2570 local->initialised = 1;
2571
2572 return status;
2573 }
2574
2575 /* Called from application thread when a client or server service is created. */
2576 VCHIQ_SERVICE_T *
vchiq_add_service_internal(VCHIQ_STATE_T * state,const VCHIQ_SERVICE_PARAMS_T * params,int srvstate,VCHIQ_INSTANCE_T instance,VCHIQ_USERDATA_TERM_T userdata_term)2577 vchiq_add_service_internal(VCHIQ_STATE_T *state,
2578 const VCHIQ_SERVICE_PARAMS_T *params, int srvstate,
2579 VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term)
2580 {
2581 VCHIQ_SERVICE_T *service;
2582
2583 service = kmalloc(sizeof(VCHIQ_SERVICE_T), GFP_KERNEL);
2584 if (service) {
2585 service->base.fourcc = params->fourcc;
2586 service->base.callback = params->callback;
2587 service->base.userdata = params->userdata;
2588 service->handle = VCHIQ_SERVICE_HANDLE_INVALID;
2589 service->ref_count = 1;
2590 service->srvstate = VCHIQ_SRVSTATE_FREE;
2591 service->userdata_term = userdata_term;
2592 service->localport = VCHIQ_PORT_FREE;
2593 service->remoteport = VCHIQ_PORT_FREE;
2594
2595 service->public_fourcc = (srvstate == VCHIQ_SRVSTATE_OPENING) ?
2596 VCHIQ_FOURCC_INVALID : params->fourcc;
2597 service->client_id = 0;
2598 service->auto_close = 1;
2599 service->sync = 0;
2600 service->closing = 0;
2601 service->trace = 0;
2602 atomic_set(&service->poll_flags, 0);
2603 service->version = params->version;
2604 service->version_min = params->version_min;
2605 service->state = state;
2606 service->instance = instance;
2607 service->service_use_count = 0;
2608 init_bulk_queue(&service->bulk_tx);
2609 init_bulk_queue(&service->bulk_rx);
2610 _sema_init(&service->remove_event, 0);
2611 _sema_init(&service->bulk_remove_event, 0);
2612 lmutex_init(&service->bulk_mutex);
2613 memset(&service->stats, 0, sizeof(service->stats));
2614 } else {
2615 vchiq_log_error(vchiq_core_log_level,
2616 "Out of memory");
2617 }
2618
2619 if (service) {
2620 VCHIQ_SERVICE_T **pservice = NULL;
2621 int i;
2622
2623 /* Although it is perfectly possible to use service_spinlock
2624 ** to protect the creation of services, it is overkill as it
2625 ** disables interrupts while the array is searched.
2626 ** The only danger is of another thread trying to create a
2627 ** service - service deletion is safe.
2628 ** Therefore it is preferable to use state->mutex which,
2629 ** although slower to claim, doesn't block interrupts while
2630 ** it is held.
2631 */
2632
2633 lmutex_lock(&state->mutex);
2634
2635 /* Prepare to use a previously unused service */
2636 if (state->unused_service < VCHIQ_MAX_SERVICES)
2637 pservice = &state->services[state->unused_service];
2638
2639 if (srvstate == VCHIQ_SRVSTATE_OPENING) {
2640 for (i = 0; i < state->unused_service; i++) {
2641 VCHIQ_SERVICE_T *srv = state->services[i];
2642 if (!srv) {
2643 pservice = &state->services[i];
2644 break;
2645 }
2646 }
2647 } else {
2648 for (i = (state->unused_service - 1); i >= 0; i--) {
2649 VCHIQ_SERVICE_T *srv = state->services[i];
2650 if (!srv)
2651 pservice = &state->services[i];
2652 else if ((srv->public_fourcc == params->fourcc)
2653 && ((srv->instance != instance) ||
2654 (srv->base.callback !=
2655 params->callback))) {
2656 /* There is another server using this
2657 ** fourcc which doesn't match. */
2658 pservice = NULL;
2659 break;
2660 }
2661 }
2662 }
2663
2664 if (pservice) {
2665 service->localport = (pservice - state->services);
2666 if (!handle_seq)
2667 handle_seq = VCHIQ_MAX_STATES *
2668 VCHIQ_MAX_SERVICES;
2669 service->handle = handle_seq |
2670 (state->id * VCHIQ_MAX_SERVICES) |
2671 service->localport;
2672 handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES;
2673 *pservice = service;
2674 if (pservice == &state->services[state->unused_service])
2675 state->unused_service++;
2676 }
2677
2678 lmutex_unlock(&state->mutex);
2679
2680 if (!pservice) {
2681 _sema_destroy(&service->remove_event);
2682 _sema_destroy(&service->bulk_remove_event);
2683 lmutex_destroy(&service->bulk_mutex);
2684
2685 kfree(service);
2686 service = NULL;
2687 }
2688 }
2689
2690 if (service) {
2691 VCHIQ_SERVICE_QUOTA_T *service_quota =
2692 &state->service_quotas[service->localport];
2693 service_quota->slot_quota = state->default_slot_quota;
2694 service_quota->message_quota = state->default_message_quota;
2695 if (service_quota->slot_use_count == 0)
2696 service_quota->previous_tx_index =
2697 SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos)
2698 - 1;
2699
2700 /* Bring this service online */
2701 vchiq_set_service_state(service, srvstate);
2702
2703 vchiq_log_info(vchiq_core_msg_log_level,
2704 "%s Service %c%c%c%c SrcPort:%d",
2705 (srvstate == VCHIQ_SRVSTATE_OPENING)
2706 ? "Open" : "Add",
2707 VCHIQ_FOURCC_AS_4CHARS(params->fourcc),
2708 service->localport);
2709 }
2710
2711 /* Don't unlock the service - leave it with a ref_count of 1. */
2712
2713 return service;
2714 }
2715
2716 VCHIQ_STATUS_T
vchiq_open_service_internal(VCHIQ_SERVICE_T * service,int client_id)2717 vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id)
2718 {
2719 struct vchiq_open_payload payload = {
2720 service->base.fourcc,
2721 client_id,
2722 service->version,
2723 service->version_min
2724 };
2725 VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) };
2726 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
2727
2728 service->client_id = client_id;
2729 vchiq_use_service_internal(service);
2730 status = queue_message(service->state, NULL,
2731 VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0),
2732 &body, 1, sizeof(payload), QMFLAGS_IS_BLOCKING);
2733 if (status == VCHIQ_SUCCESS) {
2734 /* Wait for the ACK/NAK */
2735 if (down_interruptible(&service->remove_event) != 0) {
2736 status = VCHIQ_RETRY;
2737 vchiq_release_service_internal(service);
2738 } else if ((service->srvstate != VCHIQ_SRVSTATE_OPEN) &&
2739 (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) {
2740 if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT)
2741 vchiq_log_error(vchiq_core_log_level,
2742 "%d: osi - srvstate = %s (ref %d)",
2743 service->state->id,
2744 srvstate_names[service->srvstate],
2745 service->ref_count);
2746 status = VCHIQ_ERROR;
2747 VCHIQ_SERVICE_STATS_INC(service, error_count);
2748 vchiq_release_service_internal(service);
2749 }
2750 }
2751 return status;
2752 }
2753
2754 static void
release_service_messages(VCHIQ_SERVICE_T * service)2755 release_service_messages(VCHIQ_SERVICE_T *service)
2756 {
2757 VCHIQ_STATE_T *state = service->state;
2758 int slot_last = state->remote->slot_last;
2759 int i;
2760
2761 /* Release any claimed messages aimed at this service */
2762
2763 if (service->sync) {
2764 VCHIQ_HEADER_T *header =
2765 (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state,
2766 state->remote->slot_sync);
2767 if (VCHIQ_MSG_DSTPORT(header->msgid) == service->localport)
2768 release_message_sync(state, header);
2769
2770 return;
2771 }
2772
2773 for (i = state->remote->slot_first; i <= slot_last; i++) {
2774 VCHIQ_SLOT_INFO_T *slot_info =
2775 SLOT_INFO_FROM_INDEX(state, i);
2776 if (slot_info->release_count != slot_info->use_count) {
2777 char *data =
2778 (char *)SLOT_DATA_FROM_INDEX(state, i);
2779 unsigned int pos, end;
2780
2781 end = VCHIQ_SLOT_SIZE;
2782 if (data == state->rx_data)
2783 /* This buffer is still being read from - stop
2784 ** at the current read position */
2785 end = state->rx_pos & VCHIQ_SLOT_MASK;
2786
2787 pos = 0;
2788
2789 while (pos < end) {
2790 VCHIQ_HEADER_T *header =
2791 (VCHIQ_HEADER_T *)(data + pos);
2792 int msgid = header->msgid;
2793 int port = VCHIQ_MSG_DSTPORT(msgid);
2794 if ((port == service->localport) &&
2795 (msgid & VCHIQ_MSGID_CLAIMED)) {
2796 vchiq_log_info(vchiq_core_log_level,
2797 " fsi - hdr %p",
2798 header);
2799 release_slot(state, slot_info, header,
2800 NULL);
2801 }
2802 pos += calc_stride(header->size);
2803 if (pos > VCHIQ_SLOT_SIZE) {
2804 vchiq_log_error(vchiq_core_log_level,
2805 "fsi - pos %x: header %p, "
2806 "msgid %x, header->msgid %x, "
2807 "header->size %x",
2808 pos, header,
2809 msgid, header->msgid,
2810 header->size);
2811 WARN(1, "invalid slot position\n");
2812 }
2813 }
2814 }
2815 }
2816 }
2817
2818 static int
do_abort_bulks(VCHIQ_SERVICE_T * service)2819 do_abort_bulks(VCHIQ_SERVICE_T *service)
2820 {
2821 VCHIQ_STATUS_T status;
2822
2823 /* Abort any outstanding bulk transfers */
2824 if (lmutex_lock_interruptible(&service->bulk_mutex) != 0)
2825 return 0;
2826 abort_outstanding_bulks(service, &service->bulk_tx);
2827 abort_outstanding_bulks(service, &service->bulk_rx);
2828 lmutex_unlock(&service->bulk_mutex);
2829
2830 status = notify_bulks(service, &service->bulk_tx, 0/*!retry_poll*/);
2831 if (status == VCHIQ_SUCCESS)
2832 status = notify_bulks(service, &service->bulk_rx,
2833 0/*!retry_poll*/);
2834 return (status == VCHIQ_SUCCESS);
2835 }
2836
2837 static VCHIQ_STATUS_T
close_service_complete(VCHIQ_SERVICE_T * service,int failstate)2838 close_service_complete(VCHIQ_SERVICE_T *service, int failstate)
2839 {
2840 VCHIQ_STATUS_T status;
2841 int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID);
2842 int newstate;
2843
2844 switch (service->srvstate) {
2845 case VCHIQ_SRVSTATE_OPEN:
2846 case VCHIQ_SRVSTATE_CLOSESENT:
2847 case VCHIQ_SRVSTATE_CLOSERECVD:
2848 if (is_server) {
2849 if (service->auto_close) {
2850 service->client_id = 0;
2851 service->remoteport = VCHIQ_PORT_FREE;
2852 newstate = VCHIQ_SRVSTATE_LISTENING;
2853 } else
2854 newstate = VCHIQ_SRVSTATE_CLOSEWAIT;
2855 } else
2856 newstate = VCHIQ_SRVSTATE_CLOSED;
2857 vchiq_set_service_state(service, newstate);
2858 break;
2859 case VCHIQ_SRVSTATE_LISTENING:
2860 break;
2861 default:
2862 vchiq_log_error(vchiq_core_log_level,
2863 "close_service_complete(%x) called in state %s",
2864 service->handle, srvstate_names[service->srvstate]);
2865 WARN(1, "close_service_complete in unexpected state\n");
2866 return VCHIQ_ERROR;
2867 }
2868
2869 status = make_service_callback(service,
2870 VCHIQ_SERVICE_CLOSED, NULL, NULL);
2871
2872 if (status != VCHIQ_RETRY) {
2873 int uc = service->service_use_count;
2874 int i;
2875 /* Complete the close process */
2876 for (i = 0; i < uc; i++)
2877 /* cater for cases where close is forced and the
2878 ** client may not close all it's handles */
2879 vchiq_release_service_internal(service);
2880
2881 service->client_id = 0;
2882 service->remoteport = VCHIQ_PORT_FREE;
2883
2884 if (service->srvstate == VCHIQ_SRVSTATE_CLOSED)
2885 vchiq_free_service_internal(service);
2886 else if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) {
2887 if (is_server)
2888 service->closing = 0;
2889
2890 up(&service->remove_event);
2891 }
2892 } else
2893 vchiq_set_service_state(service, failstate);
2894
2895 return status;
2896 }
2897
2898 /* Called by the slot handler */
2899 VCHIQ_STATUS_T
vchiq_close_service_internal(VCHIQ_SERVICE_T * service,int close_recvd)2900 vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd)
2901 {
2902 VCHIQ_STATE_T *state = service->state;
2903 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
2904 int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID);
2905
2906 vchiq_log_info(vchiq_core_log_level, "%d: csi:%d,%d (%s)",
2907 service->state->id, service->localport, close_recvd,
2908 srvstate_names[service->srvstate]);
2909
2910 switch (service->srvstate) {
2911 case VCHIQ_SRVSTATE_CLOSED:
2912 case VCHIQ_SRVSTATE_HIDDEN:
2913 case VCHIQ_SRVSTATE_LISTENING:
2914 case VCHIQ_SRVSTATE_CLOSEWAIT:
2915 if (close_recvd)
2916 vchiq_log_error(vchiq_core_log_level,
2917 "vchiq_close_service_internal(1) called "
2918 "in state %s",
2919 srvstate_names[service->srvstate]);
2920 else if (is_server) {
2921 if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) {
2922 status = VCHIQ_ERROR;
2923 } else {
2924 service->client_id = 0;
2925 service->remoteport = VCHIQ_PORT_FREE;
2926 if (service->srvstate ==
2927 VCHIQ_SRVSTATE_CLOSEWAIT)
2928 vchiq_set_service_state(service,
2929 VCHIQ_SRVSTATE_LISTENING);
2930 }
2931 up(&service->remove_event);
2932 } else
2933 vchiq_free_service_internal(service);
2934 break;
2935 case VCHIQ_SRVSTATE_OPENING:
2936 if (close_recvd) {
2937 /* The open was rejected - tell the user */
2938 vchiq_set_service_state(service,
2939 VCHIQ_SRVSTATE_CLOSEWAIT);
2940 up(&service->remove_event);
2941 } else {
2942 /* Shutdown mid-open - let the other side know */
2943 status = queue_message(state, service,
2944 VCHIQ_MAKE_MSG
2945 (VCHIQ_MSG_CLOSE,
2946 service->localport,
2947 VCHIQ_MSG_DSTPORT(service->remoteport)),
2948 NULL, 0, 0, 0);
2949 }
2950 break;
2951
2952 case VCHIQ_SRVSTATE_OPENSYNC:
2953 lmutex_lock(&state->sync_mutex);
2954 /* Drop through */
2955
2956 case VCHIQ_SRVSTATE_OPEN:
2957 if (state->is_master || close_recvd) {
2958 if (!do_abort_bulks(service))
2959 status = VCHIQ_RETRY;
2960 }
2961
2962 release_service_messages(service);
2963
2964 if (status == VCHIQ_SUCCESS)
2965 status = queue_message(state, service,
2966 VCHIQ_MAKE_MSG
2967 (VCHIQ_MSG_CLOSE,
2968 service->localport,
2969 VCHIQ_MSG_DSTPORT(service->remoteport)),
2970 NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK);
2971
2972 if (status == VCHIQ_SUCCESS) {
2973 if (!close_recvd) {
2974 /* Change the state while the mutex is
2975 still held */
2976 vchiq_set_service_state(service,
2977 VCHIQ_SRVSTATE_CLOSESENT);
2978 lmutex_unlock(&state->slot_mutex);
2979 if (service->sync)
2980 lmutex_unlock(&state->sync_mutex);
2981 break;
2982 }
2983 } else if (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC) {
2984 lmutex_unlock(&state->sync_mutex);
2985 break;
2986 } else
2987 break;
2988
2989 /* Change the state while the mutex is still held */
2990 vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSERECVD);
2991 lmutex_unlock(&state->slot_mutex);
2992 if (service->sync)
2993 lmutex_unlock(&state->sync_mutex);
2994
2995 status = close_service_complete(service,
2996 VCHIQ_SRVSTATE_CLOSERECVD);
2997 break;
2998
2999 case VCHIQ_SRVSTATE_CLOSESENT:
3000 if (!close_recvd)
3001 /* This happens when a process is killed mid-close */
3002 break;
3003
3004 if (!state->is_master) {
3005 if (!do_abort_bulks(service)) {
3006 status = VCHIQ_RETRY;
3007 break;
3008 }
3009 }
3010
3011 if (status == VCHIQ_SUCCESS)
3012 status = close_service_complete(service,
3013 VCHIQ_SRVSTATE_CLOSERECVD);
3014 break;
3015
3016 case VCHIQ_SRVSTATE_CLOSERECVD:
3017 if (!close_recvd && is_server)
3018 /* Force into LISTENING mode */
3019 vchiq_set_service_state(service,
3020 VCHIQ_SRVSTATE_LISTENING);
3021 status = close_service_complete(service,
3022 VCHIQ_SRVSTATE_CLOSERECVD);
3023 break;
3024
3025 default:
3026 vchiq_log_error(vchiq_core_log_level,
3027 "vchiq_close_service_internal(%d) called in state %s",
3028 close_recvd, srvstate_names[service->srvstate]);
3029 break;
3030 }
3031
3032 return status;
3033 }
3034
3035 /* Called from the application process upon process death */
3036 void
vchiq_terminate_service_internal(VCHIQ_SERVICE_T * service)3037 vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service)
3038 {
3039 VCHIQ_STATE_T *state = service->state;
3040
3041 vchiq_log_info(vchiq_core_log_level, "%d: tsi - (%d<->%d)",
3042 state->id, service->localport, service->remoteport);
3043
3044 mark_service_closing(service);
3045
3046 /* Mark the service for removal by the slot handler */
3047 request_poll(state, service, VCHIQ_POLL_REMOVE);
3048 }
3049
3050 /* Called from the slot handler */
3051 void
vchiq_free_service_internal(VCHIQ_SERVICE_T * service)3052 vchiq_free_service_internal(VCHIQ_SERVICE_T *service)
3053 {
3054 VCHIQ_STATE_T *state = service->state;
3055
3056 vchiq_log_info(vchiq_core_log_level, "%d: fsi - (%d)",
3057 state->id, service->localport);
3058
3059 switch (service->srvstate) {
3060 case VCHIQ_SRVSTATE_OPENING:
3061 case VCHIQ_SRVSTATE_CLOSED:
3062 case VCHIQ_SRVSTATE_HIDDEN:
3063 case VCHIQ_SRVSTATE_LISTENING:
3064 case VCHIQ_SRVSTATE_CLOSEWAIT:
3065 break;
3066 default:
3067 vchiq_log_error(vchiq_core_log_level,
3068 "%d: fsi - (%d) in state %s",
3069 state->id, service->localport,
3070 srvstate_names[service->srvstate]);
3071 return;
3072 }
3073
3074 vchiq_set_service_state(service, VCHIQ_SRVSTATE_FREE);
3075
3076 up(&service->remove_event);
3077
3078 /* Release the initial lock */
3079 unlock_service(service);
3080 }
3081
3082 VCHIQ_STATUS_T
vchiq_connect_internal(VCHIQ_STATE_T * state,VCHIQ_INSTANCE_T instance)3083 vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance)
3084 {
3085 VCHIQ_SERVICE_T *service;
3086 int i;
3087
3088 /* Find all services registered to this client and enable them. */
3089 i = 0;
3090 while ((service = next_service_by_instance(state, instance,
3091 &i)) != NULL) {
3092 if (service->srvstate == VCHIQ_SRVSTATE_HIDDEN)
3093 vchiq_set_service_state(service,
3094 VCHIQ_SRVSTATE_LISTENING);
3095 unlock_service(service);
3096 }
3097
3098 if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) {
3099 if (queue_message(state, NULL,
3100 VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0,
3101 0, QMFLAGS_IS_BLOCKING) == VCHIQ_RETRY)
3102 return VCHIQ_RETRY;
3103
3104 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING);
3105 }
3106
3107 if (state->conn_state == VCHIQ_CONNSTATE_CONNECTING) {
3108 if (down_interruptible(&state->connect) != 0)
3109 return VCHIQ_RETRY;
3110
3111 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED);
3112 up(&state->connect);
3113 }
3114
3115 return VCHIQ_SUCCESS;
3116 }
3117
3118 VCHIQ_STATUS_T
vchiq_shutdown_internal(VCHIQ_STATE_T * state,VCHIQ_INSTANCE_T instance)3119 vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance)
3120 {
3121 VCHIQ_SERVICE_T *service;
3122 int i;
3123
3124 /* Find all services registered to this client and enable them. */
3125 i = 0;
3126 while ((service = next_service_by_instance(state, instance,
3127 &i)) != NULL) {
3128 (void)vchiq_remove_service(service->handle);
3129 unlock_service(service);
3130 }
3131
3132 return VCHIQ_SUCCESS;
3133 }
3134
3135 VCHIQ_STATUS_T
vchiq_pause_internal(VCHIQ_STATE_T * state)3136 vchiq_pause_internal(VCHIQ_STATE_T *state)
3137 {
3138 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
3139
3140 switch (state->conn_state) {
3141 case VCHIQ_CONNSTATE_CONNECTED:
3142 /* Request a pause */
3143 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSING);
3144 request_poll(state, NULL, 0);
3145 break;
3146 default:
3147 vchiq_log_error(vchiq_core_log_level,
3148 "vchiq_pause_internal in state %s\n",
3149 conn_state_names[state->conn_state]);
3150 status = VCHIQ_ERROR;
3151 VCHIQ_STATS_INC(state, error_count);
3152 break;
3153 }
3154
3155 return status;
3156 }
3157
3158 VCHIQ_STATUS_T
vchiq_resume_internal(VCHIQ_STATE_T * state)3159 vchiq_resume_internal(VCHIQ_STATE_T *state)
3160 {
3161 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
3162
3163 if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) {
3164 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_RESUMING);
3165 request_poll(state, NULL, 0);
3166 } else {
3167 status = VCHIQ_ERROR;
3168 VCHIQ_STATS_INC(state, error_count);
3169 }
3170
3171 return status;
3172 }
3173
3174 VCHIQ_STATUS_T
vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle)3175 vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle)
3176 {
3177 /* Unregister the service */
3178 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3179 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
3180
3181 if (!service)
3182 return VCHIQ_ERROR;
3183
3184 vchiq_log_info(vchiq_core_log_level,
3185 "%d: close_service:%d",
3186 service->state->id, service->localport);
3187
3188 if ((service->srvstate == VCHIQ_SRVSTATE_FREE) ||
3189 (service->srvstate == VCHIQ_SRVSTATE_LISTENING) ||
3190 (service->srvstate == VCHIQ_SRVSTATE_HIDDEN)) {
3191 unlock_service(service);
3192 return VCHIQ_ERROR;
3193 }
3194
3195 mark_service_closing(service);
3196
3197 if (current == service->state->slot_handler_thread) {
3198 status = vchiq_close_service_internal(service,
3199 0/*!close_recvd*/);
3200 BUG_ON(status == VCHIQ_RETRY);
3201 } else {
3202 /* Mark the service for termination by the slot handler */
3203 request_poll(service->state, service, VCHIQ_POLL_TERMINATE);
3204 }
3205
3206 while (1) {
3207 if (down_interruptible(&service->remove_event) != 0) {
3208 status = VCHIQ_RETRY;
3209 break;
3210 }
3211
3212 if ((service->srvstate == VCHIQ_SRVSTATE_FREE) ||
3213 (service->srvstate == VCHIQ_SRVSTATE_LISTENING) ||
3214 (service->srvstate == VCHIQ_SRVSTATE_OPEN))
3215 break;
3216
3217 vchiq_log_warning(vchiq_core_log_level,
3218 "%d: close_service:%d - waiting in state %s",
3219 service->state->id, service->localport,
3220 srvstate_names[service->srvstate]);
3221 }
3222
3223 if ((status == VCHIQ_SUCCESS) &&
3224 (service->srvstate != VCHIQ_SRVSTATE_FREE) &&
3225 (service->srvstate != VCHIQ_SRVSTATE_LISTENING))
3226 status = VCHIQ_ERROR;
3227
3228 unlock_service(service);
3229
3230 return status;
3231 }
3232
3233 VCHIQ_STATUS_T
vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle)3234 vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle)
3235 {
3236 /* Unregister the service */
3237 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3238 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
3239
3240 if (!service)
3241 return VCHIQ_ERROR;
3242
3243 vchiq_log_info(vchiq_core_log_level,
3244 "%d: remove_service:%d",
3245 service->state->id, service->localport);
3246
3247 if (service->srvstate == VCHIQ_SRVSTATE_FREE) {
3248 unlock_service(service);
3249 return VCHIQ_ERROR;
3250 }
3251
3252 mark_service_closing(service);
3253
3254 if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) ||
3255 (current == service->state->slot_handler_thread)) {
3256 /* Make it look like a client, because it must be removed and
3257 not left in the LISTENING state. */
3258 service->public_fourcc = VCHIQ_FOURCC_INVALID;
3259
3260 status = vchiq_close_service_internal(service,
3261 0/*!close_recvd*/);
3262 BUG_ON(status == VCHIQ_RETRY);
3263 } else {
3264 /* Mark the service for removal by the slot handler */
3265 request_poll(service->state, service, VCHIQ_POLL_REMOVE);
3266 }
3267 while (1) {
3268 if (down_interruptible(&service->remove_event) != 0) {
3269 status = VCHIQ_RETRY;
3270 break;
3271 }
3272
3273 if ((service->srvstate == VCHIQ_SRVSTATE_FREE) ||
3274 (service->srvstate == VCHIQ_SRVSTATE_OPEN))
3275 break;
3276
3277 vchiq_log_warning(vchiq_core_log_level,
3278 "%d: remove_service:%d - waiting in state %s",
3279 service->state->id, service->localport,
3280 srvstate_names[service->srvstate]);
3281 }
3282
3283 if ((status == VCHIQ_SUCCESS) &&
3284 (service->srvstate != VCHIQ_SRVSTATE_FREE))
3285 status = VCHIQ_ERROR;
3286
3287 unlock_service(service);
3288
3289 return status;
3290 }
3291
3292
3293 /* This function may be called by kernel threads or user threads.
3294 * User threads may receive VCHIQ_RETRY to indicate that a signal has been
3295 * received and the call should be retried after being returned to user
3296 * context.
3297 * When called in blocking mode, the userdata field points to a bulk_waiter
3298 * structure.
3299 */
3300 VCHIQ_STATUS_T
vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle,VCHI_MEM_HANDLE_T memhandle,void * offset,int size,void * userdata,VCHIQ_BULK_MODE_T mode,VCHIQ_BULK_DIR_T dir)3301 vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle,
3302 VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata,
3303 VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir)
3304 {
3305 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3306 VCHIQ_BULK_QUEUE_T *queue;
3307 VCHIQ_BULK_T *bulk;
3308 VCHIQ_STATE_T *state;
3309 struct bulk_waiter *bulk_waiter = NULL;
3310 const char dir_char = (dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r';
3311 const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ?
3312 VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX;
3313 VCHIQ_STATUS_T status = VCHIQ_ERROR;
3314
3315 if (!service ||
3316 (service->srvstate != VCHIQ_SRVSTATE_OPEN) ||
3317 ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL)) ||
3318 (vchiq_check_service(service) != VCHIQ_SUCCESS))
3319 goto error_exit;
3320
3321 switch (mode) {
3322 case VCHIQ_BULK_MODE_NOCALLBACK:
3323 case VCHIQ_BULK_MODE_CALLBACK:
3324 break;
3325 case VCHIQ_BULK_MODE_BLOCKING:
3326 bulk_waiter = (struct bulk_waiter *)userdata;
3327 _sema_init(&bulk_waiter->event, 0);
3328 bulk_waiter->actual = 0;
3329 bulk_waiter->bulk = NULL;
3330 break;
3331 case VCHIQ_BULK_MODE_WAITING:
3332 bulk_waiter = (struct bulk_waiter *)userdata;
3333 bulk = bulk_waiter->bulk;
3334 goto waiting;
3335 default:
3336 goto error_exit;
3337 }
3338
3339 state = service->state;
3340
3341 queue = (dir == VCHIQ_BULK_TRANSMIT) ?
3342 &service->bulk_tx : &service->bulk_rx;
3343
3344 if (lmutex_lock_interruptible(&service->bulk_mutex) != 0) {
3345 status = VCHIQ_RETRY;
3346 goto error_exit;
3347 }
3348
3349 if (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS) {
3350 VCHIQ_SERVICE_STATS_INC(service, bulk_stalls);
3351 do {
3352 lmutex_unlock(&service->bulk_mutex);
3353 if (down_interruptible(&service->bulk_remove_event)
3354 != 0) {
3355 status = VCHIQ_RETRY;
3356 goto error_exit;
3357 }
3358 if (lmutex_lock_interruptible(&service->bulk_mutex)
3359 != 0) {
3360 status = VCHIQ_RETRY;
3361 goto error_exit;
3362 }
3363 } while (queue->local_insert == queue->remove +
3364 VCHIQ_NUM_SERVICE_BULKS);
3365 }
3366
3367 bulk = &queue->bulks[BULK_INDEX(queue->local_insert)];
3368
3369 bulk->mode = mode;
3370 bulk->dir = dir;
3371 bulk->userdata = userdata;
3372 bulk->size = size;
3373 bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED;
3374
3375 if (vchiq_prepare_bulk_data(bulk, memhandle, offset, size, dir) !=
3376 VCHIQ_SUCCESS)
3377 goto unlock_error_exit;
3378
3379 wmb();
3380
3381 vchiq_log_info(vchiq_core_log_level,
3382 "%d: bt (%d->%d) %cx %x@%p %p",
3383 state->id,
3384 service->localport, service->remoteport, dir_char,
3385 size, bulk->data, userdata);
3386
3387 /* The slot mutex must be held when the service is being closed, so
3388 claim it here to ensure that isn't happening */
3389 if (lmutex_lock_interruptible(&state->slot_mutex) != 0) {
3390 status = VCHIQ_RETRY;
3391 goto cancel_bulk_error_exit;
3392 }
3393
3394 if (service->srvstate != VCHIQ_SRVSTATE_OPEN)
3395 goto unlock_both_error_exit;
3396
3397 if (state->is_master) {
3398 queue->local_insert++;
3399 if (resolve_bulks(service, queue))
3400 request_poll(state, service,
3401 (dir == VCHIQ_BULK_TRANSMIT) ?
3402 VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY);
3403 } else {
3404 uint32_t payload[2] = { (uint32_t)(uintptr_t)bulk->data, bulk->size };
3405 VCHIQ_ELEMENT_T element = { payload, sizeof(payload) };
3406
3407 status = queue_message(state, NULL,
3408 VCHIQ_MAKE_MSG(dir_msgtype,
3409 service->localport, service->remoteport),
3410 &element, 1, sizeof(payload),
3411 QMFLAGS_IS_BLOCKING |
3412 QMFLAGS_NO_MUTEX_LOCK |
3413 QMFLAGS_NO_MUTEX_UNLOCK);
3414 if (status != VCHIQ_SUCCESS) {
3415 goto unlock_both_error_exit;
3416 }
3417 queue->local_insert++;
3418 }
3419
3420 lmutex_unlock(&state->slot_mutex);
3421 lmutex_unlock(&service->bulk_mutex);
3422
3423 vchiq_log_trace(vchiq_core_log_level,
3424 "%d: bt:%d %cx li=%x ri=%x p=%x",
3425 state->id,
3426 service->localport, dir_char,
3427 queue->local_insert, queue->remote_insert, queue->process);
3428
3429 waiting:
3430 unlock_service(service);
3431
3432 status = VCHIQ_SUCCESS;
3433
3434 if (bulk_waiter) {
3435 bulk_waiter->bulk = bulk;
3436 if (down_interruptible(&bulk_waiter->event) != 0)
3437 status = VCHIQ_RETRY;
3438 else if (bulk_waiter->actual == VCHIQ_BULK_ACTUAL_ABORTED)
3439 status = VCHIQ_ERROR;
3440 }
3441
3442 return status;
3443
3444 unlock_both_error_exit:
3445 lmutex_unlock(&state->slot_mutex);
3446 cancel_bulk_error_exit:
3447 vchiq_complete_bulk(bulk);
3448 unlock_error_exit:
3449 lmutex_unlock(&service->bulk_mutex);
3450
3451 error_exit:
3452 if (service)
3453 unlock_service(service);
3454 return status;
3455 }
3456
3457 VCHIQ_STATUS_T
vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,const VCHIQ_ELEMENT_T * elements,unsigned int count)3458 vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
3459 const VCHIQ_ELEMENT_T *elements, unsigned int count)
3460 {
3461 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3462 VCHIQ_STATUS_T status = VCHIQ_ERROR;
3463
3464 unsigned int size = 0;
3465 unsigned int i;
3466
3467 if (!service ||
3468 (vchiq_check_service(service) != VCHIQ_SUCCESS))
3469 goto error_exit;
3470
3471 for (i = 0; i < (unsigned int)count; i++) {
3472 if (elements[i].size) {
3473 if (elements[i].data == NULL) {
3474 VCHIQ_SERVICE_STATS_INC(service, error_count);
3475 goto error_exit;
3476 }
3477 size += elements[i].size;
3478 }
3479 }
3480
3481 if (size > VCHIQ_MAX_MSG_SIZE) {
3482 VCHIQ_SERVICE_STATS_INC(service, error_count);
3483 goto error_exit;
3484 }
3485
3486 switch (service->srvstate) {
3487 case VCHIQ_SRVSTATE_OPEN:
3488 status = queue_message(service->state, service,
3489 VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
3490 service->localport,
3491 service->remoteport),
3492 elements, count, size, 1);
3493 break;
3494 case VCHIQ_SRVSTATE_OPENSYNC:
3495 status = queue_message_sync(service->state, service,
3496 VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
3497 service->localport,
3498 service->remoteport),
3499 elements, count, size, 1);
3500 break;
3501 default:
3502 status = VCHIQ_ERROR;
3503 break;
3504 }
3505
3506 error_exit:
3507 if (service)
3508 unlock_service(service);
3509
3510 return status;
3511 }
3512
3513 void
vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle,VCHIQ_HEADER_T * header)3514 vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, VCHIQ_HEADER_T *header)
3515 {
3516 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3517 VCHIQ_SHARED_STATE_T *remote;
3518 VCHIQ_STATE_T *state;
3519 int slot_index;
3520
3521 if (!service)
3522 return;
3523
3524 state = service->state;
3525 remote = state->remote;
3526
3527 slot_index = SLOT_INDEX_FROM_DATA(state, (void *)header);
3528
3529 if ((slot_index >= remote->slot_first) &&
3530 (slot_index <= remote->slot_last)) {
3531 int msgid = header->msgid;
3532 if (msgid & VCHIQ_MSGID_CLAIMED) {
3533 VCHIQ_SLOT_INFO_T *slot_info =
3534 SLOT_INFO_FROM_INDEX(state, slot_index);
3535
3536 release_slot(state, slot_info, header, service);
3537 }
3538 } else if (slot_index == remote->slot_sync)
3539 release_message_sync(state, header);
3540
3541 unlock_service(service);
3542 }
3543
3544 static void
release_message_sync(VCHIQ_STATE_T * state,VCHIQ_HEADER_T * header)3545 release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header)
3546 {
3547 header->msgid = VCHIQ_MSGID_PADDING;
3548 remote_event_signal(&state->remote->sync_release);
3549 }
3550
3551 VCHIQ_STATUS_T
vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle,short * peer_version)3552 vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, short *peer_version)
3553 {
3554 VCHIQ_STATUS_T status = VCHIQ_ERROR;
3555 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3556
3557 if (!service ||
3558 (vchiq_check_service(service) != VCHIQ_SUCCESS) ||
3559 !peer_version)
3560 goto exit;
3561 *peer_version = service->peer_version;
3562 status = VCHIQ_SUCCESS;
3563
3564 exit:
3565 if (service)
3566 unlock_service(service);
3567 return status;
3568 }
3569
3570 VCHIQ_STATUS_T
vchiq_get_config(VCHIQ_INSTANCE_T instance,int config_size,VCHIQ_CONFIG_T * pconfig)3571 vchiq_get_config(VCHIQ_INSTANCE_T instance,
3572 int config_size, VCHIQ_CONFIG_T *pconfig)
3573 {
3574 VCHIQ_CONFIG_T config;
3575
3576 (void)instance;
3577
3578 config.max_msg_size = VCHIQ_MAX_MSG_SIZE;
3579 config.bulk_threshold = VCHIQ_MAX_MSG_SIZE;
3580 config.max_outstanding_bulks = VCHIQ_NUM_SERVICE_BULKS;
3581 config.max_services = VCHIQ_MAX_SERVICES;
3582 config.version = VCHIQ_VERSION;
3583 config.version_min = VCHIQ_VERSION_MIN;
3584
3585 if (config_size > sizeof(VCHIQ_CONFIG_T))
3586 return VCHIQ_ERROR;
3587
3588 memcpy(pconfig, &config,
3589 min(config_size, (int)(sizeof(VCHIQ_CONFIG_T))));
3590
3591 return VCHIQ_SUCCESS;
3592 }
3593
3594 VCHIQ_STATUS_T
vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle,VCHIQ_SERVICE_OPTION_T option,int value)3595 vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle,
3596 VCHIQ_SERVICE_OPTION_T option, int value)
3597 {
3598 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3599 VCHIQ_STATUS_T status = VCHIQ_ERROR;
3600
3601 if (service) {
3602 switch (option) {
3603 case VCHIQ_SERVICE_OPTION_AUTOCLOSE:
3604 service->auto_close = value;
3605 status = VCHIQ_SUCCESS;
3606 break;
3607
3608 case VCHIQ_SERVICE_OPTION_SLOT_QUOTA: {
3609 VCHIQ_SERVICE_QUOTA_T *service_quota =
3610 &service->state->service_quotas[
3611 service->localport];
3612 if (value == 0)
3613 value = service->state->default_slot_quota;
3614 if ((value >= service_quota->slot_use_count) &&
3615 (value < (unsigned short)~0)) {
3616 service_quota->slot_quota = value;
3617 if ((value >= service_quota->slot_use_count) &&
3618 (service_quota->message_quota >=
3619 service_quota->message_use_count)) {
3620 /* Signal the service that it may have
3621 ** dropped below its quota */
3622 up(&service_quota->quota_event);
3623 }
3624 status = VCHIQ_SUCCESS;
3625 }
3626 } break;
3627
3628 case VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA: {
3629 VCHIQ_SERVICE_QUOTA_T *service_quota =
3630 &service->state->service_quotas[
3631 service->localport];
3632 if (value == 0)
3633 value = service->state->default_message_quota;
3634 if ((value >= service_quota->message_use_count) &&
3635 (value < (unsigned short)~0)) {
3636 service_quota->message_quota = value;
3637 if ((value >=
3638 service_quota->message_use_count) &&
3639 (service_quota->slot_quota >=
3640 service_quota->slot_use_count))
3641 /* Signal the service that it may have
3642 ** dropped below its quota */
3643 up(&service_quota->quota_event);
3644 status = VCHIQ_SUCCESS;
3645 }
3646 } break;
3647
3648 case VCHIQ_SERVICE_OPTION_SYNCHRONOUS:
3649 if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) ||
3650 (service->srvstate ==
3651 VCHIQ_SRVSTATE_LISTENING)) {
3652 service->sync = value;
3653 status = VCHIQ_SUCCESS;
3654 }
3655 break;
3656
3657 case VCHIQ_SERVICE_OPTION_TRACE:
3658 service->trace = value;
3659 status = VCHIQ_SUCCESS;
3660 break;
3661
3662 default:
3663 break;
3664 }
3665 unlock_service(service);
3666 }
3667
3668 return status;
3669 }
3670
3671 static void
vchiq_dump_shared_state(void * dump_context,VCHIQ_STATE_T * state,VCHIQ_SHARED_STATE_T * shared,const char * label)3672 vchiq_dump_shared_state(void *dump_context, VCHIQ_STATE_T *state,
3673 VCHIQ_SHARED_STATE_T *shared, const char *label)
3674 {
3675 static const char *const debug_names[] = {
3676 "<entries>",
3677 "SLOT_HANDLER_COUNT",
3678 "SLOT_HANDLER_LINE",
3679 "PARSE_LINE",
3680 "PARSE_HEADER",
3681 "PARSE_MSGID",
3682 "AWAIT_COMPLETION_LINE",
3683 "DEQUEUE_MESSAGE_LINE",
3684 "SERVICE_CALLBACK_LINE",
3685 "MSG_QUEUE_FULL_COUNT",
3686 "COMPLETION_QUEUE_FULL_COUNT"
3687 };
3688 int i;
3689
3690 char buf[80];
3691 int len;
3692 len = snprintf(buf, sizeof(buf),
3693 " %s: slots %d-%d tx_pos=%x recycle=%x",
3694 label, shared->slot_first, shared->slot_last,
3695 shared->tx_pos, shared->slot_queue_recycle);
3696 vchiq_dump(dump_context, buf, len + 1);
3697
3698 len = snprintf(buf, sizeof(buf),
3699 " Slots claimed:");
3700 vchiq_dump(dump_context, buf, len + 1);
3701
3702 for (i = shared->slot_first; i <= shared->slot_last; i++) {
3703 VCHIQ_SLOT_INFO_T slot_info = *SLOT_INFO_FROM_INDEX(state, i);
3704 if (slot_info.use_count != slot_info.release_count) {
3705 len = snprintf(buf, sizeof(buf),
3706 " %d: %d/%d", i, slot_info.use_count,
3707 slot_info.release_count);
3708 vchiq_dump(dump_context, buf, len + 1);
3709 }
3710 }
3711
3712 for (i = 1; i < shared->debug[DEBUG_ENTRIES]; i++) {
3713 len = snprintf(buf, sizeof(buf), " DEBUG: %s = %d(%x)",
3714 debug_names[i], shared->debug[i], shared->debug[i]);
3715 vchiq_dump(dump_context, buf, len + 1);
3716 }
3717 }
3718
3719 void
vchiq_dump_state(void * dump_context,VCHIQ_STATE_T * state)3720 vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state)
3721 {
3722 char buf[80];
3723 int len;
3724 int i;
3725
3726 len = snprintf(buf, sizeof(buf), "State %d: %s", state->id,
3727 conn_state_names[state->conn_state]);
3728 vchiq_dump(dump_context, buf, len + 1);
3729
3730 len = snprintf(buf, sizeof(buf),
3731 " tx_pos=%x(@%p), rx_pos=%x(@%p)",
3732 state->local->tx_pos,
3733 state->tx_data +
3734 (state->local_tx_pos & VCHIQ_SLOT_MASK),
3735 state->rx_pos,
3736 state->rx_data +
3737 (state->rx_pos & VCHIQ_SLOT_MASK));
3738 vchiq_dump(dump_context, buf, len + 1);
3739
3740 len = snprintf(buf, sizeof(buf),
3741 " Version: %d (min %d)",
3742 VCHIQ_VERSION, VCHIQ_VERSION_MIN);
3743 vchiq_dump(dump_context, buf, len + 1);
3744
3745 if (VCHIQ_ENABLE_STATS) {
3746 len = snprintf(buf, sizeof(buf),
3747 " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, "
3748 "error_count=%d",
3749 state->stats.ctrl_tx_count, state->stats.ctrl_rx_count,
3750 state->stats.error_count);
3751 vchiq_dump(dump_context, buf, len + 1);
3752 }
3753
3754 len = snprintf(buf, sizeof(buf),
3755 " Slots: %d available (%d data), %d recyclable, %d stalls "
3756 "(%d data)",
3757 ((state->slot_queue_available * VCHIQ_SLOT_SIZE) -
3758 state->local_tx_pos) / VCHIQ_SLOT_SIZE,
3759 state->data_quota - state->data_use_count,
3760 state->local->slot_queue_recycle - state->slot_queue_available,
3761 state->stats.slot_stalls, state->stats.data_stalls);
3762 vchiq_dump(dump_context, buf, len + 1);
3763
3764 vchiq_dump_platform_state(dump_context);
3765
3766 vchiq_dump_shared_state(dump_context, state, state->local, "Local");
3767 vchiq_dump_shared_state(dump_context, state, state->remote, "Remote");
3768
3769 vchiq_dump_platform_instances(dump_context);
3770
3771 for (i = 0; i < state->unused_service; i++) {
3772 VCHIQ_SERVICE_T *service = find_service_by_port(state, i);
3773
3774 if (service) {
3775 vchiq_dump_service_state(dump_context, service);
3776 unlock_service(service);
3777 }
3778 }
3779 }
3780
3781 void
vchiq_dump_service_state(void * dump_context,VCHIQ_SERVICE_T * service)3782 vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service)
3783 {
3784 char buf[120];
3785 int len;
3786
3787 len = snprintf(buf, sizeof(buf), "Service %d: %s (ref %u)",
3788 service->localport, srvstate_names[service->srvstate],
3789 service->ref_count - 1); /*Don't include the lock just taken*/
3790
3791 if (service->srvstate != VCHIQ_SRVSTATE_FREE) {
3792 char remoteport[30];
3793 VCHIQ_SERVICE_QUOTA_T *service_quota =
3794 &service->state->service_quotas[service->localport];
3795 int fourcc = service->base.fourcc;
3796 int tx_pending, rx_pending;
3797 if (service->remoteport != VCHIQ_PORT_FREE) {
3798 int len2 = snprintf(remoteport, sizeof(remoteport),
3799 "%d", service->remoteport);
3800 if (service->public_fourcc != VCHIQ_FOURCC_INVALID)
3801 snprintf(remoteport + len2,
3802 sizeof(remoteport) - len2,
3803 " (client %8x)", service->client_id);
3804 } else
3805 strcpy(remoteport, "n/a");
3806
3807 len += snprintf(buf + len, sizeof(buf) - len,
3808 " '%c%c%c%c' remote %s (msg use %d/%d, slot use %d/%d)",
3809 VCHIQ_FOURCC_AS_4CHARS(fourcc),
3810 remoteport,
3811 service_quota->message_use_count,
3812 service_quota->message_quota,
3813 service_quota->slot_use_count,
3814 service_quota->slot_quota);
3815
3816 vchiq_dump(dump_context, buf, len + 1);
3817
3818 tx_pending = service->bulk_tx.local_insert -
3819 service->bulk_tx.remote_insert;
3820
3821 rx_pending = service->bulk_rx.local_insert -
3822 service->bulk_rx.remote_insert;
3823
3824 len = snprintf(buf, sizeof(buf),
3825 " Bulk: tx_pending=%d (size %d),"
3826 " rx_pending=%d (size %d)",
3827 tx_pending,
3828 tx_pending ? service->bulk_tx.bulks[
3829 BULK_INDEX(service->bulk_tx.remove)].size : 0,
3830 rx_pending,
3831 rx_pending ? service->bulk_rx.bulks[
3832 BULK_INDEX(service->bulk_rx.remove)].size : 0);
3833
3834 if (VCHIQ_ENABLE_STATS) {
3835 vchiq_dump(dump_context, buf, len + 1);
3836
3837 len = snprintf(buf, sizeof(buf),
3838 " Ctrl: tx_count=%d, tx_bytes=%ju, "
3839 "rx_count=%d, rx_bytes=%ju",
3840 service->stats.ctrl_tx_count,
3841 (uintmax_t) service->stats.ctrl_tx_bytes,
3842 service->stats.ctrl_rx_count,
3843 (uintmax_t) service->stats.ctrl_rx_bytes);
3844 vchiq_dump(dump_context, buf, len + 1);
3845
3846 len = snprintf(buf, sizeof(buf),
3847 " Bulk: tx_count=%d, tx_bytes=%ju, "
3848 "rx_count=%d, rx_bytes=%ju",
3849 service->stats.bulk_tx_count,
3850 (uintmax_t) service->stats.bulk_tx_bytes,
3851 service->stats.bulk_rx_count,
3852 (uintmax_t) service->stats.bulk_rx_bytes);
3853 vchiq_dump(dump_context, buf, len + 1);
3854
3855 len = snprintf(buf, sizeof(buf),
3856 " %d quota stalls, %d slot stalls, "
3857 "%d bulk stalls, %d aborted, %d errors",
3858 service->stats.quota_stalls,
3859 service->stats.slot_stalls,
3860 service->stats.bulk_stalls,
3861 service->stats.bulk_aborted_count,
3862 service->stats.error_count);
3863 }
3864 }
3865
3866 vchiq_dump(dump_context, buf, len + 1);
3867
3868 if (service->srvstate != VCHIQ_SRVSTATE_FREE)
3869 vchiq_dump_platform_service_state(dump_context, service);
3870 }
3871
3872
3873 void
vchiq_loud_error_header(void)3874 vchiq_loud_error_header(void)
3875 {
3876 vchiq_log_error(vchiq_core_log_level,
3877 "============================================================"
3878 "================");
3879 vchiq_log_error(vchiq_core_log_level,
3880 "============================================================"
3881 "================");
3882 vchiq_log_error(vchiq_core_log_level, "=====");
3883 }
3884
3885 void
vchiq_loud_error_footer(void)3886 vchiq_loud_error_footer(void)
3887 {
3888 vchiq_log_error(vchiq_core_log_level, "=====");
3889 vchiq_log_error(vchiq_core_log_level,
3890 "============================================================"
3891 "================");
3892 vchiq_log_error(vchiq_core_log_level,
3893 "============================================================"
3894 "================");
3895 }
3896
3897
vchiq_send_remote_use(VCHIQ_STATE_T * state)3898 VCHIQ_STATUS_T vchiq_send_remote_use(VCHIQ_STATE_T *state)
3899 {
3900 VCHIQ_STATUS_T status = VCHIQ_RETRY;
3901 if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED)
3902 status = queue_message(state, NULL,
3903 VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE, 0, 0),
3904 NULL, 0, 0, 0);
3905 return status;
3906 }
3907
vchiq_send_remote_release(VCHIQ_STATE_T * state)3908 VCHIQ_STATUS_T vchiq_send_remote_release(VCHIQ_STATE_T *state)
3909 {
3910 VCHIQ_STATUS_T status = VCHIQ_RETRY;
3911 if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED)
3912 status = queue_message(state, NULL,
3913 VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_RELEASE, 0, 0),
3914 NULL, 0, 0, 0);
3915 return status;
3916 }
3917
vchiq_send_remote_use_active(VCHIQ_STATE_T * state)3918 VCHIQ_STATUS_T vchiq_send_remote_use_active(VCHIQ_STATE_T *state)
3919 {
3920 VCHIQ_STATUS_T status = VCHIQ_RETRY;
3921 if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED)
3922 status = queue_message(state, NULL,
3923 VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE_ACTIVE, 0, 0),
3924 NULL, 0, 0, 0);
3925 return status;
3926 }
3927
vchiq_log_dump_mem(const char * label,uint32_t addr,const void * voidMem,size_t numBytes)3928 void vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem,
3929 size_t numBytes)
3930 {
3931 const uint8_t *mem = (const uint8_t *)voidMem;
3932 size_t offset;
3933 char lineBuf[100];
3934 char *s;
3935
3936 while (numBytes > 0) {
3937 s = lineBuf;
3938
3939 for (offset = 0; offset < 16; offset++) {
3940 if (offset < numBytes)
3941 s += snprintf(s, 4, "%02x ", mem[offset]);
3942 else
3943 s += snprintf(s, 4, " ");
3944 }
3945
3946 for (offset = 0; offset < 16; offset++) {
3947 if (offset < numBytes) {
3948 uint8_t ch = mem[offset];
3949
3950 if ((ch < ' ') || (ch > '~'))
3951 ch = '.';
3952 *s++ = (char)ch;
3953 }
3954 }
3955 *s++ = '\0';
3956
3957 if ((label != NULL) && (*label != '\0'))
3958 vchiq_log_trace(VCHIQ_LOG_TRACE,
3959 "%s: %08x: %s", label, addr, lineBuf);
3960 else
3961 vchiq_log_trace(VCHIQ_LOG_TRACE,
3962 "%08x: %s", addr, lineBuf);
3963
3964 addr += 16;
3965 mem += 16;
3966 if (numBytes > 16)
3967 numBytes -= 16;
3968 else
3969 numBytes = 0;
3970 }
3971 }
3972