2 * Copyright (c) 2010-2012 Broadcom. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
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.
34 #include "vchiq_core.h"
35 #include "vchiq_killable.h"
37 #define VCHIQ_SLOT_HANDLER_STACK 8192
39 #define HANDLE_STATE_SHIFT 12
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)) / \
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))
51 #define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1))
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)))
58 struct vchiq_open_payload
{
65 struct vchiq_openack_payload
{
71 QMFLAGS_IS_BLOCKING
= (1 << 0),
72 QMFLAGS_NO_MUTEX_LOCK
= (1 << 1),
73 QMFLAGS_NO_MUTEX_UNLOCK
= (1 << 2)
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
);
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
;
89 static atomic_t pause_bulks_count
= ATOMIC_INIT(0);
91 static DEFINE_SPINLOCK(service_spinlock
);
92 DEFINE_SPINLOCK(bulk_waiter_spinlock
);
93 DEFINE_SPINLOCK(quota_spinlock
);
95 VCHIQ_STATE_T
*vchiq_states
[VCHIQ_MAX_STATES
];
96 static unsigned int handle_seq
;
98 static const char *const srvstate_names
[] = {
111 static const char *const reason_names
[] = {
115 "BULK_TRANSMIT_DONE",
117 "BULK_TRANSMIT_ABORTED",
118 "BULK_RECEIVE_ABORTED"
121 static const char *const conn_state_names
[] = {
135 release_message_sync(VCHIQ_STATE_T
*state
, VCHIQ_HEADER_T
*header
);
137 static const char *msg_type_str(unsigned int msg_type
)
140 case VCHIQ_MSG_PADDING
: return "PADDING";
141 case VCHIQ_MSG_CONNECT
: return "CONNECT";
142 case VCHIQ_MSG_OPEN
: return "OPEN";
143 case VCHIQ_MSG_OPENACK
: return "OPENACK";
144 case VCHIQ_MSG_CLOSE
: return "CLOSE";
145 case VCHIQ_MSG_DATA
: return "DATA";
146 case VCHIQ_MSG_BULK_RX
: return "BULK_RX";
147 case VCHIQ_MSG_BULK_TX
: return "BULK_TX";
148 case VCHIQ_MSG_BULK_RX_DONE
: return "BULK_RX_DONE";
149 case VCHIQ_MSG_BULK_TX_DONE
: return "BULK_TX_DONE";
150 case VCHIQ_MSG_PAUSE
: return "PAUSE";
151 case VCHIQ_MSG_RESUME
: return "RESUME";
152 case VCHIQ_MSG_REMOTE_USE
: return "REMOTE_USE";
153 case VCHIQ_MSG_REMOTE_RELEASE
: return "REMOTE_RELEASE";
154 case VCHIQ_MSG_REMOTE_USE_ACTIVE
: return "REMOTE_USE_ACTIVE";
160 vchiq_set_service_state(VCHIQ_SERVICE_T
*service
, int newstate
)
162 vchiq_log_info(vchiq_core_log_level
, "%d: srv:%d %s->%s",
163 service
->state
->id
, service
->localport
,
164 srvstate_names
[service
->srvstate
],
165 srvstate_names
[newstate
]);
166 service
->srvstate
= newstate
;
170 find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle
)
172 VCHIQ_SERVICE_T
*service
;
174 spin_lock(&service_spinlock
);
175 service
= handle_to_service(handle
);
176 if (service
&& (service
->srvstate
!= VCHIQ_SRVSTATE_FREE
) &&
177 (service
->handle
== handle
)) {
178 BUG_ON(service
->ref_count
== 0);
179 service
->ref_count
++;
182 spin_unlock(&service_spinlock
);
185 vchiq_log_info(vchiq_core_log_level
,
186 "Invalid service handle 0x%x", handle
);
192 find_service_by_port(VCHIQ_STATE_T
*state
, int localport
)
194 VCHIQ_SERVICE_T
*service
= NULL
;
195 if ((unsigned int)localport
<= VCHIQ_PORT_MAX
) {
196 spin_lock(&service_spinlock
);
197 service
= state
->services
[localport
];
198 if (service
&& (service
->srvstate
!= VCHIQ_SRVSTATE_FREE
)) {
199 BUG_ON(service
->ref_count
== 0);
200 service
->ref_count
++;
203 spin_unlock(&service_spinlock
);
207 vchiq_log_info(vchiq_core_log_level
,
208 "Invalid port %d", localport
);
214 find_service_for_instance(VCHIQ_INSTANCE_T instance
,
215 VCHIQ_SERVICE_HANDLE_T handle
) {
216 VCHIQ_SERVICE_T
*service
;
218 spin_lock(&service_spinlock
);
219 service
= handle_to_service(handle
);
220 if (service
&& (service
->srvstate
!= VCHIQ_SRVSTATE_FREE
) &&
221 (service
->handle
== handle
) &&
222 (service
->instance
== instance
)) {
223 BUG_ON(service
->ref_count
== 0);
224 service
->ref_count
++;
227 spin_unlock(&service_spinlock
);
230 vchiq_log_info(vchiq_core_log_level
,
231 "Invalid service handle 0x%x", handle
);
237 find_closed_service_for_instance(VCHIQ_INSTANCE_T instance
,
238 VCHIQ_SERVICE_HANDLE_T handle
) {
239 VCHIQ_SERVICE_T
*service
;
241 spin_lock(&service_spinlock
);
242 service
= handle_to_service(handle
);
244 ((service
->srvstate
== VCHIQ_SRVSTATE_FREE
) ||
245 (service
->srvstate
== VCHIQ_SRVSTATE_CLOSED
)) &&
246 (service
->handle
== handle
) &&
247 (service
->instance
== instance
)) {
248 BUG_ON(service
->ref_count
== 0);
249 service
->ref_count
++;
252 spin_unlock(&service_spinlock
);
255 vchiq_log_info(vchiq_core_log_level
,
256 "Invalid service handle 0x%x", handle
);
262 next_service_by_instance(VCHIQ_STATE_T
*state
, VCHIQ_INSTANCE_T instance
,
265 VCHIQ_SERVICE_T
*service
= NULL
;
268 spin_lock(&service_spinlock
);
269 while (idx
< state
->unused_service
) {
270 VCHIQ_SERVICE_T
*srv
= state
->services
[idx
++];
271 if (srv
&& (srv
->srvstate
!= VCHIQ_SRVSTATE_FREE
) &&
272 (srv
->instance
== instance
)) {
274 BUG_ON(service
->ref_count
== 0);
275 service
->ref_count
++;
279 spin_unlock(&service_spinlock
);
287 lock_service(VCHIQ_SERVICE_T
*service
)
289 spin_lock(&service_spinlock
);
290 BUG_ON(!service
|| (service
->ref_count
== 0));
292 service
->ref_count
++;
293 spin_unlock(&service_spinlock
);
297 unlock_service(VCHIQ_SERVICE_T
*service
)
299 spin_lock(&service_spinlock
);
300 BUG_ON(!service
|| (service
->ref_count
== 0));
301 if (service
&& service
->ref_count
) {
302 service
->ref_count
--;
303 if (!service
->ref_count
) {
304 VCHIQ_STATE_T
*state
= service
->state
;
306 BUG_ON(service
->srvstate
!= VCHIQ_SRVSTATE_FREE
);
307 state
->services
[service
->localport
] = NULL
;
311 spin_unlock(&service_spinlock
);
313 if (service
&& service
->userdata_term
)
314 service
->userdata_term(service
->base
.userdata
);
320 vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle
)
322 VCHIQ_SERVICE_T
*service
= find_service_by_handle(handle
);
325 id
= service
? service
->client_id
: 0;
327 unlock_service(service
);
333 vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle
)
335 VCHIQ_SERVICE_T
*service
= handle_to_service(handle
);
337 return service
? service
->base
.userdata
: NULL
;
341 vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle
)
343 VCHIQ_SERVICE_T
*service
= handle_to_service(handle
);
345 return service
? service
->base
.fourcc
: 0;
349 mark_service_closing_internal(VCHIQ_SERVICE_T
*service
, int sh_thread
)
351 VCHIQ_STATE_T
*state
= service
->state
;
352 VCHIQ_SERVICE_QUOTA_T
*service_quota
;
354 service
->closing
= 1;
356 /* Synchronise with other threads. */
357 mutex_lock(&state
->recycle_mutex
);
358 mutex_unlock(&state
->recycle_mutex
);
359 if (!sh_thread
|| (state
->conn_state
!= VCHIQ_CONNSTATE_PAUSE_SENT
)) {
360 /* If we're pausing then the slot_mutex is held until resume
361 * by the slot handler. Therefore don't try to acquire this
362 * mutex if we're the slot handler and in the pause sent state.
363 * We don't need to in this case anyway. */
364 mutex_lock(&state
->slot_mutex
);
365 mutex_unlock(&state
->slot_mutex
);
368 /* Unblock any sending thread. */
369 service_quota
= &state
->service_quotas
[service
->localport
];
370 up(&service_quota
->quota_event
);
374 mark_service_closing(VCHIQ_SERVICE_T
*service
)
376 mark_service_closing_internal(service
, 0);
379 static inline VCHIQ_STATUS_T
380 make_service_callback(VCHIQ_SERVICE_T
*service
, VCHIQ_REASON_T reason
,
381 VCHIQ_HEADER_T
*header
, void *bulk_userdata
)
383 VCHIQ_STATUS_T status
;
384 vchiq_log_trace(vchiq_core_log_level
, "%d: callback:%d (%s, %pK, %pK)",
385 service
->state
->id
, service
->localport
, reason_names
[reason
],
386 header
, bulk_userdata
);
387 status
= service
->base
.callback(reason
, header
, service
->handle
,
389 if (status
== VCHIQ_ERROR
) {
390 vchiq_log_warning(vchiq_core_log_level
,
391 "%d: ignoring ERROR from callback to service %x",
392 service
->state
->id
, service
->handle
);
393 status
= VCHIQ_SUCCESS
;
399 vchiq_set_conn_state(VCHIQ_STATE_T
*state
, VCHIQ_CONNSTATE_T newstate
)
401 VCHIQ_CONNSTATE_T oldstate
= state
->conn_state
;
402 vchiq_log_info(vchiq_core_log_level
, "%d: %s->%s", state
->id
,
403 conn_state_names
[oldstate
],
404 conn_state_names
[newstate
]);
405 state
->conn_state
= newstate
;
406 vchiq_platform_conn_state_changed(state
, oldstate
, newstate
);
410 remote_event_create(VCHIQ_STATE_T
*state
, REMOTE_EVENT_T
*event
)
413 /* Don't clear the 'fired' flag because it may already have been set
414 ** by the other side. */
415 sema_init((struct semaphore
*)((char *)state
+ event
->event
), 0);
419 remote_event_wait(VCHIQ_STATE_T
*state
, REMOTE_EVENT_T
*event
)
425 if (down_interruptible(
427 ((char *)state
+ event
->event
)) != 0) {
441 remote_event_signal_local(VCHIQ_STATE_T
*state
, REMOTE_EVENT_T
*event
)
444 up((struct semaphore
*)((char *)state
+ event
->event
));
448 remote_event_poll(VCHIQ_STATE_T
*state
, REMOTE_EVENT_T
*event
)
450 if (event
->fired
&& event
->armed
)
451 remote_event_signal_local(state
, event
);
455 remote_event_pollall(VCHIQ_STATE_T
*state
)
457 remote_event_poll(state
, &state
->local
->sync_trigger
);
458 remote_event_poll(state
, &state
->local
->sync_release
);
459 remote_event_poll(state
, &state
->local
->trigger
);
460 remote_event_poll(state
, &state
->local
->recycle
);
463 /* Round up message sizes so that any space at the end of a slot is always big
464 ** enough for a header. This relies on header size being a power of two, which
465 ** has been verified earlier by a static assertion. */
467 static inline unsigned int
468 calc_stride(unsigned int size
)
470 /* Allow room for the header */
471 size
+= sizeof(VCHIQ_HEADER_T
);
474 return (size
+ sizeof(VCHIQ_HEADER_T
) - 1) & ~(sizeof(VCHIQ_HEADER_T
)
478 /* Called by the slot handler thread */
479 static VCHIQ_SERVICE_T
*
480 get_listening_service(VCHIQ_STATE_T
*state
, int fourcc
)
484 WARN_ON(fourcc
== VCHIQ_FOURCC_INVALID
);
486 for (i
= 0; i
< state
->unused_service
; i
++) {
487 VCHIQ_SERVICE_T
*service
= state
->services
[i
];
489 (service
->public_fourcc
== fourcc
) &&
490 ((service
->srvstate
== VCHIQ_SRVSTATE_LISTENING
) ||
491 ((service
->srvstate
== VCHIQ_SRVSTATE_OPEN
) &&
492 (service
->remoteport
== VCHIQ_PORT_FREE
)))) {
493 lock_service(service
);
501 /* Called by the slot handler thread */
502 static VCHIQ_SERVICE_T
*
503 get_connected_service(VCHIQ_STATE_T
*state
, unsigned int port
)
506 for (i
= 0; i
< state
->unused_service
; i
++) {
507 VCHIQ_SERVICE_T
*service
= state
->services
[i
];
508 if (service
&& (service
->srvstate
== VCHIQ_SRVSTATE_OPEN
)
509 && (service
->remoteport
== port
)) {
510 lock_service(service
);
518 request_poll(VCHIQ_STATE_T
*state
, VCHIQ_SERVICE_T
*service
, int poll_type
)
524 value
= atomic_read(&service
->poll_flags
);
525 } while (atomic_cmpxchg(&service
->poll_flags
, value
,
526 value
| (1 << poll_type
)) != value
);
529 value
= atomic_read(&state
->poll_services
[
530 service
->localport
>>5]);
531 } while (atomic_cmpxchg(
532 &state
->poll_services
[service
->localport
>>5],
533 value
, value
| (1 << (service
->localport
& 0x1f)))
537 state
->poll_needed
= 1;
540 /* ... and ensure the slot handler runs. */
541 remote_event_signal_local(state
, &state
->local
->trigger
);
544 /* Called from queue_message, by the slot handler and application threads,
545 ** with slot_mutex held */
546 static VCHIQ_HEADER_T
*
547 reserve_space(VCHIQ_STATE_T
*state
, int space
, int is_blocking
)
549 VCHIQ_SHARED_STATE_T
*local
= state
->local
;
550 int tx_pos
= state
->local_tx_pos
;
551 int slot_space
= VCHIQ_SLOT_SIZE
- (tx_pos
& VCHIQ_SLOT_MASK
);
553 if (space
> slot_space
) {
554 VCHIQ_HEADER_T
*header
;
555 /* Fill the remaining space with padding */
556 WARN_ON(state
->tx_data
== NULL
);
557 header
= (VCHIQ_HEADER_T
*)
558 (state
->tx_data
+ (tx_pos
& VCHIQ_SLOT_MASK
));
559 header
->msgid
= VCHIQ_MSGID_PADDING
;
560 header
->size
= slot_space
- sizeof(VCHIQ_HEADER_T
);
562 tx_pos
+= slot_space
;
565 /* If necessary, get the next slot. */
566 if ((tx_pos
& VCHIQ_SLOT_MASK
) == 0) {
569 /* If there is no free slot... */
571 if (down_trylock(&state
->slot_available_event
) != 0) {
572 /* ...wait for one. */
574 VCHIQ_STATS_INC(state
, slot_stalls
);
576 /* But first, flush through the last slot. */
577 state
->local_tx_pos
= tx_pos
;
578 local
->tx_pos
= tx_pos
;
579 remote_event_signal(&state
->remote
->trigger
);
583 &state
->slot_available_event
) != 0))
584 return NULL
; /* No space available */
588 (state
->slot_queue_available
* VCHIQ_SLOT_SIZE
));
590 slot_index
= local
->slot_queue
[
591 SLOT_QUEUE_INDEX_FROM_POS(tx_pos
) &
592 VCHIQ_SLOT_QUEUE_MASK
];
594 (char *)SLOT_DATA_FROM_INDEX(state
, slot_index
);
597 state
->local_tx_pos
= tx_pos
+ space
;
599 return (VCHIQ_HEADER_T
*)(state
->tx_data
+ (tx_pos
& VCHIQ_SLOT_MASK
));
602 /* Called by the recycle thread. */
604 process_free_queue(VCHIQ_STATE_T
*state
)
606 VCHIQ_SHARED_STATE_T
*local
= state
->local
;
607 BITSET_T service_found
[BITSET_SIZE(VCHIQ_MAX_SERVICES
)];
608 int slot_queue_available
;
610 /* Use a read memory barrier to ensure that any state that may have
611 ** been modified by another thread is not masked by stale prefetched
615 /* Find slots which have been freed by the other side, and return them
616 ** to the available queue. */
617 slot_queue_available
= state
->slot_queue_available
;
619 while (slot_queue_available
!= local
->slot_queue_recycle
) {
621 int slot_index
= local
->slot_queue
[slot_queue_available
++ &
622 VCHIQ_SLOT_QUEUE_MASK
];
623 char *data
= (char *)SLOT_DATA_FROM_INDEX(state
, slot_index
);
626 vchiq_log_trace(vchiq_core_log_level
, "%d: pfq %d=%pK %x %x",
627 state
->id
, slot_index
, data
,
628 local
->slot_queue_recycle
, slot_queue_available
);
630 /* Initialise the bitmask for services which have used this
632 BITSET_ZERO(service_found
);
636 while (pos
< VCHIQ_SLOT_SIZE
) {
637 VCHIQ_HEADER_T
*header
=
638 (VCHIQ_HEADER_T
*)(data
+ pos
);
639 int msgid
= header
->msgid
;
640 if (VCHIQ_MSG_TYPE(msgid
) == VCHIQ_MSG_DATA
) {
641 int port
= VCHIQ_MSG_SRCPORT(msgid
);
642 VCHIQ_SERVICE_QUOTA_T
*service_quota
=
643 &state
->service_quotas
[port
];
645 spin_lock("a_spinlock
);
646 count
= service_quota
->message_use_count
;
648 service_quota
->message_use_count
=
650 spin_unlock("a_spinlock
);
652 if (count
== service_quota
->message_quota
)
653 /* Signal the service that it
654 ** has dropped below its quota
656 up(&service_quota
->quota_event
);
657 else if (count
== 0) {
658 vchiq_log_error(vchiq_core_log_level
,
659 "service %d message_use_count=%d (header %pK, msgid %x, header->msgid %x, header->size %x)",
661 service_quota
->message_use_count
,
662 header
, msgid
, header
->msgid
,
664 WARN(1, "invalid message use count\n");
666 if (!BITSET_IS_SET(service_found
, port
)) {
667 /* Set the found bit for this service */
668 BITSET_SET(service_found
, port
);
670 spin_lock("a_spinlock
);
671 count
= service_quota
->slot_use_count
;
673 service_quota
->slot_use_count
=
675 spin_unlock("a_spinlock
);
678 /* Signal the service in case
679 ** it has dropped below its
681 up(&service_quota
->quota_event
);
683 vchiq_core_log_level
,
684 "%d: pfq:%d %x@%pK - slot_use->%d",
686 header
->size
, header
,
690 vchiq_core_log_level
,
691 "service %d slot_use_count=%d (header %pK, msgid %x, header->msgid %x, header->size %x)",
693 msgid
, header
->msgid
,
695 WARN(1, "bad slot use count\n");
702 pos
+= calc_stride(header
->size
);
703 if (pos
> VCHIQ_SLOT_SIZE
) {
704 vchiq_log_error(vchiq_core_log_level
,
705 "pfq - pos %x: header %pK, msgid %x, header->msgid %x, header->size %x",
706 pos
, header
, msgid
, header
->msgid
,
708 WARN(1, "invalid slot position\n");
714 spin_lock("a_spinlock
);
715 count
= state
->data_use_count
;
717 state
->data_use_count
=
719 spin_unlock("a_spinlock
);
720 if (count
== state
->data_quota
)
721 up(&state
->data_quota_event
);
724 state
->slot_queue_available
= slot_queue_available
;
725 up(&state
->slot_available_event
);
729 /* Called by the slot handler and application threads */
730 static VCHIQ_STATUS_T
731 queue_message(VCHIQ_STATE_T
*state
, VCHIQ_SERVICE_T
*service
,
732 int msgid
, const VCHIQ_ELEMENT_T
*elements
,
733 int count
, int size
, int flags
)
735 VCHIQ_SHARED_STATE_T
*local
;
736 VCHIQ_SERVICE_QUOTA_T
*service_quota
= NULL
;
737 VCHIQ_HEADER_T
*header
;
738 int type
= VCHIQ_MSG_TYPE(msgid
);
742 local
= state
->local
;
744 stride
= calc_stride(size
);
746 WARN_ON(!(stride
<= VCHIQ_SLOT_SIZE
));
748 if (!(flags
& QMFLAGS_NO_MUTEX_LOCK
) &&
749 (mutex_lock_killable(&state
->slot_mutex
) != 0))
752 if (type
== VCHIQ_MSG_DATA
) {
756 BUG_ON((flags
& (QMFLAGS_NO_MUTEX_LOCK
|
757 QMFLAGS_NO_MUTEX_UNLOCK
)) != 0);
759 if (service
->closing
) {
760 /* The service has been closed */
761 mutex_unlock(&state
->slot_mutex
);
765 service_quota
= &state
->service_quotas
[service
->localport
];
767 spin_lock("a_spinlock
);
769 /* Ensure this service doesn't use more than its quota of
770 ** messages or slots */
771 tx_end_index
= SLOT_QUEUE_INDEX_FROM_POS(
772 state
->local_tx_pos
+ stride
- 1);
774 /* Ensure data messages don't use more than their quota of
776 while ((tx_end_index
!= state
->previous_data_index
) &&
777 (state
->data_use_count
== state
->data_quota
)) {
778 VCHIQ_STATS_INC(state
, data_stalls
);
779 spin_unlock("a_spinlock
);
780 mutex_unlock(&state
->slot_mutex
);
782 if (down_interruptible(&state
->data_quota_event
)
786 mutex_lock(&state
->slot_mutex
);
787 spin_lock("a_spinlock
);
788 tx_end_index
= SLOT_QUEUE_INDEX_FROM_POS(
789 state
->local_tx_pos
+ stride
- 1);
790 if ((tx_end_index
== state
->previous_data_index
) ||
791 (state
->data_use_count
< state
->data_quota
)) {
792 /* Pass the signal on to other waiters */
793 up(&state
->data_quota_event
);
798 while ((service_quota
->message_use_count
==
799 service_quota
->message_quota
) ||
800 ((tx_end_index
!= service_quota
->previous_tx_index
) &&
801 (service_quota
->slot_use_count
==
802 service_quota
->slot_quota
))) {
803 spin_unlock("a_spinlock
);
804 vchiq_log_trace(vchiq_core_log_level
,
805 "%d: qm:%d %s,%zx - quota stall "
807 state
->id
, service
->localport
,
808 msg_type_str(type
), size
,
809 service_quota
->message_use_count
,
810 service_quota
->slot_use_count
);
811 VCHIQ_SERVICE_STATS_INC(service
, quota_stalls
);
812 mutex_unlock(&state
->slot_mutex
);
813 if (down_interruptible(&service_quota
->quota_event
)
816 if (service
->closing
)
818 if (mutex_lock_killable(&state
->slot_mutex
) != 0)
820 if (service
->srvstate
!= VCHIQ_SRVSTATE_OPEN
) {
821 /* The service has been closed */
822 mutex_unlock(&state
->slot_mutex
);
825 spin_lock("a_spinlock
);
826 tx_end_index
= SLOT_QUEUE_INDEX_FROM_POS(
827 state
->local_tx_pos
+ stride
- 1);
830 spin_unlock("a_spinlock
);
833 header
= reserve_space(state
, stride
, flags
& QMFLAGS_IS_BLOCKING
);
837 VCHIQ_SERVICE_STATS_INC(service
, slot_stalls
);
838 /* In the event of a failure, return the mutex to the
840 if (!(flags
& QMFLAGS_NO_MUTEX_LOCK
))
841 mutex_unlock(&state
->slot_mutex
);
845 if (type
== VCHIQ_MSG_DATA
) {
850 vchiq_log_info(vchiq_core_log_level
,
851 "%d: qm %s@%pK,%zx (%d->%d)",
852 state
->id
, msg_type_str(VCHIQ_MSG_TYPE(msgid
)),
853 header
, size
, VCHIQ_MSG_SRCPORT(msgid
),
854 VCHIQ_MSG_DSTPORT(msgid
));
857 BUG_ON((flags
& (QMFLAGS_NO_MUTEX_LOCK
|
858 QMFLAGS_NO_MUTEX_UNLOCK
)) != 0);
860 for (i
= 0, pos
= 0; i
< (unsigned int)count
;
861 pos
+= elements
[i
++].size
)
862 if (elements
[i
].size
) {
863 if (vchiq_copy_from_user
864 (header
->data
+ pos
, elements
[i
].data
,
865 (size_t) elements
[i
].size
) !=
867 mutex_unlock(&state
->slot_mutex
);
868 VCHIQ_SERVICE_STATS_INC(service
,
873 if (SRVTRACE_ENABLED(service
,
875 vchiq_log_dump_mem("Sent", 0,
882 spin_lock("a_spinlock
);
883 service_quota
->message_use_count
++;
886 SLOT_QUEUE_INDEX_FROM_POS(state
->local_tx_pos
- 1);
888 /* If this transmission can't fit in the last slot used by any
889 ** service, the data_use_count must be increased. */
890 if (tx_end_index
!= state
->previous_data_index
) {
891 state
->previous_data_index
= tx_end_index
;
892 state
->data_use_count
++;
895 /* If this isn't the same slot last used by this service,
896 ** the service's slot_use_count must be increased. */
897 if (tx_end_index
!= service_quota
->previous_tx_index
) {
898 service_quota
->previous_tx_index
= tx_end_index
;
899 slot_use_count
= ++service_quota
->slot_use_count
;
904 spin_unlock("a_spinlock
);
907 vchiq_log_trace(vchiq_core_log_level
,
908 "%d: qm:%d %s,%zx - slot_use->%d (hdr %p)",
909 state
->id
, service
->localport
,
910 msg_type_str(VCHIQ_MSG_TYPE(msgid
)), size
,
911 slot_use_count
, header
);
913 VCHIQ_SERVICE_STATS_INC(service
, ctrl_tx_count
);
914 VCHIQ_SERVICE_STATS_ADD(service
, ctrl_tx_bytes
, size
);
916 vchiq_log_info(vchiq_core_log_level
,
917 "%d: qm %s@%pK,%zx (%d->%d)", state
->id
,
918 msg_type_str(VCHIQ_MSG_TYPE(msgid
)),
919 header
, size
, VCHIQ_MSG_SRCPORT(msgid
),
920 VCHIQ_MSG_DSTPORT(msgid
));
922 WARN_ON(!((count
== 1) && (size
== elements
[0].size
)));
923 memcpy(header
->data
, elements
[0].data
,
926 VCHIQ_STATS_INC(state
, ctrl_tx_count
);
929 header
->msgid
= msgid
;
936 ? service
->base
.fourcc
937 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
939 vchiq_log_info(SRVTRACE_LEVEL(service
),
940 "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%zu",
941 msg_type_str(VCHIQ_MSG_TYPE(msgid
)),
942 VCHIQ_MSG_TYPE(msgid
),
943 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc
),
944 VCHIQ_MSG_SRCPORT(msgid
),
945 VCHIQ_MSG_DSTPORT(msgid
),
949 /* Make sure the new header is visible to the peer. */
952 /* Make the new tx_pos visible to the peer. */
953 local
->tx_pos
= state
->local_tx_pos
;
956 if (service
&& (type
== VCHIQ_MSG_CLOSE
))
957 vchiq_set_service_state(service
, VCHIQ_SRVSTATE_CLOSESENT
);
959 if (!(flags
& QMFLAGS_NO_MUTEX_UNLOCK
))
960 mutex_unlock(&state
->slot_mutex
);
962 remote_event_signal(&state
->remote
->trigger
);
964 return VCHIQ_SUCCESS
;
967 /* Called by the slot handler and application threads */
968 static VCHIQ_STATUS_T
969 queue_message_sync(VCHIQ_STATE_T
*state
, VCHIQ_SERVICE_T
*service
,
970 int msgid
, const VCHIQ_ELEMENT_T
*elements
,
971 int count
, int size
, int is_blocking
)
973 VCHIQ_SHARED_STATE_T
*local
;
974 VCHIQ_HEADER_T
*header
;
976 local
= state
->local
;
978 if ((VCHIQ_MSG_TYPE(msgid
) != VCHIQ_MSG_RESUME
) &&
979 (mutex_lock_killable(&state
->sync_mutex
) != 0))
982 remote_event_wait(state
, &local
->sync_release
);
986 header
= (VCHIQ_HEADER_T
*)SLOT_DATA_FROM_INDEX(state
,
990 int oldmsgid
= header
->msgid
;
991 if (oldmsgid
!= VCHIQ_MSGID_PADDING
)
992 vchiq_log_error(vchiq_core_log_level
,
993 "%d: qms - msgid %x, not PADDING",
994 state
->id
, oldmsgid
);
1000 vchiq_log_info(vchiq_sync_log_level
,
1001 "%d: qms %s@%pK,%x (%d->%d)", state
->id
,
1002 msg_type_str(VCHIQ_MSG_TYPE(msgid
)),
1003 header
, size
, VCHIQ_MSG_SRCPORT(msgid
),
1004 VCHIQ_MSG_DSTPORT(msgid
));
1006 for (i
= 0, pos
= 0; i
< (unsigned int)count
;
1007 pos
+= elements
[i
++].size
)
1008 if (elements
[i
].size
) {
1009 if (vchiq_copy_from_user
1010 (header
->data
+ pos
, elements
[i
].data
,
1011 (size_t) elements
[i
].size
) !=
1013 mutex_unlock(&state
->sync_mutex
);
1014 VCHIQ_SERVICE_STATS_INC(service
,
1019 if (vchiq_sync_log_level
>=
1021 vchiq_log_dump_mem("Sent Sync",
1022 0, header
->data
+ pos
,
1028 VCHIQ_SERVICE_STATS_INC(service
, ctrl_tx_count
);
1029 VCHIQ_SERVICE_STATS_ADD(service
, ctrl_tx_bytes
, size
);
1031 vchiq_log_info(vchiq_sync_log_level
,
1032 "%d: qms %s@%pK,%x (%d->%d)", state
->id
,
1033 msg_type_str(VCHIQ_MSG_TYPE(msgid
)),
1034 header
, size
, VCHIQ_MSG_SRCPORT(msgid
),
1035 VCHIQ_MSG_DSTPORT(msgid
));
1037 WARN_ON(!((count
== 1) && (size
== elements
[0].size
)));
1038 memcpy(header
->data
, elements
[0].data
,
1041 VCHIQ_STATS_INC(state
, ctrl_tx_count
);
1044 header
->size
= size
;
1045 header
->msgid
= msgid
;
1047 if (vchiq_sync_log_level
>= VCHIQ_LOG_TRACE
) {
1050 svc_fourcc
= service
1051 ? service
->base
.fourcc
1052 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
1054 vchiq_log_trace(vchiq_sync_log_level
,
1055 "Sent Sync Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d",
1056 msg_type_str(VCHIQ_MSG_TYPE(msgid
)),
1057 VCHIQ_MSG_TYPE(msgid
),
1058 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc
),
1059 VCHIQ_MSG_SRCPORT(msgid
),
1060 VCHIQ_MSG_DSTPORT(msgid
),
1064 /* Make sure the new header is visible to the peer. */
1067 remote_event_signal(&state
->remote
->sync_trigger
);
1069 if (VCHIQ_MSG_TYPE(msgid
) != VCHIQ_MSG_PAUSE
)
1070 mutex_unlock(&state
->sync_mutex
);
1072 return VCHIQ_SUCCESS
;
1076 claim_slot(VCHIQ_SLOT_INFO_T
*slot
)
1082 release_slot(VCHIQ_STATE_T
*state
, VCHIQ_SLOT_INFO_T
*slot_info
,
1083 VCHIQ_HEADER_T
*header
, VCHIQ_SERVICE_T
*service
)
1087 mutex_lock(&state
->recycle_mutex
);
1090 int msgid
= header
->msgid
;
1091 if (((msgid
& VCHIQ_MSGID_CLAIMED
) == 0) ||
1092 (service
&& service
->closing
)) {
1093 mutex_unlock(&state
->recycle_mutex
);
1097 /* Rewrite the message header to prevent a double
1099 header
->msgid
= msgid
& ~VCHIQ_MSGID_CLAIMED
;
1102 release_count
= slot_info
->release_count
;
1103 slot_info
->release_count
= ++release_count
;
1105 if (release_count
== slot_info
->use_count
) {
1106 int slot_queue_recycle
;
1107 /* Add to the freed queue */
1109 /* A read barrier is necessary here to prevent speculative
1110 ** fetches of remote->slot_queue_recycle from overtaking the
1114 slot_queue_recycle
= state
->remote
->slot_queue_recycle
;
1115 state
->remote
->slot_queue
[slot_queue_recycle
&
1116 VCHIQ_SLOT_QUEUE_MASK
] =
1117 SLOT_INDEX_FROM_INFO(state
, slot_info
);
1118 state
->remote
->slot_queue_recycle
= slot_queue_recycle
+ 1;
1119 vchiq_log_info(vchiq_core_log_level
,
1120 "%d: release_slot %d - recycle->%x",
1121 state
->id
, SLOT_INDEX_FROM_INFO(state
, slot_info
),
1122 state
->remote
->slot_queue_recycle
);
1124 /* A write barrier is necessary, but remote_event_signal
1126 remote_event_signal(&state
->remote
->recycle
);
1129 mutex_unlock(&state
->recycle_mutex
);
1132 /* Called by the slot handler - don't hold the bulk mutex */
1133 static VCHIQ_STATUS_T
1134 notify_bulks(VCHIQ_SERVICE_T
*service
, VCHIQ_BULK_QUEUE_T
*queue
,
1137 VCHIQ_STATUS_T status
= VCHIQ_SUCCESS
;
1139 vchiq_log_trace(vchiq_core_log_level
,
1140 "%d: nb:%d %cx - p=%x rn=%x r=%x",
1141 service
->state
->id
, service
->localport
,
1142 (queue
== &service
->bulk_tx
) ? 't' : 'r',
1143 queue
->process
, queue
->remote_notify
, queue
->remove
);
1145 if (service
->state
->is_master
) {
1146 while (queue
->remote_notify
!= queue
->process
) {
1147 VCHIQ_BULK_T
*bulk
=
1148 &queue
->bulks
[BULK_INDEX(queue
->remote_notify
)];
1149 int msgtype
= (bulk
->dir
== VCHIQ_BULK_TRANSMIT
) ?
1150 VCHIQ_MSG_BULK_RX_DONE
: VCHIQ_MSG_BULK_TX_DONE
;
1151 int msgid
= VCHIQ_MAKE_MSG(msgtype
, service
->localport
,
1152 service
->remoteport
);
1153 VCHIQ_ELEMENT_T element
= { &bulk
->actual
, 4 };
1154 /* Only reply to non-dummy bulk requests */
1155 if (bulk
->remote_data
) {
1156 status
= queue_message(service
->state
, NULL
,
1157 msgid
, &element
, 1, 4, 0);
1158 if (status
!= VCHIQ_SUCCESS
)
1161 queue
->remote_notify
++;
1164 queue
->remote_notify
= queue
->process
;
1167 if (status
== VCHIQ_SUCCESS
) {
1168 while (queue
->remove
!= queue
->remote_notify
) {
1169 VCHIQ_BULK_T
*bulk
=
1170 &queue
->bulks
[BULK_INDEX(queue
->remove
)];
1172 /* Only generate callbacks for non-dummy bulk
1173 ** requests, and non-terminated services */
1174 if (bulk
->data
&& service
->instance
) {
1175 if (bulk
->actual
!= VCHIQ_BULK_ACTUAL_ABORTED
) {
1176 if (bulk
->dir
== VCHIQ_BULK_TRANSMIT
) {
1177 VCHIQ_SERVICE_STATS_INC(service
,
1179 VCHIQ_SERVICE_STATS_ADD(service
,
1183 VCHIQ_SERVICE_STATS_INC(service
,
1185 VCHIQ_SERVICE_STATS_ADD(service
,
1190 VCHIQ_SERVICE_STATS_INC(service
,
1191 bulk_aborted_count
);
1193 if (bulk
->mode
== VCHIQ_BULK_MODE_BLOCKING
) {
1194 struct bulk_waiter
*waiter
;
1195 spin_lock(&bulk_waiter_spinlock
);
1196 waiter
= bulk
->userdata
;
1198 waiter
->actual
= bulk
->actual
;
1201 spin_unlock(&bulk_waiter_spinlock
);
1202 } else if (bulk
->mode
==
1203 VCHIQ_BULK_MODE_CALLBACK
) {
1204 VCHIQ_REASON_T reason
= (bulk
->dir
==
1205 VCHIQ_BULK_TRANSMIT
) ?
1207 VCHIQ_BULK_ACTUAL_ABORTED
) ?
1208 VCHIQ_BULK_TRANSMIT_ABORTED
:
1209 VCHIQ_BULK_TRANSMIT_DONE
) :
1211 VCHIQ_BULK_ACTUAL_ABORTED
) ?
1212 VCHIQ_BULK_RECEIVE_ABORTED
:
1213 VCHIQ_BULK_RECEIVE_DONE
);
1214 status
= make_service_callback(service
,
1215 reason
, NULL
, bulk
->userdata
);
1216 if (status
== VCHIQ_RETRY
)
1222 up(&service
->bulk_remove_event
);
1225 status
= VCHIQ_SUCCESS
;
1228 if (status
== VCHIQ_RETRY
)
1229 request_poll(service
->state
, service
,
1230 (queue
== &service
->bulk_tx
) ?
1231 VCHIQ_POLL_TXNOTIFY
: VCHIQ_POLL_RXNOTIFY
);
1236 /* Called by the slot handler thread */
1238 poll_services(VCHIQ_STATE_T
*state
)
1242 for (group
= 0; group
< BITSET_SIZE(state
->unused_service
); group
++) {
1244 flags
= atomic_xchg(&state
->poll_services
[group
], 0);
1245 for (i
= 0; flags
; i
++) {
1246 if (flags
& (1 << i
)) {
1247 VCHIQ_SERVICE_T
*service
=
1248 find_service_by_port(state
,
1250 uint32_t service_flags
;
1255 atomic_xchg(&service
->poll_flags
, 0);
1257 (1 << VCHIQ_POLL_REMOVE
)) {
1258 vchiq_log_info(vchiq_core_log_level
,
1259 "%d: ps - remove %d<->%d",
1260 state
->id
, service
->localport
,
1261 service
->remoteport
);
1263 /* Make it look like a client, because
1264 it must be removed and not left in
1265 the LISTENING state. */
1266 service
->public_fourcc
=
1267 VCHIQ_FOURCC_INVALID
;
1269 if (vchiq_close_service_internal(
1270 service
, 0/*!close_recvd*/) !=
1272 request_poll(state
, service
,
1274 } else if (service_flags
&
1275 (1 << VCHIQ_POLL_TERMINATE
)) {
1276 vchiq_log_info(vchiq_core_log_level
,
1277 "%d: ps - terminate %d<->%d",
1278 state
->id
, service
->localport
,
1279 service
->remoteport
);
1280 if (vchiq_close_service_internal(
1281 service
, 0/*!close_recvd*/) !=
1283 request_poll(state
, service
,
1284 VCHIQ_POLL_TERMINATE
);
1286 if (service_flags
& (1 << VCHIQ_POLL_TXNOTIFY
))
1287 notify_bulks(service
,
1290 if (service_flags
& (1 << VCHIQ_POLL_RXNOTIFY
))
1291 notify_bulks(service
,
1294 unlock_service(service
);
1300 /* Called by the slot handler or application threads, holding the bulk mutex. */
1302 resolve_bulks(VCHIQ_SERVICE_T
*service
, VCHIQ_BULK_QUEUE_T
*queue
)
1304 VCHIQ_STATE_T
*state
= service
->state
;
1308 while ((queue
->process
!= queue
->local_insert
) &&
1309 (queue
->process
!= queue
->remote_insert
)) {
1310 VCHIQ_BULK_T
*bulk
= &queue
->bulks
[BULK_INDEX(queue
->process
)];
1312 vchiq_log_trace(vchiq_core_log_level
,
1313 "%d: rb:%d %cx - li=%x ri=%x p=%x",
1314 state
->id
, service
->localport
,
1315 (queue
== &service
->bulk_tx
) ? 't' : 'r',
1316 queue
->local_insert
, queue
->remote_insert
,
1319 WARN_ON(!((int)(queue
->local_insert
- queue
->process
) > 0));
1320 WARN_ON(!((int)(queue
->remote_insert
- queue
->process
) > 0));
1322 rc
= mutex_lock_killable(&state
->bulk_transfer_mutex
);
1326 vchiq_transfer_bulk(bulk
);
1327 mutex_unlock(&state
->bulk_transfer_mutex
);
1329 if (SRVTRACE_ENABLED(service
, VCHIQ_LOG_INFO
)) {
1330 const char *header
= (queue
== &service
->bulk_tx
) ?
1331 "Send Bulk to" : "Recv Bulk from";
1332 if (bulk
->actual
!= VCHIQ_BULK_ACTUAL_ABORTED
)
1333 vchiq_log_info(SRVTRACE_LEVEL(service
),
1334 "%s %c%c%c%c d:%d len:%d %pK<->%pK",
1336 VCHIQ_FOURCC_AS_4CHARS(
1337 service
->base
.fourcc
),
1338 service
->remoteport
, bulk
->size
,
1339 bulk
->data
, bulk
->remote_data
);
1341 vchiq_log_info(SRVTRACE_LEVEL(service
),
1342 "%s %c%c%c%c d:%d ABORTED - tx len:%d,"
1343 " rx len:%d %pK<->%pK",
1345 VCHIQ_FOURCC_AS_4CHARS(
1346 service
->base
.fourcc
),
1347 service
->remoteport
,
1348 bulk
->size
, bulk
->remote_size
,
1349 bulk
->data
, bulk
->remote_data
);
1352 vchiq_complete_bulk(bulk
);
1359 /* Called with the bulk_mutex held */
1361 abort_outstanding_bulks(VCHIQ_SERVICE_T
*service
, VCHIQ_BULK_QUEUE_T
*queue
)
1363 int is_tx
= (queue
== &service
->bulk_tx
);
1364 vchiq_log_trace(vchiq_core_log_level
,
1365 "%d: aob:%d %cx - li=%x ri=%x p=%x",
1366 service
->state
->id
, service
->localport
, is_tx
? 't' : 'r',
1367 queue
->local_insert
, queue
->remote_insert
, queue
->process
);
1369 WARN_ON(!((int)(queue
->local_insert
- queue
->process
) >= 0));
1370 WARN_ON(!((int)(queue
->remote_insert
- queue
->process
) >= 0));
1372 while ((queue
->process
!= queue
->local_insert
) ||
1373 (queue
->process
!= queue
->remote_insert
)) {
1374 VCHIQ_BULK_T
*bulk
= &queue
->bulks
[BULK_INDEX(queue
->process
)];
1376 if (queue
->process
== queue
->remote_insert
) {
1377 /* fabricate a matching dummy bulk */
1378 bulk
->remote_data
= NULL
;
1379 bulk
->remote_size
= 0;
1380 queue
->remote_insert
++;
1383 if (queue
->process
!= queue
->local_insert
) {
1384 vchiq_complete_bulk(bulk
);
1386 vchiq_log_info(SRVTRACE_LEVEL(service
),
1387 "%s %c%c%c%c d:%d ABORTED - tx len:%d, "
1389 is_tx
? "Send Bulk to" : "Recv Bulk from",
1390 VCHIQ_FOURCC_AS_4CHARS(service
->base
.fourcc
),
1391 service
->remoteport
,
1395 /* fabricate a matching dummy bulk */
1398 bulk
->actual
= VCHIQ_BULK_ACTUAL_ABORTED
;
1399 bulk
->dir
= is_tx
? VCHIQ_BULK_TRANSMIT
:
1401 queue
->local_insert
++;
1408 /* Called from the slot handler thread */
1410 pause_bulks(VCHIQ_STATE_T
*state
)
1412 if (unlikely(atomic_inc_return(&pause_bulks_count
) != 1)) {
1414 atomic_set(&pause_bulks_count
, 1);
1418 /* Block bulk transfers from all services */
1419 mutex_lock(&state
->bulk_transfer_mutex
);
1422 /* Called from the slot handler thread */
1424 resume_bulks(VCHIQ_STATE_T
*state
)
1427 if (unlikely(atomic_dec_return(&pause_bulks_count
) != 0)) {
1429 atomic_set(&pause_bulks_count
, 0);
1433 /* Allow bulk transfers from all services */
1434 mutex_unlock(&state
->bulk_transfer_mutex
);
1436 if (state
->deferred_bulks
== 0)
1439 /* Deal with any bulks which had to be deferred due to being in
1440 * paused state. Don't try to match up to number of deferred bulks
1441 * in case we've had something come and close the service in the
1442 * interim - just process all bulk queues for all services */
1443 vchiq_log_info(vchiq_core_log_level
, "%s: processing %d deferred bulks",
1444 __func__
, state
->deferred_bulks
);
1446 for (i
= 0; i
< state
->unused_service
; i
++) {
1447 VCHIQ_SERVICE_T
*service
= state
->services
[i
];
1448 int resolved_rx
= 0;
1449 int resolved_tx
= 0;
1450 if (!service
|| (service
->srvstate
!= VCHIQ_SRVSTATE_OPEN
))
1453 mutex_lock(&service
->bulk_mutex
);
1454 resolved_rx
= resolve_bulks(service
, &service
->bulk_rx
);
1455 resolved_tx
= resolve_bulks(service
, &service
->bulk_tx
);
1456 mutex_unlock(&service
->bulk_mutex
);
1458 notify_bulks(service
, &service
->bulk_rx
, 1);
1460 notify_bulks(service
, &service
->bulk_tx
, 1);
1462 state
->deferred_bulks
= 0;
1466 parse_open(VCHIQ_STATE_T
*state
, VCHIQ_HEADER_T
*header
)
1468 VCHIQ_SERVICE_T
*service
= NULL
;
1471 unsigned int localport
, remoteport
;
1473 msgid
= header
->msgid
;
1474 size
= header
->size
;
1475 type
= VCHIQ_MSG_TYPE(msgid
);
1476 localport
= VCHIQ_MSG_DSTPORT(msgid
);
1477 remoteport
= VCHIQ_MSG_SRCPORT(msgid
);
1478 if (size
>= sizeof(struct vchiq_open_payload
)) {
1479 const struct vchiq_open_payload
*payload
=
1480 (struct vchiq_open_payload
*)header
->data
;
1481 unsigned int fourcc
;
1483 fourcc
= payload
->fourcc
;
1484 vchiq_log_info(vchiq_core_log_level
,
1485 "%d: prs OPEN@%pK (%d->'%c%c%c%c')",
1486 state
->id
, header
, localport
,
1487 VCHIQ_FOURCC_AS_4CHARS(fourcc
));
1489 service
= get_listening_service(state
, fourcc
);
1492 /* A matching service exists */
1493 short version
= payload
->version
;
1494 short version_min
= payload
->version_min
;
1495 if ((service
->version
< version_min
) ||
1496 (version
< service
->version_min
)) {
1497 /* Version mismatch */
1498 vchiq_loud_error_header();
1499 vchiq_loud_error("%d: service %d (%c%c%c%c) "
1500 "version mismatch - local (%d, min %d)"
1501 " vs. remote (%d, min %d)",
1502 state
->id
, service
->localport
,
1503 VCHIQ_FOURCC_AS_4CHARS(fourcc
),
1504 service
->version
, service
->version_min
,
1505 version
, version_min
);
1506 vchiq_loud_error_footer();
1507 unlock_service(service
);
1511 service
->peer_version
= version
;
1513 if (service
->srvstate
== VCHIQ_SRVSTATE_LISTENING
) {
1514 struct vchiq_openack_payload ack_payload
= {
1517 VCHIQ_ELEMENT_T body
= {
1522 if (state
->version_common
<
1523 VCHIQ_VERSION_SYNCHRONOUS_MODE
)
1526 /* Acknowledge the OPEN */
1527 if (service
->sync
&&
1528 (state
->version_common
>=
1529 VCHIQ_VERSION_SYNCHRONOUS_MODE
)) {
1530 if (queue_message_sync(state
, NULL
,
1535 &body
, 1, sizeof(ack_payload
),
1537 goto bail_not_ready
;
1539 if (queue_message(state
, NULL
,
1544 &body
, 1, sizeof(ack_payload
),
1546 goto bail_not_ready
;
1549 /* The service is now open */
1550 vchiq_set_service_state(service
,
1551 service
->sync
? VCHIQ_SRVSTATE_OPENSYNC
1552 : VCHIQ_SRVSTATE_OPEN
);
1555 service
->remoteport
= remoteport
;
1556 service
->client_id
= ((int *)header
->data
)[1];
1557 if (make_service_callback(service
, VCHIQ_SERVICE_OPENED
,
1558 NULL
, NULL
) == VCHIQ_RETRY
) {
1559 /* Bail out if not ready */
1560 service
->remoteport
= VCHIQ_PORT_FREE
;
1561 goto bail_not_ready
;
1564 /* Success - the message has been dealt with */
1565 unlock_service(service
);
1571 /* No available service, or an invalid request - send a CLOSE */
1572 if (queue_message(state
, NULL
,
1573 VCHIQ_MAKE_MSG(VCHIQ_MSG_CLOSE
, 0, VCHIQ_MSG_SRCPORT(msgid
)),
1574 NULL
, 0, 0, 0) == VCHIQ_RETRY
)
1575 goto bail_not_ready
;
1581 unlock_service(service
);
1586 /* Called by the slot handler thread */
1588 parse_rx_slots(VCHIQ_STATE_T
*state
)
1590 VCHIQ_SHARED_STATE_T
*remote
= state
->remote
;
1591 VCHIQ_SERVICE_T
*service
= NULL
;
1593 DEBUG_INITIALISE(state
->local
)
1595 tx_pos
= remote
->tx_pos
;
1597 while (state
->rx_pos
!= tx_pos
) {
1598 VCHIQ_HEADER_T
*header
;
1601 unsigned int localport
, remoteport
;
1603 DEBUG_TRACE(PARSE_LINE
);
1604 if (!state
->rx_data
) {
1606 WARN_ON(!((state
->rx_pos
& VCHIQ_SLOT_MASK
) == 0));
1607 rx_index
= remote
->slot_queue
[
1608 SLOT_QUEUE_INDEX_FROM_POS(state
->rx_pos
) &
1609 VCHIQ_SLOT_QUEUE_MASK
];
1610 state
->rx_data
= (char *)SLOT_DATA_FROM_INDEX(state
,
1612 state
->rx_info
= SLOT_INFO_FROM_INDEX(state
, rx_index
);
1614 /* Initialise use_count to one, and increment
1615 ** release_count at the end of the slot to avoid
1616 ** releasing the slot prematurely. */
1617 state
->rx_info
->use_count
= 1;
1618 state
->rx_info
->release_count
= 0;
1621 header
= (VCHIQ_HEADER_T
*)(state
->rx_data
+
1622 (state
->rx_pos
& VCHIQ_SLOT_MASK
));
1623 DEBUG_VALUE(PARSE_HEADER
, (int)(long)header
);
1624 msgid
= header
->msgid
;
1625 DEBUG_VALUE(PARSE_MSGID
, msgid
);
1626 size
= header
->size
;
1627 type
= VCHIQ_MSG_TYPE(msgid
);
1628 localport
= VCHIQ_MSG_DSTPORT(msgid
);
1629 remoteport
= VCHIQ_MSG_SRCPORT(msgid
);
1631 if (type
!= VCHIQ_MSG_DATA
)
1632 VCHIQ_STATS_INC(state
, ctrl_rx_count
);
1635 case VCHIQ_MSG_OPENACK
:
1636 case VCHIQ_MSG_CLOSE
:
1637 case VCHIQ_MSG_DATA
:
1638 case VCHIQ_MSG_BULK_RX
:
1639 case VCHIQ_MSG_BULK_TX
:
1640 case VCHIQ_MSG_BULK_RX_DONE
:
1641 case VCHIQ_MSG_BULK_TX_DONE
:
1642 service
= find_service_by_port(state
, localport
);
1644 ((service
->remoteport
!= remoteport
) &&
1645 (service
->remoteport
!= VCHIQ_PORT_FREE
))) &&
1647 (type
== VCHIQ_MSG_CLOSE
)) {
1648 /* This could be a CLOSE from a client which
1649 hadn't yet received the OPENACK - look for
1650 the connected service */
1652 unlock_service(service
);
1653 service
= get_connected_service(state
,
1656 vchiq_log_warning(vchiq_core_log_level
,
1657 "%d: prs %s@%pK (%d->%d) - found connected service %d",
1658 state
->id
, msg_type_str(type
),
1659 header
, remoteport
, localport
,
1660 service
->localport
);
1664 vchiq_log_error(vchiq_core_log_level
,
1665 "%d: prs %s@%pK (%d->%d) - invalid/closed service %d",
1666 state
->id
, msg_type_str(type
),
1667 header
, remoteport
, localport
,
1676 if (SRVTRACE_ENABLED(service
, VCHIQ_LOG_INFO
)) {
1679 svc_fourcc
= service
1680 ? service
->base
.fourcc
1681 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
1682 vchiq_log_info(SRVTRACE_LEVEL(service
),
1683 "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d "
1685 msg_type_str(type
), type
,
1686 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc
),
1687 remoteport
, localport
, size
);
1689 vchiq_log_dump_mem("Rcvd", 0, header
->data
,
1693 if (((unsigned long)header
& VCHIQ_SLOT_MASK
) +
1694 calc_stride(size
) > VCHIQ_SLOT_SIZE
) {
1695 vchiq_log_error(vchiq_core_log_level
,
1696 "header %pK (msgid %x) - size %x too big for slot",
1697 header
, (unsigned int)msgid
,
1698 (unsigned int)size
);
1699 WARN(1, "oversized for slot\n");
1703 case VCHIQ_MSG_OPEN
:
1704 WARN_ON(!(VCHIQ_MSG_DSTPORT(msgid
) == 0));
1705 if (!parse_open(state
, header
))
1706 goto bail_not_ready
;
1708 case VCHIQ_MSG_OPENACK
:
1709 if (size
>= sizeof(struct vchiq_openack_payload
)) {
1710 const struct vchiq_openack_payload
*payload
=
1711 (struct vchiq_openack_payload
*)
1713 service
->peer_version
= payload
->version
;
1715 vchiq_log_info(vchiq_core_log_level
,
1716 "%d: prs OPENACK@%pK,%x (%d->%d) v:%d",
1717 state
->id
, header
, size
, remoteport
, localport
,
1718 service
->peer_version
);
1719 if (service
->srvstate
==
1720 VCHIQ_SRVSTATE_OPENING
) {
1721 service
->remoteport
= remoteport
;
1722 vchiq_set_service_state(service
,
1723 VCHIQ_SRVSTATE_OPEN
);
1724 up(&service
->remove_event
);
1726 vchiq_log_error(vchiq_core_log_level
,
1727 "OPENACK received in state %s",
1728 srvstate_names
[service
->srvstate
]);
1730 case VCHIQ_MSG_CLOSE
:
1731 WARN_ON(size
!= 0); /* There should be no data */
1733 vchiq_log_info(vchiq_core_log_level
,
1734 "%d: prs CLOSE@%pK (%d->%d)",
1735 state
->id
, header
, remoteport
, localport
);
1737 mark_service_closing_internal(service
, 1);
1739 if (vchiq_close_service_internal(service
,
1740 1/*close_recvd*/) == VCHIQ_RETRY
)
1741 goto bail_not_ready
;
1743 vchiq_log_info(vchiq_core_log_level
,
1744 "Close Service %c%c%c%c s:%u d:%d",
1745 VCHIQ_FOURCC_AS_4CHARS(service
->base
.fourcc
),
1747 service
->remoteport
);
1749 case VCHIQ_MSG_DATA
:
1750 vchiq_log_info(vchiq_core_log_level
,
1751 "%d: prs DATA@%pK,%x (%d->%d)",
1752 state
->id
, header
, size
, remoteport
, localport
);
1754 if ((service
->remoteport
== remoteport
)
1755 && (service
->srvstate
==
1756 VCHIQ_SRVSTATE_OPEN
)) {
1757 header
->msgid
= msgid
| VCHIQ_MSGID_CLAIMED
;
1758 claim_slot(state
->rx_info
);
1759 DEBUG_TRACE(PARSE_LINE
);
1760 if (make_service_callback(service
,
1761 VCHIQ_MESSAGE_AVAILABLE
, header
,
1762 NULL
) == VCHIQ_RETRY
) {
1763 DEBUG_TRACE(PARSE_LINE
);
1764 goto bail_not_ready
;
1766 VCHIQ_SERVICE_STATS_INC(service
, ctrl_rx_count
);
1767 VCHIQ_SERVICE_STATS_ADD(service
, ctrl_rx_bytes
,
1770 VCHIQ_STATS_INC(state
, error_count
);
1773 case VCHIQ_MSG_CONNECT
:
1774 vchiq_log_info(vchiq_core_log_level
,
1775 "%d: prs CONNECT@%pK", state
->id
, header
);
1776 state
->version_common
= ((VCHIQ_SLOT_ZERO_T
*)
1777 state
->slot_data
)->version
;
1778 up(&state
->connect
);
1780 case VCHIQ_MSG_BULK_RX
:
1781 case VCHIQ_MSG_BULK_TX
: {
1782 VCHIQ_BULK_QUEUE_T
*queue
;
1783 WARN_ON(!state
->is_master
);
1784 queue
= (type
== VCHIQ_MSG_BULK_RX
) ?
1785 &service
->bulk_tx
: &service
->bulk_rx
;
1786 if ((service
->remoteport
== remoteport
)
1787 && (service
->srvstate
==
1788 VCHIQ_SRVSTATE_OPEN
)) {
1792 DEBUG_TRACE(PARSE_LINE
);
1793 if (mutex_lock_killable(
1794 &service
->bulk_mutex
) != 0) {
1795 DEBUG_TRACE(PARSE_LINE
);
1796 goto bail_not_ready
;
1799 WARN_ON(!(queue
->remote_insert
< queue
->remove
+
1800 VCHIQ_NUM_SERVICE_BULKS
));
1801 bulk
= &queue
->bulks
[
1802 BULK_INDEX(queue
->remote_insert
)];
1804 (void *)(long)((int *)header
->data
)[0];
1805 bulk
->remote_size
= ((int *)header
->data
)[1];
1808 vchiq_log_info(vchiq_core_log_level
,
1809 "%d: prs %s@%pK (%d->%d) %x@%pK",
1810 state
->id
, msg_type_str(type
),
1811 header
, remoteport
, localport
,
1812 bulk
->remote_size
, bulk
->remote_data
);
1814 queue
->remote_insert
++;
1816 if (atomic_read(&pause_bulks_count
)) {
1817 state
->deferred_bulks
++;
1818 vchiq_log_info(vchiq_core_log_level
,
1819 "%s: deferring bulk (%d)",
1821 state
->deferred_bulks
);
1822 if (state
->conn_state
!=
1823 VCHIQ_CONNSTATE_PAUSE_SENT
)
1825 vchiq_core_log_level
,
1826 "%s: bulks paused in "
1827 "unexpected state %s",
1830 state
->conn_state
]);
1831 } else if (state
->conn_state
==
1832 VCHIQ_CONNSTATE_CONNECTED
) {
1833 DEBUG_TRACE(PARSE_LINE
);
1834 resolved
= resolve_bulks(service
,
1838 mutex_unlock(&service
->bulk_mutex
);
1840 notify_bulks(service
, queue
,
1844 case VCHIQ_MSG_BULK_RX_DONE
:
1845 case VCHIQ_MSG_BULK_TX_DONE
:
1846 WARN_ON(state
->is_master
);
1847 if ((service
->remoteport
== remoteport
)
1848 && (service
->srvstate
!=
1849 VCHIQ_SRVSTATE_FREE
)) {
1850 VCHIQ_BULK_QUEUE_T
*queue
;
1853 queue
= (type
== VCHIQ_MSG_BULK_RX_DONE
) ?
1854 &service
->bulk_rx
: &service
->bulk_tx
;
1856 DEBUG_TRACE(PARSE_LINE
);
1857 if (mutex_lock_killable(
1858 &service
->bulk_mutex
) != 0) {
1859 DEBUG_TRACE(PARSE_LINE
);
1860 goto bail_not_ready
;
1862 if ((int)(queue
->remote_insert
-
1863 queue
->local_insert
) >= 0) {
1864 vchiq_log_error(vchiq_core_log_level
,
1865 "%d: prs %s@%pK (%d->%d) "
1866 "unexpected (ri=%d,li=%d)",
1867 state
->id
, msg_type_str(type
),
1868 header
, remoteport
, localport
,
1869 queue
->remote_insert
,
1870 queue
->local_insert
);
1871 mutex_unlock(&service
->bulk_mutex
);
1875 BUG_ON(queue
->process
== queue
->local_insert
);
1876 BUG_ON(queue
->process
!= queue
->remote_insert
);
1878 bulk
= &queue
->bulks
[
1879 BULK_INDEX(queue
->remote_insert
)];
1880 bulk
->actual
= *(int *)header
->data
;
1881 queue
->remote_insert
++;
1883 vchiq_log_info(vchiq_core_log_level
,
1884 "%d: prs %s@%pK (%d->%d) %x@%pK",
1885 state
->id
, msg_type_str(type
),
1886 header
, remoteport
, localport
,
1887 bulk
->actual
, bulk
->data
);
1889 vchiq_log_trace(vchiq_core_log_level
,
1890 "%d: prs:%d %cx li=%x ri=%x p=%x",
1891 state
->id
, localport
,
1892 (type
== VCHIQ_MSG_BULK_RX_DONE
) ?
1894 queue
->local_insert
,
1895 queue
->remote_insert
, queue
->process
);
1897 DEBUG_TRACE(PARSE_LINE
);
1898 WARN_ON(queue
->process
== queue
->local_insert
);
1899 vchiq_complete_bulk(bulk
);
1901 mutex_unlock(&service
->bulk_mutex
);
1902 DEBUG_TRACE(PARSE_LINE
);
1903 notify_bulks(service
, queue
, 1/*retry_poll*/);
1904 DEBUG_TRACE(PARSE_LINE
);
1907 case VCHIQ_MSG_PADDING
:
1908 vchiq_log_trace(vchiq_core_log_level
,
1909 "%d: prs PADDING@%pK,%x",
1910 state
->id
, header
, size
);
1912 case VCHIQ_MSG_PAUSE
:
1913 /* If initiated, signal the application thread */
1914 vchiq_log_trace(vchiq_core_log_level
,
1915 "%d: prs PAUSE@%pK,%x",
1916 state
->id
, header
, size
);
1917 if (state
->conn_state
== VCHIQ_CONNSTATE_PAUSED
) {
1918 vchiq_log_error(vchiq_core_log_level
,
1919 "%d: PAUSE received in state PAUSED",
1923 if (state
->conn_state
!= VCHIQ_CONNSTATE_PAUSE_SENT
) {
1924 /* Send a PAUSE in response */
1925 if (queue_message(state
, NULL
,
1926 VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE
, 0, 0),
1927 NULL
, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK
)
1929 goto bail_not_ready
;
1930 if (state
->is_master
)
1933 /* At this point slot_mutex is held */
1934 vchiq_set_conn_state(state
, VCHIQ_CONNSTATE_PAUSED
);
1935 vchiq_platform_paused(state
);
1937 case VCHIQ_MSG_RESUME
:
1938 vchiq_log_trace(vchiq_core_log_level
,
1939 "%d: prs RESUME@%pK,%x",
1940 state
->id
, header
, size
);
1941 /* Release the slot mutex */
1942 mutex_unlock(&state
->slot_mutex
);
1943 if (state
->is_master
)
1944 resume_bulks(state
);
1945 vchiq_set_conn_state(state
, VCHIQ_CONNSTATE_CONNECTED
);
1946 vchiq_platform_resumed(state
);
1949 case VCHIQ_MSG_REMOTE_USE
:
1950 vchiq_on_remote_use(state
);
1952 case VCHIQ_MSG_REMOTE_RELEASE
:
1953 vchiq_on_remote_release(state
);
1955 case VCHIQ_MSG_REMOTE_USE_ACTIVE
:
1956 vchiq_on_remote_use_active(state
);
1960 vchiq_log_error(vchiq_core_log_level
,
1961 "%d: prs invalid msgid %x@%pK,%x",
1962 state
->id
, msgid
, header
, size
);
1963 WARN(1, "invalid message\n");
1969 unlock_service(service
);
1973 state
->rx_pos
+= calc_stride(size
);
1975 DEBUG_TRACE(PARSE_LINE
);
1976 /* Perform some housekeeping when the end of the slot is
1978 if ((state
->rx_pos
& VCHIQ_SLOT_MASK
) == 0) {
1979 /* Remove the extra reference count. */
1980 release_slot(state
, state
->rx_info
, NULL
, NULL
);
1981 state
->rx_data
= NULL
;
1987 unlock_service(service
);
1990 /* Called by the slot handler thread */
1992 slot_handler_func(void *v
)
1994 VCHIQ_STATE_T
*state
= (VCHIQ_STATE_T
*) v
;
1995 VCHIQ_SHARED_STATE_T
*local
= state
->local
;
1996 DEBUG_INITIALISE(local
)
1999 DEBUG_COUNT(SLOT_HANDLER_COUNT
);
2000 DEBUG_TRACE(SLOT_HANDLER_LINE
);
2001 remote_event_wait(state
, &local
->trigger
);
2005 DEBUG_TRACE(SLOT_HANDLER_LINE
);
2006 if (state
->poll_needed
) {
2007 /* Check if we need to suspend - may change our
2009 vchiq_platform_check_suspend(state
);
2011 state
->poll_needed
= 0;
2013 /* Handle service polling and other rare conditions here
2014 ** out of the mainline code */
2015 switch (state
->conn_state
) {
2016 case VCHIQ_CONNSTATE_CONNECTED
:
2017 /* Poll the services as requested */
2018 poll_services(state
);
2021 case VCHIQ_CONNSTATE_PAUSING
:
2022 if (state
->is_master
)
2024 if (queue_message(state
, NULL
,
2025 VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE
, 0, 0),
2027 QMFLAGS_NO_MUTEX_UNLOCK
)
2029 vchiq_set_conn_state(state
,
2030 VCHIQ_CONNSTATE_PAUSE_SENT
);
2032 if (state
->is_master
)
2033 resume_bulks(state
);
2035 state
->poll_needed
= 1;
2039 case VCHIQ_CONNSTATE_PAUSED
:
2040 vchiq_platform_resume(state
);
2043 case VCHIQ_CONNSTATE_RESUMING
:
2044 if (queue_message(state
, NULL
,
2045 VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME
, 0, 0),
2046 NULL
, 0, 0, QMFLAGS_NO_MUTEX_LOCK
)
2048 if (state
->is_master
)
2049 resume_bulks(state
);
2050 vchiq_set_conn_state(state
,
2051 VCHIQ_CONNSTATE_CONNECTED
);
2052 vchiq_platform_resumed(state
);
2054 /* This should really be impossible,
2055 ** since the PAUSE should have flushed
2056 ** through outstanding messages. */
2057 vchiq_log_error(vchiq_core_log_level
,
2058 "Failed to send RESUME "
2064 case VCHIQ_CONNSTATE_PAUSE_TIMEOUT
:
2065 case VCHIQ_CONNSTATE_RESUME_TIMEOUT
:
2066 vchiq_platform_handle_timeout(state
);
2075 DEBUG_TRACE(SLOT_HANDLER_LINE
);
2076 parse_rx_slots(state
);
2082 /* Called by the recycle thread */
2084 recycle_func(void *v
)
2086 VCHIQ_STATE_T
*state
= (VCHIQ_STATE_T
*) v
;
2087 VCHIQ_SHARED_STATE_T
*local
= state
->local
;
2090 remote_event_wait(state
, &local
->recycle
);
2092 process_free_queue(state
);
2098 /* Called by the sync thread */
2102 VCHIQ_STATE_T
*state
= (VCHIQ_STATE_T
*) v
;
2103 VCHIQ_SHARED_STATE_T
*local
= state
->local
;
2104 VCHIQ_HEADER_T
*header
= (VCHIQ_HEADER_T
*)SLOT_DATA_FROM_INDEX(state
,
2105 state
->remote
->slot_sync
);
2108 VCHIQ_SERVICE_T
*service
;
2111 unsigned int localport
, remoteport
;
2113 remote_event_wait(state
, &local
->sync_trigger
);
2117 msgid
= header
->msgid
;
2118 size
= header
->size
;
2119 type
= VCHIQ_MSG_TYPE(msgid
);
2120 localport
= VCHIQ_MSG_DSTPORT(msgid
);
2121 remoteport
= VCHIQ_MSG_SRCPORT(msgid
);
2123 service
= find_service_by_port(state
, localport
);
2126 vchiq_log_error(vchiq_sync_log_level
,
2127 "%d: sf %s@%pK (%d->%d) - invalid/closed service %d",
2128 state
->id
, msg_type_str(type
),
2129 header
, remoteport
, localport
, localport
);
2130 release_message_sync(state
, header
);
2134 if (vchiq_sync_log_level
>= VCHIQ_LOG_TRACE
) {
2137 svc_fourcc
= service
2138 ? service
->base
.fourcc
2139 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
2140 vchiq_log_trace(vchiq_sync_log_level
,
2141 "Rcvd Msg %s from %c%c%c%c s:%d d:%d len:%d",
2143 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc
),
2144 remoteport
, localport
, size
);
2146 vchiq_log_dump_mem("Rcvd", 0, header
->data
,
2151 case VCHIQ_MSG_OPENACK
:
2152 if (size
>= sizeof(struct vchiq_openack_payload
)) {
2153 const struct vchiq_openack_payload
*payload
=
2154 (struct vchiq_openack_payload
*)
2156 service
->peer_version
= payload
->version
;
2158 vchiq_log_info(vchiq_sync_log_level
,
2159 "%d: sf OPENACK@%pK,%x (%d->%d) v:%d",
2160 state
->id
, header
, size
, remoteport
, localport
,
2161 service
->peer_version
);
2162 if (service
->srvstate
== VCHIQ_SRVSTATE_OPENING
) {
2163 service
->remoteport
= remoteport
;
2164 vchiq_set_service_state(service
,
2165 VCHIQ_SRVSTATE_OPENSYNC
);
2167 up(&service
->remove_event
);
2169 release_message_sync(state
, header
);
2172 case VCHIQ_MSG_DATA
:
2173 vchiq_log_trace(vchiq_sync_log_level
,
2174 "%d: sf DATA@%pK,%x (%d->%d)",
2175 state
->id
, header
, size
, remoteport
, localport
);
2177 if ((service
->remoteport
== remoteport
) &&
2178 (service
->srvstate
==
2179 VCHIQ_SRVSTATE_OPENSYNC
)) {
2180 if (make_service_callback(service
,
2181 VCHIQ_MESSAGE_AVAILABLE
, header
,
2182 NULL
) == VCHIQ_RETRY
)
2183 vchiq_log_error(vchiq_sync_log_level
,
2184 "synchronous callback to "
2185 "service %d returns "
2192 vchiq_log_error(vchiq_sync_log_level
,
2193 "%d: sf unexpected msgid %x@%pK,%x",
2194 state
->id
, msgid
, header
, size
);
2195 release_message_sync(state
, header
);
2199 unlock_service(service
);
2207 init_bulk_queue(VCHIQ_BULK_QUEUE_T
*queue
)
2209 queue
->local_insert
= 0;
2210 queue
->remote_insert
= 0;
2212 queue
->remote_notify
= 0;
2218 get_conn_state_name(VCHIQ_CONNSTATE_T conn_state
)
2220 return conn_state_names
[conn_state
];
2225 vchiq_init_slots(void *mem_base
, int mem_size
)
2228 (int)((VCHIQ_SLOT_SIZE
- (long)mem_base
) & VCHIQ_SLOT_MASK
);
2229 VCHIQ_SLOT_ZERO_T
*slot_zero
=
2230 (VCHIQ_SLOT_ZERO_T
*)((char *)mem_base
+ mem_align
);
2231 int num_slots
= (mem_size
- mem_align
)/VCHIQ_SLOT_SIZE
;
2232 int first_data_slot
= VCHIQ_SLOT_ZERO_SLOTS
;
2234 /* Ensure there is enough memory to run an absolutely minimum system */
2235 num_slots
-= first_data_slot
;
2237 if (num_slots
< 4) {
2238 vchiq_log_error(vchiq_core_log_level
,
2239 "vchiq_init_slots - insufficient memory %x bytes",
2244 memset(slot_zero
, 0, sizeof(VCHIQ_SLOT_ZERO_T
));
2246 slot_zero
->magic
= VCHIQ_MAGIC
;
2247 slot_zero
->version
= VCHIQ_VERSION
;
2248 slot_zero
->version_min
= VCHIQ_VERSION_MIN
;
2249 slot_zero
->slot_zero_size
= sizeof(VCHIQ_SLOT_ZERO_T
);
2250 slot_zero
->slot_size
= VCHIQ_SLOT_SIZE
;
2251 slot_zero
->max_slots
= VCHIQ_MAX_SLOTS
;
2252 slot_zero
->max_slots_per_side
= VCHIQ_MAX_SLOTS_PER_SIDE
;
2254 slot_zero
->master
.slot_sync
= first_data_slot
;
2255 slot_zero
->master
.slot_first
= first_data_slot
+ 1;
2256 slot_zero
->master
.slot_last
= first_data_slot
+ (num_slots
/2) - 1;
2257 slot_zero
->slave
.slot_sync
= first_data_slot
+ (num_slots
/2);
2258 slot_zero
->slave
.slot_first
= first_data_slot
+ (num_slots
/2) + 1;
2259 slot_zero
->slave
.slot_last
= first_data_slot
+ num_slots
- 1;
2265 vchiq_init_state(VCHIQ_STATE_T
*state
, VCHIQ_SLOT_ZERO_T
*slot_zero
,
2268 VCHIQ_SHARED_STATE_T
*local
;
2269 VCHIQ_SHARED_STATE_T
*remote
;
2270 VCHIQ_STATUS_T status
;
2271 char threadname
[10];
2275 vchiq_log_warning(vchiq_core_log_level
,
2276 "%s: slot_zero = %pK, is_master = %d",
2277 __func__
, slot_zero
, is_master
);
2279 /* Check the input configuration */
2281 if (slot_zero
->magic
!= VCHIQ_MAGIC
) {
2282 vchiq_loud_error_header();
2283 vchiq_loud_error("Invalid VCHIQ magic value found.");
2284 vchiq_loud_error("slot_zero=%pK: magic=%x (expected %x)",
2285 slot_zero
, slot_zero
->magic
, VCHIQ_MAGIC
);
2286 vchiq_loud_error_footer();
2290 if (slot_zero
->version
< VCHIQ_VERSION_MIN
) {
2291 vchiq_loud_error_header();
2292 vchiq_loud_error("Incompatible VCHIQ versions found.");
2293 vchiq_loud_error("slot_zero=%pK: VideoCore version=%d (minimum %d)",
2294 slot_zero
, slot_zero
->version
, VCHIQ_VERSION_MIN
);
2295 vchiq_loud_error("Restart with a newer VideoCore image.");
2296 vchiq_loud_error_footer();
2300 if (VCHIQ_VERSION
< slot_zero
->version_min
) {
2301 vchiq_loud_error_header();
2302 vchiq_loud_error("Incompatible VCHIQ versions found.");
2303 vchiq_loud_error("slot_zero=%pK: version=%d (VideoCore minimum %d)",
2304 slot_zero
, VCHIQ_VERSION
, slot_zero
->version_min
);
2305 vchiq_loud_error("Restart with a newer kernel.");
2306 vchiq_loud_error_footer();
2310 if ((slot_zero
->slot_zero_size
!= sizeof(VCHIQ_SLOT_ZERO_T
)) ||
2311 (slot_zero
->slot_size
!= VCHIQ_SLOT_SIZE
) ||
2312 (slot_zero
->max_slots
!= VCHIQ_MAX_SLOTS
) ||
2313 (slot_zero
->max_slots_per_side
!= VCHIQ_MAX_SLOTS_PER_SIDE
)) {
2314 vchiq_loud_error_header();
2315 if (slot_zero
->slot_zero_size
!= sizeof(VCHIQ_SLOT_ZERO_T
))
2316 vchiq_loud_error("slot_zero=%pK: slot_zero_size=%d (expected %d)",
2317 slot_zero
, slot_zero
->slot_zero_size
,
2318 (int)sizeof(VCHIQ_SLOT_ZERO_T
));
2319 if (slot_zero
->slot_size
!= VCHIQ_SLOT_SIZE
)
2320 vchiq_loud_error("slot_zero=%pK: slot_size=%d (expected %d)",
2321 slot_zero
, slot_zero
->slot_size
,
2323 if (slot_zero
->max_slots
!= VCHIQ_MAX_SLOTS
)
2324 vchiq_loud_error("slot_zero=%pK: max_slots=%d (expected %d)",
2325 slot_zero
, slot_zero
->max_slots
,
2327 if (slot_zero
->max_slots_per_side
!= VCHIQ_MAX_SLOTS_PER_SIDE
)
2328 vchiq_loud_error("slot_zero=%pK: max_slots_per_side=%d (expected %d)",
2329 slot_zero
, slot_zero
->max_slots_per_side
,
2330 VCHIQ_MAX_SLOTS_PER_SIDE
);
2331 vchiq_loud_error_footer();
2335 if (VCHIQ_VERSION
< slot_zero
->version
)
2336 slot_zero
->version
= VCHIQ_VERSION
;
2339 local
= &slot_zero
->master
;
2340 remote
= &slot_zero
->slave
;
2342 local
= &slot_zero
->slave
;
2343 remote
= &slot_zero
->master
;
2346 if (local
->initialised
) {
2347 vchiq_loud_error_header();
2348 if (remote
->initialised
)
2349 vchiq_loud_error("local state has already been "
2352 vchiq_loud_error("master/slave mismatch - two %ss",
2353 is_master
? "master" : "slave");
2354 vchiq_loud_error_footer();
2358 memset(state
, 0, sizeof(VCHIQ_STATE_T
));
2361 state
->is_master
= is_master
;
2364 initialize shared state pointers
2367 state
->local
= local
;
2368 state
->remote
= remote
;
2369 state
->slot_data
= (VCHIQ_SLOT_T
*)slot_zero
;
2372 initialize events and mutexes
2375 sema_init(&state
->connect
, 0);
2376 mutex_init(&state
->mutex
);
2377 sema_init(&state
->trigger_event
, 0);
2378 sema_init(&state
->recycle_event
, 0);
2379 sema_init(&state
->sync_trigger_event
, 0);
2380 sema_init(&state
->sync_release_event
, 0);
2382 mutex_init(&state
->slot_mutex
);
2383 mutex_init(&state
->recycle_mutex
);
2384 mutex_init(&state
->sync_mutex
);
2385 mutex_init(&state
->bulk_transfer_mutex
);
2387 sema_init(&state
->slot_available_event
, 0);
2388 sema_init(&state
->slot_remove_event
, 0);
2389 sema_init(&state
->data_quota_event
, 0);
2391 state
->slot_queue_available
= 0;
2393 for (i
= 0; i
< VCHIQ_MAX_SERVICES
; i
++) {
2394 VCHIQ_SERVICE_QUOTA_T
*service_quota
=
2395 &state
->service_quotas
[i
];
2396 sema_init(&service_quota
->quota_event
, 0);
2399 for (i
= local
->slot_first
; i
<= local
->slot_last
; i
++) {
2400 local
->slot_queue
[state
->slot_queue_available
++] = i
;
2401 up(&state
->slot_available_event
);
2404 state
->default_slot_quota
= state
->slot_queue_available
/2;
2405 state
->default_message_quota
=
2406 min((unsigned short)(state
->default_slot_quota
* 256),
2407 (unsigned short)~0);
2409 state
->previous_data_index
= -1;
2410 state
->data_use_count
= 0;
2411 state
->data_quota
= state
->slot_queue_available
- 1;
2413 local
->trigger
.event
= offsetof(VCHIQ_STATE_T
, trigger_event
);
2414 remote_event_create(state
, &local
->trigger
);
2417 local
->recycle
.event
= offsetof(VCHIQ_STATE_T
, recycle_event
);
2418 remote_event_create(state
, &local
->recycle
);
2419 local
->slot_queue_recycle
= state
->slot_queue_available
;
2421 local
->sync_trigger
.event
= offsetof(VCHIQ_STATE_T
, sync_trigger_event
);
2422 remote_event_create(state
, &local
->sync_trigger
);
2424 local
->sync_release
.event
= offsetof(VCHIQ_STATE_T
, sync_release_event
);
2425 remote_event_create(state
, &local
->sync_release
);
2427 /* At start-of-day, the slot is empty and available */
2428 ((VCHIQ_HEADER_T
*)SLOT_DATA_FROM_INDEX(state
, local
->slot_sync
))->msgid
2429 = VCHIQ_MSGID_PADDING
;
2430 remote_event_signal_local(state
, &local
->sync_release
);
2432 local
->debug
[DEBUG_ENTRIES
] = DEBUG_MAX
;
2434 status
= vchiq_platform_init_state(state
);
2437 bring up slot handler thread
2439 snprintf(threadname
, sizeof(threadname
), "VCHIQ-%d", state
->id
);
2440 state
->slot_handler_thread
= kthread_create(&slot_handler_func
,
2444 if (IS_ERR(state
->slot_handler_thread
)) {
2445 vchiq_loud_error_header();
2446 vchiq_loud_error("couldn't create thread %s", threadname
);
2447 vchiq_loud_error_footer();
2450 set_user_nice(state
->slot_handler_thread
, -19);
2451 wake_up_process(state
->slot_handler_thread
);
2453 snprintf(threadname
, sizeof(threadname
), "VCHIQr-%d", state
->id
);
2454 state
->recycle_thread
= kthread_create(&recycle_func
,
2457 if (IS_ERR(state
->recycle_thread
)) {
2458 vchiq_loud_error_header();
2459 vchiq_loud_error("couldn't create thread %s", threadname
);
2460 vchiq_loud_error_footer();
2463 set_user_nice(state
->recycle_thread
, -19);
2464 wake_up_process(state
->recycle_thread
);
2466 snprintf(threadname
, sizeof(threadname
), "VCHIQs-%d", state
->id
);
2467 state
->sync_thread
= kthread_create(&sync_func
,
2470 if (IS_ERR(state
->sync_thread
)) {
2471 vchiq_loud_error_header();
2472 vchiq_loud_error("couldn't create thread %s", threadname
);
2473 vchiq_loud_error_footer();
2476 set_user_nice(state
->sync_thread
, -20);
2477 wake_up_process(state
->sync_thread
);
2479 BUG_ON(state
->id
>= VCHIQ_MAX_STATES
);
2480 vchiq_states
[state
->id
] = state
;
2482 /* Indicate readiness to the other side */
2483 local
->initialised
= 1;
2488 /* Called from application thread when a client or server service is created. */
2490 vchiq_add_service_internal(VCHIQ_STATE_T
*state
,
2491 const VCHIQ_SERVICE_PARAMS_T
*params
, int srvstate
,
2492 VCHIQ_INSTANCE_T instance
, VCHIQ_USERDATA_TERM_T userdata_term
)
2494 VCHIQ_SERVICE_T
*service
;
2496 service
= kmalloc(sizeof(VCHIQ_SERVICE_T
), GFP_KERNEL
);
2498 service
->base
.fourcc
= params
->fourcc
;
2499 service
->base
.callback
= params
->callback
;
2500 service
->base
.userdata
= params
->userdata
;
2501 service
->handle
= VCHIQ_SERVICE_HANDLE_INVALID
;
2502 service
->ref_count
= 1;
2503 service
->srvstate
= VCHIQ_SRVSTATE_FREE
;
2504 service
->userdata_term
= userdata_term
;
2505 service
->localport
= VCHIQ_PORT_FREE
;
2506 service
->remoteport
= VCHIQ_PORT_FREE
;
2508 service
->public_fourcc
= (srvstate
== VCHIQ_SRVSTATE_OPENING
) ?
2509 VCHIQ_FOURCC_INVALID
: params
->fourcc
;
2510 service
->client_id
= 0;
2511 service
->auto_close
= 1;
2513 service
->closing
= 0;
2515 atomic_set(&service
->poll_flags
, 0);
2516 service
->version
= params
->version
;
2517 service
->version_min
= params
->version_min
;
2518 service
->state
= state
;
2519 service
->instance
= instance
;
2520 service
->service_use_count
= 0;
2521 init_bulk_queue(&service
->bulk_tx
);
2522 init_bulk_queue(&service
->bulk_rx
);
2523 sema_init(&service
->remove_event
, 0);
2524 sema_init(&service
->bulk_remove_event
, 0);
2525 mutex_init(&service
->bulk_mutex
);
2526 memset(&service
->stats
, 0, sizeof(service
->stats
));
2528 vchiq_log_error(vchiq_core_log_level
,
2533 VCHIQ_SERVICE_T
**pservice
= NULL
;
2536 /* Although it is perfectly possible to use service_spinlock
2537 ** to protect the creation of services, it is overkill as it
2538 ** disables interrupts while the array is searched.
2539 ** The only danger is of another thread trying to create a
2540 ** service - service deletion is safe.
2541 ** Therefore it is preferable to use state->mutex which,
2542 ** although slower to claim, doesn't block interrupts while
2546 mutex_lock(&state
->mutex
);
2548 /* Prepare to use a previously unused service */
2549 if (state
->unused_service
< VCHIQ_MAX_SERVICES
)
2550 pservice
= &state
->services
[state
->unused_service
];
2552 if (srvstate
== VCHIQ_SRVSTATE_OPENING
) {
2553 for (i
= 0; i
< state
->unused_service
; i
++) {
2554 VCHIQ_SERVICE_T
*srv
= state
->services
[i
];
2556 pservice
= &state
->services
[i
];
2561 for (i
= (state
->unused_service
- 1); i
>= 0; i
--) {
2562 VCHIQ_SERVICE_T
*srv
= state
->services
[i
];
2564 pservice
= &state
->services
[i
];
2565 else if ((srv
->public_fourcc
== params
->fourcc
)
2566 && ((srv
->instance
!= instance
) ||
2567 (srv
->base
.callback
!=
2568 params
->callback
))) {
2569 /* There is another server using this
2570 ** fourcc which doesn't match. */
2578 service
->localport
= (pservice
- state
->services
);
2580 handle_seq
= VCHIQ_MAX_STATES
*
2582 service
->handle
= handle_seq
|
2583 (state
->id
* VCHIQ_MAX_SERVICES
) |
2585 handle_seq
+= VCHIQ_MAX_STATES
* VCHIQ_MAX_SERVICES
;
2586 *pservice
= service
;
2587 if (pservice
== &state
->services
[state
->unused_service
])
2588 state
->unused_service
++;
2591 mutex_unlock(&state
->mutex
);
2600 VCHIQ_SERVICE_QUOTA_T
*service_quota
=
2601 &state
->service_quotas
[service
->localport
];
2602 service_quota
->slot_quota
= state
->default_slot_quota
;
2603 service_quota
->message_quota
= state
->default_message_quota
;
2604 if (service_quota
->slot_use_count
== 0)
2605 service_quota
->previous_tx_index
=
2606 SLOT_QUEUE_INDEX_FROM_POS(state
->local_tx_pos
)
2609 /* Bring this service online */
2610 vchiq_set_service_state(service
, srvstate
);
2612 vchiq_log_info(vchiq_core_msg_log_level
,
2613 "%s Service %c%c%c%c SrcPort:%d",
2614 (srvstate
== VCHIQ_SRVSTATE_OPENING
)
2616 VCHIQ_FOURCC_AS_4CHARS(params
->fourcc
),
2617 service
->localport
);
2620 /* Don't unlock the service - leave it with a ref_count of 1. */
2626 vchiq_open_service_internal(VCHIQ_SERVICE_T
*service
, int client_id
)
2628 struct vchiq_open_payload payload
= {
2629 service
->base
.fourcc
,
2632 service
->version_min
2634 VCHIQ_ELEMENT_T body
= { &payload
, sizeof(payload
) };
2635 VCHIQ_STATUS_T status
= VCHIQ_SUCCESS
;
2637 service
->client_id
= client_id
;
2638 vchiq_use_service_internal(service
);
2639 status
= queue_message(service
->state
, NULL
,
2640 VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN
, service
->localport
, 0),
2641 &body
, 1, sizeof(payload
), QMFLAGS_IS_BLOCKING
);
2642 if (status
== VCHIQ_SUCCESS
) {
2643 /* Wait for the ACK/NAK */
2644 if (down_interruptible(&service
->remove_event
) != 0) {
2645 status
= VCHIQ_RETRY
;
2646 vchiq_release_service_internal(service
);
2647 } else if ((service
->srvstate
!= VCHIQ_SRVSTATE_OPEN
) &&
2648 (service
->srvstate
!= VCHIQ_SRVSTATE_OPENSYNC
)) {
2649 if (service
->srvstate
!= VCHIQ_SRVSTATE_CLOSEWAIT
)
2650 vchiq_log_error(vchiq_core_log_level
,
2651 "%d: osi - srvstate = %s (ref %d)",
2653 srvstate_names
[service
->srvstate
],
2654 service
->ref_count
);
2655 status
= VCHIQ_ERROR
;
2656 VCHIQ_SERVICE_STATS_INC(service
, error_count
);
2657 vchiq_release_service_internal(service
);
2664 release_service_messages(VCHIQ_SERVICE_T
*service
)
2666 VCHIQ_STATE_T
*state
= service
->state
;
2667 int slot_last
= state
->remote
->slot_last
;
2670 /* Release any claimed messages aimed at this service */
2672 if (service
->sync
) {
2673 VCHIQ_HEADER_T
*header
=
2674 (VCHIQ_HEADER_T
*)SLOT_DATA_FROM_INDEX(state
,
2675 state
->remote
->slot_sync
);
2676 if (VCHIQ_MSG_DSTPORT(header
->msgid
) == service
->localport
)
2677 release_message_sync(state
, header
);
2682 for (i
= state
->remote
->slot_first
; i
<= slot_last
; i
++) {
2683 VCHIQ_SLOT_INFO_T
*slot_info
=
2684 SLOT_INFO_FROM_INDEX(state
, i
);
2685 if (slot_info
->release_count
!= slot_info
->use_count
) {
2687 (char *)SLOT_DATA_FROM_INDEX(state
, i
);
2688 unsigned int pos
, end
;
2690 end
= VCHIQ_SLOT_SIZE
;
2691 if (data
== state
->rx_data
)
2692 /* This buffer is still being read from - stop
2693 ** at the current read position */
2694 end
= state
->rx_pos
& VCHIQ_SLOT_MASK
;
2699 VCHIQ_HEADER_T
*header
=
2700 (VCHIQ_HEADER_T
*)(data
+ pos
);
2701 int msgid
= header
->msgid
;
2702 int port
= VCHIQ_MSG_DSTPORT(msgid
);
2703 if ((port
== service
->localport
) &&
2704 (msgid
& VCHIQ_MSGID_CLAIMED
)) {
2705 vchiq_log_info(vchiq_core_log_level
,
2706 " fsi - hdr %pK", header
);
2707 release_slot(state
, slot_info
, header
,
2710 pos
+= calc_stride(header
->size
);
2711 if (pos
> VCHIQ_SLOT_SIZE
) {
2712 vchiq_log_error(vchiq_core_log_level
,
2713 "fsi - pos %x: header %pK, msgid %x, header->msgid %x, header->size %x",
2715 header
->msgid
, header
->size
);
2716 WARN(1, "invalid slot position\n");
2724 do_abort_bulks(VCHIQ_SERVICE_T
*service
)
2726 VCHIQ_STATUS_T status
;
2728 /* Abort any outstanding bulk transfers */
2729 if (mutex_lock_killable(&service
->bulk_mutex
) != 0)
2731 abort_outstanding_bulks(service
, &service
->bulk_tx
);
2732 abort_outstanding_bulks(service
, &service
->bulk_rx
);
2733 mutex_unlock(&service
->bulk_mutex
);
2735 status
= notify_bulks(service
, &service
->bulk_tx
, 0/*!retry_poll*/);
2736 if (status
== VCHIQ_SUCCESS
)
2737 status
= notify_bulks(service
, &service
->bulk_rx
,
2739 return (status
== VCHIQ_SUCCESS
);
2742 static VCHIQ_STATUS_T
2743 close_service_complete(VCHIQ_SERVICE_T
*service
, int failstate
)
2745 VCHIQ_STATUS_T status
;
2746 int is_server
= (service
->public_fourcc
!= VCHIQ_FOURCC_INVALID
);
2749 switch (service
->srvstate
) {
2750 case VCHIQ_SRVSTATE_OPEN
:
2751 case VCHIQ_SRVSTATE_CLOSESENT
:
2752 case VCHIQ_SRVSTATE_CLOSERECVD
:
2754 if (service
->auto_close
) {
2755 service
->client_id
= 0;
2756 service
->remoteport
= VCHIQ_PORT_FREE
;
2757 newstate
= VCHIQ_SRVSTATE_LISTENING
;
2759 newstate
= VCHIQ_SRVSTATE_CLOSEWAIT
;
2761 newstate
= VCHIQ_SRVSTATE_CLOSED
;
2762 vchiq_set_service_state(service
, newstate
);
2764 case VCHIQ_SRVSTATE_LISTENING
:
2767 vchiq_log_error(vchiq_core_log_level
,
2768 "close_service_complete(%x) called in state %s",
2769 service
->handle
, srvstate_names
[service
->srvstate
]);
2770 WARN(1, "close_service_complete in unexpected state\n");
2774 status
= make_service_callback(service
,
2775 VCHIQ_SERVICE_CLOSED
, NULL
, NULL
);
2777 if (status
!= VCHIQ_RETRY
) {
2778 int uc
= service
->service_use_count
;
2780 /* Complete the close process */
2781 for (i
= 0; i
< uc
; i
++)
2782 /* cater for cases where close is forced and the
2783 ** client may not close all it's handles */
2784 vchiq_release_service_internal(service
);
2786 service
->client_id
= 0;
2787 service
->remoteport
= VCHIQ_PORT_FREE
;
2789 if (service
->srvstate
== VCHIQ_SRVSTATE_CLOSED
)
2790 vchiq_free_service_internal(service
);
2791 else if (service
->srvstate
!= VCHIQ_SRVSTATE_CLOSEWAIT
) {
2793 service
->closing
= 0;
2795 up(&service
->remove_event
);
2798 vchiq_set_service_state(service
, failstate
);
2803 /* Called by the slot handler */
2805 vchiq_close_service_internal(VCHIQ_SERVICE_T
*service
, int close_recvd
)
2807 VCHIQ_STATE_T
*state
= service
->state
;
2808 VCHIQ_STATUS_T status
= VCHIQ_SUCCESS
;
2809 int is_server
= (service
->public_fourcc
!= VCHIQ_FOURCC_INVALID
);
2811 vchiq_log_info(vchiq_core_log_level
, "%d: csi:%d,%d (%s)",
2812 service
->state
->id
, service
->localport
, close_recvd
,
2813 srvstate_names
[service
->srvstate
]);
2815 switch (service
->srvstate
) {
2816 case VCHIQ_SRVSTATE_CLOSED
:
2817 case VCHIQ_SRVSTATE_HIDDEN
:
2818 case VCHIQ_SRVSTATE_LISTENING
:
2819 case VCHIQ_SRVSTATE_CLOSEWAIT
:
2821 vchiq_log_error(vchiq_core_log_level
,
2822 "vchiq_close_service_internal(1) called "
2824 srvstate_names
[service
->srvstate
]);
2825 else if (is_server
) {
2826 if (service
->srvstate
== VCHIQ_SRVSTATE_LISTENING
) {
2827 status
= VCHIQ_ERROR
;
2829 service
->client_id
= 0;
2830 service
->remoteport
= VCHIQ_PORT_FREE
;
2831 if (service
->srvstate
==
2832 VCHIQ_SRVSTATE_CLOSEWAIT
)
2833 vchiq_set_service_state(service
,
2834 VCHIQ_SRVSTATE_LISTENING
);
2836 up(&service
->remove_event
);
2838 vchiq_free_service_internal(service
);
2840 case VCHIQ_SRVSTATE_OPENING
:
2842 /* The open was rejected - tell the user */
2843 vchiq_set_service_state(service
,
2844 VCHIQ_SRVSTATE_CLOSEWAIT
);
2845 up(&service
->remove_event
);
2847 /* Shutdown mid-open - let the other side know */
2848 status
= queue_message(state
, service
,
2852 VCHIQ_MSG_DSTPORT(service
->remoteport
)),
2857 case VCHIQ_SRVSTATE_OPENSYNC
:
2858 mutex_lock(&state
->sync_mutex
);
2861 case VCHIQ_SRVSTATE_OPEN
:
2862 if (state
->is_master
|| close_recvd
) {
2863 if (!do_abort_bulks(service
))
2864 status
= VCHIQ_RETRY
;
2867 release_service_messages(service
);
2869 if (status
== VCHIQ_SUCCESS
)
2870 status
= queue_message(state
, service
,
2874 VCHIQ_MSG_DSTPORT(service
->remoteport
)),
2875 NULL
, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK
);
2877 if (status
== VCHIQ_SUCCESS
) {
2879 /* Change the state while the mutex is
2881 vchiq_set_service_state(service
,
2882 VCHIQ_SRVSTATE_CLOSESENT
);
2883 mutex_unlock(&state
->slot_mutex
);
2885 mutex_unlock(&state
->sync_mutex
);
2888 } else if (service
->srvstate
== VCHIQ_SRVSTATE_OPENSYNC
) {
2889 mutex_unlock(&state
->sync_mutex
);
2894 /* Change the state while the mutex is still held */
2895 vchiq_set_service_state(service
, VCHIQ_SRVSTATE_CLOSERECVD
);
2896 mutex_unlock(&state
->slot_mutex
);
2898 mutex_unlock(&state
->sync_mutex
);
2900 status
= close_service_complete(service
,
2901 VCHIQ_SRVSTATE_CLOSERECVD
);
2904 case VCHIQ_SRVSTATE_CLOSESENT
:
2906 /* This happens when a process is killed mid-close */
2909 if (!state
->is_master
) {
2910 if (!do_abort_bulks(service
)) {
2911 status
= VCHIQ_RETRY
;
2916 if (status
== VCHIQ_SUCCESS
)
2917 status
= close_service_complete(service
,
2918 VCHIQ_SRVSTATE_CLOSERECVD
);
2921 case VCHIQ_SRVSTATE_CLOSERECVD
:
2922 if (!close_recvd
&& is_server
)
2923 /* Force into LISTENING mode */
2924 vchiq_set_service_state(service
,
2925 VCHIQ_SRVSTATE_LISTENING
);
2926 status
= close_service_complete(service
,
2927 VCHIQ_SRVSTATE_CLOSERECVD
);
2931 vchiq_log_error(vchiq_core_log_level
,
2932 "vchiq_close_service_internal(%d) called in state %s",
2933 close_recvd
, srvstate_names
[service
->srvstate
]);
2940 /* Called from the application process upon process death */
2942 vchiq_terminate_service_internal(VCHIQ_SERVICE_T
*service
)
2944 VCHIQ_STATE_T
*state
= service
->state
;
2946 vchiq_log_info(vchiq_core_log_level
, "%d: tsi - (%d<->%d)",
2947 state
->id
, service
->localport
, service
->remoteport
);
2949 mark_service_closing(service
);
2951 /* Mark the service for removal by the slot handler */
2952 request_poll(state
, service
, VCHIQ_POLL_REMOVE
);
2955 /* Called from the slot handler */
2957 vchiq_free_service_internal(VCHIQ_SERVICE_T
*service
)
2959 VCHIQ_STATE_T
*state
= service
->state
;
2961 vchiq_log_info(vchiq_core_log_level
, "%d: fsi - (%d)",
2962 state
->id
, service
->localport
);
2964 switch (service
->srvstate
) {
2965 case VCHIQ_SRVSTATE_OPENING
:
2966 case VCHIQ_SRVSTATE_CLOSED
:
2967 case VCHIQ_SRVSTATE_HIDDEN
:
2968 case VCHIQ_SRVSTATE_LISTENING
:
2969 case VCHIQ_SRVSTATE_CLOSEWAIT
:
2972 vchiq_log_error(vchiq_core_log_level
,
2973 "%d: fsi - (%d) in state %s",
2974 state
->id
, service
->localport
,
2975 srvstate_names
[service
->srvstate
]);
2979 vchiq_set_service_state(service
, VCHIQ_SRVSTATE_FREE
);
2981 up(&service
->remove_event
);
2983 /* Release the initial lock */
2984 unlock_service(service
);
2988 vchiq_connect_internal(VCHIQ_STATE_T
*state
, VCHIQ_INSTANCE_T instance
)
2990 VCHIQ_SERVICE_T
*service
;
2993 /* Find all services registered to this client and enable them. */
2995 while ((service
= next_service_by_instance(state
, instance
,
2997 if (service
->srvstate
== VCHIQ_SRVSTATE_HIDDEN
)
2998 vchiq_set_service_state(service
,
2999 VCHIQ_SRVSTATE_LISTENING
);
3000 unlock_service(service
);
3003 if (state
->conn_state
== VCHIQ_CONNSTATE_DISCONNECTED
) {
3004 if (queue_message(state
, NULL
,
3005 VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT
, 0, 0), NULL
, 0,
3006 0, QMFLAGS_IS_BLOCKING
) == VCHIQ_RETRY
)
3009 vchiq_set_conn_state(state
, VCHIQ_CONNSTATE_CONNECTING
);
3012 if (state
->conn_state
== VCHIQ_CONNSTATE_CONNECTING
) {
3013 if (down_interruptible(&state
->connect
) != 0)
3016 vchiq_set_conn_state(state
, VCHIQ_CONNSTATE_CONNECTED
);
3017 up(&state
->connect
);
3020 return VCHIQ_SUCCESS
;
3024 vchiq_shutdown_internal(VCHIQ_STATE_T
*state
, VCHIQ_INSTANCE_T instance
)
3026 VCHIQ_SERVICE_T
*service
;
3029 /* Find all services registered to this client and enable them. */
3031 while ((service
= next_service_by_instance(state
, instance
,
3033 (void)vchiq_remove_service(service
->handle
);
3034 unlock_service(service
);
3037 return VCHIQ_SUCCESS
;
3041 vchiq_pause_internal(VCHIQ_STATE_T
*state
)
3043 VCHIQ_STATUS_T status
= VCHIQ_SUCCESS
;
3045 switch (state
->conn_state
) {
3046 case VCHIQ_CONNSTATE_CONNECTED
:
3047 /* Request a pause */
3048 vchiq_set_conn_state(state
, VCHIQ_CONNSTATE_PAUSING
);
3049 request_poll(state
, NULL
, 0);
3052 vchiq_log_error(vchiq_core_log_level
,
3053 "vchiq_pause_internal in state %s\n",
3054 conn_state_names
[state
->conn_state
]);
3055 status
= VCHIQ_ERROR
;
3056 VCHIQ_STATS_INC(state
, error_count
);
3064 vchiq_resume_internal(VCHIQ_STATE_T
*state
)
3066 VCHIQ_STATUS_T status
= VCHIQ_SUCCESS
;
3068 if (state
->conn_state
== VCHIQ_CONNSTATE_PAUSED
) {
3069 vchiq_set_conn_state(state
, VCHIQ_CONNSTATE_RESUMING
);
3070 request_poll(state
, NULL
, 0);
3072 status
= VCHIQ_ERROR
;
3073 VCHIQ_STATS_INC(state
, error_count
);
3080 vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle
)
3082 /* Unregister the service */
3083 VCHIQ_SERVICE_T
*service
= find_service_by_handle(handle
);
3084 VCHIQ_STATUS_T status
= VCHIQ_SUCCESS
;
3089 vchiq_log_info(vchiq_core_log_level
,
3090 "%d: close_service:%d",
3091 service
->state
->id
, service
->localport
);
3093 if ((service
->srvstate
== VCHIQ_SRVSTATE_FREE
) ||
3094 (service
->srvstate
== VCHIQ_SRVSTATE_LISTENING
) ||
3095 (service
->srvstate
== VCHIQ_SRVSTATE_HIDDEN
)) {
3096 unlock_service(service
);
3100 mark_service_closing(service
);
3102 if (current
== service
->state
->slot_handler_thread
) {
3103 status
= vchiq_close_service_internal(service
,
3105 BUG_ON(status
== VCHIQ_RETRY
);
3107 /* Mark the service for termination by the slot handler */
3108 request_poll(service
->state
, service
, VCHIQ_POLL_TERMINATE
);
3112 if (down_interruptible(&service
->remove_event
) != 0) {
3113 status
= VCHIQ_RETRY
;
3117 if ((service
->srvstate
== VCHIQ_SRVSTATE_FREE
) ||
3118 (service
->srvstate
== VCHIQ_SRVSTATE_LISTENING
) ||
3119 (service
->srvstate
== VCHIQ_SRVSTATE_OPEN
))
3122 vchiq_log_warning(vchiq_core_log_level
,
3123 "%d: close_service:%d - waiting in state %s",
3124 service
->state
->id
, service
->localport
,
3125 srvstate_names
[service
->srvstate
]);
3128 if ((status
== VCHIQ_SUCCESS
) &&
3129 (service
->srvstate
!= VCHIQ_SRVSTATE_FREE
) &&
3130 (service
->srvstate
!= VCHIQ_SRVSTATE_LISTENING
))
3131 status
= VCHIQ_ERROR
;
3133 unlock_service(service
);
3139 vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle
)
3141 /* Unregister the service */
3142 VCHIQ_SERVICE_T
*service
= find_service_by_handle(handle
);
3143 VCHIQ_STATUS_T status
= VCHIQ_SUCCESS
;
3148 vchiq_log_info(vchiq_core_log_level
,
3149 "%d: remove_service:%d",
3150 service
->state
->id
, service
->localport
);
3152 if (service
->srvstate
== VCHIQ_SRVSTATE_FREE
) {
3153 unlock_service(service
);
3157 mark_service_closing(service
);
3159 if ((service
->srvstate
== VCHIQ_SRVSTATE_HIDDEN
) ||
3160 (current
== service
->state
->slot_handler_thread
)) {
3161 /* Make it look like a client, because it must be removed and
3162 not left in the LISTENING state. */
3163 service
->public_fourcc
= VCHIQ_FOURCC_INVALID
;
3165 status
= vchiq_close_service_internal(service
,
3167 BUG_ON(status
== VCHIQ_RETRY
);
3169 /* Mark the service for removal by the slot handler */
3170 request_poll(service
->state
, service
, VCHIQ_POLL_REMOVE
);
3173 if (down_interruptible(&service
->remove_event
) != 0) {
3174 status
= VCHIQ_RETRY
;
3178 if ((service
->srvstate
== VCHIQ_SRVSTATE_FREE
) ||
3179 (service
->srvstate
== VCHIQ_SRVSTATE_OPEN
))
3182 vchiq_log_warning(vchiq_core_log_level
,
3183 "%d: remove_service:%d - waiting in state %s",
3184 service
->state
->id
, service
->localport
,
3185 srvstate_names
[service
->srvstate
]);
3188 if ((status
== VCHIQ_SUCCESS
) &&
3189 (service
->srvstate
!= VCHIQ_SRVSTATE_FREE
))
3190 status
= VCHIQ_ERROR
;
3192 unlock_service(service
);
3198 /* This function may be called by kernel threads or user threads.
3199 * User threads may receive VCHIQ_RETRY to indicate that a signal has been
3200 * received and the call should be retried after being returned to user
3202 * When called in blocking mode, the userdata field points to a bulk_waiter
3206 vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle
,
3207 VCHI_MEM_HANDLE_T memhandle
, void *offset
, int size
, void *userdata
,
3208 VCHIQ_BULK_MODE_T mode
, VCHIQ_BULK_DIR_T dir
)
3210 VCHIQ_SERVICE_T
*service
= find_service_by_handle(handle
);
3211 VCHIQ_BULK_QUEUE_T
*queue
;
3213 VCHIQ_STATE_T
*state
;
3214 struct bulk_waiter
*bulk_waiter
= NULL
;
3215 const char dir_char
= (dir
== VCHIQ_BULK_TRANSMIT
) ? 't' : 'r';
3216 const int dir_msgtype
= (dir
== VCHIQ_BULK_TRANSMIT
) ?
3217 VCHIQ_MSG_BULK_TX
: VCHIQ_MSG_BULK_RX
;
3218 VCHIQ_STATUS_T status
= VCHIQ_ERROR
;
3221 (service
->srvstate
!= VCHIQ_SRVSTATE_OPEN
) ||
3222 ((memhandle
== VCHI_MEM_HANDLE_INVALID
) && (offset
== NULL
)) ||
3223 (vchiq_check_service(service
) != VCHIQ_SUCCESS
))
3227 case VCHIQ_BULK_MODE_NOCALLBACK
:
3228 case VCHIQ_BULK_MODE_CALLBACK
:
3230 case VCHIQ_BULK_MODE_BLOCKING
:
3231 bulk_waiter
= (struct bulk_waiter
*)userdata
;
3232 sema_init(&bulk_waiter
->event
, 0);
3233 bulk_waiter
->actual
= 0;
3234 bulk_waiter
->bulk
= NULL
;
3236 case VCHIQ_BULK_MODE_WAITING
:
3237 bulk_waiter
= (struct bulk_waiter
*)userdata
;
3238 bulk
= bulk_waiter
->bulk
;
3244 state
= service
->state
;
3246 queue
= (dir
== VCHIQ_BULK_TRANSMIT
) ?
3247 &service
->bulk_tx
: &service
->bulk_rx
;
3249 if (mutex_lock_killable(&service
->bulk_mutex
) != 0) {
3250 status
= VCHIQ_RETRY
;
3254 if (queue
->local_insert
== queue
->remove
+ VCHIQ_NUM_SERVICE_BULKS
) {
3255 VCHIQ_SERVICE_STATS_INC(service
, bulk_stalls
);
3257 mutex_unlock(&service
->bulk_mutex
);
3258 if (down_interruptible(&service
->bulk_remove_event
)
3260 status
= VCHIQ_RETRY
;
3263 if (mutex_lock_killable(&service
->bulk_mutex
)
3265 status
= VCHIQ_RETRY
;
3268 } while (queue
->local_insert
== queue
->remove
+
3269 VCHIQ_NUM_SERVICE_BULKS
);
3272 bulk
= &queue
->bulks
[BULK_INDEX(queue
->local_insert
)];
3276 bulk
->userdata
= userdata
;
3278 bulk
->actual
= VCHIQ_BULK_ACTUAL_ABORTED
;
3280 if (vchiq_prepare_bulk_data(bulk
, memhandle
, offset
, size
, dir
) !=
3282 goto unlock_error_exit
;
3286 vchiq_log_info(vchiq_core_log_level
,
3287 "%d: bt (%d->%d) %cx %x@%pK %pK",
3288 state
->id
, service
->localport
, service
->remoteport
, dir_char
,
3289 size
, bulk
->data
, userdata
);
3291 /* The slot mutex must be held when the service is being closed, so
3292 claim it here to ensure that isn't happening */
3293 if (mutex_lock_killable(&state
->slot_mutex
) != 0) {
3294 status
= VCHIQ_RETRY
;
3295 goto cancel_bulk_error_exit
;
3298 if (service
->srvstate
!= VCHIQ_SRVSTATE_OPEN
)
3299 goto unlock_both_error_exit
;
3301 if (state
->is_master
) {
3302 queue
->local_insert
++;
3303 if (resolve_bulks(service
, queue
))
3304 request_poll(state
, service
,
3305 (dir
== VCHIQ_BULK_TRANSMIT
) ?
3306 VCHIQ_POLL_TXNOTIFY
: VCHIQ_POLL_RXNOTIFY
);
3308 int payload
[2] = { (int)(long)bulk
->data
, bulk
->size
};
3309 VCHIQ_ELEMENT_T element
= { payload
, sizeof(payload
) };
3311 status
= queue_message(state
, NULL
,
3312 VCHIQ_MAKE_MSG(dir_msgtype
,
3313 service
->localport
, service
->remoteport
),
3314 &element
, 1, sizeof(payload
),
3315 QMFLAGS_IS_BLOCKING
|
3316 QMFLAGS_NO_MUTEX_LOCK
|
3317 QMFLAGS_NO_MUTEX_UNLOCK
);
3318 if (status
!= VCHIQ_SUCCESS
) {
3319 goto unlock_both_error_exit
;
3321 queue
->local_insert
++;
3324 mutex_unlock(&state
->slot_mutex
);
3325 mutex_unlock(&service
->bulk_mutex
);
3327 vchiq_log_trace(vchiq_core_log_level
,
3328 "%d: bt:%d %cx li=%x ri=%x p=%x",
3330 service
->localport
, dir_char
,
3331 queue
->local_insert
, queue
->remote_insert
, queue
->process
);
3334 unlock_service(service
);
3336 status
= VCHIQ_SUCCESS
;
3339 bulk_waiter
->bulk
= bulk
;
3340 if (down_interruptible(&bulk_waiter
->event
) != 0)
3341 status
= VCHIQ_RETRY
;
3342 else if (bulk_waiter
->actual
== VCHIQ_BULK_ACTUAL_ABORTED
)
3343 status
= VCHIQ_ERROR
;
3348 unlock_both_error_exit
:
3349 mutex_unlock(&state
->slot_mutex
);
3350 cancel_bulk_error_exit
:
3351 vchiq_complete_bulk(bulk
);
3353 mutex_unlock(&service
->bulk_mutex
);
3357 unlock_service(service
);
3362 vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle
,
3363 const VCHIQ_ELEMENT_T
*elements
, unsigned int count
)
3365 VCHIQ_SERVICE_T
*service
= find_service_by_handle(handle
);
3366 VCHIQ_STATUS_T status
= VCHIQ_ERROR
;
3368 unsigned int size
= 0;
3372 (vchiq_check_service(service
) != VCHIQ_SUCCESS
))
3375 for (i
= 0; i
< (unsigned int)count
; i
++) {
3376 if (elements
[i
].size
) {
3377 if (elements
[i
].data
== NULL
) {
3378 VCHIQ_SERVICE_STATS_INC(service
, error_count
);
3381 size
+= elements
[i
].size
;
3385 if (size
> VCHIQ_MAX_MSG_SIZE
) {
3386 VCHIQ_SERVICE_STATS_INC(service
, error_count
);
3390 switch (service
->srvstate
) {
3391 case VCHIQ_SRVSTATE_OPEN
:
3392 status
= queue_message(service
->state
, service
,
3393 VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA
,
3395 service
->remoteport
),
3396 elements
, count
, size
, 1);
3398 case VCHIQ_SRVSTATE_OPENSYNC
:
3399 status
= queue_message_sync(service
->state
, service
,
3400 VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA
,
3402 service
->remoteport
),
3403 elements
, count
, size
, 1);
3406 status
= VCHIQ_ERROR
;
3412 unlock_service(service
);
3418 vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle
, VCHIQ_HEADER_T
*header
)
3420 VCHIQ_SERVICE_T
*service
= find_service_by_handle(handle
);
3421 VCHIQ_SHARED_STATE_T
*remote
;
3422 VCHIQ_STATE_T
*state
;
3428 state
= service
->state
;
3429 remote
= state
->remote
;
3431 slot_index
= SLOT_INDEX_FROM_DATA(state
, (void *)header
);
3433 if ((slot_index
>= remote
->slot_first
) &&
3434 (slot_index
<= remote
->slot_last
)) {
3435 int msgid
= header
->msgid
;
3436 if (msgid
& VCHIQ_MSGID_CLAIMED
) {
3437 VCHIQ_SLOT_INFO_T
*slot_info
=
3438 SLOT_INFO_FROM_INDEX(state
, slot_index
);
3440 release_slot(state
, slot_info
, header
, service
);
3442 } else if (slot_index
== remote
->slot_sync
)
3443 release_message_sync(state
, header
);
3445 unlock_service(service
);
3449 release_message_sync(VCHIQ_STATE_T
*state
, VCHIQ_HEADER_T
*header
)
3451 header
->msgid
= VCHIQ_MSGID_PADDING
;
3453 remote_event_signal(&state
->remote
->sync_release
);
3457 vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle
, short *peer_version
)
3459 VCHIQ_STATUS_T status
= VCHIQ_ERROR
;
3460 VCHIQ_SERVICE_T
*service
= find_service_by_handle(handle
);
3463 (vchiq_check_service(service
) != VCHIQ_SUCCESS
) ||
3466 *peer_version
= service
->peer_version
;
3467 status
= VCHIQ_SUCCESS
;
3471 unlock_service(service
);
3476 vchiq_get_config(VCHIQ_INSTANCE_T instance
,
3477 int config_size
, VCHIQ_CONFIG_T
*pconfig
)
3479 VCHIQ_CONFIG_T config
;
3483 config
.max_msg_size
= VCHIQ_MAX_MSG_SIZE
;
3484 config
.bulk_threshold
= VCHIQ_MAX_MSG_SIZE
;
3485 config
.max_outstanding_bulks
= VCHIQ_NUM_SERVICE_BULKS
;
3486 config
.max_services
= VCHIQ_MAX_SERVICES
;
3487 config
.version
= VCHIQ_VERSION
;
3488 config
.version_min
= VCHIQ_VERSION_MIN
;
3490 if (config_size
> sizeof(VCHIQ_CONFIG_T
))
3493 memcpy(pconfig
, &config
,
3494 min(config_size
, (int)(sizeof(VCHIQ_CONFIG_T
))));
3496 return VCHIQ_SUCCESS
;
3500 vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle
,
3501 VCHIQ_SERVICE_OPTION_T option
, int value
)
3503 VCHIQ_SERVICE_T
*service
= find_service_by_handle(handle
);
3504 VCHIQ_STATUS_T status
= VCHIQ_ERROR
;
3508 case VCHIQ_SERVICE_OPTION_AUTOCLOSE
:
3509 service
->auto_close
= value
;
3510 status
= VCHIQ_SUCCESS
;
3513 case VCHIQ_SERVICE_OPTION_SLOT_QUOTA
: {
3514 VCHIQ_SERVICE_QUOTA_T
*service_quota
=
3515 &service
->state
->service_quotas
[
3516 service
->localport
];
3518 value
= service
->state
->default_slot_quota
;
3519 if ((value
>= service_quota
->slot_use_count
) &&
3520 (value
< (unsigned short)~0)) {
3521 service_quota
->slot_quota
= value
;
3522 if ((value
>= service_quota
->slot_use_count
) &&
3523 (service_quota
->message_quota
>=
3524 service_quota
->message_use_count
)) {
3525 /* Signal the service that it may have
3526 ** dropped below its quota */
3527 up(&service_quota
->quota_event
);
3529 status
= VCHIQ_SUCCESS
;
3533 case VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA
: {
3534 VCHIQ_SERVICE_QUOTA_T
*service_quota
=
3535 &service
->state
->service_quotas
[
3536 service
->localport
];
3538 value
= service
->state
->default_message_quota
;
3539 if ((value
>= service_quota
->message_use_count
) &&
3540 (value
< (unsigned short)~0)) {
3541 service_quota
->message_quota
= value
;
3543 service_quota
->message_use_count
) &&
3544 (service_quota
->slot_quota
>=
3545 service_quota
->slot_use_count
))
3546 /* Signal the service that it may have
3547 ** dropped below its quota */
3548 up(&service_quota
->quota_event
);
3549 status
= VCHIQ_SUCCESS
;
3553 case VCHIQ_SERVICE_OPTION_SYNCHRONOUS
:
3554 if ((service
->srvstate
== VCHIQ_SRVSTATE_HIDDEN
) ||
3555 (service
->srvstate
==
3556 VCHIQ_SRVSTATE_LISTENING
)) {
3557 service
->sync
= value
;
3558 status
= VCHIQ_SUCCESS
;
3562 case VCHIQ_SERVICE_OPTION_TRACE
:
3563 service
->trace
= value
;
3564 status
= VCHIQ_SUCCESS
;
3570 unlock_service(service
);
3577 vchiq_dump_shared_state(void *dump_context
, VCHIQ_STATE_T
*state
,
3578 VCHIQ_SHARED_STATE_T
*shared
, const char *label
)
3580 static const char *const debug_names
[] = {
3582 "SLOT_HANDLER_COUNT",
3583 "SLOT_HANDLER_LINE",
3587 "AWAIT_COMPLETION_LINE",
3588 "DEQUEUE_MESSAGE_LINE",
3589 "SERVICE_CALLBACK_LINE",
3590 "MSG_QUEUE_FULL_COUNT",
3591 "COMPLETION_QUEUE_FULL_COUNT"
3597 len
= snprintf(buf
, sizeof(buf
),
3598 " %s: slots %d-%d tx_pos=%x recycle=%x",
3599 label
, shared
->slot_first
, shared
->slot_last
,
3600 shared
->tx_pos
, shared
->slot_queue_recycle
);
3601 vchiq_dump(dump_context
, buf
, len
+ 1);
3603 len
= snprintf(buf
, sizeof(buf
),
3605 vchiq_dump(dump_context
, buf
, len
+ 1);
3607 for (i
= shared
->slot_first
; i
<= shared
->slot_last
; i
++) {
3608 VCHIQ_SLOT_INFO_T slot_info
= *SLOT_INFO_FROM_INDEX(state
, i
);
3609 if (slot_info
.use_count
!= slot_info
.release_count
) {
3610 len
= snprintf(buf
, sizeof(buf
),
3611 " %d: %d/%d", i
, slot_info
.use_count
,
3612 slot_info
.release_count
);
3613 vchiq_dump(dump_context
, buf
, len
+ 1);
3617 for (i
= 1; i
< shared
->debug
[DEBUG_ENTRIES
]; i
++) {
3618 len
= snprintf(buf
, sizeof(buf
), " DEBUG: %s = %d(%x)",
3619 debug_names
[i
], shared
->debug
[i
], shared
->debug
[i
]);
3620 vchiq_dump(dump_context
, buf
, len
+ 1);
3625 vchiq_dump_state(void *dump_context
, VCHIQ_STATE_T
*state
)
3631 len
= snprintf(buf
, sizeof(buf
), "State %d: %s", state
->id
,
3632 conn_state_names
[state
->conn_state
]);
3633 vchiq_dump(dump_context
, buf
, len
+ 1);
3635 len
= snprintf(buf
, sizeof(buf
),
3636 " tx_pos=%x(@%pK), rx_pos=%x(@%pK)",
3637 state
->local
->tx_pos
,
3638 state
->tx_data
+ (state
->local_tx_pos
& VCHIQ_SLOT_MASK
),
3640 state
->rx_data
+ (state
->rx_pos
& VCHIQ_SLOT_MASK
));
3641 vchiq_dump(dump_context
, buf
, len
+ 1);
3643 len
= snprintf(buf
, sizeof(buf
),
3644 " Version: %d (min %d)",
3645 VCHIQ_VERSION
, VCHIQ_VERSION_MIN
);
3646 vchiq_dump(dump_context
, buf
, len
+ 1);
3648 if (VCHIQ_ENABLE_STATS
) {
3649 len
= snprintf(buf
, sizeof(buf
),
3650 " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, "
3652 state
->stats
.ctrl_tx_count
, state
->stats
.ctrl_rx_count
,
3653 state
->stats
.error_count
);
3654 vchiq_dump(dump_context
, buf
, len
+ 1);
3657 len
= snprintf(buf
, sizeof(buf
),
3658 " Slots: %d available (%d data), %d recyclable, %d stalls "
3660 ((state
->slot_queue_available
* VCHIQ_SLOT_SIZE
) -
3661 state
->local_tx_pos
) / VCHIQ_SLOT_SIZE
,
3662 state
->data_quota
- state
->data_use_count
,
3663 state
->local
->slot_queue_recycle
- state
->slot_queue_available
,
3664 state
->stats
.slot_stalls
, state
->stats
.data_stalls
);
3665 vchiq_dump(dump_context
, buf
, len
+ 1);
3667 vchiq_dump_platform_state(dump_context
);
3669 vchiq_dump_shared_state(dump_context
, state
, state
->local
, "Local");
3670 vchiq_dump_shared_state(dump_context
, state
, state
->remote
, "Remote");
3672 vchiq_dump_platform_instances(dump_context
);
3674 for (i
= 0; i
< state
->unused_service
; i
++) {
3675 VCHIQ_SERVICE_T
*service
= find_service_by_port(state
, i
);
3678 vchiq_dump_service_state(dump_context
, service
);
3679 unlock_service(service
);
3685 vchiq_dump_service_state(void *dump_context
, VCHIQ_SERVICE_T
*service
)
3690 len
= snprintf(buf
, sizeof(buf
), "Service %u: %s (ref %u)",
3691 service
->localport
, srvstate_names
[service
->srvstate
],
3692 service
->ref_count
- 1); /*Don't include the lock just taken*/
3694 if (service
->srvstate
!= VCHIQ_SRVSTATE_FREE
) {
3695 char remoteport
[30];
3696 VCHIQ_SERVICE_QUOTA_T
*service_quota
=
3697 &service
->state
->service_quotas
[service
->localport
];
3698 int fourcc
= service
->base
.fourcc
;
3699 int tx_pending
, rx_pending
;
3700 if (service
->remoteport
!= VCHIQ_PORT_FREE
) {
3701 int len2
= snprintf(remoteport
, sizeof(remoteport
),
3702 "%u", service
->remoteport
);
3703 if (service
->public_fourcc
!= VCHIQ_FOURCC_INVALID
)
3704 snprintf(remoteport
+ len2
,
3705 sizeof(remoteport
) - len2
,
3706 " (client %x)", service
->client_id
);
3708 strcpy(remoteport
, "n/a");
3710 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
3711 " '%c%c%c%c' remote %s (msg use %d/%d, slot use %d/%d)",
3712 VCHIQ_FOURCC_AS_4CHARS(fourcc
),
3714 service_quota
->message_use_count
,
3715 service_quota
->message_quota
,
3716 service_quota
->slot_use_count
,
3717 service_quota
->slot_quota
);
3719 vchiq_dump(dump_context
, buf
, len
+ 1);
3721 tx_pending
= service
->bulk_tx
.local_insert
-
3722 service
->bulk_tx
.remote_insert
;
3724 rx_pending
= service
->bulk_rx
.local_insert
-
3725 service
->bulk_rx
.remote_insert
;
3727 len
= snprintf(buf
, sizeof(buf
),
3728 " Bulk: tx_pending=%d (size %d),"
3729 " rx_pending=%d (size %d)",
3731 tx_pending
? service
->bulk_tx
.bulks
[
3732 BULK_INDEX(service
->bulk_tx
.remove
)].size
: 0,
3734 rx_pending
? service
->bulk_rx
.bulks
[
3735 BULK_INDEX(service
->bulk_rx
.remove
)].size
: 0);
3737 if (VCHIQ_ENABLE_STATS
) {
3738 vchiq_dump(dump_context
, buf
, len
+ 1);
3740 len
= snprintf(buf
, sizeof(buf
),
3741 " Ctrl: tx_count=%d, tx_bytes=%llu, "
3742 "rx_count=%d, rx_bytes=%llu",
3743 service
->stats
.ctrl_tx_count
,
3744 service
->stats
.ctrl_tx_bytes
,
3745 service
->stats
.ctrl_rx_count
,
3746 service
->stats
.ctrl_rx_bytes
);
3747 vchiq_dump(dump_context
, buf
, len
+ 1);
3749 len
= snprintf(buf
, sizeof(buf
),
3750 " Bulk: tx_count=%d, tx_bytes=%llu, "
3751 "rx_count=%d, rx_bytes=%llu",
3752 service
->stats
.bulk_tx_count
,
3753 service
->stats
.bulk_tx_bytes
,
3754 service
->stats
.bulk_rx_count
,
3755 service
->stats
.bulk_rx_bytes
);
3756 vchiq_dump(dump_context
, buf
, len
+ 1);
3758 len
= snprintf(buf
, sizeof(buf
),
3759 " %d quota stalls, %d slot stalls, "
3760 "%d bulk stalls, %d aborted, %d errors",
3761 service
->stats
.quota_stalls
,
3762 service
->stats
.slot_stalls
,
3763 service
->stats
.bulk_stalls
,
3764 service
->stats
.bulk_aborted_count
,
3765 service
->stats
.error_count
);
3769 vchiq_dump(dump_context
, buf
, len
+ 1);
3771 if (service
->srvstate
!= VCHIQ_SRVSTATE_FREE
)
3772 vchiq_dump_platform_service_state(dump_context
, service
);
3777 vchiq_loud_error_header(void)
3779 vchiq_log_error(vchiq_core_log_level
,
3780 "============================================================"
3781 "================");
3782 vchiq_log_error(vchiq_core_log_level
,
3783 "============================================================"
3784 "================");
3785 vchiq_log_error(vchiq_core_log_level
, "=====");
3789 vchiq_loud_error_footer(void)
3791 vchiq_log_error(vchiq_core_log_level
, "=====");
3792 vchiq_log_error(vchiq_core_log_level
,
3793 "============================================================"
3794 "================");
3795 vchiq_log_error(vchiq_core_log_level
,
3796 "============================================================"
3797 "================");
3801 VCHIQ_STATUS_T
vchiq_send_remote_use(VCHIQ_STATE_T
*state
)
3803 VCHIQ_STATUS_T status
= VCHIQ_RETRY
;
3804 if (state
->conn_state
!= VCHIQ_CONNSTATE_DISCONNECTED
)
3805 status
= queue_message(state
, NULL
,
3806 VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE
, 0, 0),
3811 VCHIQ_STATUS_T
vchiq_send_remote_release(VCHIQ_STATE_T
*state
)
3813 VCHIQ_STATUS_T status
= VCHIQ_RETRY
;
3814 if (state
->conn_state
!= VCHIQ_CONNSTATE_DISCONNECTED
)
3815 status
= queue_message(state
, NULL
,
3816 VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_RELEASE
, 0, 0),
3821 VCHIQ_STATUS_T
vchiq_send_remote_use_active(VCHIQ_STATE_T
*state
)
3823 VCHIQ_STATUS_T status
= VCHIQ_RETRY
;
3824 if (state
->conn_state
!= VCHIQ_CONNSTATE_DISCONNECTED
)
3825 status
= queue_message(state
, NULL
,
3826 VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE_ACTIVE
, 0, 0),
3831 void vchiq_log_dump_mem(const char *label
, uint32_t addr
, const void *void_mem
,
3834 const uint8_t *mem
= (const uint8_t *)void_mem
;
3839 while (num_bytes
> 0) {
3842 for (offset
= 0; offset
< 16; offset
++) {
3843 if (offset
< num_bytes
)
3844 s
+= snprintf(s
, 4, "%02x ", mem
[offset
]);
3846 s
+= snprintf(s
, 4, " ");
3849 for (offset
= 0; offset
< 16; offset
++) {
3850 if (offset
< num_bytes
) {
3851 uint8_t ch
= mem
[offset
];
3853 if ((ch
< ' ') || (ch
> '~'))
3860 if ((label
!= NULL
) && (*label
!= '\0'))
3861 vchiq_log_trace(VCHIQ_LOG_TRACE
,
3862 "%s: %08x: %s", label
, addr
, line_buf
);
3864 vchiq_log_trace(VCHIQ_LOG_TRACE
,
3865 "%08x: %s", addr
, line_buf
);