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