/*
 * Copyright (c) 2014, Cisco Systems, Inc. All rights reserved.
 *
 * LICENSE_BEGIN
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - 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.
 *
 * 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 HOLDER 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.
 *
 * LICENSE_END
 *
 *
 */

#ifndef _USD_POST_H_
#define _USD_POST_H_

#include <sys/uio.h>

#include "usd.h"
#include "usd_util.h"

static inline uint32_t
_usd_post_send_one(
    struct usd_wq *wq,
    const void *packet,
    size_t length,
    u_int8_t cq_entry)
{
    struct vnic_wq *vwq;
    uint32_t index;
    struct wq_enet_desc *desc;
    uint64_t wr;
    u_int8_t offload_mode = 0, eop = 1;
    u_int16_t mss = 7, header_length = 0, vlan_tag = 0;
    u_int8_t vlan_tag_insert = 0, loopback = 0, fcoe_encap = 0;

    vwq = &wq->uwq_vnic_wq;
    desc = wq->uwq_next_desc;
    index = wq->uwq_post_index;

    wq_enet_desc_enc(desc, (uintptr_t)packet, length,
        mss, header_length, offload_mode,
        eop, cq_entry, fcoe_encap,
        vlan_tag_insert, vlan_tag, loopback);
    wmb();

    wr = vnic_cached_posted_index((dma_addr_t)packet, length, index);
    iowrite64(wr, &vwq->ctrl->posted_index);

    wq->uwq_next_desc = (struct wq_enet_desc *)
        ((uintptr_t)wq->uwq_desc_ring + (index<<4));
    wq->uwq_post_index = (index+1) & wq->uwq_post_index_mask;
    wq->uwq_send_credits--;

    return index;
}

static inline uint32_t
_usd_post_send_two(
    struct usd_wq *wq,
    const void *hdr,
    size_t hdrlen,
    const void *pkt,
    size_t pktlen,
    u_int8_t cq_entry)
{
    struct vnic_wq *vwq;
    uint32_t index;
    struct wq_enet_desc *desc;
    u_int8_t offload_mode = 0, eop;
    u_int16_t mss = 7, header_length = 0, vlan_tag = 0;
    u_int8_t vlan_tag_insert = 0, loopback = 0, fcoe_encap = 0;

    vwq = &wq->uwq_vnic_wq;
    desc = wq->uwq_next_desc;
    index = wq->uwq_post_index;

    eop = 0;
    wq_enet_desc_enc(desc, (uintptr_t)hdr, hdrlen,
        mss, header_length, offload_mode,
        eop, 0, fcoe_encap,
        vlan_tag_insert, vlan_tag, loopback);

    desc = (struct wq_enet_desc *) ((uintptr_t)wq->uwq_desc_ring + (index<<4));
    index = (index+1) & wq->uwq_post_index_mask;

    eop = 1;
    wq_enet_desc_enc(desc, (uintptr_t)pkt, pktlen,
        mss, header_length, offload_mode,
        eop, cq_entry, fcoe_encap,
        vlan_tag_insert, vlan_tag, loopback);
    wmb();

    iowrite32(index, &vwq->ctrl->posted_index);

    wq->uwq_next_desc = (struct wq_enet_desc *)
        ((uintptr_t)wq->uwq_desc_ring + (index<<4));
    wq->uwq_post_index = (index+1) & wq->uwq_post_index_mask;
    wq->uwq_send_credits -= 2;

    return index;
}

static inline uint32_t
_usd_post_send_two_vlan(
    struct usd_wq *wq,
    const void *hdr,
    size_t hdrlen,
    const void *pkt,
    size_t pktlen,
    u_int8_t cq_entry,
    u_int16_t vlan_tag)
{
    struct vnic_wq *vwq;
    uint32_t index;
    struct wq_enet_desc *desc;
    u_int8_t offload_mode = 0, eop;
    u_int16_t mss = 7, header_length = 0;
    u_int8_t vlan_tag_insert = 1, loopback = 0, fcoe_encap = 0;

    vwq = &wq->uwq_vnic_wq;
    desc = wq->uwq_next_desc;
    index = wq->uwq_post_index;

    eop = 0;
    wq_enet_desc_enc(desc, (uintptr_t)hdr, hdrlen,
        mss, header_length, offload_mode,
        eop, 0, fcoe_encap,
        vlan_tag_insert, vlan_tag, loopback);

    desc = (struct wq_enet_desc *) ((uintptr_t)wq->uwq_desc_ring + (index<<4));
    index = (index+1) & wq->uwq_post_index_mask;

    eop = 1;
    wq_enet_desc_enc(desc, (uintptr_t)pkt, pktlen,
        mss, header_length, offload_mode,
        eop, cq_entry, fcoe_encap,
        vlan_tag_insert, vlan_tag, loopback);
    wmb();

    iowrite32(index, &vwq->ctrl->posted_index);

    wq->uwq_next_desc = (struct wq_enet_desc *)
        ((uintptr_t)wq->uwq_desc_ring + (index<<4));
    wq->uwq_post_index = (index+1) & wq->uwq_post_index_mask;
    wq->uwq_send_credits -= 2;

    return index;
}

/*
 * Consume iov count credits, assumes that iov[0] includes usnic header
 */
static inline uint32_t
_usd_post_send_iov(
    struct usd_wq *wq,
    const struct iovec *iov,
    size_t count,
    u_int8_t cq_entry)
{
    struct vnic_wq *vwq;
    uint32_t index;
    struct wq_enet_desc *desc;
    u_int8_t offload_mode = 0;
    u_int16_t mss = 7, header_length = 0, vlan_tag = 0;
    u_int8_t vlan_tag_insert = 0, loopback = 0, fcoe_encap = 0;
    unsigned i;

    vwq = &wq->uwq_vnic_wq;
    desc = wq->uwq_next_desc;
    index = wq->uwq_post_index;

    for (i = 0; i < count - 1; i++) {
        wq_enet_desc_enc(desc, (uintptr_t)(iov[i].iov_base),
            iov[i].iov_len, mss, header_length, offload_mode,
            0, 0, fcoe_encap, vlan_tag_insert, vlan_tag, loopback);
        desc = (struct wq_enet_desc *) ((uintptr_t)wq->uwq_desc_ring
                                            + (index<<4));
        index = (index+1) & wq->uwq_post_index_mask;
    }

    wq_enet_desc_enc(desc, (uintptr_t)(iov[i].iov_base),
            iov[i].iov_len, mss, header_length, offload_mode,
	        1, cq_entry, fcoe_encap, vlan_tag_insert, vlan_tag, loopback);

    wmb();

    iowrite32(index, &vwq->ctrl->posted_index);

    wq->uwq_next_desc = (struct wq_enet_desc *)
        ((uintptr_t)wq->uwq_desc_ring + (index<<4));
    wq->uwq_post_index = (index+1) & wq->uwq_post_index_mask;
    wq->uwq_send_credits -= count;

    return index;
}

#endif /* _USD_POST_H_ */
