vchiq_arm.c revision 1.13.4.2 1 /**
2 * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved.
3 * Copyright (c) 2010-2012 Broadcom. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions, and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The names of the above-listed copyright holders may not be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * ALTERNATIVELY, this software may be distributed under the terms of the
19 * GNU General Public License ("GPL") version 2, as published by the Free
20 * Software Foundation.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
26 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/file.h>
39 #include <sys/filedesc.h>
40 #include <sys/kmem.h>
41
42 #include "vchiq_core.h"
43 #include "vchiq_ioctl.h"
44 #include "vchiq_arm.h"
45 #include "vchiq_debugfs.h"
46
47 #define DEVICE_NAME "vchiq"
48
49 /* Override the default prefix, which would be vchiq_arm (from the filename) */
50 #undef MODULE_PARAM_PREFIX
51 #define MODULE_PARAM_PREFIX DEVICE_NAME "."
52
53 #define VCHIQ_MINOR 0
54
55 /* Some per-instance constants */
56 #define MAX_COMPLETIONS 128
57 #define MAX_SERVICES 64
58 #define MAX_ELEMENTS 8
59 #define MSG_QUEUE_SIZE 128
60
61 #define KEEPALIVE_VER 1
62 #define KEEPALIVE_VER_MIN KEEPALIVE_VER
63
64 MALLOC_DEFINE(M_VCHIQ, "vchiq_cdev", "VideoCore cdev memory");
65
66 /* Run time control of log level, based on KERN_XXX level. */
67 int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT;
68 int vchiq_susp_log_level = VCHIQ_LOG_ERROR;
69
70 #define SUSPEND_TIMER_TIMEOUT_MS 100
71 #define SUSPEND_RETRY_TIMER_TIMEOUT_MS 1000
72
73 #define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */
74 static const char *const suspend_state_names[] = {
75 "VC_SUSPEND_FORCE_CANCELED",
76 "VC_SUSPEND_REJECTED",
77 "VC_SUSPEND_FAILED",
78 "VC_SUSPEND_IDLE",
79 "VC_SUSPEND_REQUESTED",
80 "VC_SUSPEND_IN_PROGRESS",
81 "VC_SUSPEND_SUSPENDED"
82 };
83 #define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */
84 static const char *const resume_state_names[] = {
85 "VC_RESUME_FAILED",
86 "VC_RESUME_IDLE",
87 "VC_RESUME_REQUESTED",
88 "VC_RESUME_IN_PROGRESS",
89 "VC_RESUME_RESUMED"
90 };
91 /* The number of times we allow force suspend to timeout before actually
92 ** _forcing_ suspend. This is to cater for SW which fails to release vchiq
93 ** correctly - we don't want to prevent ARM suspend indefinitely in this case.
94 */
95 #define FORCE_SUSPEND_FAIL_MAX 8
96
97 /* The time in ms allowed for videocore to go idle when force suspend has been
98 * requested */
99 #define FORCE_SUSPEND_TIMEOUT_MS 200
100
101
102 static void suspend_timer_callback(unsigned long context);
103
104
105 typedef struct user_service_struct {
106 VCHIQ_SERVICE_T *service;
107 void *userdata;
108 VCHIQ_INSTANCE_T instance;
109 char is_vchi;
110 char dequeue_pending;
111 char close_pending;
112 int message_available_pos;
113 int msg_insert;
114 int msg_remove;
115 struct semaphore insert_event;
116 struct semaphore remove_event;
117 struct semaphore close_event;
118 VCHIQ_HEADER_T * msg_queue[MSG_QUEUE_SIZE];
119 } USER_SERVICE_T;
120
121 struct bulk_waiter_node {
122 struct bulk_waiter bulk_waiter;
123 int pid;
124 struct list_head list;
125 };
126
127 struct vchiq_instance_struct {
128 VCHIQ_STATE_T *state;
129 VCHIQ_COMPLETION_DATA_T completions[MAX_COMPLETIONS];
130 int completion_insert;
131 int completion_remove;
132 struct semaphore insert_event;
133 struct semaphore remove_event;
134 struct mutex completion_mutex;
135
136 int connected;
137 int closing;
138 int pid;
139 int mark;
140 int use_close_delivered;
141 int trace;
142
143 struct list_head bulk_waiter_list;
144 struct mutex bulk_waiter_list_mutex;
145
146 VCHIQ_DEBUGFS_NODE_T debugfs_node;
147 };
148
149 typedef struct dump_context_struct {
150 char __user *buf;
151 size_t actual;
152 size_t space;
153 loff_t offset;
154 } DUMP_CONTEXT_T;
155
156 VCHIQ_STATE_T g_state;
157 static DEFINE_SPINLOCK(msg_queue_spinlock);
158
159 static const char *const ioctl_names[] = {
160 "CONNECT",
161 "SHUTDOWN",
162 "CREATE_SERVICE",
163 "REMOVE_SERVICE",
164 "QUEUE_MESSAGE",
165 "QUEUE_BULK_TRANSMIT",
166 "QUEUE_BULK_RECEIVE",
167 "AWAIT_COMPLETION",
168 "DEQUEUE_MESSAGE",
169 "GET_CLIENT_ID",
170 "GET_CONFIG",
171 "CLOSE_SERVICE",
172 "USE_SERVICE",
173 "RELEASE_SERVICE",
174 "SET_SERVICE_OPTION",
175 "DUMP_PHYS_MEM",
176 "LIB_VERSION",
177 "CLOSE_DELIVERED"
178 };
179
180 vchiq_static_assert((sizeof(ioctl_names)/sizeof(ioctl_names[0])) ==
181 (VCHIQ_IOC_MAX + 1));
182
183 static dev_type_open(vchiq_open);
184
185 struct cdevsw vchiq_cdevsw = {
186 .d_open = vchiq_open,
187 .d_close = noclose,
188 .d_read = noread,
189 .d_write = nowrite,
190 .d_ioctl = noioctl,
191 .d_stop = nostop,
192 .d_tty = notty,
193 .d_poll = nopoll,
194 .d_mmap = nommap,
195 .d_kqfilter = nokqfilter,
196 .d_discard = nodiscard,
197 .d_flag = D_OTHER | D_MPSAFE
198 };
199
200 extern struct cfdriver vchiq_cd;
201
202 static int vchiq_ioctl(struct file *, u_long, void *);
203 static int vchiq_close(struct file *);
204 static int vchiq_read(struct file *, off_t *, struct uio *, kauth_cred_t, int);
205
206 static const struct fileops vchiq_fileops = {
207 .fo_read = vchiq_read,
208 .fo_write = fbadop_write,
209 .fo_ioctl = vchiq_ioctl,
210 .fo_fcntl = fnullop_fcntl,
211 .fo_poll = fnullop_poll,
212 .fo_stat = fbadop_stat,
213 .fo_close = vchiq_close,
214 .fo_kqfilter = fnullop_kqfilter,
215 };
216
217 #if 0
218 static void
219 dump_phys_mem(void *virt_addr, uint32_t num_bytes);
220 #endif
221
222 /****************************************************************************
223 *
224 * add_completion
225 *
226 ***************************************************************************/
227
228 static VCHIQ_STATUS_T
229 add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason,
230 VCHIQ_HEADER_T *header, USER_SERVICE_T *user_service,
231 void *bulk_userdata)
232 {
233 VCHIQ_COMPLETION_DATA_T *completion;
234 int insert;
235 DEBUG_INITIALISE(g_state.local)
236
237 insert = instance->completion_insert;
238 while ((insert - instance->completion_remove) >= MAX_COMPLETIONS) {
239 /* Out of space - wait for the client */
240 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
241 vchiq_log_trace(vchiq_arm_log_level,
242 "add_completion - completion queue full");
243 DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT);
244
245 if (down_interruptible(&instance->remove_event) != 0) {
246 vchiq_log_info(vchiq_arm_log_level,
247 "service_callback interrupted");
248 return VCHIQ_RETRY;
249 }
250
251 if (instance->closing) {
252 vchiq_log_info(vchiq_arm_log_level,
253 "service_callback closing");
254 return VCHIQ_SUCCESS;
255 }
256 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
257 }
258
259 completion = &instance->completions[insert & (MAX_COMPLETIONS - 1)];
260
261 completion->header = header;
262 completion->reason = reason;
263 /* N.B. service_userdata is updated while processing AWAIT_COMPLETION */
264 completion->service_userdata = user_service->service;
265 completion->bulk_userdata = bulk_userdata;
266
267 if (reason == VCHIQ_SERVICE_CLOSED) {
268 /* Take an extra reference, to be held until
269 this CLOSED notification is delivered. */
270 lock_service(user_service->service);
271 if (instance->use_close_delivered)
272 user_service->close_pending = 1;
273 }
274
275 /* A write barrier is needed here to ensure that the entire completion
276 record is written out before the insert point. */
277 wmb();
278
279 if (reason == VCHIQ_MESSAGE_AVAILABLE)
280 user_service->message_available_pos = insert;
281
282 instance->completion_insert = ++insert;
283
284 up(&instance->insert_event);
285
286 return VCHIQ_SUCCESS;
287 }
288
289 /****************************************************************************
290 *
291 * service_callback
292 *
293 ***************************************************************************/
294
295 static VCHIQ_STATUS_T
296 service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header,
297 VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata)
298 {
299 /* How do we ensure the callback goes to the right client?
300 ** The service_user data points to a USER_SERVICE_T record containing
301 ** the original callback and the user state structure, which contains a
302 ** circular buffer for completion records.
303 */
304 USER_SERVICE_T *user_service;
305 VCHIQ_SERVICE_T *service;
306 VCHIQ_INSTANCE_T instance;
307 int skip_completion = 0;
308 DEBUG_INITIALISE(g_state.local)
309
310 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
311
312 service = handle_to_service(handle);
313 BUG_ON(!service);
314 user_service = (USER_SERVICE_T *)service->base.userdata;
315 instance = user_service->instance;
316
317 if (!instance || instance->closing)
318 return VCHIQ_SUCCESS;
319
320 vchiq_log_trace(vchiq_arm_log_level,
321 "service_callback - service %lx(%d,%p), reason %d, header %lx, "
322 "instance %lx, bulk_userdata %lx",
323 (unsigned long)user_service,
324 service->localport, user_service->userdata,
325 reason, (unsigned long)header,
326 (unsigned long)instance, (unsigned long)bulk_userdata);
327
328 if (header && user_service->is_vchi) {
329 spin_lock(&msg_queue_spinlock);
330 while (user_service->msg_insert ==
331 (user_service->msg_remove + MSG_QUEUE_SIZE)) {
332 spin_unlock(&msg_queue_spinlock);
333 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
334 DEBUG_COUNT(MSG_QUEUE_FULL_COUNT);
335 vchiq_log_trace(vchiq_arm_log_level,
336 "service_callback - msg queue full");
337 /* If there is no MESSAGE_AVAILABLE in the completion
338 ** queue, add one
339 */
340 if ((user_service->message_available_pos -
341 instance->completion_remove) < 0) {
342 VCHIQ_STATUS_T status;
343 vchiq_log_info(vchiq_arm_log_level,
344 "Inserting extra MESSAGE_AVAILABLE");
345 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
346 status = add_completion(instance, reason,
347 NULL, user_service, bulk_userdata);
348 if (status != VCHIQ_SUCCESS) {
349 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
350 return status;
351 }
352 }
353
354 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
355 if (down_interruptible(&user_service->remove_event)
356 != 0) {
357 vchiq_log_info(vchiq_arm_log_level,
358 "service_callback interrupted");
359 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
360 return VCHIQ_RETRY;
361 } else if (instance->closing) {
362 vchiq_log_info(vchiq_arm_log_level,
363 "service_callback closing");
364 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
365 return VCHIQ_ERROR;
366 }
367 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
368 spin_lock(&msg_queue_spinlock);
369 }
370
371 user_service->msg_queue[user_service->msg_insert &
372 (MSG_QUEUE_SIZE - 1)] = header;
373 user_service->msg_insert++;
374
375 /* If there is a thread waiting in DEQUEUE_MESSAGE, or if
376 ** there is a MESSAGE_AVAILABLE in the completion queue then
377 ** bypass the completion queue.
378 */
379 if (((user_service->message_available_pos -
380 instance->completion_remove) >= 0) ||
381 user_service->dequeue_pending) {
382 user_service->dequeue_pending = 0;
383 skip_completion = 1;
384 }
385
386 spin_unlock(&msg_queue_spinlock);
387
388 up(&user_service->insert_event);
389
390 header = NULL;
391 }
392
393 if (skip_completion) {
394 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
395 return VCHIQ_SUCCESS;
396 }
397
398 DEBUG_TRACE(SERVICE_CALLBACK_LINE);
399
400 return add_completion(instance, reason, header, user_service,
401 bulk_userdata);
402 }
403
404 /****************************************************************************
405 *
406 * user_service_free
407 *
408 ***************************************************************************/
409 static void
410 user_service_free(void *userdata)
411 {
412 USER_SERVICE_T *user_service = userdata;
413
414 _sema_destroy(&user_service->insert_event);
415 _sema_destroy(&user_service->remove_event);
416
417 kfree(user_service);
418 }
419
420 /****************************************************************************
421 *
422 * close_delivered
423 *
424 ***************************************************************************/
425 static void close_delivered(USER_SERVICE_T *user_service)
426 {
427 vchiq_log_info(vchiq_arm_log_level,
428 "close_delivered(handle=%x)",
429 user_service->service->handle);
430
431 if (user_service->close_pending) {
432 /* Allow the underlying service to be culled */
433 unlock_service(user_service->service);
434
435 /* Wake the user-thread blocked in close_ or remove_service */
436 up(&user_service->close_event);
437
438 user_service->close_pending = 0;
439 }
440 }
441
442 /****************************************************************************
443 *
444 * vchiq_ioctl
445 *
446 ***************************************************************************/
447
448 static int
449 vchiq_ioctl(struct file *fp, u_long cmd, void *arg)
450 {
451 VCHIQ_INSTANCE_T instance = fp->f_data;
452 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
453 VCHIQ_SERVICE_T *service = NULL;
454 int ret = 0;
455 int i, rc;
456 DEBUG_INITIALISE(g_state.local)
457
458 /* XXXBSD: HACK! */
459 #define _IOC_NR(x) ((x) & 0xff)
460 #define _IOC_TYPE(x) IOCGROUP(x)
461
462 vchiq_log_trace(vchiq_arm_log_level,
463 "vchiq_ioctl - instance %x, cmd %s, arg %p",
464 (unsigned int)instance,
465 ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) &&
466 (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ?
467 ioctl_names[_IOC_NR(cmd)] : "<invalid>", arg);
468
469 switch (cmd) {
470 case VCHIQ_IOC_SHUTDOWN:
471 if (!instance->connected)
472 break;
473
474 /* Remove all services */
475 i = 0;
476 while ((service = next_service_by_instance(instance->state,
477 instance, &i)) != NULL) {
478 status = vchiq_remove_service(service->handle);
479 unlock_service(service);
480 if (status != VCHIQ_SUCCESS)
481 break;
482 }
483 service = NULL;
484
485 if (status == VCHIQ_SUCCESS) {
486 /* Wake the completion thread and ask it to exit */
487 instance->closing = 1;
488 up(&instance->insert_event);
489 }
490
491 break;
492
493 case VCHIQ_IOC_CONNECT:
494 if (instance->connected) {
495 ret = -EINVAL;
496 break;
497 }
498 rc = lmutex_lock_interruptible(&instance->state->mutex);
499 if (rc != 0) {
500 vchiq_log_error(vchiq_arm_log_level,
501 "vchiq: connect: could not lock mutex for "
502 "state %d: %d",
503 instance->state->id, rc);
504 ret = -EINTR;
505 break;
506 }
507 status = vchiq_connect_internal(instance->state, instance);
508 lmutex_unlock(&instance->state->mutex);
509
510 if (status == VCHIQ_SUCCESS)
511 instance->connected = 1;
512 else
513 vchiq_log_error(vchiq_arm_log_level,
514 "vchiq: could not connect: %d", status);
515 break;
516
517 case VCHIQ_IOC_CREATE_SERVICE: {
518 VCHIQ_CREATE_SERVICE_T *pargs = arg;
519 VCHIQ_CREATE_SERVICE_T args = *pargs;
520 USER_SERVICE_T *user_service = NULL;
521 void *userdata;
522 int srvstate;
523
524 /* XXXNH kmalloc */
525 user_service = kzalloc(sizeof(USER_SERVICE_T), GFP_KERNEL);
526 if (!user_service) {
527 ret = -ENOMEM;
528 break;
529 }
530
531 if (args.is_open) {
532 if (!instance->connected) {
533 ret = -ENOTCONN;
534 kfree(user_service);
535 break;
536 }
537 srvstate = VCHIQ_SRVSTATE_OPENING;
538 } else {
539 srvstate =
540 instance->connected ?
541 VCHIQ_SRVSTATE_LISTENING :
542 VCHIQ_SRVSTATE_HIDDEN;
543 }
544
545 userdata = args.params.userdata;
546 pargs->params.callback = service_callback;
547 pargs->params.userdata = user_service;
548 args.params.callback = service_callback;
549 args.params.userdata = user_service;
550 service = vchiq_add_service_internal(
551 instance->state,
552 &pargs->params, srvstate,
553 instance, user_service_free);
554
555 if (service != NULL) {
556 user_service->service = service;
557 user_service->userdata = userdata;
558 user_service->instance = instance;
559 user_service->is_vchi = (args.is_vchi != 0);
560 user_service->dequeue_pending = 0;
561 user_service->close_pending = 0;
562 user_service->message_available_pos =
563 instance->completion_remove - 1;
564 user_service->msg_insert = 0;
565 user_service->msg_remove = 0;
566 _sema_init(&user_service->insert_event, 0);
567 _sema_init(&user_service->remove_event, 0);
568 _sema_init(&user_service->close_event, 0);
569
570 if (args.is_open) {
571 status = vchiq_open_service_internal
572 (service, instance->pid);
573 if (status != VCHIQ_SUCCESS) {
574 vchiq_remove_service(service->handle);
575 service = NULL;
576 ret = (status == VCHIQ_RETRY) ?
577 -EINTR : -EIO;
578 break;
579 }
580 }
581
582 #ifdef VCHIQ_IOCTL_DEBUG
583 printf("%s: [CREATE SERVICE] handle = %08x\n", __func__, service->handle);
584 #endif
585 pargs->handle = service->handle;
586
587 service = NULL;
588 } else {
589 ret = -EEXIST;
590 kfree(user_service);
591 }
592 } break;
593
594 case VCHIQ_IOC_CLOSE_SERVICE: {
595 VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg;
596
597 #ifdef VCHIQ_IOCTL_DEBUG
598 printf("%s: [CLOSE SERVICE] handle = %08x\n", __func__, handle);
599 #endif
600
601 service = find_service_for_instance(instance, handle);
602 if (service != NULL) {
603 USER_SERVICE_T *user_service =
604 (USER_SERVICE_T *)service->base.userdata;
605 /* close_pending is false on first entry, and when the
606 wait in vchiq_close_service has been interrupted. */
607 if (!user_service->close_pending) {
608 status = vchiq_close_service(service->handle);
609 if (status != VCHIQ_SUCCESS)
610 break;
611 }
612
613 /* close_pending is true once the underlying service
614 has been closed until the client library calls the
615 CLOSE_DELIVERED ioctl, signalling close_event. */
616 if (user_service->close_pending &&
617 down_interruptible(&user_service->close_event))
618 status = VCHIQ_RETRY;
619 }
620 else
621 ret = -EINVAL;
622 } break;
623
624 case VCHIQ_IOC_REMOVE_SERVICE: {
625 VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg;
626
627 #ifdef VCHIQ_IOCTL_DEBUG
628 printf("%s: [REMOVE SERVICE] handle = %08x\n", __func__, handle);
629 #endif
630
631 service = find_service_for_instance(instance, handle);
632 if (service != NULL) {
633 USER_SERVICE_T *user_service =
634 (USER_SERVICE_T *)service->base.userdata;
635 /* close_pending is false on first entry, and when the
636 wait in vchiq_close_service has been interrupted. */
637 if (!user_service->close_pending) {
638 status = vchiq_remove_service(service->handle);
639 if (status != VCHIQ_SUCCESS)
640 break;
641 }
642
643 /* close_pending is true once the underlying service
644 has been closed until the client library calls the
645 CLOSE_DELIVERED ioctl, signalling close_event. */
646 if (user_service->close_pending &&
647 down_interruptible(&user_service->close_event))
648 status = VCHIQ_RETRY;
649 }
650 else
651 ret = -EINVAL;
652 } break;
653
654 case VCHIQ_IOC_USE_SERVICE:
655 case VCHIQ_IOC_RELEASE_SERVICE: {
656 VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg;
657
658 #ifdef VCHIQ_IOCTL_DEBUG
659 printf("%s: [%s SERVICE] handle = %08x\n", __func__,
660 cmd == VCHIQ_IOC_USE_SERVICE ? "USE" : "RELEASE", handle);
661 #endif
662
663 service = find_service_for_instance(instance, handle);
664 if (service != NULL) {
665 status = (cmd == VCHIQ_IOC_USE_SERVICE) ?
666 vchiq_use_service_internal(service) :
667 vchiq_release_service_internal(service);
668 if (status != VCHIQ_SUCCESS) {
669 vchiq_log_error(vchiq_susp_log_level,
670 "%s: cmd %s returned error %d for "
671 "service %c%c%c%c:%8x",
672 __func__,
673 (cmd == VCHIQ_IOC_USE_SERVICE) ?
674 "VCHIQ_IOC_USE_SERVICE" :
675 "VCHIQ_IOC_RELEASE_SERVICE",
676 status,
677 VCHIQ_FOURCC_AS_4CHARS(
678 service->base.fourcc),
679 service->client_id);
680 ret = -EINVAL;
681 }
682 } else
683 ret = -EINVAL;
684 } break;
685
686 case VCHIQ_IOC_QUEUE_MESSAGE: {
687 VCHIQ_QUEUE_MESSAGE_T *pargs = arg;
688 VCHIQ_QUEUE_MESSAGE_T args = *pargs;
689
690 #ifdef VCHIQ_IOCTL_DEBUG
691 printf("%s: [QUEUE MESSAGE] handle = %08x\n", __func__, args.handle);
692 #endif
693
694 service = find_service_for_instance(instance, args.handle);
695
696 if ((service != NULL) && (args.count <= MAX_ELEMENTS)) {
697 /* Copy elements into kernel space */
698 VCHIQ_ELEMENT_T elements[MAX_ELEMENTS];
699 if (copy_from_user(elements, args.elements,
700 args.count * sizeof(VCHIQ_ELEMENT_T)) == 0)
701 status = vchiq_queue_message
702 (args.handle,
703 elements, args.count);
704 else
705 ret = -EFAULT;
706 } else {
707 ret = -EINVAL;
708 }
709 } break;
710
711 case VCHIQ_IOC_QUEUE_BULK_TRANSMIT:
712 case VCHIQ_IOC_QUEUE_BULK_RECEIVE: {
713 VCHIQ_QUEUE_BULK_TRANSFER_T *pargs = arg;
714 VCHIQ_QUEUE_BULK_TRANSFER_T args = *pargs;
715 struct bulk_waiter_node *waiter = NULL;
716 VCHIQ_BULK_DIR_T dir =
717 (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
718 VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
719
720 service = find_service_for_instance(instance, args.handle);
721 if (!service) {
722 ret = -EINVAL;
723 break;
724 }
725
726 if (args.mode == VCHIQ_BULK_MODE_BLOCKING) {
727 waiter = kzalloc(sizeof(struct bulk_waiter_node),
728 GFP_KERNEL);
729 if (!waiter) {
730 ret = -ENOMEM;
731 break;
732 }
733 args.userdata = &waiter->bulk_waiter;
734 } else if (args.mode == VCHIQ_BULK_MODE_WAITING) {
735 struct list_head *pos;
736 lmutex_lock(&instance->bulk_waiter_list_mutex);
737 list_for_each(pos, &instance->bulk_waiter_list) {
738 if (list_entry(pos, struct bulk_waiter_node,
739 list)->pid == current->l_proc->p_pid) {
740 waiter = list_entry(pos,
741 struct bulk_waiter_node,
742 list);
743 list_del(pos);
744 break;
745 }
746
747 }
748 lmutex_unlock(&instance->bulk_waiter_list_mutex);
749 if (!waiter) {
750 vchiq_log_error(vchiq_arm_log_level,
751 "no bulk_waiter found for pid %d",
752 current->l_proc->p_pid);
753 ret = -ESRCH;
754 break;
755 }
756 vchiq_log_info(vchiq_arm_log_level,
757 "found bulk_waiter %x for pid %d",
758 (unsigned int)waiter, current->l_proc->p_pid);
759 args.userdata = &waiter->bulk_waiter;
760 }
761 status = vchiq_bulk_transfer
762 (args.handle,
763 VCHI_MEM_HANDLE_INVALID,
764 args.data, args.size,
765 args.userdata, args.mode,
766 dir);
767 if (!waiter)
768 break;
769 if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
770 !waiter->bulk_waiter.bulk) {
771 if (waiter->bulk_waiter.bulk) {
772 /* Cancel the signal when the transfer
773 ** completes. */
774 spin_lock(&bulk_waiter_spinlock);
775 waiter->bulk_waiter.bulk->userdata = NULL;
776 spin_unlock(&bulk_waiter_spinlock);
777 }
778 _sema_destroy(&waiter->bulk_waiter.event);
779 kfree(waiter);
780 } else {
781 const VCHIQ_BULK_MODE_T mode_waiting =
782 VCHIQ_BULK_MODE_WAITING;
783 waiter->pid = current->l_proc->p_pid;
784 lmutex_lock(&instance->bulk_waiter_list_mutex);
785 list_add(&waiter->list, &instance->bulk_waiter_list);
786 lmutex_unlock(&instance->bulk_waiter_list_mutex);
787 vchiq_log_info(vchiq_arm_log_level,
788 "saved bulk_waiter %x for pid %d",
789 (unsigned int)waiter, current->l_proc->p_pid);
790
791 pargs->mode = mode_waiting;
792 }
793 } break;
794
795 case VCHIQ_IOC_AWAIT_COMPLETION: {
796 VCHIQ_AWAIT_COMPLETION_T *pargs = arg;
797 VCHIQ_AWAIT_COMPLETION_T args = *pargs;
798 int count = 0;
799
800 DEBUG_TRACE(AWAIT_COMPLETION_LINE);
801 if (!instance->connected) {
802 ret = -ENOTCONN;
803 break;
804 }
805
806 lmutex_lock(&instance->completion_mutex);
807
808 DEBUG_TRACE(AWAIT_COMPLETION_LINE);
809 while ((instance->completion_remove ==
810 instance->completion_insert)
811 && !instance->closing) {
812 DEBUG_TRACE(AWAIT_COMPLETION_LINE);
813 lmutex_unlock(&instance->completion_mutex);
814 rc = down_interruptible(&instance->insert_event);
815 lmutex_lock(&instance->completion_mutex);
816 if (rc != 0) {
817 DEBUG_TRACE(AWAIT_COMPLETION_LINE);
818 vchiq_log_info(vchiq_arm_log_level,
819 "AWAIT_COMPLETION interrupted");
820 ret = -EINTR;
821 break;
822 }
823 }
824 DEBUG_TRACE(AWAIT_COMPLETION_LINE);
825
826 if (ret == 0) {
827 int msgbufcount = args.msgbufcount;
828 int remove;
829
830 remove = instance->completion_remove;
831
832 for (count = 0; count < args.count; count++) {
833 VCHIQ_COMPLETION_DATA_T *completion;
834 VCHIQ_SERVICE_T *service1;
835 USER_SERVICE_T *user_service;
836 VCHIQ_HEADER_T *header;
837
838 if (remove == instance->completion_insert)
839 break;
840
841 completion = &instance->completions[
842 remove & (MAX_COMPLETIONS - 1)];
843
844 /* A read memory barrier is needed to prevent
845 ** the prefetch of a stale completion record
846 */
847 rmb();
848
849
850 service1 = completion->service_userdata;
851 user_service = service1->base.userdata;
852 completion->service_userdata =
853 user_service->userdata;
854
855 header = completion->header;
856 if (header) {
857 void __user *msgbuf;
858 int msglen;
859
860 msglen = header->size +
861 sizeof(VCHIQ_HEADER_T);
862 /* This must be a VCHIQ-style service */
863 if (args.msgbufsize < msglen) {
864 vchiq_log_error(
865 vchiq_arm_log_level,
866 "header %x: msgbufsize"
867 " %x < msglen %x",
868 (unsigned int)header,
869 args.msgbufsize,
870 msglen);
871 WARN(1, "invalid message "
872 "size\n");
873 if (count == 0)
874 ret = -EMSGSIZE;
875 break;
876 }
877 if (msgbufcount <= 0)
878 /* Stall here for lack of a
879 ** buffer for the message. */
880 break;
881 /* Get the pointer from user space */
882 msgbufcount--;
883 if (copy_from_user(&msgbuf,
884 (const void __user *)
885 &args.msgbufs[msgbufcount],
886 sizeof(msgbuf)) != 0) {
887 if (count == 0)
888 ret = -EFAULT;
889 break;
890 }
891
892 /* Copy the message to user space */
893 if (copy_to_user(msgbuf, header,
894 msglen) != 0) {
895 if (count == 0)
896 ret = -EFAULT;
897 break;
898 }
899
900 /* Now it has been copied, the message
901 ** can be released. */
902 vchiq_release_message(service1->handle,
903 header);
904
905 /* The completion must point to the
906 ** msgbuf. */
907 completion->header = msgbuf;
908 }
909
910 if ((completion->reason ==
911 VCHIQ_SERVICE_CLOSED) &&
912 !instance->use_close_delivered)
913 unlock_service(service1);
914
915 if (copy_to_user((void __user *)(
916 (size_t)args.buf +
917 count * sizeof(VCHIQ_COMPLETION_DATA_T)),
918 completion,
919 sizeof(VCHIQ_COMPLETION_DATA_T)) != 0) {
920 if (count == 0)
921 ret = -EFAULT;
922 break;
923 }
924
925 /* Ensure that the above copy has completed
926 ** before advancing the remove pointer. */
927 mb();
928
929 instance->completion_remove = ++remove;
930 }
931
932 pargs->msgbufcount = msgbufcount;
933 pargs->count = count;
934 }
935 if (count != 0)
936 up(&instance->remove_event);
937
938 lmutex_unlock(&instance->completion_mutex);
939 DEBUG_TRACE(AWAIT_COMPLETION_LINE);
940 } break;
941
942 case VCHIQ_IOC_DEQUEUE_MESSAGE: {
943 VCHIQ_DEQUEUE_MESSAGE_T *pargs = arg;
944 VCHIQ_DEQUEUE_MESSAGE_T args = *pargs;
945 USER_SERVICE_T *user_service;
946 VCHIQ_HEADER_T *header;
947
948 DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
949 service = find_service_for_instance(instance, args.handle);
950 if (!service) {
951 ret = -EINVAL;
952 break;
953 }
954 user_service = (USER_SERVICE_T *)service->base.userdata;
955 if (user_service->is_vchi == 0) {
956 ret = -EINVAL;
957 break;
958 }
959
960 spin_lock(&msg_queue_spinlock);
961 if (user_service->msg_remove == user_service->msg_insert) {
962 if (!args.blocking) {
963 spin_unlock(&msg_queue_spinlock);
964 DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
965 ret = -EWOULDBLOCK;
966 break;
967 }
968 user_service->dequeue_pending = 1;
969 do {
970 spin_unlock(&msg_queue_spinlock);
971 DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
972 if (down_interruptible(
973 &user_service->insert_event) != 0) {
974 vchiq_log_info(vchiq_arm_log_level,
975 "DEQUEUE_MESSAGE interrupted");
976 ret = -EINTR;
977 break;
978 }
979 spin_lock(&msg_queue_spinlock);
980 } while (user_service->msg_remove ==
981 user_service->msg_insert);
982
983 if (ret)
984 break;
985 }
986
987 BUG_ON((int)(user_service->msg_insert -
988 user_service->msg_remove) < 0);
989
990 header = user_service->msg_queue[user_service->msg_remove &
991 (MSG_QUEUE_SIZE - 1)];
992 user_service->msg_remove++;
993 spin_unlock(&msg_queue_spinlock);
994
995 up(&user_service->remove_event);
996 if (header == NULL)
997 ret = -ENOTCONN;
998 else if (header->size <= args.bufsize) {
999 /* Copy to user space if msgbuf is not NULL */
1000 if ((args.buf == NULL) ||
1001 (copy_to_user((void __user *)args.buf,
1002 header->data,
1003 header->size) == 0)) {
1004 pargs->bufsize = header->size;
1005 vchiq_release_message(
1006 service->handle,
1007 header);
1008 } else
1009 ret = -EFAULT;
1010 } else {
1011 vchiq_log_error(vchiq_arm_log_level,
1012 "header %x: bufsize %x < size %x",
1013 (unsigned int)header, args.bufsize,
1014 header->size);
1015 WARN(1, "invalid size\n");
1016 ret = -EMSGSIZE;
1017 }
1018 DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
1019 } break;
1020
1021 case VCHIQ_IOC_GET_CLIENT_ID: {
1022 VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg;
1023
1024 ret = vchiq_get_client_id(handle);
1025 } break;
1026
1027 case VCHIQ_IOC_GET_CONFIG: {
1028 VCHIQ_GET_CONFIG_T *pargs = arg;
1029 VCHIQ_GET_CONFIG_T args = *pargs;
1030 VCHIQ_CONFIG_T config;
1031
1032 if (args.config_size > sizeof(config)) {
1033 ret = -EINVAL;
1034 break;
1035 }
1036 status = vchiq_get_config(instance, args.config_size, &config);
1037 if (status == VCHIQ_SUCCESS) {
1038 if (copy_to_user((void __user *)args.pconfig,
1039 &config, args.config_size) != 0) {
1040 ret = -EFAULT;
1041 break;
1042 }
1043 }
1044 } break;
1045
1046 case VCHIQ_IOC_SET_SERVICE_OPTION: {
1047 VCHIQ_SET_SERVICE_OPTION_T *pargs = arg;
1048 VCHIQ_SET_SERVICE_OPTION_T args = *pargs;
1049
1050 service = find_service_for_instance(instance, args.handle);
1051 if (!service) {
1052 ret = -EINVAL;
1053 break;
1054 }
1055
1056 status = vchiq_set_service_option(
1057 args.handle, args.option, args.value);
1058 } break;
1059
1060 case VCHIQ_IOC_DUMP_PHYS_MEM: {
1061 #if 0
1062 VCHIQ_DUMP_MEM_T *pargs = arg;
1063 #endif
1064
1065 printf("IMPLEMENT ME: %s:%d\n", __FILE__, __LINE__);
1066 #if 0
1067 dump_phys_mem(pargs->virt_addr, pargs->num_bytes);
1068 #endif
1069 } break;
1070
1071 case VCHIQ_IOC_LIB_VERSION: {
1072 unsigned int lib_version = (unsigned int)arg;
1073
1074 if (lib_version < VCHIQ_VERSION_MIN)
1075 ret = -EINVAL;
1076 else if (lib_version >= VCHIQ_VERSION_CLOSE_DELIVERED)
1077 instance->use_close_delivered = 1;
1078 } break;
1079
1080 case VCHIQ_IOC_CLOSE_DELIVERED: {
1081 VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg;
1082
1083 service = find_closed_service_for_instance(instance, handle);
1084 if (service != NULL) {
1085 USER_SERVICE_T *user_service =
1086 (USER_SERVICE_T *)service->base.userdata;
1087 close_delivered(user_service);
1088 }
1089 else
1090 ret = -EINVAL;
1091 } break;
1092
1093 default:
1094 ret = -ENOTTY;
1095 break;
1096 }
1097
1098 if (service)
1099 unlock_service(service);
1100
1101 if (ret == 0) {
1102 if (status == VCHIQ_ERROR)
1103 ret = -EIO;
1104 else if (status == VCHIQ_RETRY)
1105 ret = -EINTR;
1106 }
1107
1108 if ((status == VCHIQ_SUCCESS) && (ret < 0) && (ret != -EINTR) &&
1109 (ret != -EWOULDBLOCK))
1110 vchiq_log_info(vchiq_arm_log_level,
1111 " ioctl instance %lx, cmd %s -> status %d, %d",
1112 (unsigned long)instance,
1113 (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ?
1114 ioctl_names[_IOC_NR(cmd)] :
1115 "<invalid>",
1116 status, ret);
1117 else
1118 vchiq_log_trace(vchiq_arm_log_level,
1119 " ioctl instance %lx, cmd %s -> status %d, %d",
1120 (unsigned long)instance,
1121 (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ?
1122 ioctl_names[_IOC_NR(cmd)] :
1123 "<invalid>",
1124 status, ret);
1125
1126 /* XXXBSD: report BSD-style error to userland */
1127 if (ret < 0)
1128 ret = -ret;
1129
1130 return ret;
1131 }
1132
1133 /****************************************************************************
1134 *
1135 * vchiq_open
1136 *
1137 ***************************************************************************/
1138
1139 static int
1140 vchiq_open(dev_t dev, int flags, int mode, lwp_t *l)
1141 {
1142 VCHIQ_INSTANCE_T instance = NULL;
1143 struct file *fp;
1144 int err, fd;
1145
1146 vchiq_log_info(vchiq_arm_log_level, "vchiq_open");
1147
1148 /* XXXBSD: do we really need this check? */
1149 if (device_lookup_private(&vchiq_cd, minor(dev)) != NULL) {
1150 VCHIQ_STATE_T *state = vchiq_get_state();
1151
1152 if (!state) {
1153 vchiq_log_error(vchiq_arm_log_level,
1154 "vchiq has no connection to VideoCore");
1155 return -ENOTCONN;
1156 }
1157
1158 instance = kzalloc(sizeof(*instance), GFP_KERNEL);
1159 if (!instance)
1160 return -ENOMEM;
1161
1162 err = fd_allocfile(&fp, &fd);
1163 if (err) {
1164 kfree(instance);
1165 return -err;
1166 }
1167
1168 instance->state = state;
1169 instance->pid = current->l_proc->p_pid;
1170
1171 #ifdef notyet
1172 ret = vchiq_debugfs_add_instance(instance);
1173 if (ret != 0) {
1174 kfree(instance);
1175 return ret;
1176 }
1177 #endif
1178
1179 _sema_init(&instance->insert_event, 0);
1180 _sema_init(&instance->remove_event, 0);
1181 lmutex_init(&instance->completion_mutex);
1182 lmutex_init(&instance->bulk_waiter_list_mutex);
1183 INIT_LIST_HEAD(&instance->bulk_waiter_list);
1184
1185 }
1186 else {
1187 vchiq_log_error(vchiq_arm_log_level,
1188 "Unknown minor device");
1189 return -ENXIO;
1190 }
1191
1192 return fd_clone(fp, fd, flags, &vchiq_fileops, instance);
1193 }
1194
1195 /****************************************************************************
1196 *
1197 * vchiq_release
1198 *
1199 ***************************************************************************/
1200
1201 static int
1202 vchiq_close(struct file *fp)
1203 {
1204 int ret = 0;
1205 if (1) {
1206 VCHIQ_INSTANCE_T instance = fp->f_data;
1207 VCHIQ_STATE_T *state = vchiq_get_state();
1208 VCHIQ_SERVICE_T *service;
1209 int i;
1210
1211 vchiq_log_info(vchiq_arm_log_level,
1212 "vchiq_release: instance=%lx",
1213 (unsigned long)instance);
1214
1215 if (!state) {
1216 ret = -EPERM;
1217 goto out;
1218 }
1219
1220 /* Ensure videocore is awake to allow termination. */
1221 vchiq_use_internal(instance->state, NULL,
1222 USE_TYPE_VCHIQ);
1223
1224 lmutex_lock(&instance->completion_mutex);
1225
1226 /* Wake the completion thread and ask it to exit */
1227 instance->closing = 1;
1228 up(&instance->insert_event);
1229
1230 lmutex_unlock(&instance->completion_mutex);
1231
1232 /* Wake the slot handler if the completion queue is full. */
1233 up(&instance->remove_event);
1234
1235 /* Mark all services for termination... */
1236 i = 0;
1237 while ((service = next_service_by_instance(state, instance,
1238 &i)) != NULL) {
1239 USER_SERVICE_T *user_service = service->base.userdata;
1240
1241 /* Wake the slot handler if the msg queue is full. */
1242 up(&user_service->remove_event);
1243
1244 vchiq_terminate_service_internal(service);
1245 unlock_service(service);
1246 }
1247
1248 /* ...and wait for them to die */
1249 i = 0;
1250 while ((service = next_service_by_instance(state, instance, &i))
1251 != NULL) {
1252 USER_SERVICE_T *user_service = service->base.userdata;
1253
1254 down(&service->remove_event);
1255
1256 BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE);
1257
1258 spin_lock(&msg_queue_spinlock);
1259
1260 while (user_service->msg_remove !=
1261 user_service->msg_insert) {
1262 VCHIQ_HEADER_T *header = user_service->
1263 msg_queue[user_service->msg_remove &
1264 (MSG_QUEUE_SIZE - 1)];
1265 user_service->msg_remove++;
1266 spin_unlock(&msg_queue_spinlock);
1267
1268 if (header)
1269 vchiq_release_message(
1270 service->handle,
1271 header);
1272 spin_lock(&msg_queue_spinlock);
1273 }
1274
1275 spin_unlock(&msg_queue_spinlock);
1276
1277 unlock_service(service);
1278 }
1279
1280 /* Release any closed services */
1281 while (instance->completion_remove !=
1282 instance->completion_insert) {
1283 VCHIQ_COMPLETION_DATA_T *completion;
1284 VCHIQ_SERVICE_T *service1;
1285 completion = &instance->completions[
1286 instance->completion_remove &
1287 (MAX_COMPLETIONS - 1)];
1288 service1 = completion->service_userdata;
1289 if (completion->reason == VCHIQ_SERVICE_CLOSED)
1290 {
1291 USER_SERVICE_T *user_service =
1292 service->base.userdata;
1293
1294 /* Wake any blocked user-thread */
1295 if (instance->use_close_delivered)
1296 up(&user_service->close_event);
1297 unlock_service(service1);
1298 }
1299 instance->completion_remove++;
1300 }
1301
1302 /* Release the PEER service count. */
1303 vchiq_release_internal(instance->state, NULL);
1304
1305 {
1306 struct list_head *pos, *next;
1307 list_for_each_safe(pos, next,
1308 &instance->bulk_waiter_list) {
1309 struct bulk_waiter_node *waiter;
1310 waiter = list_entry(pos,
1311 struct bulk_waiter_node,
1312 list);
1313 list_del(pos);
1314 vchiq_log_info(vchiq_arm_log_level,
1315 "bulk_waiter - cleaned up %x "
1316 "for pid %d",
1317 (unsigned int)waiter, waiter->pid);
1318 _sema_destroy(&waiter->bulk_waiter.event);
1319 kfree(waiter);
1320 }
1321 }
1322
1323 }
1324 else {
1325 vchiq_log_error(vchiq_arm_log_level,
1326 "Unknown minor device");
1327 ret = -ENXIO;
1328 }
1329
1330 out:
1331 return ret;
1332 }
1333
1334 /****************************************************************************
1335 *
1336 * vchiq_dump
1337 *
1338 ***************************************************************************/
1339
1340 void
1341 vchiq_dump(void *dump_context, const char *str, int len)
1342 {
1343 DUMP_CONTEXT_T *context = (DUMP_CONTEXT_T *)dump_context;
1344
1345 if (context->actual < context->space) {
1346 int copy_bytes;
1347 if (context->offset > 0) {
1348 int skip_bytes = min(len, (int)context->offset);
1349 str += skip_bytes;
1350 len -= skip_bytes;
1351 context->offset -= skip_bytes;
1352 if (context->offset > 0)
1353 return;
1354 }
1355 copy_bytes = min(len, (int)(context->space - context->actual));
1356 if (copy_bytes == 0)
1357 return;
1358 memcpy(context->buf + context->actual, str, copy_bytes);
1359 context->actual += copy_bytes;
1360 len -= copy_bytes;
1361
1362 /* If tne terminating NUL is included in the length, then it
1363 ** marks the end of a line and should be replaced with a
1364 ** carriage return. */
1365 if ((len == 0) && (str[copy_bytes - 1] == '\0')) {
1366 char cr = '\n';
1367 memcpy(context->buf + context->actual - 1, &cr, 1);
1368 }
1369 }
1370 }
1371
1372 /****************************************************************************
1373 *
1374 * vchiq_dump_platform_instance_state
1375 *
1376 ***************************************************************************/
1377
1378 void
1379 vchiq_dump_platform_instances(void *dump_context)
1380 {
1381 VCHIQ_STATE_T *state = vchiq_get_state();
1382 char buf[80];
1383 int len;
1384 int i;
1385
1386 /* There is no list of instances, so instead scan all services,
1387 marking those that have been dumped. */
1388
1389 for (i = 0; i < state->unused_service; i++) {
1390 VCHIQ_SERVICE_T *service = state->services[i];
1391 VCHIQ_INSTANCE_T instance;
1392
1393 if (service && (service->base.callback == service_callback)) {
1394 instance = service->instance;
1395 if (instance)
1396 instance->mark = 0;
1397 }
1398 }
1399
1400 for (i = 0; i < state->unused_service; i++) {
1401 VCHIQ_SERVICE_T *service = state->services[i];
1402 VCHIQ_INSTANCE_T instance;
1403
1404 if (service && (service->base.callback == service_callback)) {
1405 instance = service->instance;
1406 if (instance && !instance->mark) {
1407 len = snprintf(buf, sizeof(buf),
1408 "Instance %x: pid %d,%s completions "
1409 "%d/%d",
1410 (unsigned int)instance, instance->pid,
1411 instance->connected ? " connected, " :
1412 "",
1413 instance->completion_insert -
1414 instance->completion_remove,
1415 MAX_COMPLETIONS);
1416
1417 vchiq_dump(dump_context, buf, len + 1);
1418
1419 instance->mark = 1;
1420 }
1421 }
1422 }
1423 }
1424
1425 /****************************************************************************
1426 *
1427 * vchiq_dump_platform_service_state
1428 *
1429 ***************************************************************************/
1430
1431 void
1432 vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service)
1433 {
1434 USER_SERVICE_T *user_service = (USER_SERVICE_T *)service->base.userdata;
1435 char buf[80];
1436 int len;
1437
1438 len = snprintf(buf, sizeof(buf), " instance %x",
1439 (unsigned int)service->instance);
1440
1441 if ((service->base.callback == service_callback) &&
1442 user_service->is_vchi) {
1443 len += snprintf(buf + len, sizeof(buf) - len,
1444 ", %d/%d messages",
1445 user_service->msg_insert - user_service->msg_remove,
1446 MSG_QUEUE_SIZE);
1447
1448 if (user_service->dequeue_pending)
1449 len += snprintf(buf + len, sizeof(buf) - len,
1450 " (dequeue pending)");
1451 }
1452
1453 vchiq_dump(dump_context, buf, len + 1);
1454 }
1455
1456 #ifdef notyet
1457 /****************************************************************************
1458 *
1459 * dump_user_mem
1460 *
1461 ***************************************************************************/
1462
1463 static void
1464 dump_phys_mem(void *virt_addr, uint32_t num_bytes)
1465 {
1466 int rc;
1467 uint8_t *end_virt_addr = virt_addr + num_bytes;
1468 int num_pages;
1469 int offset;
1470 int end_offset;
1471 int page_idx;
1472 int prev_idx;
1473 struct page *page;
1474 struct page **pages;
1475 uint8_t *kmapped_virt_ptr;
1476
1477 /* Align virtAddr and endVirtAddr to 16 byte boundaries. */
1478
1479 virt_addr = (void *)((unsigned long)virt_addr & ~0x0fuL);
1480 end_virt_addr = (void *)(((unsigned long)end_virt_addr + 15uL) &
1481 ~0x0fuL);
1482
1483 offset = (int)(long)virt_addr & (PAGE_SIZE - 1);
1484 end_offset = (int)(long)end_virt_addr & (PAGE_SIZE - 1);
1485
1486 num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE;
1487
1488 pages = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL);
1489 if (pages == NULL) {
1490 vchiq_log_error(vchiq_arm_log_level,
1491 "Unable to allocation memory for %d pages\n",
1492 num_pages);
1493 return;
1494 }
1495
1496 down_read(¤t->mm->mmap_sem);
1497 rc = get_user_pages(current, /* task */
1498 current->mm, /* mm */
1499 (unsigned long)virt_addr, /* start */
1500 num_pages, /* len */
1501 0, /* write */
1502 0, /* force */
1503 pages, /* pages (array of page pointers) */
1504 NULL); /* vmas */
1505 up_read(¤t->mm->mmap_sem);
1506
1507 prev_idx = -1;
1508 page = NULL;
1509
1510 while (offset < end_offset) {
1511
1512 int page_offset = offset % PAGE_SIZE;
1513 page_idx = offset / PAGE_SIZE;
1514
1515 if (page_idx != prev_idx) {
1516
1517 if (page != NULL)
1518 kunmap(page);
1519 page = pages[page_idx];
1520 kmapped_virt_ptr = kmap(page);
1521
1522 prev_idx = page_idx;
1523 }
1524
1525 if (vchiq_arm_log_level >= VCHIQ_LOG_TRACE)
1526 vchiq_log_dump_mem("ph",
1527 (uint32_t)(unsigned long)&kmapped_virt_ptr[
1528 page_offset],
1529 &kmapped_virt_ptr[page_offset], 16);
1530
1531 offset += 16;
1532 }
1533 if (page != NULL)
1534 kunmap(page);
1535
1536 for (page_idx = 0; page_idx < num_pages; page_idx++)
1537 page_cache_release(pages[page_idx]);
1538
1539 kfree(pages);
1540 }
1541 #endif
1542
1543 /****************************************************************************
1544 *
1545 * vchiq_read
1546 *
1547 ***************************************************************************/
1548
1549 static int
1550 vchiq_read(struct file *file, off_t *ppos, struct uio *uio, kauth_cred_t cred,
1551 int flags)
1552 {
1553 int result;
1554
1555 char *buf = kmem_zalloc(PAGE_SIZE, KM_SLEEP);
1556
1557 DUMP_CONTEXT_T context;
1558 context.buf = buf;
1559 context.actual = 0;
1560 context.space = PAGE_SIZE;
1561 context.offset = *ppos;
1562
1563 vchiq_dump_state(&context, &g_state);
1564
1565 *ppos += context.actual;
1566
1567 result = uiomove(buf, context.actual, uio);
1568 kmem_free(buf, PAGE_SIZE);
1569
1570 return result;
1571 }
1572
1573 VCHIQ_STATE_T *
1574 vchiq_get_state(void)
1575 {
1576
1577 if (g_state.remote == NULL)
1578 printk(KERN_ERR "%s: g_state.remote == NULL\n", __func__);
1579 else if (g_state.remote->initialised != 1)
1580 printk(KERN_NOTICE "%s: g_state.remote->initialised != 1 (%d)\n",
1581 __func__, g_state.remote->initialised);
1582
1583 return ((g_state.remote != NULL) &&
1584 (g_state.remote->initialised == 1)) ? &g_state : NULL;
1585 }
1586
1587 /*
1588 * Autosuspend related functionality
1589 */
1590
1591 int
1592 vchiq_videocore_wanted(VCHIQ_STATE_T *state)
1593 {
1594 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
1595 if (!arm_state)
1596 /* autosuspend not supported - always return wanted */
1597 return 1;
1598 else if (arm_state->blocked_count)
1599 return 1;
1600 else if (!arm_state->videocore_use_count)
1601 /* usage count zero - check for override unless we're forcing */
1602 if (arm_state->resume_blocked)
1603 return 0;
1604 else
1605 return vchiq_platform_videocore_wanted(state);
1606 else
1607 /* non-zero usage count - videocore still required */
1608 return 1;
1609 }
1610
1611 static VCHIQ_STATUS_T
1612 vchiq_keepalive_vchiq_callback(VCHIQ_REASON_T reason,
1613 VCHIQ_HEADER_T *header,
1614 VCHIQ_SERVICE_HANDLE_T service_user,
1615 void *bulk_user)
1616 {
1617 vchiq_log_error(vchiq_susp_log_level,
1618 "%s callback reason %d", __func__, reason);
1619 return 0;
1620 }
1621
1622 static int
1623 vchiq_keepalive_thread_func(void *v)
1624 {
1625 VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
1626 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
1627
1628 VCHIQ_STATUS_T status;
1629 VCHIQ_INSTANCE_T instance;
1630 VCHIQ_SERVICE_HANDLE_T ka_handle;
1631
1632 VCHIQ_SERVICE_PARAMS_T params = {
1633 .fourcc = VCHIQ_MAKE_FOURCC('K', 'E', 'E', 'P'),
1634 .callback = vchiq_keepalive_vchiq_callback,
1635 .version = KEEPALIVE_VER,
1636 .version_min = KEEPALIVE_VER_MIN
1637 };
1638
1639 status = vchiq_initialise(&instance);
1640 if (status != VCHIQ_SUCCESS) {
1641 vchiq_log_error(vchiq_susp_log_level,
1642 "%s vchiq_initialise failed %d", __func__, status);
1643 goto exit;
1644 }
1645
1646 status = vchiq_connect(instance);
1647 if (status != VCHIQ_SUCCESS) {
1648 vchiq_log_error(vchiq_susp_log_level,
1649 "%s vchiq_connect failed %d", __func__, status);
1650 goto shutdown;
1651 }
1652
1653 status = vchiq_add_service(instance, ¶ms, &ka_handle);
1654 if (status != VCHIQ_SUCCESS) {
1655 vchiq_log_error(vchiq_susp_log_level,
1656 "%s vchiq_open_service failed %d", __func__, status);
1657 goto shutdown;
1658 }
1659
1660 while (1) {
1661 long rc = 0, uc = 0;
1662 if (wait_for_completion_interruptible(&arm_state->ka_evt)
1663 != 0) {
1664 vchiq_log_error(vchiq_susp_log_level,
1665 "%s interrupted", __func__);
1666 flush_signals(current);
1667 continue;
1668 }
1669
1670 /* read and clear counters. Do release_count then use_count to
1671 * prevent getting more releases than uses */
1672 rc = atomic_xchg(&arm_state->ka_release_count, 0);
1673 uc = atomic_xchg(&arm_state->ka_use_count, 0);
1674
1675 /* Call use/release service the requisite number of times.
1676 * Process use before release so use counts don't go negative */
1677 while (uc--) {
1678 atomic_inc(&arm_state->ka_use_ack_count);
1679 status = vchiq_use_service(ka_handle);
1680 if (status != VCHIQ_SUCCESS) {
1681 vchiq_log_error(vchiq_susp_log_level,
1682 "%s vchiq_use_service error %d",
1683 __func__, status);
1684 }
1685 }
1686 while (rc--) {
1687 status = vchiq_release_service(ka_handle);
1688 if (status != VCHIQ_SUCCESS) {
1689 vchiq_log_error(vchiq_susp_log_level,
1690 "%s vchiq_release_service error %d",
1691 __func__, status);
1692 }
1693 }
1694 }
1695
1696 shutdown:
1697 vchiq_shutdown(instance);
1698 exit:
1699 return 0;
1700 }
1701
1702
1703
1704 VCHIQ_STATUS_T
1705 vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state)
1706 {
1707 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
1708
1709 if (arm_state) {
1710 rwlock_init(&arm_state->susp_res_lock);
1711
1712 init_completion(&arm_state->ka_evt);
1713 atomic_set(&arm_state->ka_use_count, 0);
1714 atomic_set(&arm_state->ka_use_ack_count, 0);
1715 atomic_set(&arm_state->ka_release_count, 0);
1716
1717 init_completion(&arm_state->vc_suspend_complete);
1718
1719 init_completion(&arm_state->vc_resume_complete);
1720 /* Initialise to 'done' state. We only want to block on resume
1721 * completion while videocore is suspended. */
1722 set_resume_state(arm_state, VC_RESUME_RESUMED);
1723
1724 init_completion(&arm_state->resume_blocker);
1725 /* Initialise to 'done' state. We only want to block on this
1726 * completion while resume is blocked */
1727 complete_all(&arm_state->resume_blocker);
1728
1729 init_completion(&arm_state->blocked_blocker);
1730 /* Initialise to 'done' state. We only want to block on this
1731 * completion while things are waiting on the resume blocker */
1732 complete_all(&arm_state->blocked_blocker);
1733
1734 arm_state->suspend_timer_timeout = SUSPEND_TIMER_TIMEOUT_MS;
1735 arm_state->suspend_timer_running = 0;
1736 init_timer(&arm_state->suspend_timer);
1737 arm_state->suspend_timer.data = (unsigned long)(state);
1738 arm_state->suspend_timer.function = suspend_timer_callback;
1739
1740 arm_state->first_connect = 0;
1741
1742 }
1743 return status;
1744 }
1745
1746 /*
1747 ** Functions to modify the state variables;
1748 ** set_suspend_state
1749 ** set_resume_state
1750 **
1751 ** There are more state variables than we might like, so ensure they remain in
1752 ** step. Suspend and resume state are maintained separately, since most of
1753 ** these state machines can operate independently. However, there are a few
1754 ** states where state transitions in one state machine cause a reset to the
1755 ** other state machine. In addition, there are some completion events which
1756 ** need to occur on state machine reset and end-state(s), so these are also
1757 ** dealt with in these functions.
1758 **
1759 ** In all states we set the state variable according to the input, but in some
1760 ** cases we perform additional steps outlined below;
1761 **
1762 ** VC_SUSPEND_IDLE - Initialise the suspend completion at the same time.
1763 ** The suspend completion is completed after any suspend
1764 ** attempt. When we reset the state machine we also reset
1765 ** the completion. This reset occurs when videocore is
1766 ** resumed, and also if we initiate suspend after a suspend
1767 ** failure.
1768 **
1769 ** VC_SUSPEND_IN_PROGRESS - This state is considered the point of no return for
1770 ** suspend - ie from this point on we must try to suspend
1771 ** before resuming can occur. We therefore also reset the
1772 ** resume state machine to VC_RESUME_IDLE in this state.
1773 **
1774 ** VC_SUSPEND_SUSPENDED - Suspend has completed successfully. Also call
1775 ** complete_all on the suspend completion to notify
1776 ** anything waiting for suspend to happen.
1777 **
1778 ** VC_SUSPEND_REJECTED - Videocore rejected suspend. Videocore will also
1779 ** initiate resume, so no need to alter resume state.
1780 ** We call complete_all on the suspend completion to notify
1781 ** of suspend rejection.
1782 **
1783 ** VC_SUSPEND_FAILED - We failed to initiate videocore suspend. We notify the
1784 ** suspend completion and reset the resume state machine.
1785 **
1786 ** VC_RESUME_IDLE - Initialise the resume completion at the same time. The
1787 ** resume completion is in it's 'done' state whenever
1788 ** videcore is running. Therfore, the VC_RESUME_IDLE state
1789 ** implies that videocore is suspended.
1790 ** Hence, any thread which needs to wait until videocore is
1791 ** running can wait on this completion - it will only block
1792 ** if videocore is suspended.
1793 **
1794 ** VC_RESUME_RESUMED - Resume has completed successfully. Videocore is running.
1795 ** Call complete_all on the resume completion to unblock
1796 ** any threads waiting for resume. Also reset the suspend
1797 ** state machine to it's idle state.
1798 **
1799 ** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists.
1800 */
1801
1802 inline void
1803 set_suspend_state(VCHIQ_ARM_STATE_T *arm_state,
1804 enum vc_suspend_status new_state)
1805 {
1806 /* set the state in all cases */
1807 arm_state->vc_suspend_state = new_state;
1808
1809 /* state specific additional actions */
1810 switch (new_state) {
1811 case VC_SUSPEND_FORCE_CANCELED:
1812 complete_all(&arm_state->vc_suspend_complete);
1813 break;
1814 case VC_SUSPEND_REJECTED:
1815 complete_all(&arm_state->vc_suspend_complete);
1816 break;
1817 case VC_SUSPEND_FAILED:
1818 complete_all(&arm_state->vc_suspend_complete);
1819 arm_state->vc_resume_state = VC_RESUME_RESUMED;
1820 complete_all(&arm_state->vc_resume_complete);
1821 break;
1822 case VC_SUSPEND_IDLE:
1823 reinit_completion(&arm_state->vc_suspend_complete);
1824 break;
1825 case VC_SUSPEND_REQUESTED:
1826 break;
1827 case VC_SUSPEND_IN_PROGRESS:
1828 set_resume_state(arm_state, VC_RESUME_IDLE);
1829 break;
1830 case VC_SUSPEND_SUSPENDED:
1831 complete_all(&arm_state->vc_suspend_complete);
1832 break;
1833 default:
1834 BUG();
1835 break;
1836 }
1837 }
1838
1839 inline void
1840 set_resume_state(VCHIQ_ARM_STATE_T *arm_state,
1841 enum vc_resume_status new_state)
1842 {
1843 /* set the state in all cases */
1844 arm_state->vc_resume_state = new_state;
1845
1846 /* state specific additional actions */
1847 switch (new_state) {
1848 case VC_RESUME_FAILED:
1849 break;
1850 case VC_RESUME_IDLE:
1851 reinit_completion(&arm_state->vc_resume_complete);
1852 break;
1853 case VC_RESUME_REQUESTED:
1854 break;
1855 case VC_RESUME_IN_PROGRESS:
1856 break;
1857 case VC_RESUME_RESUMED:
1858 complete_all(&arm_state->vc_resume_complete);
1859 set_suspend_state(arm_state, VC_SUSPEND_IDLE);
1860 break;
1861 default:
1862 BUG();
1863 break;
1864 }
1865 }
1866
1867
1868 /* should be called with the write lock held */
1869 inline void
1870 start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state)
1871 {
1872 del_timer(&arm_state->suspend_timer);
1873 arm_state->suspend_timer.expires = jiffies +
1874 msecs_to_jiffies(arm_state->
1875 suspend_timer_timeout);
1876 add_timer(&arm_state->suspend_timer);
1877 arm_state->suspend_timer_running = 1;
1878 }
1879
1880 /* should be called with the write lock held */
1881 static inline void
1882 stop_suspend_timer(VCHIQ_ARM_STATE_T *arm_state)
1883 {
1884 if (arm_state->suspend_timer_running) {
1885 del_timer(&arm_state->suspend_timer);
1886 arm_state->suspend_timer_running = 0;
1887 }
1888 }
1889
1890 static inline int
1891 need_resume(VCHIQ_STATE_T *state)
1892 {
1893 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
1894 return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) &&
1895 (arm_state->vc_resume_state < VC_RESUME_REQUESTED) &&
1896 vchiq_videocore_wanted(state);
1897 }
1898
1899 static int
1900 block_resume(VCHIQ_ARM_STATE_T *arm_state)
1901 {
1902 int status = VCHIQ_SUCCESS;
1903 const unsigned long timeout_val =
1904 msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS);
1905 int resume_count = 0;
1906
1907 /* Allow any threads which were blocked by the last force suspend to
1908 * complete if they haven't already. Only give this one shot; if
1909 * blocked_count is incremented after blocked_blocker is completed
1910 * (which only happens when blocked_count hits 0) then those threads
1911 * will have to wait until next time around */
1912 if (arm_state->blocked_count) {
1913 reinit_completion(&arm_state->blocked_blocker);
1914 write_unlock_bh(&arm_state->susp_res_lock);
1915 vchiq_log_info(vchiq_susp_log_level, "%s wait for previously "
1916 "blocked clients", __func__);
1917 if (wait_for_completion_interruptible_timeout(
1918 &arm_state->blocked_blocker, timeout_val)
1919 <= 0) {
1920 vchiq_log_error(vchiq_susp_log_level, "%s wait for "
1921 "previously blocked clients failed" , __func__);
1922 status = VCHIQ_ERROR;
1923 write_lock_bh(&arm_state->susp_res_lock);
1924 goto out;
1925 }
1926 vchiq_log_info(vchiq_susp_log_level, "%s previously blocked "
1927 "clients resumed", __func__);
1928 write_lock_bh(&arm_state->susp_res_lock);
1929 }
1930
1931 /* We need to wait for resume to complete if it's in process */
1932 while (arm_state->vc_resume_state != VC_RESUME_RESUMED &&
1933 arm_state->vc_resume_state > VC_RESUME_IDLE) {
1934 if (resume_count > 1) {
1935 status = VCHIQ_ERROR;
1936 vchiq_log_error(vchiq_susp_log_level, "%s waited too "
1937 "many times for resume" , __func__);
1938 goto out;
1939 }
1940 write_unlock_bh(&arm_state->susp_res_lock);
1941 vchiq_log_info(vchiq_susp_log_level, "%s wait for resume",
1942 __func__);
1943 if (wait_for_completion_interruptible_timeout(
1944 &arm_state->vc_resume_complete, timeout_val)
1945 <= 0) {
1946 vchiq_log_error(vchiq_susp_log_level, "%s wait for "
1947 "resume failed (%s)", __func__,
1948 resume_state_names[arm_state->vc_resume_state +
1949 VC_RESUME_NUM_OFFSET]);
1950 status = VCHIQ_ERROR;
1951 write_lock_bh(&arm_state->susp_res_lock);
1952 goto out;
1953 }
1954 vchiq_log_info(vchiq_susp_log_level, "%s resumed", __func__);
1955 write_lock_bh(&arm_state->susp_res_lock);
1956 resume_count++;
1957 }
1958 reinit_completion(&arm_state->resume_blocker);
1959 arm_state->resume_blocked = 1;
1960
1961 out:
1962 return status;
1963 }
1964
1965 static inline void
1966 unblock_resume(VCHIQ_ARM_STATE_T *arm_state)
1967 {
1968 complete_all(&arm_state->resume_blocker);
1969 arm_state->resume_blocked = 0;
1970 }
1971
1972 /* Initiate suspend via slot handler. Should be called with the write lock
1973 * held */
1974 VCHIQ_STATUS_T
1975 vchiq_arm_vcsuspend(VCHIQ_STATE_T *state)
1976 {
1977 VCHIQ_STATUS_T status = VCHIQ_ERROR;
1978 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
1979
1980 if (!arm_state)
1981 goto out;
1982
1983 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
1984 status = VCHIQ_SUCCESS;
1985
1986
1987 switch (arm_state->vc_suspend_state) {
1988 case VC_SUSPEND_REQUESTED:
1989 vchiq_log_info(vchiq_susp_log_level, "%s: suspend already "
1990 "requested", __func__);
1991 break;
1992 case VC_SUSPEND_IN_PROGRESS:
1993 vchiq_log_info(vchiq_susp_log_level, "%s: suspend already in "
1994 "progress", __func__);
1995 break;
1996
1997 default:
1998 /* We don't expect to be in other states, so log but continue
1999 * anyway */
2000 vchiq_log_error(vchiq_susp_log_level,
2001 "%s unexpected suspend state %s", __func__,
2002 suspend_state_names[arm_state->vc_suspend_state +
2003 VC_SUSPEND_NUM_OFFSET]);
2004 /* fall through */
2005 case VC_SUSPEND_REJECTED:
2006 case VC_SUSPEND_FAILED:
2007 /* Ensure any idle state actions have been run */
2008 set_suspend_state(arm_state, VC_SUSPEND_IDLE);
2009 /* fall through */
2010 case VC_SUSPEND_IDLE:
2011 vchiq_log_info(vchiq_susp_log_level,
2012 "%s: suspending", __func__);
2013 set_suspend_state(arm_state, VC_SUSPEND_REQUESTED);
2014 /* kick the slot handler thread to initiate suspend */
2015 request_poll(state, NULL, 0);
2016 break;
2017 }
2018
2019 out:
2020 vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status);
2021 return status;
2022 }
2023
2024 void
2025 vchiq_platform_check_suspend(VCHIQ_STATE_T *state)
2026 {
2027 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2028 int susp = 0;
2029
2030 if (!arm_state)
2031 goto out;
2032
2033 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2034
2035 write_lock_bh(&arm_state->susp_res_lock);
2036 if (arm_state->vc_suspend_state == VC_SUSPEND_REQUESTED &&
2037 arm_state->vc_resume_state == VC_RESUME_RESUMED) {
2038 set_suspend_state(arm_state, VC_SUSPEND_IN_PROGRESS);
2039 susp = 1;
2040 }
2041 write_unlock_bh(&arm_state->susp_res_lock);
2042
2043 if (susp)
2044 vchiq_platform_suspend(state);
2045
2046 out:
2047 vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__);
2048 return;
2049 }
2050
2051
2052 static void
2053 output_timeout_error(VCHIQ_STATE_T *state)
2054 {
2055 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2056 char service_err[50] = "";
2057 int vc_use_count = arm_state->videocore_use_count;
2058 int active_services = state->unused_service;
2059 int i;
2060
2061 if (!arm_state->videocore_use_count) {
2062 snprintf(service_err, 50, " Videocore usecount is 0");
2063 goto output_msg;
2064 }
2065 for (i = 0; i < active_services; i++) {
2066 VCHIQ_SERVICE_T *service_ptr = state->services[i];
2067 if (service_ptr && service_ptr->service_use_count &&
2068 (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) {
2069 snprintf(service_err, 50, " %c%c%c%c(%8x) service has "
2070 "use count %d%s", VCHIQ_FOURCC_AS_4CHARS(
2071 service_ptr->base.fourcc),
2072 service_ptr->client_id,
2073 service_ptr->service_use_count,
2074 service_ptr->service_use_count ==
2075 vc_use_count ? "" : " (+ more)");
2076 break;
2077 }
2078 }
2079
2080 output_msg:
2081 vchiq_log_error(vchiq_susp_log_level,
2082 "timed out waiting for vc suspend (%d).%s",
2083 arm_state->autosuspend_override, service_err);
2084
2085 }
2086
2087 /* Try to get videocore into suspended state, regardless of autosuspend state.
2088 ** We don't actually force suspend, since videocore may get into a bad state
2089 ** if we force suspend at a bad time. Instead, we wait for autosuspend to
2090 ** determine a good point to suspend. If this doesn't happen within 100ms we
2091 ** report failure.
2092 **
2093 ** Returns VCHIQ_SUCCESS if videocore suspended successfully, VCHIQ_RETRY if
2094 ** videocore failed to suspend in time or VCHIQ_ERROR if interrupted.
2095 */
2096 VCHIQ_STATUS_T
2097 vchiq_arm_force_suspend(VCHIQ_STATE_T *state)
2098 {
2099 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2100 VCHIQ_STATUS_T status = VCHIQ_ERROR;
2101 long rc = 0;
2102 int repeat = -1;
2103
2104 if (!arm_state)
2105 goto out;
2106
2107 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2108
2109 write_lock_bh(&arm_state->susp_res_lock);
2110
2111 status = block_resume(arm_state);
2112 if (status != VCHIQ_SUCCESS)
2113 goto unlock;
2114 if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) {
2115 /* Already suspended - just block resume and exit */
2116 vchiq_log_info(vchiq_susp_log_level, "%s already suspended",
2117 __func__);
2118 status = VCHIQ_SUCCESS;
2119 goto unlock;
2120 } else if (arm_state->vc_suspend_state <= VC_SUSPEND_IDLE) {
2121 /* initiate suspend immediately in the case that we're waiting
2122 * for the timeout */
2123 stop_suspend_timer(arm_state);
2124 if (!vchiq_videocore_wanted(state)) {
2125 vchiq_log_info(vchiq_susp_log_level, "%s videocore "
2126 "idle, initiating suspend", __func__);
2127 status = vchiq_arm_vcsuspend(state);
2128 } else if (arm_state->autosuspend_override <
2129 FORCE_SUSPEND_FAIL_MAX) {
2130 vchiq_log_info(vchiq_susp_log_level, "%s letting "
2131 "videocore go idle", __func__);
2132 status = VCHIQ_SUCCESS;
2133 } else {
2134 vchiq_log_warning(vchiq_susp_log_level, "%s failed too "
2135 "many times - attempting suspend", __func__);
2136 status = vchiq_arm_vcsuspend(state);
2137 }
2138 } else {
2139 vchiq_log_info(vchiq_susp_log_level, "%s videocore suspend "
2140 "in progress - wait for completion", __func__);
2141 status = VCHIQ_SUCCESS;
2142 }
2143
2144 /* Wait for suspend to happen due to system idle (not forced..) */
2145 if (status != VCHIQ_SUCCESS)
2146 goto unblock_resume;
2147
2148 do {
2149 write_unlock_bh(&arm_state->susp_res_lock);
2150
2151 rc = wait_for_completion_interruptible_timeout(
2152 &arm_state->vc_suspend_complete,
2153 msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS));
2154
2155 write_lock_bh(&arm_state->susp_res_lock);
2156 if (rc < 0) {
2157 vchiq_log_warning(vchiq_susp_log_level, "%s "
2158 "interrupted waiting for suspend", __func__);
2159 status = VCHIQ_ERROR;
2160 goto unblock_resume;
2161 } else if (rc == 0) {
2162 if (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) {
2163 /* Repeat timeout once if in progress */
2164 if (repeat < 0) {
2165 repeat = 1;
2166 continue;
2167 }
2168 }
2169 arm_state->autosuspend_override++;
2170 output_timeout_error(state);
2171
2172 status = VCHIQ_RETRY;
2173 goto unblock_resume;
2174 }
2175 } while (0 < (repeat--));
2176
2177 /* Check and report state in case we need to abort ARM suspend */
2178 if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED) {
2179 status = VCHIQ_RETRY;
2180 vchiq_log_error(vchiq_susp_log_level,
2181 "%s videocore suspend failed (state %s)", __func__,
2182 suspend_state_names[arm_state->vc_suspend_state +
2183 VC_SUSPEND_NUM_OFFSET]);
2184 /* Reset the state only if it's still in an error state.
2185 * Something could have already initiated another suspend. */
2186 if (arm_state->vc_suspend_state < VC_SUSPEND_IDLE)
2187 set_suspend_state(arm_state, VC_SUSPEND_IDLE);
2188
2189 goto unblock_resume;
2190 }
2191
2192 /* successfully suspended - unlock and exit */
2193 goto unlock;
2194
2195 unblock_resume:
2196 /* all error states need to unblock resume before exit */
2197 unblock_resume(arm_state);
2198
2199 unlock:
2200 write_unlock_bh(&arm_state->susp_res_lock);
2201
2202 out:
2203 vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status);
2204 return status;
2205 }
2206
2207 void
2208 vchiq_check_suspend(VCHIQ_STATE_T *state)
2209 {
2210 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2211
2212 if (!arm_state)
2213 goto out;
2214
2215 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2216
2217 write_lock_bh(&arm_state->susp_res_lock);
2218 if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED &&
2219 arm_state->first_connect &&
2220 !vchiq_videocore_wanted(state)) {
2221 vchiq_arm_vcsuspend(state);
2222 }
2223 write_unlock_bh(&arm_state->susp_res_lock);
2224
2225 out:
2226 vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__);
2227 return;
2228 }
2229
2230
2231 int
2232 vchiq_arm_allow_resume(VCHIQ_STATE_T *state)
2233 {
2234 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2235 int resume = 0;
2236 int ret = -1;
2237
2238 if (!arm_state)
2239 goto out;
2240
2241 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2242
2243 write_lock_bh(&arm_state->susp_res_lock);
2244 unblock_resume(arm_state);
2245 resume = vchiq_check_resume(state);
2246 write_unlock_bh(&arm_state->susp_res_lock);
2247
2248 if (resume) {
2249 if (wait_for_completion_interruptible(
2250 &arm_state->vc_resume_complete) < 0) {
2251 vchiq_log_error(vchiq_susp_log_level,
2252 "%s interrupted", __func__);
2253 /* failed, cannot accurately derive suspend
2254 * state, so exit early. */
2255 goto out;
2256 }
2257 }
2258
2259 read_lock_bh(&arm_state->susp_res_lock);
2260 if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) {
2261 vchiq_log_info(vchiq_susp_log_level,
2262 "%s: Videocore remains suspended", __func__);
2263 } else {
2264 vchiq_log_info(vchiq_susp_log_level,
2265 "%s: Videocore resumed", __func__);
2266 ret = 0;
2267 }
2268 read_unlock_bh(&arm_state->susp_res_lock);
2269 out:
2270 vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret);
2271 return ret;
2272 }
2273
2274 /* This function should be called with the write lock held */
2275 int
2276 vchiq_check_resume(VCHIQ_STATE_T *state)
2277 {
2278 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2279 int resume = 0;
2280
2281 if (!arm_state)
2282 goto out;
2283
2284 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2285
2286 if (need_resume(state)) {
2287 set_resume_state(arm_state, VC_RESUME_REQUESTED);
2288 request_poll(state, NULL, 0);
2289 resume = 1;
2290 }
2291
2292 out:
2293 vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__);
2294 return resume;
2295 }
2296
2297 #ifdef notyet
2298 void
2299 vchiq_platform_check_resume(VCHIQ_STATE_T *state)
2300 {
2301 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2302 int res = 0;
2303
2304 if (!arm_state)
2305 goto out;
2306
2307 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2308
2309 write_lock_bh(&arm_state->susp_res_lock);
2310 if (arm_state->wake_address == 0) {
2311 vchiq_log_info(vchiq_susp_log_level,
2312 "%s: already awake", __func__);
2313 goto unlock;
2314 }
2315 if (arm_state->vc_resume_state == VC_RESUME_IN_PROGRESS) {
2316 vchiq_log_info(vchiq_susp_log_level,
2317 "%s: already resuming", __func__);
2318 goto unlock;
2319 }
2320
2321 if (arm_state->vc_resume_state == VC_RESUME_REQUESTED) {
2322 set_resume_state(arm_state, VC_RESUME_IN_PROGRESS);
2323 res = 1;
2324 } else
2325 vchiq_log_trace(vchiq_susp_log_level,
2326 "%s: not resuming (resume state %s)", __func__,
2327 resume_state_names[arm_state->vc_resume_state +
2328 VC_RESUME_NUM_OFFSET]);
2329
2330 unlock:
2331 write_unlock_bh(&arm_state->susp_res_lock);
2332
2333 if (res)
2334 vchiq_platform_resume(state);
2335
2336 out:
2337 vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__);
2338 return;
2339
2340 }
2341 #endif
2342
2343
2344
2345 VCHIQ_STATUS_T
2346 vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
2347 enum USE_TYPE_E use_type)
2348 {
2349 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2350 VCHIQ_STATUS_T ret = VCHIQ_SUCCESS;
2351 char entity[16];
2352 int *entity_uc;
2353 int local_uc, local_entity_uc;
2354
2355 if (!arm_state)
2356 goto out;
2357
2358 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2359
2360 if (use_type == USE_TYPE_VCHIQ) {
2361 snprintf(entity, sizeof(entity), "VCHIQ: ");
2362 entity_uc = &arm_state->peer_use_count;
2363 } else if (service) {
2364 snprintf(entity, sizeof(entity), "%c%c%c%c:%8x",
2365 VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
2366 service->client_id);
2367 entity_uc = &service->service_use_count;
2368 } else {
2369 vchiq_log_error(vchiq_susp_log_level, "%s null service "
2370 "ptr", __func__);
2371 ret = VCHIQ_ERROR;
2372 goto out;
2373 }
2374
2375 write_lock_bh(&arm_state->susp_res_lock);
2376 while (arm_state->resume_blocked) {
2377 /* If we call 'use' while force suspend is waiting for suspend,
2378 * then we're about to block the thread which the force is
2379 * waiting to complete, so we're bound to just time out. In this
2380 * case, set the suspend state such that the wait will be
2381 * canceled, so we can complete as quickly as possible. */
2382 if (arm_state->resume_blocked && arm_state->vc_suspend_state ==
2383 VC_SUSPEND_IDLE) {
2384 set_suspend_state(arm_state, VC_SUSPEND_FORCE_CANCELED);
2385 break;
2386 }
2387 /* If suspend is already in progress then we need to block */
2388 if (!try_wait_for_completion(&arm_state->resume_blocker)) {
2389 /* Indicate that there are threads waiting on the resume
2390 * blocker. These need to be allowed to complete before
2391 * a _second_ call to force suspend can complete,
2392 * otherwise low priority threads might never actually
2393 * continue */
2394 arm_state->blocked_count++;
2395 write_unlock_bh(&arm_state->susp_res_lock);
2396 vchiq_log_info(vchiq_susp_log_level, "%s %s resume "
2397 "blocked - waiting...", __func__, entity);
2398 if (wait_for_completion_killable(
2399 &arm_state->resume_blocker) != 0) {
2400 vchiq_log_error(vchiq_susp_log_level, "%s %s "
2401 "wait for resume blocker interrupted",
2402 __func__, entity);
2403 ret = VCHIQ_ERROR;
2404 write_lock_bh(&arm_state->susp_res_lock);
2405 arm_state->blocked_count--;
2406 write_unlock_bh(&arm_state->susp_res_lock);
2407 goto out;
2408 }
2409 vchiq_log_info(vchiq_susp_log_level, "%s %s resume "
2410 "unblocked", __func__, entity);
2411 write_lock_bh(&arm_state->susp_res_lock);
2412 if (--arm_state->blocked_count == 0)
2413 complete_all(&arm_state->blocked_blocker);
2414 }
2415 }
2416
2417 stop_suspend_timer(arm_state);
2418
2419 local_uc = ++arm_state->videocore_use_count;
2420 local_entity_uc = ++(*entity_uc);
2421
2422 /* If there's a pending request which hasn't yet been serviced then
2423 * just clear it. If we're past VC_SUSPEND_REQUESTED state then
2424 * vc_resume_complete will block until we either resume or fail to
2425 * suspend */
2426 if (arm_state->vc_suspend_state <= VC_SUSPEND_REQUESTED)
2427 set_suspend_state(arm_state, VC_SUSPEND_IDLE);
2428
2429 if ((use_type != USE_TYPE_SERVICE_NO_RESUME) && need_resume(state)) {
2430 set_resume_state(arm_state, VC_RESUME_REQUESTED);
2431 vchiq_log_info(vchiq_susp_log_level,
2432 "%s %s count %d, state count %d",
2433 __func__, entity, local_entity_uc, local_uc);
2434 request_poll(state, NULL, 0);
2435 } else
2436 vchiq_log_trace(vchiq_susp_log_level,
2437 "%s %s count %d, state count %d",
2438 __func__, entity, *entity_uc, local_uc);
2439
2440
2441 write_unlock_bh(&arm_state->susp_res_lock);
2442
2443 /* Completion is in a done state when we're not suspended, so this won't
2444 * block for the non-suspended case. */
2445 if (!try_wait_for_completion(&arm_state->vc_resume_complete)) {
2446 vchiq_log_info(vchiq_susp_log_level, "%s %s wait for resume",
2447 __func__, entity);
2448 if (wait_for_completion_killable(
2449 &arm_state->vc_resume_complete) != 0) {
2450 vchiq_log_error(vchiq_susp_log_level, "%s %s wait for "
2451 "resume interrupted", __func__, entity);
2452 ret = VCHIQ_ERROR;
2453 goto out;
2454 }
2455 vchiq_log_info(vchiq_susp_log_level, "%s %s resumed", __func__,
2456 entity);
2457 }
2458
2459 if (ret == VCHIQ_SUCCESS) {
2460 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
2461 long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0);
2462 while (ack_cnt && (status == VCHIQ_SUCCESS)) {
2463 /* Send the use notify to videocore */
2464 status = vchiq_send_remote_use_active(state);
2465 if (status == VCHIQ_SUCCESS)
2466 ack_cnt--;
2467 else
2468 atomic_add(ack_cnt,
2469 &arm_state->ka_use_ack_count);
2470 }
2471 }
2472
2473 out:
2474 vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret);
2475 return ret;
2476 }
2477
2478 VCHIQ_STATUS_T
2479 vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service)
2480 {
2481 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2482 VCHIQ_STATUS_T ret = VCHIQ_SUCCESS;
2483 char entity[16];
2484 int *entity_uc;
2485
2486 if (!arm_state)
2487 goto out;
2488
2489 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2490
2491 if (service) {
2492 snprintf(entity, sizeof(entity), "%c%c%c%c:%8x",
2493 VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
2494 service->client_id);
2495 entity_uc = &service->service_use_count;
2496 } else {
2497 snprintf(entity, sizeof(entity), "PEER: ");
2498 entity_uc = &arm_state->peer_use_count;
2499 }
2500
2501 write_lock_bh(&arm_state->susp_res_lock);
2502 if (!arm_state->videocore_use_count || !(*entity_uc)) {
2503 /* Don't use BUG_ON - don't allow user thread to crash kernel */
2504 WARN_ON(!arm_state->videocore_use_count);
2505 WARN_ON(!(*entity_uc));
2506 ret = VCHIQ_ERROR;
2507 goto unlock;
2508 }
2509 --arm_state->videocore_use_count;
2510 --(*entity_uc);
2511
2512 if (!vchiq_videocore_wanted(state)) {
2513 if (vchiq_platform_use_suspend_timer() &&
2514 !arm_state->resume_blocked) {
2515 /* Only use the timer if we're not trying to force
2516 * suspend (=> resume_blocked) */
2517 start_suspend_timer(arm_state);
2518 } else {
2519 vchiq_log_info(vchiq_susp_log_level,
2520 "%s %s count %d, state count %d - suspending",
2521 __func__, entity, *entity_uc,
2522 arm_state->videocore_use_count);
2523 vchiq_arm_vcsuspend(state);
2524 }
2525 } else
2526 vchiq_log_trace(vchiq_susp_log_level,
2527 "%s %s count %d, state count %d",
2528 __func__, entity, *entity_uc,
2529 arm_state->videocore_use_count);
2530
2531 unlock:
2532 write_unlock_bh(&arm_state->susp_res_lock);
2533
2534 out:
2535 vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret);
2536 return ret;
2537 }
2538
2539 void
2540 vchiq_on_remote_use(VCHIQ_STATE_T *state)
2541 {
2542 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2543 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2544 atomic_inc(&arm_state->ka_use_count);
2545 complete(&arm_state->ka_evt);
2546 }
2547
2548 void
2549 vchiq_on_remote_release(VCHIQ_STATE_T *state)
2550 {
2551 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2552 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2553 atomic_inc(&arm_state->ka_release_count);
2554 complete(&arm_state->ka_evt);
2555 }
2556
2557 VCHIQ_STATUS_T
2558 vchiq_use_service_internal(VCHIQ_SERVICE_T *service)
2559 {
2560 return vchiq_use_internal(service->state, service, USE_TYPE_SERVICE);
2561 }
2562
2563 VCHIQ_STATUS_T
2564 vchiq_release_service_internal(VCHIQ_SERVICE_T *service)
2565 {
2566 return vchiq_release_internal(service->state, service);
2567 }
2568
2569 #ifdef notyet
2570 VCHIQ_DEBUGFS_NODE_T *
2571 vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance)
2572 {
2573 return &instance->debugfs_node;
2574 }
2575
2576 int
2577 vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance)
2578 {
2579 VCHIQ_SERVICE_T *service;
2580 int use_count = 0, i;
2581 i = 0;
2582 while ((service = next_service_by_instance(instance->state,
2583 instance, &i)) != NULL) {
2584 use_count += service->service_use_count;
2585 unlock_service(service);
2586 }
2587 return use_count;
2588 }
2589
2590 int
2591 vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance)
2592 {
2593 return instance->pid;
2594 }
2595
2596 int
2597 vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance)
2598 {
2599 return instance->trace;
2600 }
2601
2602 void
2603 vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace)
2604 {
2605 VCHIQ_SERVICE_T *service;
2606 int i;
2607 i = 0;
2608 while ((service = next_service_by_instance(instance->state,
2609 instance, &i)) != NULL) {
2610 service->trace = trace;
2611 unlock_service(service);
2612 }
2613 instance->trace = (trace != 0);
2614 }
2615 #endif
2616
2617 static void suspend_timer_callback(unsigned long context)
2618 {
2619 VCHIQ_STATE_T *state = (VCHIQ_STATE_T *)context;
2620 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2621 if (!arm_state)
2622 goto out;
2623 vchiq_log_info(vchiq_susp_log_level,
2624 "%s - suspend timer expired - check suspend", __func__);
2625 vchiq_check_suspend(state);
2626 out:
2627 return;
2628 }
2629
2630 VCHIQ_STATUS_T
2631 vchiq_use_service_no_resume(VCHIQ_SERVICE_HANDLE_T handle)
2632 {
2633 VCHIQ_STATUS_T ret = VCHIQ_ERROR;
2634 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
2635 if (service) {
2636 ret = vchiq_use_internal(service->state, service,
2637 USE_TYPE_SERVICE_NO_RESUME);
2638 unlock_service(service);
2639 }
2640 return ret;
2641 }
2642
2643 VCHIQ_STATUS_T
2644 vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle)
2645 {
2646 VCHIQ_STATUS_T ret = VCHIQ_ERROR;
2647 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
2648 if (service) {
2649 ret = vchiq_use_internal(service->state, service,
2650 USE_TYPE_SERVICE);
2651 unlock_service(service);
2652 }
2653 return ret;
2654 }
2655
2656 VCHIQ_STATUS_T
2657 vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle)
2658 {
2659 VCHIQ_STATUS_T ret = VCHIQ_ERROR;
2660 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
2661 if (service) {
2662 ret = vchiq_release_internal(service->state, service);
2663 unlock_service(service);
2664 }
2665 return ret;
2666 }
2667
2668 void
2669 vchiq_dump_service_use_state(VCHIQ_STATE_T *state)
2670 {
2671 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2672 int i, j = 0;
2673 /* Only dump 64 services */
2674 #define local_max_services 64
2675 /* If there's more than 64 services, only dump ones with
2676 * non-zero counts */
2677 int only_nonzero = 0;
2678 static const char *nz = "<-- preventing suspend";
2679
2680 enum vc_suspend_status vc_suspend_state;
2681 enum vc_resume_status vc_resume_state;
2682 int peer_count;
2683 int vc_use_count;
2684 int active_services;
2685 struct service_data_struct {
2686 int fourcc;
2687 int clientid;
2688 int use_count;
2689 } service_data[local_max_services];
2690
2691 if (!arm_state)
2692 return;
2693
2694 read_lock_bh(&arm_state->susp_res_lock);
2695 vc_suspend_state = arm_state->vc_suspend_state;
2696 vc_resume_state = arm_state->vc_resume_state;
2697 peer_count = arm_state->peer_use_count;
2698 vc_use_count = arm_state->videocore_use_count;
2699 active_services = state->unused_service;
2700 if (active_services > local_max_services)
2701 only_nonzero = 1;
2702
2703 for (i = 0; (i < active_services) && (j < local_max_services); i++) {
2704 VCHIQ_SERVICE_T *service_ptr = state->services[i];
2705 if (!service_ptr)
2706 continue;
2707
2708 if (only_nonzero && !service_ptr->service_use_count)
2709 continue;
2710
2711 if (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE) {
2712 service_data[j].fourcc = service_ptr->base.fourcc;
2713 service_data[j].clientid = service_ptr->client_id;
2714 service_data[j++].use_count = service_ptr->
2715 service_use_count;
2716 }
2717 }
2718
2719 read_unlock_bh(&arm_state->susp_res_lock);
2720
2721 vchiq_log_warning(vchiq_susp_log_level,
2722 "-- Videcore suspend state: %s --",
2723 suspend_state_names[vc_suspend_state + VC_SUSPEND_NUM_OFFSET]);
2724 vchiq_log_warning(vchiq_susp_log_level,
2725 "-- Videcore resume state: %s --",
2726 resume_state_names[vc_resume_state + VC_RESUME_NUM_OFFSET]);
2727
2728 if (only_nonzero)
2729 vchiq_log_warning(vchiq_susp_log_level, "Too many active "
2730 "services (%d). Only dumping up to first %d services "
2731 "with non-zero use-count", active_services,
2732 local_max_services);
2733
2734 for (i = 0; i < j; i++) {
2735 vchiq_log_warning(vchiq_susp_log_level,
2736 "----- %c%c%c%c:%d service count %d %s",
2737 VCHIQ_FOURCC_AS_4CHARS(service_data[i].fourcc),
2738 service_data[i].clientid,
2739 service_data[i].use_count,
2740 service_data[i].use_count ? nz : "");
2741 }
2742 vchiq_log_warning(vchiq_susp_log_level,
2743 "----- VCHIQ use count count %d", peer_count);
2744 vchiq_log_warning(vchiq_susp_log_level,
2745 "--- Overall vchiq instance use count %d", vc_use_count);
2746
2747 vchiq_dump_platform_use_state(state);
2748 }
2749
2750 VCHIQ_STATUS_T
2751 vchiq_check_service(VCHIQ_SERVICE_T *service)
2752 {
2753 VCHIQ_ARM_STATE_T *arm_state;
2754 VCHIQ_STATUS_T ret = VCHIQ_ERROR;
2755
2756 if (!service || !service->state)
2757 goto out;
2758
2759 vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
2760
2761 arm_state = vchiq_platform_get_arm_state(service->state);
2762
2763 read_lock_bh(&arm_state->susp_res_lock);
2764 if (service->service_use_count)
2765 ret = VCHIQ_SUCCESS;
2766 read_unlock_bh(&arm_state->susp_res_lock);
2767
2768 if (ret == VCHIQ_ERROR) {
2769 vchiq_log_error(vchiq_susp_log_level,
2770 "%s ERROR - %c%c%c%c:%8x service count %d, "
2771 "state count %d, videocore suspend state %s", __func__,
2772 VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
2773 service->client_id, service->service_use_count,
2774 arm_state->videocore_use_count,
2775 suspend_state_names[arm_state->vc_suspend_state +
2776 VC_SUSPEND_NUM_OFFSET]);
2777 vchiq_dump_service_use_state(service->state);
2778 }
2779 out:
2780 return ret;
2781 }
2782
2783 /* stub functions */
2784 void vchiq_on_remote_use_active(VCHIQ_STATE_T *state)
2785 {
2786 (void)state;
2787 }
2788
2789 void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state,
2790 VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate)
2791 {
2792 VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
2793 vchiq_log_info(vchiq_susp_log_level, "%d: %s->%s", state->id,
2794 get_conn_state_name(oldstate), get_conn_state_name(newstate));
2795 if (state->conn_state == VCHIQ_CONNSTATE_CONNECTED) {
2796 write_lock_bh(&arm_state->susp_res_lock);
2797 if (!arm_state->first_connect) {
2798 char threadname[10];
2799 arm_state->first_connect = 1;
2800 write_unlock_bh(&arm_state->susp_res_lock);
2801 snprintf(threadname, sizeof(threadname), "VCHIQka-%d",
2802 state->id);
2803 arm_state->ka_thread = vchiq_thread_create(
2804 &vchiq_keepalive_thread_func,
2805 (void *)state,
2806 threadname);
2807 if (arm_state->ka_thread == NULL) {
2808 vchiq_log_error(vchiq_susp_log_level,
2809 "vchiq: FATAL: couldn't create thread %s",
2810 threadname);
2811 } else {
2812 wake_up_process(arm_state->ka_thread);
2813 }
2814 } else
2815 write_unlock_bh(&arm_state->susp_res_lock);
2816 }
2817 }
2818
2819
2820 /****************************************************************************
2821 *
2822 * vchiq_init - called when the module is loaded.
2823 *
2824 ***************************************************************************/
2825
2826 int __init vchiq_init(void);
2827 int __init
2828 vchiq_init(void)
2829 {
2830 int err;
2831
2832 #ifdef notyet
2833 /* create proc entries */
2834 err = vchiq_proc_init();
2835 if (err != 0)
2836 goto failed_proc_init;
2837 #endif
2838
2839 spin_lock_init(&msg_queue_spinlock);
2840
2841 err = vchiq_platform_init(&g_state);
2842 if (err != 0)
2843 goto failed_platform_init;
2844
2845 vchiq_log_info(vchiq_arm_log_level,
2846 "vchiq: initialised - version %d (min %d)",
2847 VCHIQ_VERSION, VCHIQ_VERSION_MIN);
2848
2849 return 0;
2850
2851 failed_platform_init:
2852 vchiq_log_warning(vchiq_arm_log_level, "could not load vchiq");
2853 return err;
2854 }
2855
2856 /****************************************************************************
2857 *
2858 * vchiq_exit - called when the module is unloaded.
2859 *
2860 ***************************************************************************/
2861
2862 void vchiq_exit(void);
2863 void
2864 vchiq_exit(void)
2865 {
2866 vchiq_platform_exit(&g_state);
2867 }
2868