Path: blob/main/sys/contrib/vchiq/interface/vchiq_arm/vchiq_kern_lib.c
48383 views
/**1* Copyright (c) 2010-2012 Broadcom. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6* 1. Redistributions of source code must retain the above copyright7* notice, this list of conditions, and the following disclaimer,8* without modification.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. The names of the above-listed copyright holders may not be used13* to endorse or promote products derived from this software without14* specific prior written permission.15*16* ALTERNATIVELY, this software may be distributed under the terms of the17* GNU General Public License ("GPL") version 2, as published by the Free18* Software Foundation.19*20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS21* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,22* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR23* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR24* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,25* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,26* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR27* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF28* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING29* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS30* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31*/3233/* ---- Include Files ---------------------------------------------------- */3435#include "vchiq_core.h"36#include "vchiq_arm.h"37#include "vchiq_killable.h"3839/* ---- Public Variables ------------------------------------------------- */4041/* ---- Private Constants and Types -------------------------------------- */4243struct bulk_waiter_node {44struct bulk_waiter bulk_waiter;45int pid;46struct list_head list;47};4849struct vchiq_instance_struct {50VCHIQ_STATE_T *state;5152int connected;5354struct list_head bulk_waiter_list;55struct mutex bulk_waiter_list_mutex;56};5758static VCHIQ_STATUS_T59vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,60unsigned int size, VCHIQ_BULK_DIR_T dir);6162/****************************************************************************63*64* vchiq_initialise65*66***************************************************************************/67#define VCHIQ_INIT_RETRIES 1068VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut)69{70VCHIQ_STATUS_T status = VCHIQ_ERROR;71VCHIQ_STATE_T *state;72VCHIQ_INSTANCE_T instance = NULL;73int i;7475vchiq_log_trace(vchiq_core_log_level, "%s called", __func__);7677/* VideoCore may not be ready due to boot up timing.78It may never be ready if kernel and firmware are mismatched, so don't block forever. */79for (i=0; i<VCHIQ_INIT_RETRIES; i++) {80state = vchiq_get_state();81if (state)82break;83udelay(500);84}85if (i==VCHIQ_INIT_RETRIES) {86vchiq_log_error(vchiq_core_log_level,87"%s: videocore not initialized\n", __func__);88goto failed;89} else if (i>0) {90vchiq_log_warning(vchiq_core_log_level,91"%s: videocore initialized after %d retries\n", __func__, i);92}9394instance = kzalloc(sizeof(*instance), GFP_KERNEL);95if (!instance) {96vchiq_log_error(vchiq_core_log_level,97"%s: error allocating vchiq instance\n", __func__);98goto failed;99}100101instance->connected = 0;102instance->state = state;103lmutex_init(&instance->bulk_waiter_list_mutex);104INIT_LIST_HEAD(&instance->bulk_waiter_list);105106*instanceOut = instance;107108status = VCHIQ_SUCCESS;109110failed:111vchiq_log_trace(vchiq_core_log_level,112"%s(%p): returning %d", __func__, instance, status);113114return status;115}116EXPORT_SYMBOL(vchiq_initialise);117118/****************************************************************************119*120* vchiq_shutdown121*122***************************************************************************/123124VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance)125{126VCHIQ_STATUS_T status;127VCHIQ_STATE_T *state = instance->state;128129vchiq_log_trace(vchiq_core_log_level,130"%s(%p) called", __func__, instance);131132if (lmutex_lock_interruptible(&state->mutex) != 0)133return VCHIQ_RETRY;134135/* Remove all services */136status = vchiq_shutdown_internal(state, instance);137138lmutex_unlock(&state->mutex);139140vchiq_log_trace(vchiq_core_log_level,141"%s(%p): returning %d", __func__, instance, status);142143if (status == VCHIQ_SUCCESS) {144struct list_head *pos, *next;145list_for_each_safe(pos, next,146&instance->bulk_waiter_list) {147struct bulk_waiter_node *waiter;148waiter = list_entry(pos,149struct bulk_waiter_node,150list);151list_del(pos);152vchiq_log_info(vchiq_arm_log_level,153"bulk_waiter - cleaned up %p "154"for pid %d",155waiter, waiter->pid);156_sema_destroy(&waiter->bulk_waiter.event);157158kfree(waiter);159}160161lmutex_destroy(&instance->bulk_waiter_list_mutex);162163kfree(instance);164}165166return status;167}168EXPORT_SYMBOL(vchiq_shutdown);169170/****************************************************************************171*172* vchiq_is_connected173*174***************************************************************************/175176static int vchiq_is_connected(VCHIQ_INSTANCE_T instance)177{178return instance->connected;179}180181/****************************************************************************182*183* vchiq_connect184*185***************************************************************************/186187VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance)188{189VCHIQ_STATUS_T status;190VCHIQ_STATE_T *state = instance->state;191192vchiq_log_trace(vchiq_core_log_level,193"%s(%p) called", __func__, instance);194195if (lmutex_lock_interruptible(&state->mutex) != 0) {196vchiq_log_trace(vchiq_core_log_level,197"%s: call to lmutex_lock failed", __func__);198status = VCHIQ_RETRY;199goto failed;200}201status = vchiq_connect_internal(state, instance);202203if (status == VCHIQ_SUCCESS)204instance->connected = 1;205206lmutex_unlock(&state->mutex);207208failed:209vchiq_log_trace(vchiq_core_log_level,210"%s(%p): returning %d", __func__, instance, status);211212return status;213}214EXPORT_SYMBOL(vchiq_connect);215216/****************************************************************************217*218* vchiq_add_service219*220***************************************************************************/221222VCHIQ_STATUS_T vchiq_add_service(223VCHIQ_INSTANCE_T instance,224const VCHIQ_SERVICE_PARAMS_T *params,225VCHIQ_SERVICE_HANDLE_T *phandle)226{227VCHIQ_STATUS_T status;228VCHIQ_STATE_T *state = instance->state;229VCHIQ_SERVICE_T *service = NULL;230int srvstate;231232vchiq_log_trace(vchiq_core_log_level,233"%s(%p) called", __func__, instance);234235*phandle = VCHIQ_SERVICE_HANDLE_INVALID;236237srvstate = vchiq_is_connected(instance)238? VCHIQ_SRVSTATE_LISTENING239: VCHIQ_SRVSTATE_HIDDEN;240241service = vchiq_add_service_internal(242state,243params,244srvstate,245instance,246NULL);247248if (service) {249*phandle = service->handle;250status = VCHIQ_SUCCESS;251} else252status = VCHIQ_ERROR;253254vchiq_log_trace(vchiq_core_log_level,255"%s(%p): returning %d", __func__, instance, status);256257return status;258}259EXPORT_SYMBOL(vchiq_add_service);260261/****************************************************************************262*263* vchiq_open_service264*265***************************************************************************/266267VCHIQ_STATUS_T vchiq_open_service(268VCHIQ_INSTANCE_T instance,269const VCHIQ_SERVICE_PARAMS_T *params,270VCHIQ_SERVICE_HANDLE_T *phandle)271{272VCHIQ_STATUS_T status = VCHIQ_ERROR;273VCHIQ_STATE_T *state = instance->state;274VCHIQ_SERVICE_T *service = NULL;275276vchiq_log_trace(vchiq_core_log_level,277"%s(%p) called", __func__, instance);278279*phandle = VCHIQ_SERVICE_HANDLE_INVALID;280281if (!vchiq_is_connected(instance))282goto failed;283284service = vchiq_add_service_internal(state,285params,286VCHIQ_SRVSTATE_OPENING,287instance,288NULL);289290if (service) {291*phandle = service->handle;292status = vchiq_open_service_internal(service,293(uintptr_t)current);294if (status != VCHIQ_SUCCESS) {295vchiq_remove_service(service->handle);296*phandle = VCHIQ_SERVICE_HANDLE_INVALID;297}298}299300failed:301vchiq_log_trace(vchiq_core_log_level,302"%s(%p): returning %d", __func__, instance, status);303304return status;305}306EXPORT_SYMBOL(vchiq_open_service);307308VCHIQ_STATUS_T309vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,310void *data, unsigned int size, void *userdata)311{312return vchiq_bulk_transfer(handle,313VCHI_MEM_HANDLE_INVALID, data, size, userdata,314VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT);315}316EXPORT_SYMBOL(vchiq_queue_bulk_transmit);317318VCHIQ_STATUS_T319vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,320unsigned int size, void *userdata)321{322return vchiq_bulk_transfer(handle,323VCHI_MEM_HANDLE_INVALID, data, size, userdata,324VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE);325}326EXPORT_SYMBOL(vchiq_queue_bulk_receive);327328VCHIQ_STATUS_T329vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, void *data,330unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)331{332VCHIQ_STATUS_T status;333334switch (mode) {335case VCHIQ_BULK_MODE_NOCALLBACK:336case VCHIQ_BULK_MODE_CALLBACK:337status = vchiq_bulk_transfer(handle,338VCHI_MEM_HANDLE_INVALID, data, size, userdata,339mode, VCHIQ_BULK_TRANSMIT);340break;341case VCHIQ_BULK_MODE_BLOCKING:342status = vchiq_blocking_bulk_transfer(handle,343data, size, VCHIQ_BULK_TRANSMIT);344break;345default:346return VCHIQ_ERROR;347}348349return status;350}351EXPORT_SYMBOL(vchiq_bulk_transmit);352353VCHIQ_STATUS_T354vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,355unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)356{357VCHIQ_STATUS_T status;358359switch (mode) {360case VCHIQ_BULK_MODE_NOCALLBACK:361case VCHIQ_BULK_MODE_CALLBACK:362status = vchiq_bulk_transfer(handle,363VCHI_MEM_HANDLE_INVALID, data, size, userdata,364mode, VCHIQ_BULK_RECEIVE);365break;366case VCHIQ_BULK_MODE_BLOCKING:367status = vchiq_blocking_bulk_transfer(handle,368data, size, VCHIQ_BULK_RECEIVE);369break;370default:371return VCHIQ_ERROR;372}373374return status;375}376EXPORT_SYMBOL(vchiq_bulk_receive);377378static VCHIQ_STATUS_T379vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,380unsigned int size, VCHIQ_BULK_DIR_T dir)381{382VCHIQ_INSTANCE_T instance;383VCHIQ_SERVICE_T *service;384VCHIQ_STATUS_T status;385struct bulk_waiter_node *waiter = NULL;386struct list_head *pos;387388service = find_service_by_handle(handle);389if (!service)390return VCHIQ_ERROR;391392instance = service->instance;393394unlock_service(service);395396lmutex_lock(&instance->bulk_waiter_list_mutex);397list_for_each(pos, &instance->bulk_waiter_list) {398if (list_entry(pos, struct bulk_waiter_node,399list)->pid == current->p_pid) {400waiter = list_entry(pos,401struct bulk_waiter_node,402list);403list_del(pos);404break;405}406}407lmutex_unlock(&instance->bulk_waiter_list_mutex);408409if (waiter) {410VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;411if (bulk) {412/* This thread has an outstanding bulk transfer. */413if ((bulk->data != data) ||414(bulk->size != size)) {415/* This is not a retry of the previous one.416** Cancel the signal when the transfer417** completes. */418spin_lock(&bulk_waiter_spinlock);419bulk->userdata = NULL;420spin_unlock(&bulk_waiter_spinlock);421}422}423}424425if (!waiter) {426waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL);427if (!waiter) {428vchiq_log_error(vchiq_core_log_level,429"%s - out of memory", __func__);430return VCHIQ_ERROR;431}432}433434status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID,435data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING,436dir);437if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||438!waiter->bulk_waiter.bulk) {439VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;440if (bulk) {441/* Cancel the signal when the transfer442** completes. */443spin_lock(&bulk_waiter_spinlock);444bulk->userdata = NULL;445spin_unlock(&bulk_waiter_spinlock);446}447_sema_destroy(&waiter->bulk_waiter.event);448449kfree(waiter);450} else {451waiter->pid = current->p_pid;452lmutex_lock(&instance->bulk_waiter_list_mutex);453list_add(&waiter->list, &instance->bulk_waiter_list);454lmutex_unlock(&instance->bulk_waiter_list_mutex);455vchiq_log_info(vchiq_arm_log_level,456"saved bulk_waiter %p for pid %d",457waiter, current->p_pid);458}459460return status;461}462463464