Path: blob/main/sys/contrib/vchiq/interface/vchiq_arm/vchiq_arm.c
48383 views
/**1* Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved.2* Copyright (c) 2010-2012 Broadcom. All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions, and the following disclaimer,9* without modification.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13* 3. The names of the above-listed copyright holders may not be used14* to endorse or promote products derived from this software without15* specific prior written permission.16*17* ALTERNATIVELY, this software may be distributed under the terms of the18* GNU General Public License ("GPL") version 2, as published by the Free19* Software Foundation.20*21* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS22* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,23* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR24* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR25* 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, OR28* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF29* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING30* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS31* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.32*/333435#include "vchiq_core.h"36#include "vchiq_ioctl.h"37#include "vchiq_arm.h"3839#define DEVICE_NAME "vchiq"4041/* Override the default prefix, which would be vchiq_arm (from the filename) */42#undef MODULE_PARAM_PREFIX43#define MODULE_PARAM_PREFIX DEVICE_NAME "."4445#define VCHIQ_MINOR 04647/* Some per-instance constants */48#define MAX_COMPLETIONS 12849#define MAX_SERVICES 6450#define MAX_ELEMENTS 851#define MSG_QUEUE_SIZE 1285253#define KEEPALIVE_VER 154#define KEEPALIVE_VER_MIN KEEPALIVE_VER5556/* Run time control of log level, based on KERN_XXX level. */57int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT;58int vchiq_susp_log_level = VCHIQ_LOG_ERROR;5960#define SUSPEND_TIMER_TIMEOUT_MS 10061#define SUSPEND_RETRY_TIMER_TIMEOUT_MS 10006263#define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */64static const char *const suspend_state_names[] = {65"VC_SUSPEND_FORCE_CANCELED",66"VC_SUSPEND_REJECTED",67"VC_SUSPEND_FAILED",68"VC_SUSPEND_IDLE",69"VC_SUSPEND_REQUESTED",70"VC_SUSPEND_IN_PROGRESS",71"VC_SUSPEND_SUSPENDED"72};73#define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */74static const char *const resume_state_names[] = {75"VC_RESUME_FAILED",76"VC_RESUME_IDLE",77"VC_RESUME_REQUESTED",78"VC_RESUME_IN_PROGRESS",79"VC_RESUME_RESUMED"80};81/* The number of times we allow force suspend to timeout before actually82** _forcing_ suspend. This is to cater for SW which fails to release vchiq83** correctly - we don't want to prevent ARM suspend indefinitely in this case.84*/85#define FORCE_SUSPEND_FAIL_MAX 88687/* The time in ms allowed for videocore to go idle when force suspend has been88* requested */89#define FORCE_SUSPEND_TIMEOUT_MS 200909192static void suspend_timer_callback(unsigned long context);93#ifdef notyet94static int vchiq_proc_add_instance(VCHIQ_INSTANCE_T instance);95static void vchiq_proc_remove_instance(VCHIQ_INSTANCE_T instance);96#endif979899typedef struct user_service_struct {100VCHIQ_SERVICE_T *service;101void *userdata;102VCHIQ_INSTANCE_T instance;103char is_vchi;104char dequeue_pending;105char close_pending;106int message_available_pos;107int msg_insert;108int msg_remove;109struct semaphore insert_event;110struct semaphore remove_event;111struct semaphore close_event;112VCHIQ_HEADER_T * msg_queue[MSG_QUEUE_SIZE];113} USER_SERVICE_T;114115struct bulk_waiter_node {116struct bulk_waiter bulk_waiter;117int pid;118struct list_head list;119};120121struct vchiq_instance_struct {122VCHIQ_STATE_T *state;123VCHIQ_COMPLETION_DATA_T completions[MAX_COMPLETIONS];124int completion_insert;125int completion_remove;126struct semaphore insert_event;127struct semaphore remove_event;128struct mutex completion_mutex;129130int connected;131int closing;132int pid;133int mark;134int use_close_delivered;135int trace;136137struct list_head bulk_waiter_list;138struct mutex bulk_waiter_list_mutex;139140#ifdef notyet141VCHIQ_DEBUGFS_NODE_T proc_entry;142#endif143};144145typedef struct dump_context_struct {146char __user *buf;147size_t actual;148size_t space;149loff_t offset;150} DUMP_CONTEXT_T;151152static struct cdev * vchiq_cdev;153VCHIQ_STATE_T g_state;154static DEFINE_SPINLOCK(msg_queue_spinlock);155156static 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"LIB_VERSION",174"CLOSE_DELIVERED"175};176177vchiq_static_assert((sizeof(ioctl_names)/sizeof(ioctl_names[0])) ==178(VCHIQ_IOC_MAX + 1));179180static d_open_t vchiq_open;181static d_close_t vchiq_close;182static d_ioctl_t vchiq_ioctl;183184static struct cdevsw vchiq_cdevsw = {185.d_version = D_VERSION,186.d_ioctl = vchiq_ioctl,187.d_open = vchiq_open,188.d_close = vchiq_close,189.d_name = DEVICE_NAME,190};191192#if 0193static void194dump_phys_mem(void *virt_addr, uint32_t num_bytes);195#endif196197/****************************************************************************198*199* add_completion200*201***************************************************************************/202203static VCHIQ_STATUS_T204add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason,205VCHIQ_HEADER_T *header, USER_SERVICE_T *user_service,206void *bulk_userdata)207{208VCHIQ_COMPLETION_DATA_T *completion;209int insert;210DEBUG_INITIALISE(g_state.local)211212insert = instance->completion_insert;213while ((insert - instance->completion_remove) >= MAX_COMPLETIONS) {214/* Out of space - wait for the client */215DEBUG_TRACE(SERVICE_CALLBACK_LINE);216vchiq_log_trace(vchiq_arm_log_level,217"add_completion - completion queue full");218DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT);219220if (down_interruptible(&instance->remove_event) != 0) {221vchiq_log_info(vchiq_arm_log_level,222"service_callback interrupted");223return VCHIQ_RETRY;224}225226if (instance->closing) {227vchiq_log_info(vchiq_arm_log_level,228"service_callback closing");229return VCHIQ_SUCCESS;230}231DEBUG_TRACE(SERVICE_CALLBACK_LINE);232}233234completion = &instance->completions[insert & (MAX_COMPLETIONS - 1)];235236completion->header = header;237completion->reason = reason;238/* N.B. service_userdata is updated while processing AWAIT_COMPLETION */239completion->service_userdata = user_service->service;240completion->bulk_userdata = bulk_userdata;241242if (reason == VCHIQ_SERVICE_CLOSED) {243/* Take an extra reference, to be held until244this CLOSED notification is delivered. */245lock_service(user_service->service);246if (instance->use_close_delivered)247user_service->close_pending = 1;248}249250/* A write barrier is needed here to ensure that the entire completion251record is written out before the insert point. */252wmb();253254if (reason == VCHIQ_MESSAGE_AVAILABLE)255user_service->message_available_pos = insert;256257instance->completion_insert = ++insert;258259up(&instance->insert_event);260261return VCHIQ_SUCCESS;262}263264/****************************************************************************265*266* service_callback267*268***************************************************************************/269270static VCHIQ_STATUS_T271service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header,272VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata)273{274/* How do we ensure the callback goes to the right client?275** The service_user data points to a USER_SERVICE_T record containing276** the original callback and the user state structure, which contains a277** circular buffer for completion records.278*/279USER_SERVICE_T *user_service;280VCHIQ_SERVICE_T *service;281VCHIQ_INSTANCE_T instance;282int skip_completion = 0;283DEBUG_INITIALISE(g_state.local)284285DEBUG_TRACE(SERVICE_CALLBACK_LINE);286287service = handle_to_service(handle);288BUG_ON(!service);289user_service = (USER_SERVICE_T *)service->base.userdata;290instance = user_service->instance;291292if (!instance || instance->closing)293return VCHIQ_SUCCESS;294295vchiq_log_trace(vchiq_arm_log_level,296"service_callback - service %lx(%d,%p), reason %d, header %lx, "297"instance %lx, bulk_userdata %lx",298(unsigned long)user_service,299service->localport, user_service->userdata,300reason, (unsigned long)header,301(unsigned long)instance, (unsigned long)bulk_userdata);302303if (header && user_service->is_vchi) {304spin_lock(&msg_queue_spinlock);305while (user_service->msg_insert ==306(user_service->msg_remove + MSG_QUEUE_SIZE)) {307spin_unlock(&msg_queue_spinlock);308DEBUG_TRACE(SERVICE_CALLBACK_LINE);309DEBUG_COUNT(MSG_QUEUE_FULL_COUNT);310vchiq_log_trace(vchiq_arm_log_level,311"service_callback - msg queue full");312/* If there is no MESSAGE_AVAILABLE in the completion313** queue, add one314*/315if ((user_service->message_available_pos -316instance->completion_remove) < 0) {317VCHIQ_STATUS_T status;318vchiq_log_info(vchiq_arm_log_level,319"Inserting extra MESSAGE_AVAILABLE");320DEBUG_TRACE(SERVICE_CALLBACK_LINE);321status = add_completion(instance, reason,322NULL, user_service, bulk_userdata);323if (status != VCHIQ_SUCCESS) {324DEBUG_TRACE(SERVICE_CALLBACK_LINE);325return status;326}327}328329DEBUG_TRACE(SERVICE_CALLBACK_LINE);330if (down_interruptible(&user_service->remove_event)331!= 0) {332vchiq_log_info(vchiq_arm_log_level,333"service_callback interrupted");334DEBUG_TRACE(SERVICE_CALLBACK_LINE);335return VCHIQ_RETRY;336} else if (instance->closing) {337vchiq_log_info(vchiq_arm_log_level,338"service_callback closing");339DEBUG_TRACE(SERVICE_CALLBACK_LINE);340return VCHIQ_ERROR;341}342DEBUG_TRACE(SERVICE_CALLBACK_LINE);343spin_lock(&msg_queue_spinlock);344}345346user_service->msg_queue[user_service->msg_insert &347(MSG_QUEUE_SIZE - 1)] = header;348user_service->msg_insert++;349350/* If there is a thread waiting in DEQUEUE_MESSAGE, or if351** there is a MESSAGE_AVAILABLE in the completion queue then352** bypass the completion queue.353*/354if (((user_service->message_available_pos -355instance->completion_remove) >= 0) ||356user_service->dequeue_pending) {357user_service->dequeue_pending = 0;358skip_completion = 1;359}360361spin_unlock(&msg_queue_spinlock);362363up(&user_service->insert_event);364365header = NULL;366}367368if (skip_completion) {369DEBUG_TRACE(SERVICE_CALLBACK_LINE);370return VCHIQ_SUCCESS;371}372373DEBUG_TRACE(SERVICE_CALLBACK_LINE);374375return add_completion(instance, reason, header, user_service,376bulk_userdata);377}378379/****************************************************************************380*381* user_service_free382*383***************************************************************************/384static void385user_service_free(void *userdata)386{387USER_SERVICE_T *user_service = userdata;388389_sema_destroy(&user_service->insert_event);390_sema_destroy(&user_service->remove_event);391392kfree(user_service);393}394395/****************************************************************************396*397* close_delivered398*399***************************************************************************/400static void close_delivered(USER_SERVICE_T *user_service)401{402vchiq_log_info(vchiq_arm_log_level,403"close_delivered(handle=%x)",404user_service->service->handle);405406if (user_service->close_pending) {407/* Allow the underlying service to be culled */408unlock_service(user_service->service);409410/* Wake the user-thread blocked in close_ or remove_service */411up(&user_service->close_event);412413user_service->close_pending = 0;414}415}416417/****************************************************************************418*419* vchiq_ioctl420*421***************************************************************************/422423static int424vchiq_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag,425struct thread *td)426{427VCHIQ_INSTANCE_T instance;428VCHIQ_STATUS_T status = VCHIQ_SUCCESS;429VCHIQ_SERVICE_T *service = NULL;430int ret = 0;431int i, rc;432DEBUG_INITIALISE(g_state.local)433434if ((ret = devfs_get_cdevpriv((void**)&instance))) {435printf("vchiq_ioctl: devfs_get_cdevpriv failed: error %d\n", ret);436return (ret);437}438439/* XXXBSD: HACK! */440#define _IOC_NR(x) ((x) & 0xff)441#define _IOC_TYPE(x) IOCGROUP(x)442443vchiq_log_trace(vchiq_arm_log_level,444"vchiq_ioctl - instance %x, cmd %s, arg %p",445(unsigned int)instance,446((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) &&447(_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ?448ioctl_names[_IOC_NR(cmd)] : "<invalid>", arg);449450switch (cmd) {451case VCHIQ_IOC_SHUTDOWN:452if (!instance->connected)453break;454455/* Remove all services */456i = 0;457while ((service = next_service_by_instance(instance->state,458instance, &i)) != NULL) {459status = vchiq_remove_service(service->handle);460unlock_service(service);461if (status != VCHIQ_SUCCESS)462break;463}464service = NULL;465466if (status == VCHIQ_SUCCESS) {467/* Wake the completion thread and ask it to exit */468instance->closing = 1;469up(&instance->insert_event);470}471472break;473474case VCHIQ_IOC_CONNECT:475if (instance->connected) {476ret = -EINVAL;477break;478}479rc = lmutex_lock_interruptible(&instance->state->mutex);480if (rc != 0) {481vchiq_log_error(vchiq_arm_log_level,482"vchiq: connect: could not lock mutex for "483"state %d: %d",484instance->state->id, rc);485ret = -EINTR;486break;487}488status = vchiq_connect_internal(instance->state, instance);489lmutex_unlock(&instance->state->mutex);490491if (status == VCHIQ_SUCCESS)492instance->connected = 1;493else494vchiq_log_error(vchiq_arm_log_level,495"vchiq: could not connect: %d", status);496break;497498case VCHIQ_IOC_CREATE_SERVICE: {499VCHIQ_CREATE_SERVICE_T args;500USER_SERVICE_T *user_service = NULL;501void *userdata;502int srvstate;503504memcpy(&args, (const void*)arg, sizeof(args));505506user_service = kmalloc(sizeof(USER_SERVICE_T), GFP_KERNEL);507if (!user_service) {508ret = -ENOMEM;509break;510}511512if (args.is_open) {513if (!instance->connected) {514ret = -ENOTCONN;515kfree(user_service);516break;517}518srvstate = VCHIQ_SRVSTATE_OPENING;519} else {520srvstate =521instance->connected ?522VCHIQ_SRVSTATE_LISTENING :523VCHIQ_SRVSTATE_HIDDEN;524}525526userdata = args.params.userdata;527args.params.callback = service_callback;528args.params.userdata = user_service;529service = vchiq_add_service_internal(530instance->state,531&args.params, srvstate,532instance, user_service_free);533534if (service != NULL) {535user_service->service = service;536user_service->userdata = userdata;537user_service->instance = instance;538user_service->is_vchi = (args.is_vchi != 0);539user_service->dequeue_pending = 0;540user_service->close_pending = 0;541user_service->message_available_pos =542instance->completion_remove - 1;543user_service->msg_insert = 0;544user_service->msg_remove = 0;545_sema_init(&user_service->insert_event, 0);546_sema_init(&user_service->remove_event, 0);547_sema_init(&user_service->close_event, 0);548549if (args.is_open) {550status = vchiq_open_service_internal551(service, instance->pid);552if (status != VCHIQ_SUCCESS) {553vchiq_remove_service(service->handle);554service = NULL;555ret = (status == VCHIQ_RETRY) ?556-EINTR : -EIO;557break;558}559}560561#ifdef VCHIQ_IOCTL_DEBUG562printf("%s: [CREATE SERVICE] handle = %08x\n", __func__, service->handle);563#endif564memcpy((void *)565&(((VCHIQ_CREATE_SERVICE_T*)566arg)->handle),567(const void *)&service->handle,568sizeof(service->handle));569570service = NULL;571} else {572ret = -EEXIST;573kfree(user_service);574}575} break;576577case VCHIQ_IOC_CLOSE_SERVICE: {578VCHIQ_SERVICE_HANDLE_T handle;579580memcpy(&handle, (const void*)arg, sizeof(handle));581582#ifdef VCHIQ_IOCTL_DEBUG583printf("%s: [CLOSE SERVICE] handle = %08x\n", __func__, handle);584#endif585586service = find_service_for_instance(instance, handle);587if (service != NULL) {588USER_SERVICE_T *user_service =589(USER_SERVICE_T *)service->base.userdata;590/* close_pending is false on first entry, and when the591wait in vchiq_close_service has been interrupted. */592if (!user_service->close_pending) {593status = vchiq_close_service(service->handle);594if (status != VCHIQ_SUCCESS)595break;596}597598/* close_pending is true once the underlying service599has been closed until the client library calls the600CLOSE_DELIVERED ioctl, signalling close_event. */601if (user_service->close_pending &&602down_interruptible(&user_service->close_event))603status = VCHIQ_RETRY;604}605else606ret = -EINVAL;607} break;608609case VCHIQ_IOC_REMOVE_SERVICE: {610VCHIQ_SERVICE_HANDLE_T handle;611612memcpy(&handle, (const void*)arg, sizeof(handle));613614#ifdef VCHIQ_IOCTL_DEBUG615printf("%s: [REMOVE SERVICE] handle = %08x\n", __func__, handle);616#endif617618service = find_service_for_instance(instance, handle);619if (service != NULL) {620USER_SERVICE_T *user_service =621(USER_SERVICE_T *)service->base.userdata;622/* close_pending is false on first entry, and when the623wait in vchiq_close_service has been interrupted. */624if (!user_service->close_pending) {625status = vchiq_remove_service(service->handle);626if (status != VCHIQ_SUCCESS)627break;628}629630/* close_pending is true once the underlying service631has been closed until the client library calls the632CLOSE_DELIVERED ioctl, signalling close_event. */633if (user_service->close_pending &&634down_interruptible(&user_service->close_event))635status = VCHIQ_RETRY;636}637else638ret = -EINVAL;639} break;640641case VCHIQ_IOC_USE_SERVICE:642case VCHIQ_IOC_RELEASE_SERVICE: {643VCHIQ_SERVICE_HANDLE_T handle;644645memcpy(&handle, (const void*)arg, sizeof(handle));646647#ifdef VCHIQ_IOCTL_DEBUG648printf("%s: [%s SERVICE] handle = %08x\n", __func__,649cmd == VCHIQ_IOC_USE_SERVICE ? "USE" : "RELEASE", handle);650#endif651652service = find_service_for_instance(instance, handle);653if (service != NULL) {654status = (cmd == VCHIQ_IOC_USE_SERVICE) ?655vchiq_use_service_internal(service) :656vchiq_release_service_internal(service);657if (status != VCHIQ_SUCCESS) {658vchiq_log_error(vchiq_susp_log_level,659"%s: cmd %s returned error %d for "660"service %c%c%c%c:%8x",661__func__,662(cmd == VCHIQ_IOC_USE_SERVICE) ?663"VCHIQ_IOC_USE_SERVICE" :664"VCHIQ_IOC_RELEASE_SERVICE",665status,666VCHIQ_FOURCC_AS_4CHARS(667service->base.fourcc),668service->client_id);669ret = -EINVAL;670}671} else672ret = -EINVAL;673} break;674675case VCHIQ_IOC_QUEUE_MESSAGE: {676VCHIQ_QUEUE_MESSAGE_T args;677memcpy(&args, (const void*)arg, sizeof(args));678679#ifdef VCHIQ_IOCTL_DEBUG680printf("%s: [QUEUE MESSAGE] handle = %08x\n", __func__, args.handle);681#endif682683service = find_service_for_instance(instance, args.handle);684685if ((service != NULL) && (args.count <= MAX_ELEMENTS)) {686/* Copy elements into kernel space */687VCHIQ_ELEMENT_T elements[MAX_ELEMENTS];688if (copy_from_user(elements, args.elements,689args.count * sizeof(VCHIQ_ELEMENT_T)) == 0)690status = vchiq_queue_message691(args.handle,692elements, args.count);693else694ret = -EFAULT;695} else {696ret = -EINVAL;697}698} break;699700case VCHIQ_IOC_QUEUE_BULK_TRANSMIT:701case VCHIQ_IOC_QUEUE_BULK_RECEIVE: {702VCHIQ_QUEUE_BULK_TRANSFER_T args;703struct bulk_waiter_node *waiter = NULL;704VCHIQ_BULK_DIR_T dir =705(cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?706VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;707708memcpy(&args, (const void*)arg, sizeof(args));709710service = find_service_for_instance(instance, args.handle);711if (!service) {712ret = -EINVAL;713break;714}715716if (args.mode == VCHIQ_BULK_MODE_BLOCKING) {717waiter = kzalloc(sizeof(struct bulk_waiter_node),718GFP_KERNEL);719if (!waiter) {720ret = -ENOMEM;721break;722}723args.userdata = &waiter->bulk_waiter;724} else if (args.mode == VCHIQ_BULK_MODE_WAITING) {725struct list_head *pos;726lmutex_lock(&instance->bulk_waiter_list_mutex);727list_for_each(pos, &instance->bulk_waiter_list) {728if (list_entry(pos, struct bulk_waiter_node,729list)->pid == current->p_pid) {730waiter = list_entry(pos,731struct bulk_waiter_node,732list);733list_del(pos);734break;735}736737}738lmutex_unlock(&instance->bulk_waiter_list_mutex);739if (!waiter) {740vchiq_log_error(vchiq_arm_log_level,741"no bulk_waiter found for pid %d",742current->p_pid);743ret = -ESRCH;744break;745}746vchiq_log_info(vchiq_arm_log_level,747"found bulk_waiter %x for pid %d",748(unsigned int)waiter, current->p_pid);749args.userdata = &waiter->bulk_waiter;750}751status = vchiq_bulk_transfer752(args.handle,753VCHI_MEM_HANDLE_INVALID,754args.data, args.size,755args.userdata, args.mode,756dir);757if (!waiter)758break;759if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||760!waiter->bulk_waiter.bulk) {761if (waiter->bulk_waiter.bulk) {762/* Cancel the signal when the transfer763** completes. */764spin_lock(&bulk_waiter_spinlock);765waiter->bulk_waiter.bulk->userdata = NULL;766spin_unlock(&bulk_waiter_spinlock);767}768_sema_destroy(&waiter->bulk_waiter.event);769kfree(waiter);770} else {771const VCHIQ_BULK_MODE_T mode_waiting =772VCHIQ_BULK_MODE_WAITING;773waiter->pid = current->p_pid;774lmutex_lock(&instance->bulk_waiter_list_mutex);775list_add(&waiter->list, &instance->bulk_waiter_list);776lmutex_unlock(&instance->bulk_waiter_list_mutex);777vchiq_log_info(vchiq_arm_log_level,778"saved bulk_waiter %x for pid %d",779(unsigned int)waiter, current->p_pid);780781memcpy((void *)782&(((VCHIQ_QUEUE_BULK_TRANSFER_T *)783arg)->mode),784(const void *)&mode_waiting,785sizeof(mode_waiting));786}787} break;788789case VCHIQ_IOC_AWAIT_COMPLETION: {790VCHIQ_AWAIT_COMPLETION_T args;791int count = 0;792793DEBUG_TRACE(AWAIT_COMPLETION_LINE);794if (!instance->connected) {795ret = -ENOTCONN;796break;797}798799memcpy(&args, (const void*)arg, sizeof(args));800801lmutex_lock(&instance->completion_mutex);802803DEBUG_TRACE(AWAIT_COMPLETION_LINE);804while ((instance->completion_remove ==805instance->completion_insert)806&& !instance->closing) {807808DEBUG_TRACE(AWAIT_COMPLETION_LINE);809lmutex_unlock(&instance->completion_mutex);810rc = down_interruptible(&instance->insert_event);811lmutex_lock(&instance->completion_mutex);812if (rc != 0) {813DEBUG_TRACE(AWAIT_COMPLETION_LINE);814vchiq_log_info(vchiq_arm_log_level,815"AWAIT_COMPLETION interrupted");816ret = -EINTR;817break;818}819}820DEBUG_TRACE(AWAIT_COMPLETION_LINE);821822if (ret == 0) {823int msgbufcount = args.msgbufcount;824int remove;825826remove = instance->completion_remove;827828for (count = 0; count < args.count; count++) {829VCHIQ_COMPLETION_DATA_T *completion;830VCHIQ_SERVICE_T *service1;831USER_SERVICE_T *user_service;832VCHIQ_HEADER_T *header;833834if (remove == instance->completion_insert)835break;836837completion = &instance->completions[838remove & (MAX_COMPLETIONS - 1)];839840841/* A read memory barrier is needed to prevent842** the prefetch of a stale completion record843*/844rmb();845846service1 = completion->service_userdata;847user_service = service1->base.userdata;848completion->service_userdata =849user_service->userdata;850851header = completion->header;852if (header) {853void __user *msgbuf;854int msglen;855856msglen = header->size +857sizeof(VCHIQ_HEADER_T);858/* This must be a VCHIQ-style service */859if (args.msgbufsize < msglen) {860vchiq_log_error(861vchiq_arm_log_level,862"header %x: msgbufsize"863" %x < msglen %x",864(unsigned int)header,865args.msgbufsize,866msglen);867WARN(1, "invalid message "868"size\n");869if (count == 0)870ret = -EMSGSIZE;871break;872}873if (msgbufcount <= 0)874/* Stall here for lack of a875** buffer for the message. */876break;877/* Get the pointer from user space */878msgbufcount--;879if (copy_from_user(&msgbuf,880(const void __user *)881&args.msgbufs[msgbufcount],882sizeof(msgbuf)) != 0) {883if (count == 0)884ret = -EFAULT;885break;886}887888/* Copy the message to user space */889if (copy_to_user(msgbuf, header,890msglen) != 0) {891if (count == 0)892ret = -EFAULT;893break;894}895896/* Now it has been copied, the message897** can be released. */898vchiq_release_message(service1->handle,899header);900901/* The completion must point to the902** msgbuf. */903completion->header = msgbuf;904}905906if ((completion->reason ==907VCHIQ_SERVICE_CLOSED) &&908!instance->use_close_delivered)909unlock_service(service1);910911if (copy_to_user((void __user *)(912(size_t)args.buf +913count * sizeof(VCHIQ_COMPLETION_DATA_T)),914completion,915sizeof(VCHIQ_COMPLETION_DATA_T)) != 0) {916if (ret == 0)917ret = -EFAULT;918break;919}920921/* Ensure that the above copy has completed922** before advancing the remove pointer. */923mb();924925instance->completion_remove = ++remove;926}927928if (msgbufcount != args.msgbufcount) {929memcpy((void __user *)930&((VCHIQ_AWAIT_COMPLETION_T *)arg)->931msgbufcount,932&msgbufcount,933sizeof(msgbufcount));934}935936if (count != args.count)937{938memcpy((void __user *)939&((VCHIQ_AWAIT_COMPLETION_T *)arg)->count,940&count, sizeof(count));941}942}943944if (count != 0)945up(&instance->remove_event);946947if ((ret == 0) && instance->closing)948ret = -ENOTCONN;949/*950* XXXBSD: ioctl return codes are not negative as in linux, so951* we can not indicate success with positive number of passed952* messages953*/954if (ret > 0)955ret = 0;956957lmutex_unlock(&instance->completion_mutex);958DEBUG_TRACE(AWAIT_COMPLETION_LINE);959} break;960961case VCHIQ_IOC_DEQUEUE_MESSAGE: {962VCHIQ_DEQUEUE_MESSAGE_T args;963USER_SERVICE_T *user_service;964VCHIQ_HEADER_T *header;965966DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);967memcpy(&args, (const void*)arg, sizeof(args));968service = find_service_for_instance(instance, args.handle);969if (!service) {970ret = -EINVAL;971break;972}973user_service = (USER_SERVICE_T *)service->base.userdata;974if (user_service->is_vchi == 0) {975ret = -EINVAL;976break;977}978979spin_lock(&msg_queue_spinlock);980if (user_service->msg_remove == user_service->msg_insert) {981if (!args.blocking) {982spin_unlock(&msg_queue_spinlock);983DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);984ret = -EWOULDBLOCK;985break;986}987user_service->dequeue_pending = 1;988do {989spin_unlock(&msg_queue_spinlock);990DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);991if (down_interruptible(992&user_service->insert_event) != 0) {993vchiq_log_info(vchiq_arm_log_level,994"DEQUEUE_MESSAGE interrupted");995ret = -EINTR;996break;997}998spin_lock(&msg_queue_spinlock);999} while (user_service->msg_remove ==1000user_service->msg_insert);10011002if (ret)1003break;1004}10051006BUG_ON((int)(user_service->msg_insert -1007user_service->msg_remove) < 0);10081009header = user_service->msg_queue[user_service->msg_remove &1010(MSG_QUEUE_SIZE - 1)];1011user_service->msg_remove++;1012spin_unlock(&msg_queue_spinlock);10131014up(&user_service->remove_event);1015if (header == NULL)1016ret = -ENOTCONN;1017else if (header->size <= args.bufsize) {1018/* Copy to user space if msgbuf is not NULL */1019if ((args.buf == NULL) ||1020(copy_to_user((void __user *)args.buf,1021header->data,1022header->size) == 0)) {1023args.bufsize = header->size;1024memcpy((void *)arg, &args,1025sizeof(args));1026vchiq_release_message(1027service->handle,1028header);1029} else1030ret = -EFAULT;1031} else {1032vchiq_log_error(vchiq_arm_log_level,1033"header %x: bufsize %x < size %x",1034(unsigned int)header, args.bufsize,1035header->size);1036WARN(1, "invalid size\n");1037ret = -EMSGSIZE;1038}1039DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);1040} break;10411042case VCHIQ_IOC_GET_CLIENT_ID: {1043VCHIQ_SERVICE_HANDLE_T handle;10441045memcpy(&handle, (const void*)arg, sizeof(handle));10461047ret = vchiq_get_client_id(handle);1048} break;10491050case VCHIQ_IOC_GET_CONFIG: {1051VCHIQ_GET_CONFIG_T args;1052VCHIQ_CONFIG_T config;10531054memcpy(&args, (const void*)arg, sizeof(args));1055if (args.config_size > sizeof(config)) {1056ret = -EINVAL;1057break;1058}1059status = vchiq_get_config(instance, args.config_size, &config);1060if (status == VCHIQ_SUCCESS) {1061if (copy_to_user((void __user *)args.pconfig,1062&config, args.config_size) != 0) {1063ret = -EFAULT;1064break;1065}1066}1067} break;10681069case VCHIQ_IOC_SET_SERVICE_OPTION: {1070VCHIQ_SET_SERVICE_OPTION_T args;10711072memcpy(&args, (const void*)arg, sizeof(args));10731074service = find_service_for_instance(instance, args.handle);1075if (!service) {1076ret = -EINVAL;1077break;1078}10791080status = vchiq_set_service_option(1081args.handle, args.option, args.value);1082} break;10831084case VCHIQ_IOC_DUMP_PHYS_MEM: {1085VCHIQ_DUMP_MEM_T args;10861087memcpy(&args, (const void*)arg, sizeof(args));1088printf("IMPLEMENT ME: %s:%d\n", __FILE__, __LINE__);1089#if 01090dump_phys_mem(args.virt_addr, args.num_bytes);1091#endif1092} break;10931094case VCHIQ_IOC_LIB_VERSION: {1095unsigned int lib_version = (unsigned int)arg;10961097if (lib_version < VCHIQ_VERSION_MIN)1098ret = -EINVAL;1099else if (lib_version >= VCHIQ_VERSION_CLOSE_DELIVERED)1100instance->use_close_delivered = 1;1101} break;11021103case VCHIQ_IOC_CLOSE_DELIVERED: {1104VCHIQ_SERVICE_HANDLE_T handle;1105memcpy(&handle, (const void*)arg, sizeof(handle));11061107service = find_closed_service_for_instance(instance, handle);1108if (service != NULL) {1109USER_SERVICE_T *user_service =1110(USER_SERVICE_T *)service->base.userdata;1111close_delivered(user_service);1112}1113else1114ret = -EINVAL;1115} break;11161117default:1118ret = -ENOTTY;1119break;1120}11211122if (service)1123unlock_service(service);11241125if (ret == 0) {1126if (status == VCHIQ_ERROR)1127ret = -EIO;1128else if (status == VCHIQ_RETRY)1129ret = -EINTR;1130}11311132if ((status == VCHIQ_SUCCESS) && (ret < 0) && (ret != -EINTR) &&1133(ret != -EWOULDBLOCK))1134vchiq_log_info(vchiq_arm_log_level,1135" ioctl instance %lx, cmd %s -> status %d, %d",1136(unsigned long)instance,1137(_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ?1138ioctl_names[_IOC_NR(cmd)] :1139"<invalid>",1140status, ret);1141else1142vchiq_log_trace(vchiq_arm_log_level,1143" ioctl instance %lx, cmd %s -> status %d, %d",1144(unsigned long)instance,1145(_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ?1146ioctl_names[_IOC_NR(cmd)] :1147"<invalid>",1148status, ret);11491150/* XXXBSD: report BSD-style error to userland */1151if (ret < 0)1152ret = -ret;11531154return ret;1155}11561157static void1158instance_dtr(void *data)1159{11601161kfree(data);1162}11631164/****************************************************************************1165*1166* vchiq_open1167*1168***************************************************************************/11691170static int1171vchiq_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td)1172{1173vchiq_log_info(vchiq_arm_log_level, "vchiq_open");1174/* XXXBSD: do we really need this check? */1175if (1) {1176VCHIQ_STATE_T *state = vchiq_get_state();1177VCHIQ_INSTANCE_T instance;11781179if (!state) {1180vchiq_log_error(vchiq_arm_log_level,1181"vchiq has no connection to VideoCore");1182return -ENOTCONN;1183}11841185instance = kmalloc(sizeof(*instance), GFP_KERNEL);1186if (!instance)1187return -ENOMEM;11881189instance->state = state;1190/* XXXBSD: PID or thread ID? */1191instance->pid = td->td_proc->p_pid;11921193#ifdef notyet1194ret = vchiq_proc_add_instance(instance);1195if (ret != 0) {1196kfree(instance);1197return ret;1198}1199#endif12001201_sema_init(&instance->insert_event, 0);1202_sema_init(&instance->remove_event, 0);1203lmutex_init(&instance->completion_mutex);1204lmutex_init(&instance->bulk_waiter_list_mutex);1205INIT_LIST_HEAD(&instance->bulk_waiter_list);12061207devfs_set_cdevpriv(instance, instance_dtr);1208}1209else {1210vchiq_log_error(vchiq_arm_log_level,1211"Unknown minor device");1212return -ENXIO;1213}12141215return 0;1216}12171218/****************************************************************************1219*1220* vchiq_release1221*1222***************************************************************************/12231224static int1225vchiq_close(struct cdev *dev, int flags __unused, int fmt __unused,1226struct thread *td)1227{1228int ret = 0;1229if (1) {1230VCHIQ_INSTANCE_T instance;1231VCHIQ_STATE_T *state = vchiq_get_state();1232VCHIQ_SERVICE_T *service;1233int i;12341235if ((ret = devfs_get_cdevpriv((void**)&instance))) {1236printf("devfs_get_cdevpriv failed: error %d\n", ret);1237return (ret);1238}12391240vchiq_log_info(vchiq_arm_log_level,1241"vchiq_release: instance=%lx",1242(unsigned long)instance);12431244if (!state) {1245ret = -EPERM;1246goto out;1247}12481249/* Ensure videocore is awake to allow termination. */1250vchiq_use_internal(instance->state, NULL,1251USE_TYPE_VCHIQ);12521253lmutex_lock(&instance->completion_mutex);12541255/* Wake the completion thread and ask it to exit */1256instance->closing = 1;1257up(&instance->insert_event);12581259lmutex_unlock(&instance->completion_mutex);12601261/* Wake the slot handler if the completion queue is full. */1262up(&instance->remove_event);12631264/* Mark all services for termination... */1265i = 0;1266while ((service = next_service_by_instance(state, instance,1267&i)) != NULL) {1268USER_SERVICE_T *user_service = service->base.userdata;12691270/* Wake the slot handler if the msg queue is full. */1271up(&user_service->remove_event);12721273vchiq_terminate_service_internal(service);1274unlock_service(service);1275}12761277/* ...and wait for them to die */1278i = 0;1279while ((service = next_service_by_instance(state, instance, &i))1280!= NULL) {1281USER_SERVICE_T *user_service = service->base.userdata;12821283down(&service->remove_event);12841285BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE);12861287spin_lock(&msg_queue_spinlock);12881289while (user_service->msg_remove !=1290user_service->msg_insert) {1291VCHIQ_HEADER_T *header = user_service->1292msg_queue[user_service->msg_remove &1293(MSG_QUEUE_SIZE - 1)];1294user_service->msg_remove++;1295spin_unlock(&msg_queue_spinlock);12961297if (header)1298vchiq_release_message(1299service->handle,1300header);1301spin_lock(&msg_queue_spinlock);1302}13031304spin_unlock(&msg_queue_spinlock);13051306unlock_service(service);1307}13081309/* Release any closed services */1310while (instance->completion_remove !=1311instance->completion_insert) {1312VCHIQ_COMPLETION_DATA_T *completion;1313VCHIQ_SERVICE_T *service1;1314completion = &instance->completions[1315instance->completion_remove &1316(MAX_COMPLETIONS - 1)];1317service1 = completion->service_userdata;1318if (completion->reason == VCHIQ_SERVICE_CLOSED)1319{1320USER_SERVICE_T *user_service =1321service->base.userdata;13221323/* Wake any blocked user-thread */1324if (instance->use_close_delivered)1325up(&user_service->close_event);1326unlock_service(service1);1327}1328instance->completion_remove++;1329}13301331/* Release the PEER service count. */1332vchiq_release_internal(instance->state, NULL);13331334{1335struct list_head *pos, *next;1336list_for_each_safe(pos, next,1337&instance->bulk_waiter_list) {1338struct bulk_waiter_node *waiter;1339waiter = list_entry(pos,1340struct bulk_waiter_node,1341list);1342list_del(pos);1343vchiq_log_info(vchiq_arm_log_level,1344"bulk_waiter - cleaned up %x "1345"for pid %d",1346(unsigned int)waiter, waiter->pid);1347_sema_destroy(&waiter->bulk_waiter.event);1348kfree(waiter);1349}1350}13511352}1353else {1354vchiq_log_error(vchiq_arm_log_level,1355"Unknown minor device");1356ret = -ENXIO;1357}13581359out:1360return ret;1361}13621363/****************************************************************************1364*1365* vchiq_dump1366*1367***************************************************************************/13681369void1370vchiq_dump(void *dump_context, const char *str, int len)1371{1372DUMP_CONTEXT_T *context = (DUMP_CONTEXT_T *)dump_context;13731374if (context->actual < context->space) {1375int copy_bytes;1376if (context->offset > 0) {1377int skip_bytes = min(len, (int)context->offset);1378str += skip_bytes;1379len -= skip_bytes;1380context->offset -= skip_bytes;1381if (context->offset > 0)1382return;1383}1384copy_bytes = min(len, (int)(context->space - context->actual));1385if (copy_bytes == 0)1386return;1387memcpy(context->buf + context->actual, str, copy_bytes);1388context->actual += copy_bytes;1389len -= copy_bytes;13901391/* If tne terminating NUL is included in the length, then it1392** marks the end of a line and should be replaced with a1393** carriage return. */1394if ((len == 0) && (str[copy_bytes - 1] == '\0')) {1395char cr = '\n';1396memcpy(context->buf + context->actual - 1, &cr, 1);1397}1398}1399}14001401/****************************************************************************1402*1403* vchiq_dump_platform_instance_state1404*1405***************************************************************************/14061407void1408vchiq_dump_platform_instances(void *dump_context)1409{1410VCHIQ_STATE_T *state = vchiq_get_state();1411char buf[80];1412int len;1413int i;14141415/* There is no list of instances, so instead scan all services,1416marking those that have been dumped. */14171418for (i = 0; i < state->unused_service; i++) {1419VCHIQ_SERVICE_T *service = state->services[i];1420VCHIQ_INSTANCE_T instance;14211422if (service && (service->base.callback == service_callback)) {1423instance = service->instance;1424if (instance)1425instance->mark = 0;1426}1427}14281429for (i = 0; i < state->unused_service; i++) {1430VCHIQ_SERVICE_T *service = state->services[i];1431VCHIQ_INSTANCE_T instance;14321433if (service && (service->base.callback == service_callback)) {1434instance = service->instance;1435if (instance && !instance->mark) {1436len = snprintf(buf, sizeof(buf),1437"Instance %x: pid %d,%s completions "1438"%d/%d",1439(unsigned int)instance, instance->pid,1440instance->connected ? " connected, " :1441"",1442instance->completion_insert -1443instance->completion_remove,1444MAX_COMPLETIONS);14451446vchiq_dump(dump_context, buf, len + 1);14471448instance->mark = 1;1449}1450}1451}1452}14531454/****************************************************************************1455*1456* vchiq_dump_platform_service_state1457*1458***************************************************************************/14591460void1461vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service)1462{1463USER_SERVICE_T *user_service = (USER_SERVICE_T *)service->base.userdata;1464char buf[80];1465int len;14661467len = snprintf(buf, sizeof(buf), " instance %x",1468(unsigned int)service->instance);14691470if ((service->base.callback == service_callback) &&1471user_service->is_vchi) {1472len += snprintf(buf + len, sizeof(buf) - len,1473", %d/%d messages",1474user_service->msg_insert - user_service->msg_remove,1475MSG_QUEUE_SIZE);14761477if (user_service->dequeue_pending)1478len += snprintf(buf + len, sizeof(buf) - len,1479" (dequeue pending)");1480}14811482vchiq_dump(dump_context, buf, len + 1);1483}14841485#ifdef notyet1486/****************************************************************************1487*1488* dump_user_mem1489*1490***************************************************************************/14911492static void1493dump_phys_mem(void *virt_addr, uint32_t num_bytes)1494{1495int rc;1496uint8_t *end_virt_addr = virt_addr + num_bytes;1497int num_pages;1498int offset;1499int end_offset;1500int page_idx;1501int prev_idx;1502struct page *page;1503struct page **pages;1504uint8_t *kmapped_virt_ptr;15051506/* Align virtAddr and endVirtAddr to 16 byte boundaries. */15071508virt_addr = (void *)((unsigned long)virt_addr & ~0x0fuL);1509end_virt_addr = (void *)(((unsigned long)end_virt_addr + 15uL) &1510~0x0fuL);15111512offset = (int)(long)virt_addr & (PAGE_SIZE - 1);1513end_offset = (int)(long)end_virt_addr & (PAGE_SIZE - 1);15141515num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE;15161517pages = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL);1518if (pages == NULL) {1519vchiq_log_error(vchiq_arm_log_level,1520"Unable to allocation memory for %d pages\n",1521num_pages);1522return;1523}15241525down_read(¤t->mm->mmap_sem);1526rc = get_user_pages(current, /* task */1527current->mm, /* mm */1528(unsigned long)virt_addr, /* start */1529num_pages, /* len */15300, /* write */15310, /* force */1532pages, /* pages (array of page pointers) */1533NULL); /* vmas */1534up_read(¤t->mm->mmap_sem);15351536prev_idx = -1;1537page = NULL;15381539while (offset < end_offset) {15401541int page_offset = offset % PAGE_SIZE;1542page_idx = offset / PAGE_SIZE;15431544if (page_idx != prev_idx) {15451546if (page != NULL)1547kunmap(page);1548page = pages[page_idx];1549kmapped_virt_ptr = kmap(page);15501551prev_idx = page_idx;1552}15531554if (vchiq_arm_log_level >= VCHIQ_LOG_TRACE)1555vchiq_log_dump_mem("ph",1556(uint32_t)(unsigned long)&kmapped_virt_ptr[1557page_offset],1558&kmapped_virt_ptr[page_offset], 16);15591560offset += 16;1561}1562if (page != NULL)1563kunmap(page);15641565for (page_idx = 0; page_idx < num_pages; page_idx++)1566page_cache_release(pages[page_idx]);15671568kfree(pages);1569}15701571/****************************************************************************1572*1573* vchiq_read1574*1575***************************************************************************/15761577static ssize_t1578vchiq_read(struct file *file, char __user *buf,1579size_t count, loff_t *ppos)1580{1581DUMP_CONTEXT_T context;1582context.buf = buf;1583context.actual = 0;1584context.space = count;1585context.offset = *ppos;15861587vchiq_dump_state(&context, &g_state);15881589*ppos += context.actual;15901591return context.actual;1592}1593#endif15941595VCHIQ_STATE_T *1596vchiq_get_state(void)1597{15981599if (g_state.remote == NULL)1600printk(KERN_ERR "%s: g_state.remote == NULL\n", __func__);1601else if (g_state.remote->initialised != 1)1602printk(KERN_NOTICE "%s: g_state.remote->initialised != 1 (%d)\n",1603__func__, g_state.remote->initialised);16041605return ((g_state.remote != NULL) &&1606(g_state.remote->initialised == 1)) ? &g_state : NULL;1607}16081609/*1610* Autosuspend related functionality1611*/16121613int1614vchiq_videocore_wanted(VCHIQ_STATE_T *state)1615{1616VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);1617if (!arm_state)1618/* autosuspend not supported - always return wanted */1619return 1;1620else if (arm_state->blocked_count)1621return 1;1622else if (!arm_state->videocore_use_count)1623/* usage count zero - check for override unless we're forcing */1624if (arm_state->resume_blocked)1625return 0;1626else1627return vchiq_platform_videocore_wanted(state);1628else1629/* non-zero usage count - videocore still required */1630return 1;1631}16321633static VCHIQ_STATUS_T1634vchiq_keepalive_vchiq_callback(VCHIQ_REASON_T reason,1635VCHIQ_HEADER_T *header,1636VCHIQ_SERVICE_HANDLE_T service_user,1637void *bulk_user)1638{1639vchiq_log_error(vchiq_susp_log_level,1640"%s callback reason %d", __func__, reason);1641return 0;1642}16431644static int1645vchiq_keepalive_thread_func(void *v)1646{1647VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;1648VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);16491650VCHIQ_STATUS_T status;1651VCHIQ_INSTANCE_T instance;1652VCHIQ_SERVICE_HANDLE_T ka_handle;16531654VCHIQ_SERVICE_PARAMS_T params = {1655.fourcc = VCHIQ_MAKE_FOURCC('K', 'E', 'E', 'P'),1656.callback = vchiq_keepalive_vchiq_callback,1657.version = KEEPALIVE_VER,1658.version_min = KEEPALIVE_VER_MIN1659};16601661status = vchiq_initialise(&instance);1662if (status != VCHIQ_SUCCESS) {1663vchiq_log_error(vchiq_susp_log_level,1664"%s vchiq_initialise failed %d", __func__, status);1665goto exit;1666}16671668status = vchiq_connect(instance);1669if (status != VCHIQ_SUCCESS) {1670vchiq_log_error(vchiq_susp_log_level,1671"%s vchiq_connect failed %d", __func__, status);1672goto shutdown;1673}16741675status = vchiq_add_service(instance, ¶ms, &ka_handle);1676if (status != VCHIQ_SUCCESS) {1677vchiq_log_error(vchiq_susp_log_level,1678"%s vchiq_open_service failed %d", __func__, status);1679goto shutdown;1680}16811682while (1) {1683long rc = 0, uc = 0;1684if (wait_for_completion_interruptible(&arm_state->ka_evt)1685!= 0) {1686vchiq_log_error(vchiq_susp_log_level,1687"%s interrupted", __func__);1688flush_signals(current);1689continue;1690}16911692/* read and clear counters. Do release_count then use_count to1693* prevent getting more releases than uses */1694rc = atomic_xchg(&arm_state->ka_release_count, 0);1695uc = atomic_xchg(&arm_state->ka_use_count, 0);16961697/* Call use/release service the requisite number of times.1698* Process use before release so use counts don't go negative */1699while (uc--) {1700atomic_inc(&arm_state->ka_use_ack_count);1701status = vchiq_use_service(ka_handle);1702if (status != VCHIQ_SUCCESS) {1703vchiq_log_error(vchiq_susp_log_level,1704"%s vchiq_use_service error %d",1705__func__, status);1706}1707}1708while (rc--) {1709status = vchiq_release_service(ka_handle);1710if (status != VCHIQ_SUCCESS) {1711vchiq_log_error(vchiq_susp_log_level,1712"%s vchiq_release_service error %d",1713__func__, status);1714}1715}1716}17171718shutdown:1719vchiq_shutdown(instance);1720exit:1721return 0;1722}17231724VCHIQ_STATUS_T1725vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state)1726{1727VCHIQ_STATUS_T status = VCHIQ_SUCCESS;17281729if (arm_state) {1730rwlock_init(&arm_state->susp_res_lock);17311732init_completion(&arm_state->ka_evt);1733atomic_set(&arm_state->ka_use_count, 0);1734atomic_set(&arm_state->ka_use_ack_count, 0);1735atomic_set(&arm_state->ka_release_count, 0);17361737init_completion(&arm_state->vc_suspend_complete);17381739init_completion(&arm_state->vc_resume_complete);1740/* Initialise to 'done' state. We only want to block on resume1741* completion while videocore is suspended. */1742set_resume_state(arm_state, VC_RESUME_RESUMED);17431744init_completion(&arm_state->resume_blocker);1745/* Initialise to 'done' state. We only want to block on this1746* completion while resume is blocked */1747complete_all(&arm_state->resume_blocker);17481749init_completion(&arm_state->blocked_blocker);1750/* Initialise to 'done' state. We only want to block on this1751* completion while things are waiting on the resume blocker */1752complete_all(&arm_state->blocked_blocker);17531754arm_state->suspend_timer_timeout = SUSPEND_TIMER_TIMEOUT_MS;1755arm_state->suspend_timer_running = 0;1756vchiq_init_timer(&arm_state->suspend_timer);1757arm_state->suspend_timer.data = (unsigned long)(state);1758arm_state->suspend_timer.function = suspend_timer_callback;17591760arm_state->first_connect = 0;17611762}1763return status;1764}17651766/*1767** Functions to modify the state variables;1768** set_suspend_state1769** set_resume_state1770**1771** There are more state variables than we might like, so ensure they remain in1772** step. Suspend and resume state are maintained separately, since most of1773** these state machines can operate independently. However, there are a few1774** states where state transitions in one state machine cause a reset to the1775** other state machine. In addition, there are some completion events which1776** need to occur on state machine reset and end-state(s), so these are also1777** dealt with in these functions.1778**1779** In all states we set the state variable according to the input, but in some1780** cases we perform additional steps outlined below;1781**1782** VC_SUSPEND_IDLE - Initialise the suspend completion at the same time.1783** The suspend completion is completed after any suspend1784** attempt. When we reset the state machine we also reset1785** the completion. This reset occurs when videocore is1786** resumed, and also if we initiate suspend after a suspend1787** failure.1788**1789** VC_SUSPEND_IN_PROGRESS - This state is considered the point of no return for1790** suspend - ie from this point on we must try to suspend1791** before resuming can occur. We therefore also reset the1792** resume state machine to VC_RESUME_IDLE in this state.1793**1794** VC_SUSPEND_SUSPENDED - Suspend has completed successfully. Also call1795** complete_all on the suspend completion to notify1796** anything waiting for suspend to happen.1797**1798** VC_SUSPEND_REJECTED - Videocore rejected suspend. Videocore will also1799** initiate resume, so no need to alter resume state.1800** We call complete_all on the suspend completion to notify1801** of suspend rejection.1802**1803** VC_SUSPEND_FAILED - We failed to initiate videocore suspend. We notify the1804** suspend completion and reset the resume state machine.1805**1806** VC_RESUME_IDLE - Initialise the resume completion at the same time. The1807** resume completion is in its 'done' state whenever1808** videcore is running. Therfore, the VC_RESUME_IDLE state1809** implies that videocore is suspended.1810** Hence, any thread which needs to wait until videocore is1811** running can wait on this completion - it will only block1812** if videocore is suspended.1813**1814** VC_RESUME_RESUMED - Resume has completed successfully. Videocore is running.1815** Call complete_all on the resume completion to unblock1816** any threads waiting for resume. Also reset the suspend1817** state machine to it's idle state.1818**1819** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists.1820*/18211822void1823set_suspend_state(VCHIQ_ARM_STATE_T *arm_state,1824enum vc_suspend_status new_state)1825{1826/* set the state in all cases */1827arm_state->vc_suspend_state = new_state;18281829/* state specific additional actions */1830switch (new_state) {1831case VC_SUSPEND_FORCE_CANCELED:1832complete_all(&arm_state->vc_suspend_complete);1833break;1834case VC_SUSPEND_REJECTED:1835complete_all(&arm_state->vc_suspend_complete);1836break;1837case VC_SUSPEND_FAILED:1838complete_all(&arm_state->vc_suspend_complete);1839arm_state->vc_resume_state = VC_RESUME_RESUMED;1840complete_all(&arm_state->vc_resume_complete);1841break;1842case VC_SUSPEND_IDLE:1843/* TODO: reinit_completion */1844INIT_COMPLETION(arm_state->vc_suspend_complete);1845break;1846case VC_SUSPEND_REQUESTED:1847break;1848case VC_SUSPEND_IN_PROGRESS:1849set_resume_state(arm_state, VC_RESUME_IDLE);1850break;1851case VC_SUSPEND_SUSPENDED:1852complete_all(&arm_state->vc_suspend_complete);1853break;1854default:1855BUG();1856break;1857}1858}18591860void1861set_resume_state(VCHIQ_ARM_STATE_T *arm_state,1862enum vc_resume_status new_state)1863{1864/* set the state in all cases */1865arm_state->vc_resume_state = new_state;18661867/* state specific additional actions */1868switch (new_state) {1869case VC_RESUME_FAILED:1870break;1871case VC_RESUME_IDLE:1872/* TODO: reinit_completion */1873INIT_COMPLETION(arm_state->vc_resume_complete);1874break;1875case VC_RESUME_REQUESTED:1876break;1877case VC_RESUME_IN_PROGRESS:1878break;1879case VC_RESUME_RESUMED:1880complete_all(&arm_state->vc_resume_complete);1881set_suspend_state(arm_state, VC_SUSPEND_IDLE);1882break;1883default:1884BUG();1885break;1886}1887}188818891890/* should be called with the write lock held */1891inline void1892start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state)1893{1894vchiq_del_timer(&arm_state->suspend_timer);1895arm_state->suspend_timer.expires = jiffies +1896msecs_to_jiffies(arm_state->1897suspend_timer_timeout);1898vchiq_add_timer(&arm_state->suspend_timer);1899arm_state->suspend_timer_running = 1;1900}19011902/* should be called with the write lock held */1903static inline void1904stop_suspend_timer(VCHIQ_ARM_STATE_T *arm_state)1905{1906if (arm_state->suspend_timer_running) {1907vchiq_del_timer(&arm_state->suspend_timer);1908arm_state->suspend_timer_running = 0;1909}1910}19111912static inline int1913need_resume(VCHIQ_STATE_T *state)1914{1915VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);1916return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) &&1917(arm_state->vc_resume_state < VC_RESUME_REQUESTED) &&1918vchiq_videocore_wanted(state);1919}19201921static int1922block_resume(VCHIQ_ARM_STATE_T *arm_state)1923{1924int status = VCHIQ_SUCCESS;1925const unsigned long timeout_val =1926msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS);1927int resume_count = 0;19281929/* Allow any threads which were blocked by the last force suspend to1930* complete if they haven't already. Only give this one shot; if1931* blocked_count is incremented after blocked_blocker is completed1932* (which only happens when blocked_count hits 0) then those threads1933* will have to wait until next time around */1934if (arm_state->blocked_count) {1935/* TODO: reinit_completion */1936INIT_COMPLETION(arm_state->blocked_blocker);1937write_unlock_bh(&arm_state->susp_res_lock);1938vchiq_log_info(vchiq_susp_log_level, "%s wait for previously "1939"blocked clients", __func__);1940if (wait_for_completion_interruptible_timeout(1941&arm_state->blocked_blocker, timeout_val)1942<= 0) {1943vchiq_log_error(vchiq_susp_log_level, "%s wait for "1944"previously blocked clients failed" , __func__);1945status = VCHIQ_ERROR;1946write_lock_bh(&arm_state->susp_res_lock);1947goto out;1948}1949vchiq_log_info(vchiq_susp_log_level, "%s previously blocked "1950"clients resumed", __func__);1951write_lock_bh(&arm_state->susp_res_lock);1952}19531954/* We need to wait for resume to complete if it's in process */1955while (arm_state->vc_resume_state != VC_RESUME_RESUMED &&1956arm_state->vc_resume_state > VC_RESUME_IDLE) {1957if (resume_count > 1) {1958status = VCHIQ_ERROR;1959vchiq_log_error(vchiq_susp_log_level, "%s waited too "1960"many times for resume" , __func__);1961goto out;1962}1963write_unlock_bh(&arm_state->susp_res_lock);1964vchiq_log_info(vchiq_susp_log_level, "%s wait for resume",1965__func__);1966if (wait_for_completion_interruptible_timeout(1967&arm_state->vc_resume_complete, timeout_val)1968<= 0) {1969vchiq_log_error(vchiq_susp_log_level, "%s wait for "1970"resume failed (%s)", __func__,1971resume_state_names[arm_state->vc_resume_state +1972VC_RESUME_NUM_OFFSET]);1973status = VCHIQ_ERROR;1974write_lock_bh(&arm_state->susp_res_lock);1975goto out;1976}1977vchiq_log_info(vchiq_susp_log_level, "%s resumed", __func__);1978write_lock_bh(&arm_state->susp_res_lock);1979resume_count++;1980}1981/* TODO: reinit_completion */1982INIT_COMPLETION(arm_state->resume_blocker);1983arm_state->resume_blocked = 1;19841985out:1986return status;1987}19881989static inline void1990unblock_resume(VCHIQ_ARM_STATE_T *arm_state)1991{1992complete_all(&arm_state->resume_blocker);1993arm_state->resume_blocked = 0;1994}19951996/* Initiate suspend via slot handler. Should be called with the write lock1997* held */1998VCHIQ_STATUS_T1999vchiq_arm_vcsuspend(VCHIQ_STATE_T *state)2000{2001VCHIQ_STATUS_T status = VCHIQ_ERROR;2002VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);20032004if (!arm_state)2005goto out;20062007vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);2008status = VCHIQ_SUCCESS;200920102011switch (arm_state->vc_suspend_state) {2012case VC_SUSPEND_REQUESTED:2013vchiq_log_info(vchiq_susp_log_level, "%s: suspend already "2014"requested", __func__);2015break;2016case VC_SUSPEND_IN_PROGRESS:2017vchiq_log_info(vchiq_susp_log_level, "%s: suspend already in "2018"progress", __func__);2019break;20202021default:2022/* We don't expect to be in other states, so log but continue2023* anyway */2024vchiq_log_error(vchiq_susp_log_level,2025"%s unexpected suspend state %s", __func__,2026suspend_state_names[arm_state->vc_suspend_state +2027VC_SUSPEND_NUM_OFFSET]);2028/* fall through */2029case VC_SUSPEND_REJECTED:2030case VC_SUSPEND_FAILED:2031/* Ensure any idle state actions have been run */2032set_suspend_state(arm_state, VC_SUSPEND_IDLE);2033/* fall through */2034case VC_SUSPEND_IDLE:2035vchiq_log_info(vchiq_susp_log_level,2036"%s: suspending", __func__);2037set_suspend_state(arm_state, VC_SUSPEND_REQUESTED);2038/* kick the slot handler thread to initiate suspend */2039request_poll(state, NULL, 0);2040break;2041}20422043out:2044vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status);2045return status;2046}20472048void2049vchiq_platform_check_suspend(VCHIQ_STATE_T *state)2050{2051VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2052int susp = 0;20532054if (!arm_state)2055goto out;20562057vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);20582059write_lock_bh(&arm_state->susp_res_lock);2060if (arm_state->vc_suspend_state == VC_SUSPEND_REQUESTED &&2061arm_state->vc_resume_state == VC_RESUME_RESUMED) {2062set_suspend_state(arm_state, VC_SUSPEND_IN_PROGRESS);2063susp = 1;2064}2065write_unlock_bh(&arm_state->susp_res_lock);20662067if (susp)2068vchiq_platform_suspend(state);20692070out:2071vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__);2072return;2073}207420752076static void2077output_timeout_error(VCHIQ_STATE_T *state)2078{2079VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2080char service_err[50] = "";2081int vc_use_count = arm_state->videocore_use_count;2082int active_services = state->unused_service;2083int i;20842085if (!arm_state->videocore_use_count) {2086snprintf(service_err, 50, " Videocore usecount is 0");2087goto output_msg;2088}2089for (i = 0; i < active_services; i++) {2090VCHIQ_SERVICE_T *service_ptr = state->services[i];2091if (service_ptr && service_ptr->service_use_count &&2092(service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) {2093snprintf(service_err, 50, " %c%c%c%c(%8x) service has "2094"use count %d%s", VCHIQ_FOURCC_AS_4CHARS(2095service_ptr->base.fourcc),2096service_ptr->client_id,2097service_ptr->service_use_count,2098service_ptr->service_use_count ==2099vc_use_count ? "" : " (+ more)");2100break;2101}2102}21032104output_msg:2105vchiq_log_error(vchiq_susp_log_level,2106"timed out waiting for vc suspend (%d).%s",2107arm_state->autosuspend_override, service_err);21082109}21102111/* Try to get videocore into suspended state, regardless of autosuspend state.2112** We don't actually force suspend, since videocore may get into a bad state2113** if we force suspend at a bad time. Instead, we wait for autosuspend to2114** determine a good point to suspend. If this doesn't happen within 100ms we2115** report failure.2116**2117** Returns VCHIQ_SUCCESS if videocore suspended successfully, VCHIQ_RETRY if2118** videocore failed to suspend in time or VCHIQ_ERROR if interrupted.2119*/2120VCHIQ_STATUS_T2121vchiq_arm_force_suspend(VCHIQ_STATE_T *state)2122{2123VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2124VCHIQ_STATUS_T status = VCHIQ_ERROR;2125long rc = 0;2126int repeat = -1;21272128if (!arm_state)2129goto out;21302131vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);21322133write_lock_bh(&arm_state->susp_res_lock);21342135status = block_resume(arm_state);2136if (status != VCHIQ_SUCCESS)2137goto unlock;2138if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) {2139/* Already suspended - just block resume and exit */2140vchiq_log_info(vchiq_susp_log_level, "%s already suspended",2141__func__);2142status = VCHIQ_SUCCESS;2143goto unlock;2144} else if (arm_state->vc_suspend_state <= VC_SUSPEND_IDLE) {2145/* initiate suspend immediately in the case that we're waiting2146* for the timeout */2147stop_suspend_timer(arm_state);2148if (!vchiq_videocore_wanted(state)) {2149vchiq_log_info(vchiq_susp_log_level, "%s videocore "2150"idle, initiating suspend", __func__);2151status = vchiq_arm_vcsuspend(state);2152} else if (arm_state->autosuspend_override <2153FORCE_SUSPEND_FAIL_MAX) {2154vchiq_log_info(vchiq_susp_log_level, "%s letting "2155"videocore go idle", __func__);2156status = VCHIQ_SUCCESS;2157} else {2158vchiq_log_warning(vchiq_susp_log_level, "%s failed too "2159"many times - attempting suspend", __func__);2160status = vchiq_arm_vcsuspend(state);2161}2162} else {2163vchiq_log_info(vchiq_susp_log_level, "%s videocore suspend "2164"in progress - wait for completion", __func__);2165status = VCHIQ_SUCCESS;2166}21672168/* Wait for suspend to happen due to system idle (not forced..) */2169if (status != VCHIQ_SUCCESS)2170goto unblock_resume;21712172do {2173write_unlock_bh(&arm_state->susp_res_lock);21742175rc = wait_for_completion_interruptible_timeout(2176&arm_state->vc_suspend_complete,2177msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS));21782179write_lock_bh(&arm_state->susp_res_lock);2180if (rc < 0) {2181vchiq_log_warning(vchiq_susp_log_level, "%s "2182"interrupted waiting for suspend", __func__);2183status = VCHIQ_ERROR;2184goto unblock_resume;2185} else if (rc == 0) {2186if (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) {2187/* Repeat timeout once if in progress */2188if (repeat < 0) {2189repeat = 1;2190continue;2191}2192}2193arm_state->autosuspend_override++;2194output_timeout_error(state);21952196status = VCHIQ_RETRY;2197goto unblock_resume;2198}2199} while (0 < (repeat--));22002201/* Check and report state in case we need to abort ARM suspend */2202if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED) {2203status = VCHIQ_RETRY;2204vchiq_log_error(vchiq_susp_log_level,2205"%s videocore suspend failed (state %s)", __func__,2206suspend_state_names[arm_state->vc_suspend_state +2207VC_SUSPEND_NUM_OFFSET]);2208/* Reset the state only if it's still in an error state.2209* Something could have already initiated another suspend. */2210if (arm_state->vc_suspend_state < VC_SUSPEND_IDLE)2211set_suspend_state(arm_state, VC_SUSPEND_IDLE);22122213goto unblock_resume;2214}22152216/* successfully suspended - unlock and exit */2217goto unlock;22182219unblock_resume:2220/* all error states need to unblock resume before exit */2221unblock_resume(arm_state);22222223unlock:2224write_unlock_bh(&arm_state->susp_res_lock);22252226out:2227vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status);2228return status;2229}22302231void2232vchiq_check_suspend(VCHIQ_STATE_T *state)2233{2234VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);22352236if (!arm_state)2237goto out;22382239vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);22402241write_lock_bh(&arm_state->susp_res_lock);2242if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED &&2243arm_state->first_connect &&2244!vchiq_videocore_wanted(state)) {2245vchiq_arm_vcsuspend(state);2246}2247write_unlock_bh(&arm_state->susp_res_lock);22482249out:2250vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__);2251return;2252}225322542255int2256vchiq_arm_allow_resume(VCHIQ_STATE_T *state)2257{2258VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2259int resume = 0;2260int ret = -1;22612262if (!arm_state)2263goto out;22642265vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);22662267write_lock_bh(&arm_state->susp_res_lock);2268unblock_resume(arm_state);2269resume = vchiq_check_resume(state);2270write_unlock_bh(&arm_state->susp_res_lock);22712272if (resume) {2273if (wait_for_completion_interruptible(2274&arm_state->vc_resume_complete) < 0) {2275vchiq_log_error(vchiq_susp_log_level,2276"%s interrupted", __func__);2277/* failed, cannot accurately derive suspend2278* state, so exit early. */2279goto out;2280}2281}22822283read_lock_bh(&arm_state->susp_res_lock);2284if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) {2285vchiq_log_info(vchiq_susp_log_level,2286"%s: Videocore remains suspended", __func__);2287} else {2288vchiq_log_info(vchiq_susp_log_level,2289"%s: Videocore resumed", __func__);2290ret = 0;2291}2292read_unlock_bh(&arm_state->susp_res_lock);2293out:2294vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret);2295return ret;2296}22972298/* This function should be called with the write lock held */2299int2300vchiq_check_resume(VCHIQ_STATE_T *state)2301{2302VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2303int resume = 0;23042305if (!arm_state)2306goto out;23072308vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);23092310if (need_resume(state)) {2311set_resume_state(arm_state, VC_RESUME_REQUESTED);2312request_poll(state, NULL, 0);2313resume = 1;2314}23152316out:2317vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__);2318return resume;2319}23202321#ifdef notyet2322void2323vchiq_platform_check_resume(VCHIQ_STATE_T *state)2324{2325VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2326int res = 0;23272328if (!arm_state)2329goto out;23302331vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);23322333write_lock_bh(&arm_state->susp_res_lock);2334if (arm_state->wake_address == 0) {2335vchiq_log_info(vchiq_susp_log_level,2336"%s: already awake", __func__);2337goto unlock;2338}2339if (arm_state->vc_resume_state == VC_RESUME_IN_PROGRESS) {2340vchiq_log_info(vchiq_susp_log_level,2341"%s: already resuming", __func__);2342goto unlock;2343}23442345if (arm_state->vc_resume_state == VC_RESUME_REQUESTED) {2346set_resume_state(arm_state, VC_RESUME_IN_PROGRESS);2347res = 1;2348} else2349vchiq_log_trace(vchiq_susp_log_level,2350"%s: not resuming (resume state %s)", __func__,2351resume_state_names[arm_state->vc_resume_state +2352VC_RESUME_NUM_OFFSET]);23532354unlock:2355write_unlock_bh(&arm_state->susp_res_lock);23562357if (res)2358vchiq_platform_resume(state);23592360out:2361vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__);2362return;23632364}2365#endif2366236723682369VCHIQ_STATUS_T2370vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,2371enum USE_TYPE_E use_type)2372{2373VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2374VCHIQ_STATUS_T ret = VCHIQ_SUCCESS;2375char entity[16];2376int *entity_uc;2377int local_uc, local_entity_uc;23782379if (!arm_state)2380goto out;23812382vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);23832384if (use_type == USE_TYPE_VCHIQ) {2385snprintf(entity, sizeof(entity), "VCHIQ: ");2386entity_uc = &arm_state->peer_use_count;2387} else if (service) {2388snprintf(entity, sizeof(entity), "%c%c%c%c:%8x",2389VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),2390service->client_id);2391entity_uc = &service->service_use_count;2392} else {2393vchiq_log_error(vchiq_susp_log_level, "%s null service "2394"ptr", __func__);2395ret = VCHIQ_ERROR;2396goto out;2397}23982399write_lock_bh(&arm_state->susp_res_lock);2400while (arm_state->resume_blocked) {2401/* If we call 'use' while force suspend is waiting for suspend,2402* then we're about to block the thread which the force is2403* waiting to complete, so we're bound to just time out. In this2404* case, set the suspend state such that the wait will be2405* canceled, so we can complete as quickly as possible. */2406if (arm_state->resume_blocked && arm_state->vc_suspend_state ==2407VC_SUSPEND_IDLE) {2408set_suspend_state(arm_state, VC_SUSPEND_FORCE_CANCELED);2409break;2410}2411/* If suspend is already in progress then we need to block */2412if (!try_wait_for_completion(&arm_state->resume_blocker)) {2413/* Indicate that there are threads waiting on the resume2414* blocker. These need to be allowed to complete before2415* a _second_ call to force suspend can complete,2416* otherwise low priority threads might never actually2417* continue */2418arm_state->blocked_count++;2419write_unlock_bh(&arm_state->susp_res_lock);2420vchiq_log_info(vchiq_susp_log_level, "%s %s resume "2421"blocked - waiting...", __func__, entity);2422if (wait_for_completion_killable(2423&arm_state->resume_blocker) != 0) {2424vchiq_log_error(vchiq_susp_log_level, "%s %s "2425"wait for resume blocker interrupted",2426__func__, entity);2427ret = VCHIQ_ERROR;2428write_lock_bh(&arm_state->susp_res_lock);2429arm_state->blocked_count--;2430write_unlock_bh(&arm_state->susp_res_lock);2431goto out;2432}2433vchiq_log_info(vchiq_susp_log_level, "%s %s resume "2434"unblocked", __func__, entity);2435write_lock_bh(&arm_state->susp_res_lock);2436if (--arm_state->blocked_count == 0)2437complete_all(&arm_state->blocked_blocker);2438}2439}24402441stop_suspend_timer(arm_state);24422443local_uc = ++arm_state->videocore_use_count;2444local_entity_uc = ++(*entity_uc);24452446/* If there's a pending request which hasn't yet been serviced then2447* just clear it. If we're past VC_SUSPEND_REQUESTED state then2448* vc_resume_complete will block until we either resume or fail to2449* suspend */2450if (arm_state->vc_suspend_state <= VC_SUSPEND_REQUESTED)2451set_suspend_state(arm_state, VC_SUSPEND_IDLE);24522453if ((use_type != USE_TYPE_SERVICE_NO_RESUME) && need_resume(state)) {2454set_resume_state(arm_state, VC_RESUME_REQUESTED);2455vchiq_log_info(vchiq_susp_log_level,2456"%s %s count %d, state count %d",2457__func__, entity, local_entity_uc, local_uc);2458request_poll(state, NULL, 0);2459} else2460vchiq_log_trace(vchiq_susp_log_level,2461"%s %s count %d, state count %d",2462__func__, entity, *entity_uc, local_uc);246324642465write_unlock_bh(&arm_state->susp_res_lock);24662467/* Completion is in a done state when we're not suspended, so this won't2468* block for the non-suspended case. */2469if (!try_wait_for_completion(&arm_state->vc_resume_complete)) {2470vchiq_log_info(vchiq_susp_log_level, "%s %s wait for resume",2471__func__, entity);2472if (wait_for_completion_killable(2473&arm_state->vc_resume_complete) != 0) {2474vchiq_log_error(vchiq_susp_log_level, "%s %s wait for "2475"resume interrupted", __func__, entity);2476ret = VCHIQ_ERROR;2477goto out;2478}2479vchiq_log_info(vchiq_susp_log_level, "%s %s resumed", __func__,2480entity);2481}24822483if (ret == VCHIQ_SUCCESS) {2484VCHIQ_STATUS_T status = VCHIQ_SUCCESS;2485long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0);2486while (ack_cnt && (status == VCHIQ_SUCCESS)) {2487/* Send the use notify to videocore */2488status = vchiq_send_remote_use_active(state);2489if (status == VCHIQ_SUCCESS)2490ack_cnt--;2491else2492atomic_add(ack_cnt,2493&arm_state->ka_use_ack_count);2494}2495}24962497out:2498vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret);2499return ret;2500}25012502VCHIQ_STATUS_T2503vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service)2504{2505VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2506VCHIQ_STATUS_T ret = VCHIQ_SUCCESS;2507char entity[16];2508int *entity_uc;25092510if (!arm_state)2511goto out;25122513vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);25142515if (service) {2516snprintf(entity, sizeof(entity), "%c%c%c%c:%8x",2517VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),2518service->client_id);2519entity_uc = &service->service_use_count;2520} else {2521snprintf(entity, sizeof(entity), "PEER: ");2522entity_uc = &arm_state->peer_use_count;2523}25242525write_lock_bh(&arm_state->susp_res_lock);2526if (!arm_state->videocore_use_count || !(*entity_uc)) {2527/* Don't use BUG_ON - don't allow user thread to crash kernel */2528WARN_ON(!arm_state->videocore_use_count);2529WARN_ON(!(*entity_uc));2530ret = VCHIQ_ERROR;2531goto unlock;2532}2533--arm_state->videocore_use_count;2534--(*entity_uc);25352536if (!vchiq_videocore_wanted(state)) {2537if (vchiq_platform_use_suspend_timer() &&2538!arm_state->resume_blocked) {2539/* Only use the timer if we're not trying to force2540* suspend (=> resume_blocked) */2541start_suspend_timer(arm_state);2542} else {2543vchiq_log_info(vchiq_susp_log_level,2544"%s %s count %d, state count %d - suspending",2545__func__, entity, *entity_uc,2546arm_state->videocore_use_count);2547vchiq_arm_vcsuspend(state);2548}2549} else2550vchiq_log_trace(vchiq_susp_log_level,2551"%s %s count %d, state count %d",2552__func__, entity, *entity_uc,2553arm_state->videocore_use_count);25542555unlock:2556write_unlock_bh(&arm_state->susp_res_lock);25572558out:2559vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret);2560return ret;2561}25622563void2564vchiq_on_remote_use(VCHIQ_STATE_T *state)2565{2566VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2567vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);2568atomic_inc(&arm_state->ka_use_count);2569complete(&arm_state->ka_evt);2570}25712572void2573vchiq_on_remote_release(VCHIQ_STATE_T *state)2574{2575VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2576vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);2577atomic_inc(&arm_state->ka_release_count);2578complete(&arm_state->ka_evt);2579}25802581VCHIQ_STATUS_T2582vchiq_use_service_internal(VCHIQ_SERVICE_T *service)2583{2584return vchiq_use_internal(service->state, service, USE_TYPE_SERVICE);2585}25862587VCHIQ_STATUS_T2588vchiq_release_service_internal(VCHIQ_SERVICE_T *service)2589{2590return vchiq_release_internal(service->state, service);2591}25922593static void suspend_timer_callback(unsigned long context)2594{2595VCHIQ_STATE_T *state = (VCHIQ_STATE_T *)context;2596VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2597if (!arm_state)2598goto out;2599vchiq_log_info(vchiq_susp_log_level,2600"%s - suspend timer expired - check suspend", __func__);2601vchiq_check_suspend(state);2602out:2603return;2604}26052606VCHIQ_STATUS_T2607vchiq_use_service_no_resume(VCHIQ_SERVICE_HANDLE_T handle)2608{2609VCHIQ_STATUS_T ret = VCHIQ_ERROR;2610VCHIQ_SERVICE_T *service = find_service_by_handle(handle);2611if (service) {2612ret = vchiq_use_internal(service->state, service,2613USE_TYPE_SERVICE_NO_RESUME);2614unlock_service(service);2615}2616return ret;2617}26182619VCHIQ_STATUS_T2620vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle)2621{2622VCHIQ_STATUS_T ret = VCHIQ_ERROR;2623VCHIQ_SERVICE_T *service = find_service_by_handle(handle);2624if (service) {2625ret = vchiq_use_internal(service->state, service,2626USE_TYPE_SERVICE);2627unlock_service(service);2628}2629return ret;2630}26312632VCHIQ_STATUS_T2633vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle)2634{2635VCHIQ_STATUS_T ret = VCHIQ_ERROR;2636VCHIQ_SERVICE_T *service = find_service_by_handle(handle);2637if (service) {2638ret = vchiq_release_internal(service->state, service);2639unlock_service(service);2640}2641return ret;2642}26432644void2645vchiq_dump_service_use_state(VCHIQ_STATE_T *state)2646{2647VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2648int i, j = 0;2649/* Only dump 64 services */2650static const int local_max_services = 64;2651/* If there's more than 64 services, only dump ones with2652* non-zero counts */2653int only_nonzero = 0;2654static const char *nz = "<-- preventing suspend";26552656enum vc_suspend_status vc_suspend_state;2657enum vc_resume_status vc_resume_state;2658int peer_count;2659int vc_use_count;2660int active_services;2661struct service_data_struct {2662int fourcc;2663int clientid;2664int use_count;2665} service_data[local_max_services];26662667if (!arm_state)2668return;26692670read_lock_bh(&arm_state->susp_res_lock);2671vc_suspend_state = arm_state->vc_suspend_state;2672vc_resume_state = arm_state->vc_resume_state;2673peer_count = arm_state->peer_use_count;2674vc_use_count = arm_state->videocore_use_count;2675active_services = state->unused_service;2676if (active_services > local_max_services)2677only_nonzero = 1;26782679for (i = 0; (i < active_services) && (j < local_max_services); i++) {2680VCHIQ_SERVICE_T *service_ptr = state->services[i];2681if (!service_ptr)2682continue;26832684if (only_nonzero && !service_ptr->service_use_count)2685continue;26862687if (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE) {2688service_data[j].fourcc = service_ptr->base.fourcc;2689service_data[j].clientid = service_ptr->client_id;2690service_data[j++].use_count = service_ptr->2691service_use_count;2692}2693}26942695read_unlock_bh(&arm_state->susp_res_lock);26962697vchiq_log_warning(vchiq_susp_log_level,2698"-- Videcore suspend state: %s --",2699suspend_state_names[vc_suspend_state + VC_SUSPEND_NUM_OFFSET]);2700vchiq_log_warning(vchiq_susp_log_level,2701"-- Videcore resume state: %s --",2702resume_state_names[vc_resume_state + VC_RESUME_NUM_OFFSET]);27032704if (only_nonzero)2705vchiq_log_warning(vchiq_susp_log_level, "Too many active "2706"services (%d). Only dumping up to first %d services "2707"with non-zero use-count", active_services,2708local_max_services);27092710for (i = 0; i < j; i++) {2711vchiq_log_warning(vchiq_susp_log_level,2712"----- %c%c%c%c:%d service count %d %s",2713VCHIQ_FOURCC_AS_4CHARS(service_data[i].fourcc),2714service_data[i].clientid,2715service_data[i].use_count,2716service_data[i].use_count ? nz : "");2717}2718vchiq_log_warning(vchiq_susp_log_level,2719"----- VCHIQ use count count %d", peer_count);2720vchiq_log_warning(vchiq_susp_log_level,2721"--- Overall vchiq instance use count %d", vc_use_count);27222723vchiq_dump_platform_use_state(state);2724}27252726VCHIQ_STATUS_T2727vchiq_check_service(VCHIQ_SERVICE_T *service)2728{2729VCHIQ_ARM_STATE_T *arm_state;2730VCHIQ_STATUS_T ret = VCHIQ_ERROR;27312732if (!service || !service->state)2733goto out;27342735vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);27362737arm_state = vchiq_platform_get_arm_state(service->state);27382739read_lock_bh(&arm_state->susp_res_lock);2740if (service->service_use_count)2741ret = VCHIQ_SUCCESS;2742read_unlock_bh(&arm_state->susp_res_lock);27432744if (ret == VCHIQ_ERROR) {2745vchiq_log_error(vchiq_susp_log_level,2746"%s ERROR - %c%c%c%c:%8x service count %d, "2747"state count %d, videocore suspend state %s", __func__,2748VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),2749service->client_id, service->service_use_count,2750arm_state->videocore_use_count,2751suspend_state_names[arm_state->vc_suspend_state +2752VC_SUSPEND_NUM_OFFSET]);2753vchiq_dump_service_use_state(service->state);2754}2755out:2756return ret;2757}27582759/* stub functions */2760void vchiq_on_remote_use_active(VCHIQ_STATE_T *state)2761{2762(void)state;2763}27642765void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state,2766VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate)2767{2768VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);2769vchiq_log_info(vchiq_susp_log_level, "%d: %s->%s", state->id,2770get_conn_state_name(oldstate), get_conn_state_name(newstate));2771if (state->conn_state == VCHIQ_CONNSTATE_CONNECTED) {2772write_lock_bh(&arm_state->susp_res_lock);2773if (!arm_state->first_connect) {2774char threadname[10];2775arm_state->first_connect = 1;2776write_unlock_bh(&arm_state->susp_res_lock);2777snprintf(threadname, sizeof(threadname), "VCHIQka-%d",2778state->id);2779arm_state->ka_thread = vchiq_thread_create(2780&vchiq_keepalive_thread_func,2781(void *)state,2782threadname);2783if (arm_state->ka_thread == NULL) {2784vchiq_log_error(vchiq_susp_log_level,2785"vchiq: FATAL: couldn't create thread %s",2786threadname);2787} else {2788wake_up_process(arm_state->ka_thread);2789}2790} else2791write_unlock_bh(&arm_state->susp_res_lock);2792}2793}27942795/****************************************************************************2796*2797* vchiq_init - called when the module is loaded.2798*2799***************************************************************************/28002801int __init vchiq_init(void);2802int __init2803vchiq_init(void)2804{2805int err;28062807#ifdef notyet2808/* create proc entries */2809err = vchiq_proc_init();2810if (err != 0)2811goto failed_proc_init;2812#endif28132814vchiq_cdev = make_dev(&vchiq_cdevsw, 0,2815UID_ROOT, GID_WHEEL, 0600, "vchiq");2816if (!vchiq_cdev) {2817printf("Failed to create /dev/vchiq");2818return (-ENXIO);2819}28202821spin_lock_init(&msg_queue_spinlock);28222823err = vchiq_platform_init(&g_state);2824if (err != 0)2825goto failed_platform_init;28262827vchiq_log_info(vchiq_arm_log_level,2828"vchiq: initialised - version %d (min %d)",2829VCHIQ_VERSION, VCHIQ_VERSION_MIN);28302831return 0;28322833failed_platform_init:2834if (vchiq_cdev) {2835destroy_dev(vchiq_cdev);2836vchiq_cdev = NULL;2837}2838vchiq_log_warning(vchiq_arm_log_level, "could not load vchiq");2839return err;2840}28412842#ifdef notyet2843static int vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance)2844{2845VCHIQ_SERVICE_T *service;2846int use_count = 0, i;2847i = 0;2848while ((service = next_service_by_instance(instance->state,2849instance, &i)) != NULL) {2850use_count += service->service_use_count;2851unlock_service(service);2852}2853return use_count;2854}28552856/* read the per-process use-count */2857static int proc_read_use_count(char *page, char **start,2858off_t off, int count,2859int *eof, void *data)2860{2861VCHIQ_INSTANCE_T instance = data;2862int len, use_count;28632864use_count = vchiq_instance_get_use_count(instance);2865len = snprintf(page+off, count, "%d\n", use_count);28662867return len;2868}28692870/* add an instance (process) to the proc entries */2871static int vchiq_proc_add_instance(VCHIQ_INSTANCE_T instance)2872{2873char pidstr[32];2874struct proc_dir_entry *top, *use_count;2875struct proc_dir_entry *clients = vchiq_clients_top();2876int pid = instance->pid;28772878snprintf(pidstr, sizeof(pidstr), "%d", pid);2879top = proc_mkdir(pidstr, clients);2880if (!top)2881goto fail_top;28822883use_count = create_proc_read_entry("use_count",28840444, top,2885proc_read_use_count,2886instance);2887if (!use_count)2888goto fail_use_count;28892890instance->proc_entry = top;28912892return 0;28932894fail_use_count:2895remove_proc_entry(top->name, clients);2896fail_top:2897return -ENOMEM;2898}28992900static void vchiq_proc_remove_instance(VCHIQ_INSTANCE_T instance)2901{2902struct proc_dir_entry *clients = vchiq_clients_top();2903remove_proc_entry("use_count", instance->proc_entry);2904remove_proc_entry(instance->proc_entry->name, clients);2905}29062907#endif29082909/****************************************************************************2910*2911* vchiq_exit - called when the module is unloaded.2912*2913***************************************************************************/29142915void vchiq_exit(void);2916void2917vchiq_exit(void)2918{29192920vchiq_platform_exit(&g_state);2921if (vchiq_cdev) {2922destroy_dev(vchiq_cdev);2923vchiq_cdev = NULL;2924}2925}292629272928