/** * 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 #include "interface/vchi/vchi.h" #include "vchiq.h" #include "vchiq_core.h" #include "vchiq_util.h" #define vchiq_status_to_vchi(status) ((int32_t)status) typedef struct { VCHIQ_SERVICE_HANDLE_T handle; VCHIU_QUEUE_T queue; VCHI_CALLBACK_T callback; void *callback_param; } SHIM_SERVICE_T; /* ---------------------------------------------------------------------- * return pointer to the mphi message driver function table * -------------------------------------------------------------------- */ const VCHI_MESSAGE_DRIVER_T * vchi_mphi_message_driver_func_table(void) { return NULL; } /* ---------------------------------------------------------------------- * return a pointer to the 'single' connection driver fops * -------------------------------------------------------------------- */ const VCHI_CONNECTION_API_T * single_get_func_table(void) { return NULL; } VCHI_CONNECTION_T *vchi_create_connection( const VCHI_CONNECTION_API_T *function_table, const VCHI_MESSAGE_DRIVER_T *low_level) { (void)function_table; (void)low_level; return NULL; } /*********************************************************** * Name: vchi_msg_peek * * Arguments: const VCHI_SERVICE_HANDLE_T handle, * void **data, * uint32_t *msg_size, * VCHI_FLAGS_T flags * * Description: Routine to return a pointer to the current message (to allow in * place processing). The message can be removed using * vchi_msg_remove when you're finished * * Returns: int32_t - success == 0 * ***********************************************************/ int32_t vchi_msg_peek(VCHI_SERVICE_HANDLE_T handle, void **data, uint32_t *msg_size, VCHI_FLAGS_T flags) { SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; VCHIQ_HEADER_T *header; WARN_ON((flags != VCHI_FLAGS_NONE) && (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); if (flags == VCHI_FLAGS_NONE) if (vchiu_queue_is_empty(&service->queue)) return -1; header = vchiu_queue_peek(&service->queue); *data = header->data; *msg_size = header->size; return 0; } EXPORT_SYMBOL(vchi_msg_peek); /*********************************************************** * Name: vchi_msg_remove * * Arguments: const VCHI_SERVICE_HANDLE_T handle, * * Description: Routine to remove a message (after it has been read with * vchi_msg_peek) * * Returns: int32_t - success == 0 * ***********************************************************/ int32_t vchi_msg_remove(VCHI_SERVICE_HANDLE_T handle) { SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; VCHIQ_HEADER_T *header; header = vchiu_queue_pop(&service->queue); vchiq_release_message(service->handle, header); return 0; } EXPORT_SYMBOL(vchi_msg_remove); /*********************************************************** * Name: vchi_msg_queue * * Arguments: VCHI_SERVICE_HANDLE_T handle, * const void *data, * uint32_t data_size, * VCHI_FLAGS_T flags, * void *msg_handle, * * Description: Thin wrapper to queue a message onto a connection * * Returns: int32_t - success == 0 * ***********************************************************/ int32_t vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, const void *data, uint32_t data_size, VCHI_FLAGS_T flags, void *msg_handle) { SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; VCHIQ_ELEMENT_T element = {data, data_size}; VCHIQ_STATUS_T status; (void)msg_handle; WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); status = vchiq_queue_message(service->handle, &element, 1); /* vchiq_queue_message() may return VCHIQ_RETRY, so we need to ** implement a retry mechanism since this function is supposed ** to block until queued */ while (status == VCHIQ_RETRY) { msleep(1); status = vchiq_queue_message(service->handle, &element, 1); } return vchiq_status_to_vchi(status); } EXPORT_SYMBOL(vchi_msg_queue); /*********************************************************** * Name: vchi_bulk_queue_receive * * Arguments: VCHI_BULK_HANDLE_T handle, * void *data_dst, * const uint32_t data_size, * VCHI_FLAGS_T flags * void *bulk_handle * * Description: Routine to setup a rcv buffer * * Returns: int32_t - success == 0 * ***********************************************************/ int32_t vchi_bulk_queue_receive(VCHI_SERVICE_HANDLE_T handle, void *data_dst, uint32_t data_size, VCHI_FLAGS_T flags, void *bulk_handle) { SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; VCHIQ_BULK_MODE_T mode; VCHIQ_STATUS_T status; switch ((int)flags) { case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: WARN_ON(!service->callback); mode = VCHIQ_BULK_MODE_CALLBACK; break; case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: mode = VCHIQ_BULK_MODE_BLOCKING; break; case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: case VCHI_FLAGS_NONE: mode = VCHIQ_BULK_MODE_NOCALLBACK; break; default: WARN(1, "unsupported message\n"); return vchiq_status_to_vchi(VCHIQ_ERROR); } status = vchiq_bulk_receive(service->handle, data_dst, data_size, bulk_handle, mode); /* vchiq_bulk_receive() may return VCHIQ_RETRY, so we need to ** implement a retry mechanism since this function is supposed ** to block until queued */ while (status == VCHIQ_RETRY) { msleep(1); status = vchiq_bulk_receive(service->handle, data_dst, data_size, bulk_handle, mode); } return vchiq_status_to_vchi(status); } EXPORT_SYMBOL(vchi_bulk_queue_receive); /*********************************************************** * Name: vchi_bulk_queue_transmit * * Arguments: VCHI_BULK_HANDLE_T handle, * void *data_src, * uint32_t data_size, * VCHI_FLAGS_T flags, * void *bulk_handle * * Description: Routine to transmit some data * * Returns: int32_t - success == 0 * ***********************************************************/ int32_t vchi_bulk_queue_transmit(VCHI_SERVICE_HANDLE_T handle, void *data_src, uint32_t data_size, VCHI_FLAGS_T flags, void *bulk_handle) { SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; VCHIQ_BULK_MODE_T mode; VCHIQ_STATUS_T status; switch ((int)flags) { case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: WARN_ON(!service->callback); mode = VCHIQ_BULK_MODE_CALLBACK; break; case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: mode = VCHIQ_BULK_MODE_BLOCKING; break; case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: case VCHI_FLAGS_NONE: mode = VCHIQ_BULK_MODE_NOCALLBACK; break; default: WARN(1, "unsupported message\n"); return vchiq_status_to_vchi(VCHIQ_ERROR); } status = vchiq_bulk_transmit(service->handle, data_src, data_size, bulk_handle, mode); /* vchiq_bulk_transmit() may return VCHIQ_RETRY, so we need to ** implement a retry mechanism since this function is supposed ** to block until queued */ while (status == VCHIQ_RETRY) { msleep(1); status = vchiq_bulk_transmit(service->handle, data_src, data_size, bulk_handle, mode); } return vchiq_status_to_vchi(status); } EXPORT_SYMBOL(vchi_bulk_queue_transmit); /*********************************************************** * Name: vchi_msg_dequeue * * Arguments: VCHI_SERVICE_HANDLE_T handle, * void *data, * uint32_t max_data_size_to_read, * uint32_t *actual_msg_size * VCHI_FLAGS_T flags * * Description: Routine to dequeue a message into the supplied buffer * * Returns: int32_t - success == 0 * ***********************************************************/ int32_t vchi_msg_dequeue(VCHI_SERVICE_HANDLE_T handle, void *data, uint32_t max_data_size_to_read, uint32_t *actual_msg_size, VCHI_FLAGS_T flags) { SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; VCHIQ_HEADER_T *header; WARN_ON((flags != VCHI_FLAGS_NONE) && (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); if (flags == VCHI_FLAGS_NONE) if (vchiu_queue_is_empty(&service->queue)) return -1; header = vchiu_queue_pop(&service->queue); memcpy(data, header->data, header->size < max_data_size_to_read ? header->size : max_data_size_to_read); *actual_msg_size = header->size; vchiq_release_message(service->handle, header); return 0; } EXPORT_SYMBOL(vchi_msg_dequeue); /*********************************************************** * Name: vchi_msg_queuev * * Arguments: VCHI_SERVICE_HANDLE_T handle, * VCHI_MSG_VECTOR_T *vector, * uint32_t count, * VCHI_FLAGS_T flags, * void *msg_handle * * Description: Thin wrapper to queue a message onto a connection * * Returns: int32_t - success == 0 * ***********************************************************/ vchiq_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == offsetof(VCHIQ_ELEMENT_T, data)); vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == offsetof(VCHIQ_ELEMENT_T, size)); int32_t vchi_msg_queuev(VCHI_SERVICE_HANDLE_T handle, VCHI_MSG_VECTOR_T *vector, uint32_t count, VCHI_FLAGS_T flags, void *msg_handle) { SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; (void)msg_handle; WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); return vchiq_status_to_vchi(vchiq_queue_message(service->handle, (const VCHIQ_ELEMENT_T *)vector, count)); } EXPORT_SYMBOL(vchi_msg_queuev); /*********************************************************** * Name: vchi_held_msg_release * * Arguments: VCHI_HELD_MSG_T *message_handle * * Description: Routine to release a held message (after it has been read with * vchi_msg_hold) * * Returns: int32_t - success == 0 * ***********************************************************/ int32_t vchi_held_msg_release(VCHI_HELD_MSG_T *message_handle) { SHIM_SERVICE_T *service; VCHIQ_HEADER_T *header; service = (SHIM_SERVICE_T *)message_handle->service; header = (VCHIQ_HEADER_T *)message_handle->message; vchiq_release_message(service->handle, header); return 0; } EXPORT_SYMBOL(vchi_held_msg_release); /*********************************************************** * Name: vchi_msg_hold * * Arguments: VCHI_SERVICE_HANDLE_T handle, * void **data, * uint32_t *msg_size, * VCHI_FLAGS_T flags, * VCHI_HELD_MSG_T *message_handle * * Description: Routine to return a pointer to the current message (to allow * in place processing). The message is dequeued - don't forget * to release the message using vchi_held_msg_release when you're * finished. * * Returns: int32_t - success == 0 * ***********************************************************/ int32_t vchi_msg_hold(VCHI_SERVICE_HANDLE_T handle, void **data, uint32_t *msg_size, VCHI_FLAGS_T flags, VCHI_HELD_MSG_T *message_handle) { SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; VCHIQ_HEADER_T *header; WARN_ON((flags != VCHI_FLAGS_NONE) && (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); if (flags == VCHI_FLAGS_NONE) if (vchiu_queue_is_empty(&service->queue)) return -1; header = vchiu_queue_pop(&service->queue); *data = header->data; *msg_size = header->size; message_handle->service = (struct opaque_vchi_service_t *)(uintptr_t)service->handle; message_handle->message = header; return 0; } EXPORT_SYMBOL(vchi_msg_hold); /*********************************************************** * Name: vchi_initialise * * Arguments: VCHI_INSTANCE_T *instance_handle * * Description: Initialises the hardware but does not transmit anything * When run as a Host App this will be called twice hence the need * to malloc the state information * * Returns: 0 if successful, failure otherwise * ***********************************************************/ int32_t vchi_initialise(VCHI_INSTANCE_T *instance_handle) { VCHIQ_INSTANCE_T instance; VCHIQ_STATUS_T status; status = vchiq_initialise(&instance); *instance_handle = (VCHI_INSTANCE_T)instance; return vchiq_status_to_vchi(status); } EXPORT_SYMBOL(vchi_initialise); /*********************************************************** * Name: vchi_connect * * Arguments: VCHI_CONNECTION_T **connections * const uint32_t num_connections * VCHI_INSTANCE_T instance_handle) * * Description: Starts the command service on each connection, * causing INIT messages to be pinged back and forth * * Returns: 0 if successful, failure otherwise * ***********************************************************/ int32_t vchi_connect(VCHI_CONNECTION_T **connections, const uint32_t num_connections, VCHI_INSTANCE_T instance_handle) { VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; (void)connections; (void)num_connections; return vchiq_connect(instance); } EXPORT_SYMBOL(vchi_connect); /*********************************************************** * Name: vchi_disconnect * * Arguments: VCHI_INSTANCE_T instance_handle * * Description: Stops the command service on each connection, * causing DE-INIT messages to be pinged back and forth * * Returns: 0 if successful, failure otherwise * ***********************************************************/ int32_t vchi_disconnect(VCHI_INSTANCE_T instance_handle) { VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; return vchiq_status_to_vchi(vchiq_shutdown(instance)); } EXPORT_SYMBOL(vchi_disconnect); /*********************************************************** * Name: vchi_service_open * Name: vchi_service_create * * Arguments: VCHI_INSTANCE_T *instance_handle * SERVICE_CREATION_T *setup, * VCHI_SERVICE_HANDLE_T *handle * * Description: Routine to open a service * * Returns: int32_t - success == 0 * ***********************************************************/ static VCHIQ_STATUS_T shim_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T handle, void *bulk_user) { SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(handle); if (!service->callback) goto release; switch (reason) { case VCHIQ_MESSAGE_AVAILABLE: vchiu_queue_push(&service->queue, header); service->callback(service->callback_param, VCHI_CALLBACK_MSG_AVAILABLE, NULL); goto done; break; case VCHIQ_BULK_TRANSMIT_DONE: service->callback(service->callback_param, VCHI_CALLBACK_BULK_SENT, bulk_user); break; case VCHIQ_BULK_RECEIVE_DONE: service->callback(service->callback_param, VCHI_CALLBACK_BULK_RECEIVED, bulk_user); break; case VCHIQ_SERVICE_CLOSED: service->callback(service->callback_param, VCHI_CALLBACK_SERVICE_CLOSED, NULL); break; case VCHIQ_SERVICE_OPENED: /* No equivalent VCHI reason */ break; case VCHIQ_BULK_TRANSMIT_ABORTED: service->callback(service->callback_param, VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, bulk_user); break; case VCHIQ_BULK_RECEIVE_ABORTED: service->callback(service->callback_param, VCHI_CALLBACK_BULK_RECEIVE_ABORTED, bulk_user); break; default: WARN(1, "not supported\n"); break; } release: vchiq_release_message(service->handle, header); done: return VCHIQ_SUCCESS; } static SHIM_SERVICE_T *service_alloc(VCHIQ_INSTANCE_T instance, SERVICE_CREATION_T *setup) { SHIM_SERVICE_T *service = kzalloc(sizeof(SHIM_SERVICE_T), GFP_KERNEL); (void)instance; if (service) { if (vchiu_queue_init(&service->queue, 64)) { service->callback = setup->callback; service->callback_param = setup->callback_param; } else { kfree(service); service = NULL; } } return service; } static void service_free(SHIM_SERVICE_T *service) { if (service) { vchiu_queue_delete(&service->queue); kfree(service); } } int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, SERVICE_CREATION_T *setup, VCHI_SERVICE_HANDLE_T *handle) { VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; SHIM_SERVICE_T *service = service_alloc(instance, setup); *handle = (VCHI_SERVICE_HANDLE_T)service; if (service) { VCHIQ_SERVICE_PARAMS_T params; VCHIQ_STATUS_T status; memset(¶ms, 0, sizeof(params)); params.fourcc = setup->service_id; params.callback = shim_callback; params.userdata = service; params.version = setup->version.version; params.version_min = setup->version.version_min; status = vchiq_open_service(instance, ¶ms, &service->handle); if (status != VCHIQ_SUCCESS) { service_free(service); service = NULL; *handle = NULL; } } return (service != NULL) ? 0 : -1; } EXPORT_SYMBOL(vchi_service_open); int32_t vchi_service_create(VCHI_INSTANCE_T instance_handle, SERVICE_CREATION_T *setup, VCHI_SERVICE_HANDLE_T *handle) { VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; SHIM_SERVICE_T *service = service_alloc(instance, setup); *handle = (VCHI_SERVICE_HANDLE_T)service; if (service) { VCHIQ_SERVICE_PARAMS_T params; VCHIQ_STATUS_T status; memset(¶ms, 0, sizeof(params)); params.fourcc = setup->service_id; params.callback = shim_callback; params.userdata = service; params.version = setup->version.version; params.version_min = setup->version.version_min; status = vchiq_add_service(instance, ¶ms, &service->handle); if (status != VCHIQ_SUCCESS) { service_free(service); service = NULL; *handle = NULL; } } return (service != NULL) ? 0 : -1; } EXPORT_SYMBOL(vchi_service_create); int32_t vchi_service_close(const VCHI_SERVICE_HANDLE_T handle) { int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; if (service) { VCHIQ_STATUS_T status = vchiq_close_service(service->handle); if (status == VCHIQ_SUCCESS) { service_free(service); service = NULL; } ret = vchiq_status_to_vchi(status); } return ret; } EXPORT_SYMBOL(vchi_service_close); int32_t vchi_service_destroy(const VCHI_SERVICE_HANDLE_T handle) { int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; if (service) { VCHIQ_STATUS_T status = vchiq_remove_service(service->handle); if (status == VCHIQ_SUCCESS) { service_free(service); service = NULL; } ret = vchiq_status_to_vchi(status); } return ret; } EXPORT_SYMBOL(vchi_service_destroy); int32_t vchi_service_set_option(const VCHI_SERVICE_HANDLE_T handle, VCHI_SERVICE_OPTION_T option, int value) { int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; VCHIQ_SERVICE_OPTION_T vchiq_option; switch (option) { case VCHI_SERVICE_OPTION_TRACE: vchiq_option = VCHIQ_SERVICE_OPTION_TRACE; break; case VCHI_SERVICE_OPTION_SYNCHRONOUS: vchiq_option = VCHIQ_SERVICE_OPTION_SYNCHRONOUS; break; default: service = NULL; break; } if (service) { VCHIQ_STATUS_T status = vchiq_set_service_option(service->handle, vchiq_option, value); ret = vchiq_status_to_vchi(status); } return ret; } EXPORT_SYMBOL(vchi_service_set_option); int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, short *peer_version ) { int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; if(service) { VCHIQ_STATUS_T status = vchiq_get_peer_version(service->handle, peer_version); ret = vchiq_status_to_vchi( status ); } return ret; } EXPORT_SYMBOL(vchi_get_peer_version); #if notyet /* ---------------------------------------------------------------------- * read a uint32_t from buffer. * network format is defined to be little endian * -------------------------------------------------------------------- */ uint32_t vchi_readbuf_uint32(const void *_ptr) { const unsigned char *ptr = _ptr; return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); } /* ---------------------------------------------------------------------- * write a uint32_t to buffer. * network format is defined to be little endian * -------------------------------------------------------------------- */ void vchi_writebuf_uint32(void *_ptr, uint32_t value) { unsigned char *ptr = _ptr; ptr[0] = (unsigned char)((value >> 0) & 0xFF); ptr[1] = (unsigned char)((value >> 8) & 0xFF); ptr[2] = (unsigned char)((value >> 16) & 0xFF); ptr[3] = (unsigned char)((value >> 24) & 0xFF); } /* ---------------------------------------------------------------------- * read a uint16_t from buffer. * network format is defined to be little endian * -------------------------------------------------------------------- */ uint16_t vchi_readbuf_uint16(const void *_ptr) { const unsigned char *ptr = _ptr; return ptr[0] | (ptr[1] << 8); } /* ---------------------------------------------------------------------- * write a uint16_t into the buffer. * network format is defined to be little endian * -------------------------------------------------------------------- */ void vchi_writebuf_uint16(void *_ptr, uint16_t value) { unsigned char *ptr = _ptr; ptr[0] = (value >> 0) & 0xFF; ptr[1] = (value >> 8) & 0xFF; } #endif /*********************************************************** * Name: vchi_service_use * * Arguments: const VCHI_SERVICE_HANDLE_T handle * * Description: Routine to increment refcount on a service * * Returns: void * ***********************************************************/ int32_t vchi_service_use(const VCHI_SERVICE_HANDLE_T handle) { int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; if (service) ret = vchiq_status_to_vchi(vchiq_use_service(service->handle)); return ret; } EXPORT_SYMBOL(vchi_service_use); /*********************************************************** * Name: vchi_service_release * * Arguments: const VCHI_SERVICE_HANDLE_T handle * * Description: Routine to decrement refcount on a service * * Returns: void * ***********************************************************/ int32_t vchi_service_release(const VCHI_SERVICE_HANDLE_T handle) { int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; if (service) ret = vchiq_status_to_vchi( vchiq_release_service(service->handle)); return ret; } EXPORT_SYMBOL(vchi_service_release);