/**
 * \file
 * \brief Legacy IDC buffer format. PLEASE DO NOT USE FOR ANY NEW CODE!
 */

/*
 * Copyright (c) 2007, 2008, 2009, ETH Zurich.
 * All rights reserved.
 *
 * This file is distributed under the terms in the attached LICENSE file.
 * If you do not find this file, copies can be found by writing to:
 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
 */

#ifndef BARRELFISH_LEGACY_IDC_BUFFER_H
#define BARRELFISH_LEGACY_IDC_BUFFER_H

/**
 * \brief Maximum total length of IDC message (header + payload)
 *
 * Determined by number of registers available to transfer messages.
 */
// XXX Doesnt that sound ever so slightly machine dependent to you ???
// XXX See also related magic in syscalls.c
#define IDC_SEND_LENGTH         12

/// Length of IDC message headers
// XXX Is there a good reason that this isnt just done with sizeof() ???
#if defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ == 4
#define IDC_SEND_HEADER_LENGTH 3
#elif __SIZEOF_POINTER__ == 8
#define IDC_SEND_HEADER_LENGTH 2
#else
#error "Unknown pointer size"
#endif
#else
#error "Unknown pointer size"
#endif

/// Length of IDC message payload
#define IDC_MSG_LENGTH          (IDC_SEND_LENGTH - IDC_SEND_HEADER_LENGTH)

#include <assert.h>
#include <string.h>
#include <stdlib.h> // for abort

#if __x86_64__
typedef unsigned int idc_flag_t;
#elif __i386__
typedef unsigned int idc_flag_t;
#elif __arm__
typedef uint8_t idc_flag_t;
#else
#error "Unknown architecture."
#endif

#ifdef IN_KERNEL
#define idc_fatal_error(x, ...) panic(x, __VA_ARGS__)
#else // IN_KERNEL
#define idc_fatal_error(x, ...) abort();
#endif // IN_KERNEL

/**
 * \brief IDC sender-side header.
 */
union idc_send_header {
    uintptr_t raw[IDC_SEND_HEADER_LENGTH];
    struct {
        struct __attribute__ ((__packed__)) {
            idc_flag_t sync   :1; ///< Synchronous call (yields caller's timeslice)
            idc_flag_t yield  :1; ///< Yield to receiver if disabled/unable to transfer
        } flags;
        uint8_t     length;         ///< Length of payload in words
        uint8_t     invoke_bits;    ///< Valid bits in invoke_cptr
        uint8_t     send_bits;      ///< Valid bits in send_cptr
        capaddr_t     invoke_cptr;    ///< Cap to invoke
        capaddr_t     send_cptr;      ///< Cap to send or #CPTR_NULL
    } x;
};

/**
 * \brief IDC receiver-side header.
 */
union idc_recv_header {
    uintptr_t raw;
    struct {
        struct __attribute__ ((__packed__)) {
            idc_flag_t captransfer :1;    ///< A cap was transferred
        } flags;
        uint8_t     length;                 ///< Length of payload in words
    } x;
};

/**
 * \brief Message layout on sender side.
 */
union idc_send_msg_body {
    uintptr_t       raw[IDC_SEND_LENGTH]; ///< Raw message contents

    struct {
        union idc_send_header header;
        uintptr_t words[IDC_MSG_LENGTH];  ///< Message payload
    } x;
};

/**
 * \brief Message layout on receiver side.
 */
struct idc_recv_msg_body {
    union idc_recv_header header;     ///< Header
    uintptr_t words[IDC_MSG_LENGTH];  ///< Message payload
};

/// IDC send message, with position indicator for marshalling
struct idc_send_msg {
    int                         pos;    ///< Index in message
    union idc_send_msg_body     u;      ///< Actual transferred message content
};

/// IDC receive message, with position indicator for demarshalling
struct idc_recv_msg {
    int                         pos;    ///< Index in message
    struct idc_recv_msg_body    msg;    ///< Actual transferred message content
};

static inline uintptr_t *idc_get_raw(struct idc_send_msg *msg)
{
    return msg->u.raw;
}

static inline errval_t idc_msg_decode_word(struct idc_recv_msg *msg,
                                           uintptr_t *ret)
{
    assert(ret != NULL);
    if(msg->pos < msg->msg.header.x.length) {
        *ret = msg->msg.words[msg->pos++];
        return SYS_ERR_OK;
    } else {
        return SYS_ERR_IDC_MSG_BOUNDS;
    }
}

/**
 * \brief decode a word from the message
 * \returns the word, or 0 if out of bounds
 * \bug XXX: do not use this for new code!
 */
static inline uintptr_t idc_msg_decode_word_or_zero(struct idc_recv_msg *msg)
{
    uintptr_t w;
    if (err_is_ok(idc_msg_decode_word(msg, &w))) {
        return w;
    } else {
        return 0;
    }
}

static inline void idc_msg_encode_word(struct idc_send_msg *msg, uintptr_t word)
{
    if(msg->pos < IDC_MSG_LENGTH) {
        msg->u.x.words[msg->pos++] = word;
        msg->u.x.header.x.length = msg->pos;
    }
    else {
        idc_fatal_error("%s: msg %p pos %d",
                        __FUNCTION__, msg, (int)msg->pos);
    }
}

static inline void idc_msg_init(struct idc_send_msg *msg)
{
    int i;
    msg->pos = 0;
    for (i = 0; i < IDC_SEND_HEADER_LENGTH; i++) {
        msg->u.raw[i] = 0;
    }

    // Sync, yielding IDC is the default
    msg->u.x.header.x.flags.sync = 1;
    msg->u.x.header.x.flags.yield = 1;
}

// Set the msg flag to be async
static inline void idc_msg_set_async(struct idc_send_msg *msg)
{
    msg->u.x.header.x.flags.sync = 0;
}

// Macro cleanup
#undef idc_fatal_error

#endif // BARRELFISH_LEGACY_IDC_BUFFER_H
