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