blob: 84589470b33a32f7406f810e4acac23923da59bb [file]
/*
* Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <virtqueue.h>
#include <camkes/io.h>
#include <camkes/msgqueue.h>
#include <camkes/msgqueue_template.h>
#include <camkes/virtqueue.h>
#include <sel4/sel4.h>
#include <utils/util.h>
#include "virtqueue_common.h"
int camkes_msgqueue_sender_init(int msgqueue_id, camkes_msgqueue_sender_t *sender)
{
if (!sender) {
ZF_LOGE("sender is NULL");
return EINVAL;
}
if (sender->initialised) {
return 0;
}
camkes_msgqueue_channel_t *msgqueue_channel = camkes_msgqueue_channel_get(msgqueue_id, MSGQUEUE_SENDER);
if (!msgqueue_channel) {
ZF_LOGE("Failed to retrieve an initialised sender channel with ID %d", msgqueue_id);
return EINVAL;
}
size_t aligned_message_size = msgqueue_channel->message_size;
if (!IS_POWER_OF_2(msgqueue_channel->message_size)) {
aligned_message_size = NEXT_POWER_OF_2(msgqueue_channel->message_size);
}
int error = camkes_virtqueue_driver_init_common(&sender->sender_channel, msgqueue_channel->buffer,
msgqueue_channel->queue_len,
msgqueue_channel->buffer_size, msgqueue_channel->sender_funcs.notify,
aligned_message_size);
if (error) {
return error;
}
sender->message_size = msgqueue_channel->message_size;
sender->initialised = true;
return 0;
}
int camkes_msgqueue_receiver_init(int msgqueue_id, camkes_msgqueue_receiver_t *receiver)
{
if (!receiver) {
ZF_LOGE("receiver is NULL");
return EINVAL;
}
if (receiver->initialised) {
return 0;
}
camkes_msgqueue_channel_t *msgqueue_channel = camkes_msgqueue_channel_get(msgqueue_id, MSGQUEUE_RECEIVER);
if (!msgqueue_channel) {
ZF_LOGE("Failed to retrieve an initialised receiver channel with ID %d", msgqueue_id);
return EINVAL;
}
int error = camkes_virtqueue_device_init_common(&receiver->receiver_channel,
msgqueue_channel->buffer, msgqueue_channel->queue_len, NULL);
if (error) {
return error;
}
receiver->message_size = msgqueue_channel->message_size;
receiver->poll = msgqueue_channel->receiver_funcs.poll;
receiver->wait = msgqueue_channel->receiver_funcs.wait;
receiver->initialised = true;
return 0;
}
static void *msgqueue_alloc_buffer(camkes_msgqueue_sender_t *sender)
{
void *ret_buffer = NULL;
virtqueue_ring_object_t handle = {0};
unsigned len = 0;
UNUSED unsigned size = 0;
UNUSED vq_flags_t flag = {0};
int error = 0;
/* Grab a used buffer if possible */
if (virtqueue_get_used_buf(&sender->sender_channel, &handle, &len)) {
error = camkes_virtqueue_driver_gather_buffer(&sender->sender_channel, &handle,
(void **) &ret_buffer, &size, &flag);
ZF_LOGF_IF(error, "Failed to get a 'used' buffer even though there exists one");
return ret_buffer;
}
/* Allocate a fresh buffer */
error = camkes_virtqueue_buffer_alloc(&sender->sender_channel, &ret_buffer, sender->message_size);
if (error) {
return NULL;
}
return ret_buffer;
}
int camkes_msgqueue_send(camkes_msgqueue_sender_t *sender, void *message, size_t message_size)
{
if (!sender || !message) {
return EINVAL;
}
if (!sender->initialised) {
return EINVAL;
}
if (message_size > sender->message_size) {
ZF_LOGE("Message size is too large for this msgqueue");
return EMSGSIZE;
}
/* Grab a buffer from the channel */
void *msgqueue_buf = msgqueue_alloc_buffer(sender);
if (!msgqueue_buf) {
return ENOMEM;
}
/* Copy the contents of the received buffer */
memcpy((void *) msgqueue_buf, message, message_size);
/* Now enqueue it on the virtqueue */
int error = camkes_virtqueue_driver_send_buffer(&sender->sender_channel, msgqueue_buf, message_size);
if (error) {
memset((void *) msgqueue_buf, 0, message_size);
camkes_virtqueue_buffer_free(&sender->sender_channel, msgqueue_buf);
return ECOMM;
}
sender->sender_channel.notify();
return 0;
}
int camkes_msgqueue_poll(camkes_msgqueue_receiver_t *receiver)
{
if (!receiver) {
return EINVAL;
}
if (!receiver->initialised) {
return EINVAL;
}
return receiver->poll();
}
int camkes_msgqueue_wait(camkes_msgqueue_receiver_t *receiver)
{
if (!receiver) {
return EINVAL;
}
if (!receiver->initialised) {
return EINVAL;
}
receiver->wait();
return 0;
}
int camkes_msgqueue_get(camkes_msgqueue_receiver_t *receiver, void *buffer, size_t buffer_size)
{
if (!receiver || !buffer) {
return EINVAL;
}
if (!receiver->initialised) {
return EINVAL;
}
if (buffer_size < receiver->message_size) {
ZF_LOGE("Buffer is too small for the message");
return EMSGSIZE;
}
/* Try to get a message from the queue */
virtqueue_ring_object_t handle = {0};
if (!virtqueue_get_available_buf(&receiver->receiver_channel, &handle)) {
return ENOMSG;
}
void *message = NULL;
unsigned message_len = 0;
UNUSED vq_flags_t flags = {0};
int error = camkes_virtqueue_device_gather_buffer(&receiver->receiver_channel, &handle, &message, &message_len, &flags);
ZF_LOGF_IF(error, "Failed to dequeue a message from the queue even though there is one");
/* Copy it over and then add the buffer to the used ring */
assert(buffer_size >= message_len);
memcpy(buffer, message, message_len);
virtqueue_add_used_buf(&receiver->receiver_channel, &handle, message_len);
return 0;
}