diff --git a/drivers/staging/vc04_services/Makefile b/drivers/staging/vc04_services/Makefile index 1ecb261e04ae..fb26b826e640 100644 --- a/drivers/staging/vc04_services/Makefile +++ b/drivers/staging/vc04_services/Makefile @@ -4,7 +4,6 @@ obj-$(CONFIG_BCM2835_VCHIQ) += vchiq.o vchiq-objs := \ interface/vchiq_arm/vchiq_core.o \ interface/vchiq_arm/vchiq_arm.o \ - interface/vchiq_arm/vchiq_kern_lib.o \ interface/vchiq_arm/vchiq_2835_arm.o \ interface/vchiq_arm/vchiq_debugfs.o \ interface/vchiq_arm/vchiq_shim.o \ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index c2c440009cac..f5cefda49b22 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -193,6 +193,375 @@ static const char *const ioctl_names[] = { vchiq_static_assert(ARRAY_SIZE(ioctl_names) == (VCHIQ_IOC_MAX + 1)); +static VCHIQ_STATUS_T +vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, + unsigned int size, VCHIQ_BULK_DIR_T dir); + +#define VCHIQ_INIT_RETRIES 10 +VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instance_out) +{ + VCHIQ_STATUS_T status = VCHIQ_ERROR; + VCHIQ_STATE_T *state; + VCHIQ_INSTANCE_T instance = NULL; + int i; + + vchiq_log_trace(vchiq_core_log_level, "%s called", __func__); + + /* VideoCore may not be ready due to boot up timing. + * It may never be ready if kernel and firmware are mismatched,so don't + * block forever. + */ + for (i = 0; i < VCHIQ_INIT_RETRIES; i++) { + state = vchiq_get_state(); + if (state) + break; + udelay(500); + } + if (i == VCHIQ_INIT_RETRIES) { + vchiq_log_error(vchiq_core_log_level, + "%s: videocore not initialized\n", __func__); + goto failed; + } else if (i > 0) { + vchiq_log_warning(vchiq_core_log_level, + "%s: videocore initialized after %d retries\n", + __func__, i); + } + + instance = kzalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) { + vchiq_log_error(vchiq_core_log_level, + "%s: error allocating vchiq instance\n", __func__); + goto failed; + } + + instance->connected = 0; + instance->state = state; + mutex_init(&instance->bulk_waiter_list_mutex); + INIT_LIST_HEAD(&instance->bulk_waiter_list); + + *instance_out = instance; + + status = VCHIQ_SUCCESS; + +failed: + vchiq_log_trace(vchiq_core_log_level, + "%s(%p): returning %d", __func__, instance, status); + + return status; +} +EXPORT_SYMBOL(vchiq_initialise); + +VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance) +{ + VCHIQ_STATUS_T status; + VCHIQ_STATE_T *state = instance->state; + + vchiq_log_trace(vchiq_core_log_level, + "%s(%p) called", __func__, instance); + + if (mutex_lock_killable(&state->mutex) != 0) + return VCHIQ_RETRY; + + /* Remove all services */ + status = vchiq_shutdown_internal(state, instance); + + mutex_unlock(&state->mutex); + + vchiq_log_trace(vchiq_core_log_level, + "%s(%p): returning %d", __func__, instance, status); + + if (status == VCHIQ_SUCCESS) { + struct list_head *pos, *next; + + list_for_each_safe(pos, next, + &instance->bulk_waiter_list) { + struct bulk_waiter_node *waiter; + + waiter = list_entry(pos, + struct bulk_waiter_node, + list); + list_del(pos); + vchiq_log_info(vchiq_arm_log_level, + "bulk_waiter - cleaned up %pK for pid %d", + waiter, waiter->pid); + kfree(waiter); + } + kfree(instance); + } + + return status; +} +EXPORT_SYMBOL(vchiq_shutdown); + +static int vchiq_is_connected(VCHIQ_INSTANCE_T instance) +{ + return instance->connected; +} + +VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) +{ + VCHIQ_STATUS_T status; + VCHIQ_STATE_T *state = instance->state; + + vchiq_log_trace(vchiq_core_log_level, + "%s(%p) called", __func__, instance); + + if (mutex_lock_killable(&state->mutex) != 0) { + vchiq_log_trace(vchiq_core_log_level, + "%s: call to mutex_lock failed", __func__); + status = VCHIQ_RETRY; + goto failed; + } + status = vchiq_connect_internal(state, instance); + + if (status == VCHIQ_SUCCESS) + instance->connected = 1; + + mutex_unlock(&state->mutex); + +failed: + vchiq_log_trace(vchiq_core_log_level, + "%s(%p): returning %d", __func__, instance, status); + + return status; +} +EXPORT_SYMBOL(vchiq_connect); + +VCHIQ_STATUS_T vchiq_add_service( + VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *phandle) +{ + VCHIQ_STATUS_T status; + VCHIQ_STATE_T *state = instance->state; + VCHIQ_SERVICE_T *service = NULL; + int srvstate; + + vchiq_log_trace(vchiq_core_log_level, + "%s(%p) called", __func__, instance); + + *phandle = VCHIQ_SERVICE_HANDLE_INVALID; + + srvstate = vchiq_is_connected(instance) + ? VCHIQ_SRVSTATE_LISTENING + : VCHIQ_SRVSTATE_HIDDEN; + + service = vchiq_add_service_internal( + state, + params, + srvstate, + instance, + NULL); + + if (service) { + *phandle = service->handle; + status = VCHIQ_SUCCESS; + } else + status = VCHIQ_ERROR; + + vchiq_log_trace(vchiq_core_log_level, + "%s(%p): returning %d", __func__, instance, status); + + return status; +} +EXPORT_SYMBOL(vchiq_add_service); + +VCHIQ_STATUS_T vchiq_open_service( + VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *phandle) +{ + VCHIQ_STATUS_T status = VCHIQ_ERROR; + VCHIQ_STATE_T *state = instance->state; + VCHIQ_SERVICE_T *service = NULL; + + vchiq_log_trace(vchiq_core_log_level, + "%s(%p) called", __func__, instance); + + *phandle = VCHIQ_SERVICE_HANDLE_INVALID; + + if (!vchiq_is_connected(instance)) + goto failed; + + service = vchiq_add_service_internal(state, + params, + VCHIQ_SRVSTATE_OPENING, + instance, + NULL); + + if (service) { + *phandle = service->handle; + status = vchiq_open_service_internal(service, current->pid); + if (status != VCHIQ_SUCCESS) { + vchiq_remove_service(service->handle); + *phandle = VCHIQ_SERVICE_HANDLE_INVALID; + } + } + +failed: + vchiq_log_trace(vchiq_core_log_level, + "%s(%p): returning %d", __func__, instance, status); + + return status; +} +EXPORT_SYMBOL(vchiq_open_service); + +VCHIQ_STATUS_T +vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, + const void *data, unsigned int size, void *userdata) +{ + return vchiq_bulk_transfer(handle, + VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, + VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT); +} +EXPORT_SYMBOL(vchiq_queue_bulk_transmit); + +VCHIQ_STATUS_T +vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, + unsigned int size, void *userdata) +{ + return vchiq_bulk_transfer(handle, + VCHI_MEM_HANDLE_INVALID, data, size, userdata, + VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE); +} +EXPORT_SYMBOL(vchiq_queue_bulk_receive); + +VCHIQ_STATUS_T +vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data, + unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) +{ + VCHIQ_STATUS_T status; + + switch (mode) { + case VCHIQ_BULK_MODE_NOCALLBACK: + case VCHIQ_BULK_MODE_CALLBACK: + status = vchiq_bulk_transfer(handle, + VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, + mode, VCHIQ_BULK_TRANSMIT); + break; + case VCHIQ_BULK_MODE_BLOCKING: + status = vchiq_blocking_bulk_transfer(handle, + (void *)data, size, VCHIQ_BULK_TRANSMIT); + break; + default: + return VCHIQ_ERROR; + } + + return status; +} +EXPORT_SYMBOL(vchiq_bulk_transmit); + +VCHIQ_STATUS_T +vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, + unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) +{ + VCHIQ_STATUS_T status; + + switch (mode) { + case VCHIQ_BULK_MODE_NOCALLBACK: + case VCHIQ_BULK_MODE_CALLBACK: + status = vchiq_bulk_transfer(handle, + VCHI_MEM_HANDLE_INVALID, data, size, userdata, + mode, VCHIQ_BULK_RECEIVE); + break; + case VCHIQ_BULK_MODE_BLOCKING: + status = vchiq_blocking_bulk_transfer(handle, + (void *)data, size, VCHIQ_BULK_RECEIVE); + break; + default: + return VCHIQ_ERROR; + } + + return status; +} +EXPORT_SYMBOL(vchiq_bulk_receive); + +static VCHIQ_STATUS_T +vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, + unsigned int size, VCHIQ_BULK_DIR_T dir) +{ + VCHIQ_INSTANCE_T instance; + VCHIQ_SERVICE_T *service; + VCHIQ_STATUS_T status; + struct bulk_waiter_node *waiter = NULL; + struct list_head *pos; + + service = find_service_by_handle(handle); + if (!service) + return VCHIQ_ERROR; + + instance = service->instance; + + unlock_service(service); + + mutex_lock(&instance->bulk_waiter_list_mutex); + list_for_each(pos, &instance->bulk_waiter_list) { + if (list_entry(pos, struct bulk_waiter_node, + list)->pid == current->pid) { + waiter = list_entry(pos, + struct bulk_waiter_node, + list); + list_del(pos); + break; + } + } + mutex_unlock(&instance->bulk_waiter_list_mutex); + + if (waiter) { + VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; + + if (bulk) { + /* This thread has an outstanding bulk transfer. */ + if ((bulk->data != data) || + (bulk->size != size)) { + /* This is not a retry of the previous one. + * Cancel the signal when the transfer + * completes. + */ + spin_lock(&bulk_waiter_spinlock); + bulk->userdata = NULL; + spin_unlock(&bulk_waiter_spinlock); + } + } + } + + if (!waiter) { + waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL); + if (!waiter) { + vchiq_log_error(vchiq_core_log_level, + "%s - out of memory", __func__); + return VCHIQ_ERROR; + } + } + + status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID, + data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING, + dir); + if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || + !waiter->bulk_waiter.bulk) { + VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; + + if (bulk) { + /* Cancel the signal when the transfer + * completes. + */ + spin_lock(&bulk_waiter_spinlock); + bulk->userdata = NULL; + spin_unlock(&bulk_waiter_spinlock); + } + kfree(waiter); + } else { + waiter->pid = current->pid; + mutex_lock(&instance->bulk_waiter_list_mutex); + list_add(&waiter->list, &instance->bulk_waiter_list); + mutex_unlock(&instance->bulk_waiter_list_mutex); + vchiq_log_info(vchiq_arm_log_level, + "saved bulk_waiter %pK for pid %d", + waiter, current->pid); + } + + return status; +} /**************************************************************************** * * add_completion diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c deleted file mode 100644 index 43c89a08bda9..000000000000 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c +++ /dev/null @@ -1,431 +0,0 @@ -/** - * Copyright (c) 2010-2012 Broadcom. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* ---- Include Files ---------------------------------------------------- */ - -#include -#include -#include - -#include "vchiq_core.h" -#include "vchiq_arm.h" -#include "vchiq_killable.h" - -/* ---- Public Variables ------------------------------------------------- */ - -/* ---- Private Constants and Types -------------------------------------- */ - -struct bulk_waiter_node { - struct bulk_waiter bulk_waiter; - int pid; - struct list_head list; -}; - -struct vchiq_instance_struct { - VCHIQ_STATE_T *state; - - int connected; - - struct list_head bulk_waiter_list; - struct mutex bulk_waiter_list_mutex; -}; - -static VCHIQ_STATUS_T -vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, - unsigned int size, VCHIQ_BULK_DIR_T dir); - -#define VCHIQ_INIT_RETRIES 10 -VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instance_out) -{ - VCHIQ_STATUS_T status = VCHIQ_ERROR; - VCHIQ_STATE_T *state; - VCHIQ_INSTANCE_T instance = NULL; - int i; - - vchiq_log_trace(vchiq_core_log_level, "%s called", __func__); - - /* VideoCore may not be ready due to boot up timing. - * It may never be ready if kernel and firmware are mismatched,so don't - * block forever. - */ - for (i = 0; i < VCHIQ_INIT_RETRIES; i++) { - state = vchiq_get_state(); - if (state) - break; - udelay(500); - } - if (i == VCHIQ_INIT_RETRIES) { - vchiq_log_error(vchiq_core_log_level, - "%s: videocore not initialized\n", __func__); - goto failed; - } else if (i > 0) { - vchiq_log_warning(vchiq_core_log_level, - "%s: videocore initialized after %d retries\n", - __func__, i); - } - - instance = kzalloc(sizeof(*instance), GFP_KERNEL); - if (!instance) { - vchiq_log_error(vchiq_core_log_level, - "%s: error allocating vchiq instance\n", __func__); - goto failed; - } - - instance->connected = 0; - instance->state = state; - mutex_init(&instance->bulk_waiter_list_mutex); - INIT_LIST_HEAD(&instance->bulk_waiter_list); - - *instance_out = instance; - - status = VCHIQ_SUCCESS; - -failed: - vchiq_log_trace(vchiq_core_log_level, - "%s(%p): returning %d", __func__, instance, status); - - return status; -} -EXPORT_SYMBOL(vchiq_initialise); - -VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance) -{ - VCHIQ_STATUS_T status; - VCHIQ_STATE_T *state = instance->state; - - vchiq_log_trace(vchiq_core_log_level, - "%s(%p) called", __func__, instance); - - if (mutex_lock_killable(&state->mutex) != 0) - return VCHIQ_RETRY; - - /* Remove all services */ - status = vchiq_shutdown_internal(state, instance); - - mutex_unlock(&state->mutex); - - vchiq_log_trace(vchiq_core_log_level, - "%s(%p): returning %d", __func__, instance, status); - - if (status == VCHIQ_SUCCESS) { - struct list_head *pos, *next; - - list_for_each_safe(pos, next, - &instance->bulk_waiter_list) { - struct bulk_waiter_node *waiter; - - waiter = list_entry(pos, - struct bulk_waiter_node, - list); - list_del(pos); - vchiq_log_info(vchiq_arm_log_level, - "bulk_waiter - cleaned up %pK for pid %d", - waiter, waiter->pid); - kfree(waiter); - } - kfree(instance); - } - - return status; -} -EXPORT_SYMBOL(vchiq_shutdown); - -static int vchiq_is_connected(VCHIQ_INSTANCE_T instance) -{ - return instance->connected; -} - -VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) -{ - VCHIQ_STATUS_T status; - VCHIQ_STATE_T *state = instance->state; - - vchiq_log_trace(vchiq_core_log_level, - "%s(%p) called", __func__, instance); - - if (mutex_lock_killable(&state->mutex) != 0) { - vchiq_log_trace(vchiq_core_log_level, - "%s: call to mutex_lock failed", __func__); - status = VCHIQ_RETRY; - goto failed; - } - status = vchiq_connect_internal(state, instance); - - if (status == VCHIQ_SUCCESS) - instance->connected = 1; - - mutex_unlock(&state->mutex); - -failed: - vchiq_log_trace(vchiq_core_log_level, - "%s(%p): returning %d", __func__, instance, status); - - return status; -} -EXPORT_SYMBOL(vchiq_connect); - -VCHIQ_STATUS_T vchiq_add_service( - VCHIQ_INSTANCE_T instance, - const VCHIQ_SERVICE_PARAMS_T *params, - VCHIQ_SERVICE_HANDLE_T *phandle) -{ - VCHIQ_STATUS_T status; - VCHIQ_STATE_T *state = instance->state; - VCHIQ_SERVICE_T *service = NULL; - int srvstate; - - vchiq_log_trace(vchiq_core_log_level, - "%s(%p) called", __func__, instance); - - *phandle = VCHIQ_SERVICE_HANDLE_INVALID; - - srvstate = vchiq_is_connected(instance) - ? VCHIQ_SRVSTATE_LISTENING - : VCHIQ_SRVSTATE_HIDDEN; - - service = vchiq_add_service_internal( - state, - params, - srvstate, - instance, - NULL); - - if (service) { - *phandle = service->handle; - status = VCHIQ_SUCCESS; - } else - status = VCHIQ_ERROR; - - vchiq_log_trace(vchiq_core_log_level, - "%s(%p): returning %d", __func__, instance, status); - - return status; -} -EXPORT_SYMBOL(vchiq_add_service); - -VCHIQ_STATUS_T vchiq_open_service( - VCHIQ_INSTANCE_T instance, - const VCHIQ_SERVICE_PARAMS_T *params, - VCHIQ_SERVICE_HANDLE_T *phandle) -{ - VCHIQ_STATUS_T status = VCHIQ_ERROR; - VCHIQ_STATE_T *state = instance->state; - VCHIQ_SERVICE_T *service = NULL; - - vchiq_log_trace(vchiq_core_log_level, - "%s(%p) called", __func__, instance); - - *phandle = VCHIQ_SERVICE_HANDLE_INVALID; - - if (!vchiq_is_connected(instance)) - goto failed; - - service = vchiq_add_service_internal(state, - params, - VCHIQ_SRVSTATE_OPENING, - instance, - NULL); - - if (service) { - *phandle = service->handle; - status = vchiq_open_service_internal(service, current->pid); - if (status != VCHIQ_SUCCESS) { - vchiq_remove_service(service->handle); - *phandle = VCHIQ_SERVICE_HANDLE_INVALID; - } - } - -failed: - vchiq_log_trace(vchiq_core_log_level, - "%s(%p): returning %d", __func__, instance, status); - - return status; -} -EXPORT_SYMBOL(vchiq_open_service); - -VCHIQ_STATUS_T -vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, - const void *data, unsigned int size, void *userdata) -{ - return vchiq_bulk_transfer(handle, - VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, - VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT); -} -EXPORT_SYMBOL(vchiq_queue_bulk_transmit); - -VCHIQ_STATUS_T -vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, - unsigned int size, void *userdata) -{ - return vchiq_bulk_transfer(handle, - VCHI_MEM_HANDLE_INVALID, data, size, userdata, - VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE); -} -EXPORT_SYMBOL(vchiq_queue_bulk_receive); - -VCHIQ_STATUS_T -vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data, - unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) -{ - VCHIQ_STATUS_T status; - - switch (mode) { - case VCHIQ_BULK_MODE_NOCALLBACK: - case VCHIQ_BULK_MODE_CALLBACK: - status = vchiq_bulk_transfer(handle, - VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, - mode, VCHIQ_BULK_TRANSMIT); - break; - case VCHIQ_BULK_MODE_BLOCKING: - status = vchiq_blocking_bulk_transfer(handle, - (void *)data, size, VCHIQ_BULK_TRANSMIT); - break; - default: - return VCHIQ_ERROR; - } - - return status; -} -EXPORT_SYMBOL(vchiq_bulk_transmit); - -VCHIQ_STATUS_T -vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, - unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) -{ - VCHIQ_STATUS_T status; - - switch (mode) { - case VCHIQ_BULK_MODE_NOCALLBACK: - case VCHIQ_BULK_MODE_CALLBACK: - status = vchiq_bulk_transfer(handle, - VCHI_MEM_HANDLE_INVALID, data, size, userdata, - mode, VCHIQ_BULK_RECEIVE); - break; - case VCHIQ_BULK_MODE_BLOCKING: - status = vchiq_blocking_bulk_transfer(handle, - (void *)data, size, VCHIQ_BULK_RECEIVE); - break; - default: - return VCHIQ_ERROR; - } - - return status; -} -EXPORT_SYMBOL(vchiq_bulk_receive); - -static VCHIQ_STATUS_T -vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, - unsigned int size, VCHIQ_BULK_DIR_T dir) -{ - VCHIQ_INSTANCE_T instance; - VCHIQ_SERVICE_T *service; - VCHIQ_STATUS_T status; - struct bulk_waiter_node *waiter = NULL; - struct list_head *pos; - - service = find_service_by_handle(handle); - if (!service) - return VCHIQ_ERROR; - - instance = service->instance; - - unlock_service(service); - - mutex_lock(&instance->bulk_waiter_list_mutex); - list_for_each(pos, &instance->bulk_waiter_list) { - if (list_entry(pos, struct bulk_waiter_node, - list)->pid == current->pid) { - waiter = list_entry(pos, - struct bulk_waiter_node, - list); - list_del(pos); - break; - } - } - mutex_unlock(&instance->bulk_waiter_list_mutex); - - if (waiter) { - VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; - - if (bulk) { - /* This thread has an outstanding bulk transfer. */ - if ((bulk->data != data) || - (bulk->size != size)) { - /* This is not a retry of the previous one. - * Cancel the signal when the transfer - * completes. - */ - spin_lock(&bulk_waiter_spinlock); - bulk->userdata = NULL; - spin_unlock(&bulk_waiter_spinlock); - } - } - } - - if (!waiter) { - waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL); - if (!waiter) { - vchiq_log_error(vchiq_core_log_level, - "%s - out of memory", __func__); - return VCHIQ_ERROR; - } - } - - status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID, - data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING, - dir); - if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || - !waiter->bulk_waiter.bulk) { - VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; - - if (bulk) { - /* Cancel the signal when the transfer - * completes. - */ - spin_lock(&bulk_waiter_spinlock); - bulk->userdata = NULL; - spin_unlock(&bulk_waiter_spinlock); - } - kfree(waiter); - } else { - waiter->pid = current->pid; - mutex_lock(&instance->bulk_waiter_list_mutex); - list_add(&waiter->list, &instance->bulk_waiter_list); - mutex_unlock(&instance->bulk_waiter_list_mutex); - vchiq_log_info(vchiq_arm_log_level, - "saved bulk_waiter %pK for pid %d", - waiter, current->pid); - } - - return status; -}