/*
 * Copyright (c) 2017, ARM Limited, All Rights Reserved
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef __UVISOR_API_IPC_EXPORTS_H__
#define __UVISOR_API_IPC_EXPORTS_H__

#include "api/inc/pool_queue_exports.h"
#include "api/inc/uvisor_spinlock_exports.h"
#include "api/inc/vmpu_exports.h"
#include <stddef.h>

/* Use the invalid box ID to mean "receive from any" box. */
#define UVISOR_BOX_ID_ANY UVISOR_BOX_ID_INVALID

#define UVISOR_IPC_SEND_SLOTS 16
#define UVISOR_IPC_RECV_SLOTS 16

/* The value UVISOR_IPC_INVALID_TOKEN is defined to should be useful as a null
 * token, and preferably not having any other legitimate use. Due to the
 * internal bitfield representation of tokens, it makes a lot of sense to use 0
 * as the null token. ipc_allocate_token() returns 0 if no tokens are
 * available. Freeing 0 bits means nothing ('&= ~0' is a no-op). No other value
 * makes as much sense to use as 0. If tokens become represented internally as
 * something other than a bitfield, it would make sense to reconsider the value
 * used here. */
#define UVISOR_IPC_INVALID_TOKEN 0

typedef enum uvisor_ipc_io_state {
    UVISOR_IPC_IO_STATE_INVALID,
    UVISOR_IPC_IO_STATE_IDLE,
    UVISOR_IPC_IO_STATE_READY_TO_SEND, /* Enqueued and waiting for delivery */
    UVISOR_IPC_IO_STATE_READY_TO_RECV, /* Enqueued and waiting for delivery */
    UVISOR_IPC_IO_STATE_VALID, /* uVisor has copied the message */
} uvisor_ipc_io_state_t;

/* IPC Descriptor Structure
 * When sending:
 * @param[in]  box_id  the ID of the destination box
 * @param[in]  port    the port to send the message to
 * @param[in]  len     the length of the message
 * @param[out] token   a token that can be used to wait at a later time for
 *                     the send to complete
 *
 * When receiving before a message has been received:
 * @param[in]   box_id  an ID of a box that is allowed to send to this box, or
 *                      UVISOR_BOX_ID_ANY to allow messages from any box
 * @param[in]   port    the port to listen for messages on
 * @param[in]   len     the maximum length of message to receive
 * @param[out]  token   a token that can be used to wait at a later time for
 *                      the recv to complete
 *
 * When receiving after a message has been received:
 * @param[out]  box_id  the box ID of the sender
 * @param[out]  port    the port the message arrived on
 * @param[out]  len     the length of the message
 * @param[out]  token   not modified
 */
typedef struct uvisor_ipc_desc {
    int box_id;
    size_t port;
    size_t len;
    uint32_t token;
} uvisor_ipc_desc_t;

/* IPC IO Request Structure */
typedef struct uvisor_ipc_io {
    uvisor_ipc_desc_t * desc;
    void * msg;
    uvisor_ipc_io_state_t state;
} uvisor_ipc_io_t;

#define UVISOR_IPC_SEND_TYPE(slots) \
    struct { \
        uvisor_pool_queue_t queue; \
        uvisor_pool_t pool; \
        uvisor_pool_queue_entry_t entries[slots]; \
        uvisor_ipc_io_t io[slots]; \
    }

#define UVISOR_IPC_RECV_TYPE(slots) \
    struct { \
        uvisor_pool_queue_t queue; \
        uvisor_pool_t pool; \
        uvisor_pool_queue_entry_t entries[slots]; \
        uvisor_ipc_io_t io[slots]; \
    }

typedef UVISOR_IPC_SEND_TYPE(UVISOR_IPC_SEND_SLOTS) uvisor_ipc_send_queue_t;
typedef UVISOR_IPC_RECV_TYPE(UVISOR_IPC_RECV_SLOTS) uvisor_ipc_recv_queue_t;

typedef struct uvisor_ipc {
    uvisor_ipc_send_queue_t send_queue;
    uvisor_ipc_recv_queue_t recv_queue;
    UvisorSpinlock tokens_lock; /* Protect access to tokens. */
    uint32_t allocated_tokens; /* Endpoints read and write. */
    uint32_t completed_tokens; /* uVisor and endpoints read and write. */
} uvisor_ipc_t;

static inline uvisor_ipc_t * uvisor_ipc(UvisorBoxIndex * const index)
{
    return (uvisor_ipc_t *) index->bss.address_of.ipc;
}

#endif
