162607Sitojun/** 278064Sume * Copyright (c) 2010-2012 Broadcom. All rights reserved. 355163Sshin * 455163Sshin * Redistribution and use in source and binary forms, with or without 555163Sshin * modification, are permitted provided that the following conditions 655163Sshin * are met: 762607Sitojun * 1. Redistributions of source code must retain the above copyright 855163Sshin * notice, this list of conditions, and the following disclaimer, 955163Sshin * without modification. 1055163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer in the 1255163Sshin * documentation and/or other materials provided with the distribution. 1355163Sshin * 3. The names of the above-listed copyright holders may not be used 1455163Sshin * to endorse or promote products derived from this software without 1555163Sshin * specific prior written permission. 1655163Sshin * 1755163Sshin * ALTERNATIVELY, this software may be distributed under the terms of the 1855163Sshin * GNU General Public License ("GPL") version 2, as published by the Free 1962607Sitojun * Software Foundation. 2055163Sshin * 2155163Sshin * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 2255163Sshin * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 2355163Sshin * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2455163Sshin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 2555163Sshin * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 2655163Sshin * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 2755163Sshin * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 2855163Sshin * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 2955163Sshin * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 3055163Sshin * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 3155163Sshin * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3255163Sshin */ 3355163Sshin 3478064Sume/* ---- Include Files ---------------------------------------------------- */ 3555163Sshin 3655163Sshin#include "vchiq_core.h" 3755163Sshin#include "vchiq_arm.h" 3855163Sshin#include "vchiq_killable.h" 3955163Sshin 4055163Sshin/* ---- Public Variables ------------------------------------------------- */ 4155163Sshin 4255163Sshin/* ---- Private Constants and Types -------------------------------------- */ 4355163Sshin 4455163Sshinstruct bulk_waiter_node { 4555163Sshin struct bulk_waiter bulk_waiter; 4655163Sshin int pid; 4755163Sshin struct list_head list; 4855163Sshin}; 4955163Sshin 5055163Sshinstruct vchiq_instance_struct { 5162607Sitojun VCHIQ_STATE_T *state; 5255163Sshin 53119076Sume int connected; 54119076Sume 55119076Sume struct list_head bulk_waiter_list; 5655163Sshin struct mutex bulk_waiter_list_mutex; 5755163Sshin}; 5855163Sshin 5955163Sshinstatic VCHIQ_STATUS_T 6055163Sshinvchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, 6155163Sshin unsigned int size, VCHIQ_BULK_DIR_T dir); 6255163Sshin 6355163Sshin/**************************************************************************** 6455163Sshin* 6562607Sitojun* vchiq_initialise 6655163Sshin* 6762607Sitojun***************************************************************************/ 6862607Sitojun#define VCHIQ_INIT_RETRIES 10 6962607SitojunVCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut) 7055163Sshin{ 7162607Sitojun VCHIQ_STATUS_T status = VCHIQ_ERROR; 7262607Sitojun VCHIQ_STATE_T *state; 7355163Sshin VCHIQ_INSTANCE_T instance = NULL; 7455163Sshin int i; 7555163Sshin 7655163Sshin vchiq_log_trace(vchiq_core_log_level, "%s called", __func__); 7755163Sshin 7862607Sitojun /* VideoCore may not be ready due to boot up timing. 7955163Sshin It may never be ready if kernel and firmware are mismatched, so don't block forever. */ 8055163Sshin for (i=0; i<VCHIQ_INIT_RETRIES; i++) { 8155163Sshin state = vchiq_get_state(); 8255163Sshin if (state) 8355163Sshin break; 8455163Sshin udelay(500); 8555163Sshin } 8655163Sshin if (i==VCHIQ_INIT_RETRIES) { 8755163Sshin vchiq_log_error(vchiq_core_log_level, 8855163Sshin "%s: videocore not initialized\n", __func__); 89119039Sume goto failed; 9055163Sshin } else if (i>0) { 9155163Sshin vchiq_log_warning(vchiq_core_log_level, 9255163Sshin "%s: videocore initialized after %d retries\n", __func__, i); 9362607Sitojun } 9455163Sshin 9562607Sitojun instance = kzalloc(sizeof(*instance), GFP_KERNEL); 9655163Sshin if (!instance) { 9755163Sshin vchiq_log_error(vchiq_core_log_level, 9855163Sshin "%s: error allocating vchiq instance\n", __func__); 9955163Sshin goto failed; 10055163Sshin } 10155163Sshin 10255163Sshin instance->connected = 0; 10355163Sshin instance->state = state; 10455163Sshin lmutex_init(&instance->bulk_waiter_list_mutex); 10555163Sshin INIT_LIST_HEAD(&instance->bulk_waiter_list); 10655163Sshin 10755163Sshin *instanceOut = instance; 10855163Sshin 10955163Sshin status = VCHIQ_SUCCESS; 11055163Sshin 11155163Sshinfailed: 11255163Sshin vchiq_log_trace(vchiq_core_log_level, 11355163Sshin "%s(%p): returning %d", __func__, instance, status); 11455163Sshin 11578064Sume return status; 11678064Sume} 11755163SshinEXPORT_SYMBOL(vchiq_initialise); 11855163Sshin 11955163Sshin/**************************************************************************** 12055163Sshin* 12155163Sshin* vchiq_shutdown 12255163Sshin* 12355163Sshin***************************************************************************/ 12462607Sitojun 12555163SshinVCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance) 12655163Sshin{ 12755163Sshin VCHIQ_STATUS_T status; 12855163Sshin VCHIQ_STATE_T *state = instance->state; 12955163Sshin 13055163Sshin vchiq_log_trace(vchiq_core_log_level, 13155163Sshin "%s(%p) called", __func__, instance); 13255163Sshin 13355163Sshin if (lmutex_lock_interruptible(&state->mutex) != 0) 13455163Sshin return VCHIQ_RETRY; 13555163Sshin 13655163Sshin /* Remove all services */ 13755163Sshin status = vchiq_shutdown_internal(state, instance); 13855163Sshin 13955163Sshin lmutex_unlock(&state->mutex); 14055163Sshin 14155163Sshin vchiq_log_trace(vchiq_core_log_level, 14255163Sshin "%s(%p): returning %d", __func__, instance, status); 14355163Sshin 144119076Sume if (status == VCHIQ_SUCCESS) { 145119076Sume struct list_head *pos, *next; 146119076Sume list_for_each_safe(pos, next, 147119070Sume &instance->bulk_waiter_list) { 148119070Sume struct bulk_waiter_node *waiter; 149119070Sume waiter = list_entry(pos, 150119070Sume struct bulk_waiter_node, 151119076Sume list); 15255163Sshin list_del(pos); 15355163Sshin vchiq_log_info(vchiq_arm_log_level, 15455163Sshin "bulk_waiter - cleaned up %x " 15555163Sshin "for pid %d", 15655163Sshin (unsigned int)waiter, waiter->pid); 15755163Sshin _sema_destroy(&waiter->bulk_waiter.event); 15878064Sume 15955163Sshin kfree(waiter); 16078064Sume } 16155163Sshin 16255163Sshin lmutex_destroy(&instance->bulk_waiter_list_mutex); 16355163Sshin 16478064Sume kfree(instance); 16555163Sshin } 16655163Sshin 16755163Sshin return status; 16855163Sshin} 16955163SshinEXPORT_SYMBOL(vchiq_shutdown); 17055163Sshin 17155163Sshin/**************************************************************************** 17255163Sshin* 17355163Sshin* vchiq_is_connected 17462607Sitojun* 17562607Sitojun***************************************************************************/ 17655163Sshin 17755163Sshinstatic int vchiq_is_connected(VCHIQ_INSTANCE_T instance) 17855163Sshin{ 17955163Sshin return instance->connected; 18055163Sshin} 18155163Sshin 18255163Sshin/**************************************************************************** 18355163Sshin* 18455163Sshin* vchiq_connect 18555163Sshin* 18655163Sshin***************************************************************************/ 18755163Sshin 18855163SshinVCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) 18955163Sshin{ 19062607Sitojun VCHIQ_STATUS_T status; 19155163Sshin VCHIQ_STATE_T *state = instance->state; 19255163Sshin 19355163Sshin vchiq_log_trace(vchiq_core_log_level, 19455163Sshin "%s(%p) called", __func__, instance); 19555163Sshin 19655163Sshin if (lmutex_lock_interruptible(&state->mutex) != 0) { 19755163Sshin vchiq_log_trace(vchiq_core_log_level, 19855163Sshin "%s: call to lmutex_lock failed", __func__); 19955163Sshin status = VCHIQ_RETRY; 20055163Sshin goto failed; 20155163Sshin } 20255163Sshin status = vchiq_connect_internal(state, instance); 20355163Sshin 20455163Sshin if (status == VCHIQ_SUCCESS) 20555163Sshin instance->connected = 1; 20655163Sshin 20755163Sshin lmutex_unlock(&state->mutex); 20855163Sshin 20955163Sshinfailed: 21055163Sshin vchiq_log_trace(vchiq_core_log_level, 21155163Sshin "%s(%p): returning %d", __func__, instance, status); 21278064Sume 21378064Sume return status; 21478064Sume} 21578064SumeEXPORT_SYMBOL(vchiq_connect); 21662607Sitojun 21762607Sitojun/**************************************************************************** 21862607Sitojun* 21962607Sitojun* vchiq_add_service 22062607Sitojun* 22155163Sshin***************************************************************************/ 22255163Sshin 22378064SumeVCHIQ_STATUS_T vchiq_add_service( 22478064Sume VCHIQ_INSTANCE_T instance, 22555163Sshin const VCHIQ_SERVICE_PARAMS_T *params, 22655163Sshin VCHIQ_SERVICE_HANDLE_T *phandle) 22778064Sume{ 22855163Sshin VCHIQ_STATUS_T status; 22955163Sshin VCHIQ_STATE_T *state = instance->state; 23055163Sshin VCHIQ_SERVICE_T *service = NULL; 23162607Sitojun int srvstate; 23255163Sshin 23355163Sshin vchiq_log_trace(vchiq_core_log_level, 23455163Sshin "%s(%p) called", __func__, instance); 23555163Sshin 23655163Sshin *phandle = VCHIQ_SERVICE_HANDLE_INVALID; 23755163Sshin 23855163Sshin srvstate = vchiq_is_connected(instance) 23978064Sume ? VCHIQ_SRVSTATE_LISTENING 24078064Sume : VCHIQ_SRVSTATE_HIDDEN; 24178064Sume 24278064Sume service = vchiq_add_service_internal( 24362607Sitojun state, 24455163Sshin params, 24555163Sshin srvstate, 24655163Sshin instance, 24755163Sshin NULL); 24855163Sshin 24955163Sshin if (service) { 25055163Sshin *phandle = service->handle; 25178064Sume status = VCHIQ_SUCCESS; 25278064Sume } else 25378064Sume status = VCHIQ_ERROR; 25455163Sshin 25555163Sshin vchiq_log_trace(vchiq_core_log_level, 25678064Sume "%s(%p): returning %d", __func__, instance, status); 25755163Sshin 25855163Sshin return status; 25955163Sshin} 26055163SshinEXPORT_SYMBOL(vchiq_add_service); 26155163Sshin 26255163Sshin/**************************************************************************** 26355163Sshin* 26455163Sshin* vchiq_open_service 26555163Sshin* 26655163Sshin***************************************************************************/ 26755163Sshin 26855163SshinVCHIQ_STATUS_T vchiq_open_service( 26978064Sume VCHIQ_INSTANCE_T instance, 27055163Sshin const VCHIQ_SERVICE_PARAMS_T *params, 27155163Sshin VCHIQ_SERVICE_HANDLE_T *phandle) 27266807Skris{ 27366807Skris VCHIQ_STATUS_T status = VCHIQ_ERROR; 27466807Skris VCHIQ_STATE_T *state = instance->state; 27566807Skris VCHIQ_SERVICE_T *service = NULL; 27666807Skris 27766807Skris vchiq_log_trace(vchiq_core_log_level, 27855163Sshin "%s(%p) called", __func__, instance); 27955163Sshin 28055163Sshin *phandle = VCHIQ_SERVICE_HANDLE_INVALID; 28155163Sshin 28255163Sshin if (!vchiq_is_connected(instance)) 28355163Sshin goto failed; 28455163Sshin 28555163Sshin service = vchiq_add_service_internal(state, 28655163Sshin params, 28755163Sshin VCHIQ_SRVSTATE_OPENING, 28855163Sshin instance, 28955163Sshin NULL); 29055163Sshin 29155163Sshin if (service) { 29255163Sshin *phandle = service->handle; 29355163Sshin status = vchiq_open_service_internal(service, 29455163Sshin (uintptr_t)current); 29555163Sshin if (status != VCHIQ_SUCCESS) { 29662607Sitojun vchiq_remove_service(service->handle); 29755163Sshin *phandle = VCHIQ_SERVICE_HANDLE_INVALID; 29855163Sshin } 29955163Sshin } 30055163Sshin 30155163Sshinfailed: 30255163Sshin vchiq_log_trace(vchiq_core_log_level, 30355163Sshin "%s(%p): returning %d", __func__, instance, status); 30455163Sshin 30578064Sume return status; 30655163Sshin} 30755163SshinEXPORT_SYMBOL(vchiq_open_service); 30855163Sshin 30955163SshinVCHIQ_STATUS_T 31055163Sshinvchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, 31155163Sshin void *data, unsigned int size, void *userdata) 31262607Sitojun{ 31355163Sshin return vchiq_bulk_transfer(handle, 31462607Sitojun VCHI_MEM_HANDLE_INVALID, data, size, userdata, 31562607Sitojun VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT); 31655163Sshin} 31755163SshinEXPORT_SYMBOL(vchiq_queue_bulk_transmit); 31855163Sshin 31955163SshinVCHIQ_STATUS_T 32062607Sitojunvchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, 32162607Sitojun unsigned int size, void *userdata) 32262607Sitojun{ 32355163Sshin return vchiq_bulk_transfer(handle, 32455163Sshin VCHI_MEM_HANDLE_INVALID, data, size, userdata, 32555163Sshin VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE); 32655163Sshin} 32755163SshinEXPORT_SYMBOL(vchiq_queue_bulk_receive); 32862607Sitojun 32955163SshinVCHIQ_STATUS_T 33062607Sitojunvchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, void *data, 33162607Sitojun unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) 33255163Sshin{ 33362607Sitojun VCHIQ_STATUS_T status; 33462607Sitojun 33562607Sitojun switch (mode) { 33662607Sitojun case VCHIQ_BULK_MODE_NOCALLBACK: 33762607Sitojun case VCHIQ_BULK_MODE_CALLBACK: 33862607Sitojun status = vchiq_bulk_transfer(handle, 33962607Sitojun VCHI_MEM_HANDLE_INVALID, data, size, userdata, 34062607Sitojun mode, VCHIQ_BULK_TRANSMIT); 34162607Sitojun break; 34262607Sitojun case VCHIQ_BULK_MODE_BLOCKING: 34355163Sshin status = vchiq_blocking_bulk_transfer(handle, 34455163Sshin data, size, VCHIQ_BULK_TRANSMIT); 34555163Sshin break; 34662607Sitojun default: 34755163Sshin return VCHIQ_ERROR; 34855163Sshin } 34955163Sshin 35055163Sshin return status; 35178064Sume} 35255163SshinEXPORT_SYMBOL(vchiq_bulk_transmit); 35378064Sume 35478064SumeVCHIQ_STATUS_T 35555163Sshinvchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, 35655163Sshin unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) 35755163Sshin{ 35855163Sshin VCHIQ_STATUS_T status; 35955163Sshin 360119037Sume switch (mode) { 361119037Sume case VCHIQ_BULK_MODE_NOCALLBACK: 362119037Sume case VCHIQ_BULK_MODE_CALLBACK: 363119037Sume status = vchiq_bulk_transfer(handle, 364119037Sume VCHI_MEM_HANDLE_INVALID, data, size, userdata, 365119037Sume mode, VCHIQ_BULK_RECEIVE); 366119037Sume break; 367119037Sume case VCHIQ_BULK_MODE_BLOCKING: 36855163Sshin status = vchiq_blocking_bulk_transfer(handle, 36955163Sshin data, size, VCHIQ_BULK_RECEIVE); 37078064Sume break; 37178064Sume default: 37278064Sume return VCHIQ_ERROR; 37378064Sume } 37478064Sume 37578064Sume return status; 37678064Sume} 37778064SumeEXPORT_SYMBOL(vchiq_bulk_receive); 37878064Sume 37955163Sshinstatic VCHIQ_STATUS_T 38055163Sshinvchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, 38155163Sshin unsigned int size, VCHIQ_BULK_DIR_T dir) 38255163Sshin{ 38355163Sshin VCHIQ_INSTANCE_T instance; 38455163Sshin VCHIQ_SERVICE_T *service; 38555163Sshin VCHIQ_STATUS_T status; 38655163Sshin struct bulk_waiter_node *waiter = NULL; 38755163Sshin struct list_head *pos; 38855163Sshin 38955163Sshin service = find_service_by_handle(handle); 39055163Sshin if (!service) 39178064Sume return VCHIQ_ERROR; 39255163Sshin 39378064Sume instance = service->instance; 39478064Sume 395110666Sache unlock_service(service); 396110666Sache 397110666Sache lmutex_lock(&instance->bulk_waiter_list_mutex); 398110666Sache list_for_each(pos, &instance->bulk_waiter_list) { 399110666Sache if (list_entry(pos, struct bulk_waiter_node, 40055163Sshin list)->pid == current->p_pid) { 40155163Sshin waiter = list_entry(pos, 40255163Sshin struct bulk_waiter_node, 40355163Sshin list); 40455163Sshin list_del(pos); 40555163Sshin break; 40655163Sshin } 40755163Sshin } 40855163Sshin lmutex_unlock(&instance->bulk_waiter_list_mutex); 40955163Sshin 41055163Sshin if (waiter) { 41155163Sshin VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; 41255163Sshin if (bulk) { 41378064Sume /* This thread has an outstanding bulk transfer. */ 41455163Sshin if ((bulk->data != data) || 41578064Sume (bulk->size != size)) { 41678064Sume /* This is not a retry of the previous one. 41762607Sitojun ** Cancel the signal when the transfer 41855163Sshin ** completes. */ 41955163Sshin spin_lock(&bulk_waiter_spinlock); 42055163Sshin bulk->userdata = NULL; 42155163Sshin spin_unlock(&bulk_waiter_spinlock); 42255163Sshin } 42378064Sume } 42478064Sume } 42578064Sume 42678064Sume if (!waiter) { 42778064Sume waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL); 42878064Sume if (!waiter) { 42978064Sume vchiq_log_error(vchiq_core_log_level, 43078064Sume "%s - out of memory", __func__); 43178064Sume return VCHIQ_ERROR; 43255163Sshin } 43355163Sshin } 43455163Sshin 43555163Sshin status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID, 43655163Sshin data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING, 43755163Sshin dir); 43855163Sshin if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || 43955163Sshin !waiter->bulk_waiter.bulk) { 440119041Sume VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; 441119041Sume if (bulk) { 44255163Sshin /* Cancel the signal when the transfer 44355163Sshin ** completes. */ 44455163Sshin spin_lock(&bulk_waiter_spinlock); 44555163Sshin bulk->userdata = NULL; 44655163Sshin spin_unlock(&bulk_waiter_spinlock); 44755163Sshin } 44855163Sshin _sema_destroy(&waiter->bulk_waiter.event); 44955163Sshin 45078064Sume kfree(waiter); 45178064Sume } else { 45278064Sume waiter->pid = current->p_pid; 45378064Sume lmutex_lock(&instance->bulk_waiter_list_mutex); 45478064Sume list_add(&waiter->list, &instance->bulk_waiter_list); 45578064Sume lmutex_unlock(&instance->bulk_waiter_list_mutex); 45678064Sume vchiq_log_info(vchiq_arm_log_level, 45778064Sume "saved bulk_waiter %x for pid %d", 45878064Sume (unsigned int)waiter, current->p_pid); 45978064Sume } 46078064Sume 46178064Sume return status; 46278064Sume} 46378064Sume