}
}
-struct vchiq_io_copy_callback_context {
- VCHIQ_ELEMENT_T *current_element;
- size_t current_element_offset;
- unsigned long elements_to_go;
- size_t current_offset;
-};
-
-static ssize_t
-vchiq_ioc_copy_element_data(
- void *context,
- void *dest,
- size_t offset,
- size_t maxsize)
-{
- long res;
- size_t bytes_this_round;
- struct vchiq_io_copy_callback_context *copy_context =
- (struct vchiq_io_copy_callback_context *)context;
-
- if (offset != copy_context->current_offset)
- return 0;
-
- if (!copy_context->elements_to_go)
- return 0;
-
- /*
- * Complex logic here to handle the case of 0 size elements
- * in the middle of the array of elements.
- *
- * Need to skip over these 0 size elements.
- */
- while (1) {
- bytes_this_round = min(copy_context->current_element->size -
- copy_context->current_element_offset,
- maxsize);
-
- if (bytes_this_round)
- break;
-
- copy_context->elements_to_go--;
- copy_context->current_element++;
- copy_context->current_element_offset = 0;
-
- if (!copy_context->elements_to_go)
- return 0;
- }
-
- res = copy_from_user(dest,
- copy_context->current_element->data +
- copy_context->current_element_offset,
- bytes_this_round);
-
- if (res != 0)
- return -EFAULT;
-
- copy_context->current_element_offset += bytes_this_round;
- copy_context->current_offset += bytes_this_round;
-
- /*
- * Check if done with current element, and if so advance to the next.
- */
- if (copy_context->current_element_offset ==
- copy_context->current_element->size) {
- copy_context->elements_to_go--;
- copy_context->current_element++;
- copy_context->current_element_offset = 0;
- }
-
- return bytes_this_round;
-}
-
-/**************************************************************************
- *
- * vchiq_ioc_queue_message
- *
- **************************************************************************/
-static VCHIQ_STATUS_T
-vchiq_ioc_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
- VCHIQ_ELEMENT_T *elements,
- unsigned long count)
-{
- struct vchiq_io_copy_callback_context context;
- unsigned long i;
- size_t total_size = 0;
-
- context.current_element = elements;
- context.current_element_offset = 0;
- context.elements_to_go = count;
- context.current_offset = 0;
-
- for (i = 0; i < count; i++) {
- if (!elements[i].data && elements[i].size != 0)
- return -EFAULT;
-
- total_size += elements[i].size;
- }
-
- return vchiq_queue_message(handle, vchiq_ioc_copy_element_data,
- &context, total_size);
-}
-
/****************************************************************************
*
* vchiq_ioctl
VCHIQ_ELEMENT_T elements[MAX_ELEMENTS];
if (copy_from_user(elements, args.elements,
args.count * sizeof(VCHIQ_ELEMENT_T)) == 0)
- status = vchiq_ioc_queue_message
+ status = vchiq_queue_message
(args.handle,
elements, args.count);
else
** enough for a header. This relies on header size being a power of two, which
** has been verified earlier by a static assertion. */
-static inline size_t
-calc_stride(size_t size)
+static inline unsigned int
+calc_stride(unsigned int size)
{
/* Allow room for the header */
size += sizeof(VCHIQ_HEADER_T);
/* Called from queue_message, by the slot handler and application threads,
** with slot_mutex held */
static VCHIQ_HEADER_T *
-reserve_space(VCHIQ_STATE_T *state, size_t space, int is_blocking)
+reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking)
{
VCHIQ_SHARED_STATE_T *local = state->local;
int tx_pos = state->local_tx_pos;
}
}
-static ssize_t
-memcpy_copy_callback(
- void *context, void *dest,
- size_t offset, size_t maxsize)
-{
- void *src = context;
-
- memcpy(dest + offset, src + offset, maxsize);
- return maxsize;
-}
-
-static ssize_t
-copy_message_data(
- ssize_t (*copy_callback)(void *context, void *dest,
- size_t offset, size_t maxsize),
- void *context,
- void *dest,
- size_t size)
-{
- size_t pos = 0;
-
- while (pos < size) {
- ssize_t callback_result;
- size_t max_bytes = size - pos;
-
- callback_result =
- copy_callback(context, dest + pos,
- pos, max_bytes);
-
- if (callback_result < 0)
- return callback_result;
-
- if (!callback_result)
- return -EIO;
-
- if (callback_result > max_bytes)
- return -EIO;
-
- pos += callback_result;
- }
-
- return size;
-}
-
/* Called by the slot handler and application threads */
static VCHIQ_STATUS_T
queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
- int msgid,
- ssize_t (*copy_callback)(void *context, void *dest,
- size_t offset, size_t maxsize),
- void *context,
- size_t size,
- int flags)
+ int msgid, const VCHIQ_ELEMENT_T *elements,
+ int count, int size, int flags)
{
VCHIQ_SHARED_STATE_T *local;
VCHIQ_SERVICE_QUOTA_T *service_quota = NULL;
VCHIQ_HEADER_T *header;
int type = VCHIQ_MSG_TYPE(msgid);
- size_t stride;
+ unsigned int stride;
local = state->local;
}
if (type == VCHIQ_MSG_DATA) {
- ssize_t callback_result;
+ int i, pos;
int tx_end_index;
int slot_use_count;
BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK |
QMFLAGS_NO_MUTEX_UNLOCK)) != 0);
- callback_result =
- copy_message_data(copy_callback, context,
- header->data, size);
-
- if (callback_result < 0) {
- mutex_unlock(&state->slot_mutex);
- VCHIQ_SERVICE_STATS_INC(service,
+ for (i = 0, pos = 0; i < (unsigned int)count;
+ pos += elements[i++].size)
+ if (elements[i].size) {
+ if (vchiq_copy_from_user
+ (header->data + pos, elements[i].data,
+ (size_t) elements[i].size) !=
+ VCHIQ_SUCCESS) {
+ mutex_unlock(&state->slot_mutex);
+ VCHIQ_SERVICE_STATS_INC(service,
error_count);
- return VCHIQ_ERROR;
- }
-
- if (SRVTRACE_ENABLED(service,
- VCHIQ_LOG_INFO))
- vchiq_log_dump_mem("Sent", 0,
- header->data,
- min((size_t)64,
- (size_t)callback_result));
+ return VCHIQ_ERROR;
+ }
+ if (i == 0) {
+ if (SRVTRACE_ENABLED(service,
+ VCHIQ_LOG_INFO))
+ vchiq_log_dump_mem("Sent", 0,
+ header->data + pos,
+ min(64u,
+ elements[0].size));
+ }
+ }
spin_lock("a_spinlock);
service_quota->message_use_count++;
header, size, VCHIQ_MSG_SRCPORT(msgid),
VCHIQ_MSG_DSTPORT(msgid));
if (size != 0) {
- /* It is assumed for now that this code path
- * only happens from calls inside this file.
- *
- * External callers are through the vchiq_queue_message
- * path which always sets the type to be VCHIQ_MSG_DATA
- *
- * At first glance this appears to be correct but
- * more review is needed.
- */
- copy_message_data(copy_callback, context,
- header->data, size);
+ WARN_ON(!((count == 1) && (size == elements[0].size)));
+ memcpy(header->data, elements[0].data,
+ elements[0].size);
}
VCHIQ_STATS_INC(state, ctrl_tx_count);
}
/* Called by the slot handler and application threads */
static VCHIQ_STATUS_T
queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
- int msgid,
- ssize_t (*copy_callback)(void *context, void *dest,
- size_t offset, size_t maxsize),
- void *context,
- int size,
- int is_blocking)
+ int msgid, const VCHIQ_ELEMENT_T *elements,
+ int count, int size, int is_blocking)
{
VCHIQ_SHARED_STATE_T *local;
VCHIQ_HEADER_T *header;
- ssize_t callback_result;
local = state->local;
state->id, oldmsgid);
}
- vchiq_log_info(vchiq_sync_log_level,
- "%d: qms %s@%pK,%x (%d->%d)", state->id,
- msg_type_str(VCHIQ_MSG_TYPE(msgid)),
- header, size, VCHIQ_MSG_SRCPORT(msgid),
- VCHIQ_MSG_DSTPORT(msgid));
-
- callback_result =
- copy_message_data(copy_callback, context,
- header->data, size);
+ if (service) {
+ int i, pos;
- if (callback_result < 0) {
- mutex_unlock(&state->slot_mutex);
- VCHIQ_SERVICE_STATS_INC(service,
- error_count);
- return VCHIQ_ERROR;
- }
+ vchiq_log_info(vchiq_sync_log_level,
+ "%d: qms %s@%pK,%x (%d->%d)", state->id,
+ msg_type_str(VCHIQ_MSG_TYPE(msgid)),
+ header, size, VCHIQ_MSG_SRCPORT(msgid),
+ VCHIQ_MSG_DSTPORT(msgid));
- if (service) {
- if (SRVTRACE_ENABLED(service,
- VCHIQ_LOG_INFO))
- vchiq_log_dump_mem("Sent", 0,
- header->data,
- min((size_t)64,
- (size_t)callback_result));
+ for (i = 0, pos = 0; i < (unsigned int)count;
+ pos += elements[i++].size)
+ if (elements[i].size) {
+ if (vchiq_copy_from_user
+ (header->data + pos, elements[i].data,
+ (size_t) elements[i].size) !=
+ VCHIQ_SUCCESS) {
+ mutex_unlock(&state->sync_mutex);
+ VCHIQ_SERVICE_STATS_INC(service,
+ error_count);
+ return VCHIQ_ERROR;
+ }
+ if (i == 0) {
+ if (vchiq_sync_log_level >=
+ VCHIQ_LOG_TRACE)
+ vchiq_log_dump_mem("Sent Sync",
+ 0, header->data + pos,
+ min(64u,
+ elements[0].size));
+ }
+ }
VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count);
VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size);
} else {
+ vchiq_log_info(vchiq_sync_log_level,
+ "%d: qms %s@%pK,%x (%d->%d)", state->id,
+ msg_type_str(VCHIQ_MSG_TYPE(msgid)),
+ header, size, VCHIQ_MSG_SRCPORT(msgid),
+ VCHIQ_MSG_DSTPORT(msgid));
+ if (size != 0) {
+ WARN_ON(!((count == 1) && (size == elements[0].size)));
+ memcpy(header->data, elements[0].data,
+ elements[0].size);
+ }
VCHIQ_STATS_INC(state, ctrl_tx_count);
}
VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE;
int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport,
service->remoteport);
+ VCHIQ_ELEMENT_T element = { &bulk->actual, 4 };
/* Only reply to non-dummy bulk requests */
if (bulk->remote_data) {
- status = queue_message(
- service->state,
- NULL,
- msgid,
- memcpy_copy_callback,
- &bulk->actual,
- 4,
- 0);
+ status = queue_message(service->state, NULL,
+ msgid, &element, 1, 4, 0);
if (status != VCHIQ_SUCCESS)
break;
}
struct vchiq_openack_payload ack_payload = {
service->version
};
+ VCHIQ_ELEMENT_T body = {
+ &ack_payload,
+ sizeof(ack_payload)
+ };
if (state->version_common <
VCHIQ_VERSION_SYNCHRONOUS_MODE)
if (service->sync &&
(state->version_common >=
VCHIQ_VERSION_SYNCHRONOUS_MODE)) {
- if (queue_message_sync(
- state,
- NULL,
+ if (queue_message_sync(state, NULL,
VCHIQ_MAKE_MSG(
VCHIQ_MSG_OPENACK,
service->localport,
remoteport),
- memcpy_copy_callback,
- &ack_payload,
- sizeof(ack_payload),
+ &body, 1, sizeof(ack_payload),
0) == VCHIQ_RETRY)
goto bail_not_ready;
} else {
- if (queue_message(state,
- NULL,
- VCHIQ_MAKE_MSG(
+ if (queue_message(state, NULL,
+ VCHIQ_MAKE_MSG(
VCHIQ_MSG_OPENACK,
service->localport,
remoteport),
- memcpy_copy_callback,
- &ack_payload,
- sizeof(ack_payload),
+ &body, 1, sizeof(ack_payload),
0) == VCHIQ_RETRY)
goto bail_not_ready;
}
service->version,
service->version_min
};
+ VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) };
VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
service->client_id = client_id;
vchiq_use_service_internal(service);
- status = queue_message(service->state,
- NULL,
- VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN,
- service->localport,
- 0),
- memcpy_copy_callback,
- &payload,
- sizeof(payload),
- QMFLAGS_IS_BLOCKING);
+ status = queue_message(service->state, NULL,
+ VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0),
+ &body, 1, sizeof(payload), QMFLAGS_IS_BLOCKING);
if (status == VCHIQ_SUCCESS) {
/* Wait for the ACK/NAK */
if (down_interruptible(&service->remove_event) != 0) {
VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY);
} else {
int payload[2] = { (int)(long)bulk->data, bulk->size };
+ VCHIQ_ELEMENT_T element = { payload, sizeof(payload) };
- status = queue_message(state,
- NULL,
- VCHIQ_MAKE_MSG(dir_msgtype,
- service->localport,
- service->remoteport),
- memcpy_copy_callback,
- &payload,
- sizeof(payload),
- QMFLAGS_IS_BLOCKING |
- QMFLAGS_NO_MUTEX_LOCK |
- QMFLAGS_NO_MUTEX_UNLOCK);
+ status = queue_message(state, NULL,
+ VCHIQ_MAKE_MSG(dir_msgtype,
+ service->localport, service->remoteport),
+ &element, 1, sizeof(payload),
+ QMFLAGS_IS_BLOCKING |
+ QMFLAGS_NO_MUTEX_LOCK |
+ QMFLAGS_NO_MUTEX_UNLOCK);
if (status != VCHIQ_SUCCESS) {
goto unlock_both_error_exit;
}
VCHIQ_STATUS_T
vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
- ssize_t (*copy_callback)(void *context, void *dest,
- size_t offset, size_t maxsize),
- void *context,
- size_t size)
+ const VCHIQ_ELEMENT_T *elements, unsigned int count)
{
VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
VCHIQ_STATUS_T status = VCHIQ_ERROR;
+ unsigned int size = 0;
+ unsigned int i;
+
if (!service ||
(vchiq_check_service(service) != VCHIQ_SUCCESS))
goto error_exit;
- if (!size) {
- VCHIQ_SERVICE_STATS_INC(service, error_count);
- goto error_exit;
-
+ for (i = 0; i < (unsigned int)count; i++) {
+ if (elements[i].size) {
+ if (elements[i].data == NULL) {
+ VCHIQ_SERVICE_STATS_INC(service, error_count);
+ goto error_exit;
+ }
+ size += elements[i].size;
+ }
}
if (size > VCHIQ_MAX_MSG_SIZE) {
VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
service->localport,
service->remoteport),
- copy_callback, context, size, 1);
+ elements, count, size, 1);
break;
case VCHIQ_SRVSTATE_OPENSYNC:
status = queue_message_sync(service->state, service,
VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
service->localport,
service->remoteport),
- copy_callback, context, size, 1);
+ elements, count, size, 1);
break;
default:
status = VCHIQ_ERROR;