summaryrefslogtreecommitdiffstats
path: root/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2018-02-02 16:01:46 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-02-16 15:23:32 +0100
commit5c5e6ef6287cbf31f43c8c9f643d197ab9eb2039 (patch)
tree06202431ee81fa5e436cf38f196a453011b7125d /drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
parente5fe0af51707faa1016465e5e9775cb60fdd8629 (diff)
staging: vc04_services: merge vchiq_kern_lib.c into vchiq_arm.c
There are two incompatible definitions of 'vchiq_instance_struct', so passing them through vchiq_initialise(), vchiq_connect() or another such interface is broken, as shown by building the driver with link-time optimizations: drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h:129:0: error: type of 'vchiq_initialise' does not match original declaration [-Werror=lto-type-mismatch] extern VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *pinstance); drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c:68:0: note: 'vchiq_initialise' was previously declared here VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instance_out) drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c:68:0: note: code may be misoptimized unless -fno-strict-aliasing is used drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h:131:0: error: type of 'vchiq_connect' does not match original declaration [-Werror=lto-type-mismatch] extern VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance); drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c:168:0: note: 'vchiq_connect' was previously declared here VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) It's possible that only one of the two sides actually access the members, but it's clear that they need to agree on the layout. The easiest way to achieve this appears to be to merge the two files into one. I tried moving the structure definition into a shared header first, but ended up running into too many interdependencies that way. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Stefan Wahren <stefan.wahren@i2se.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c')
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c369
1 files changed, 369 insertions, 0 deletions
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