a0b8746173
- Add LIB_VERSION ioctl - Add CLOSE_DELIVERED ioctl - Bump code version Upstream version: 3782f2ad42c08f4d32f64138f8be7341afc380f5
861 lines
23 KiB
C
861 lines
23 KiB
C
/**
|
|
* 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 <interface/compat/vchi_bsd.h>
|
|
|
|
#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
|
|
*
|
|
* 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)
|
|
{
|
|
vchiq_release_message((VCHIQ_SERVICE_HANDLE_T)message->service,
|
|
(VCHIQ_HEADER_T *)message->message);
|
|
|
|
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 *)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);
|
|
|
|
#ifdef 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);
|