/*
 * 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_H_
#define _USD_H_

#include <sys/queue.h>

#include "kcompat.h"
#include "vnic_rq.h"
#include "vnic_wq.h"
#include "vnic_cq.h"
#include "wq_enet_desc.h"
#include "rq_enet_desc.h"
#include "vnic_intr.h"

#include "usnic_abi.h"
#include "usnic_direct.h"
#include "usd_ib_sysfs.h"

#define USD_INVALID_HANDLE (~0)
#define USD_SF_ISSET(flags, flagname) \
    ((flags >> USD_SFS_##flagname) & 1)

#define USD_SEND_MAX_COPY 992
#define USD_MAX_PRESEND 4

#define USD_CTXF_CLOSE_CMD_FD (1u << 0)
#define USD_DEVF_CLOSE_CTX (1u << 0)

#ifndef USD_DEBUG
#define USD_DEBUG 0
#endif

/*
 * Group interrupt vector userspace map info
 */
struct usd_grp_vect_map {
    void            *va;
    size_t          len;
    uint32_t        vfid;
};

/*
 * structure storing interrupt resource and its mapping to FD
 */
struct usd_cq_comp_intr {
    struct vnic_intr                uci_vintr;
    int                             uci_offset;
    int                             uci_refcnt;
    LIST_ENTRY(usd_cq_comp_intr)    uci_ctx_link;
};

/*
 * Instance of a usd context, corresponding to an
 * opened libibverbs context
 */
struct usd_context {
    struct usd_ib_dev *ucx_ib_dev;  /* parent IB dev */
    int ucx_ib_dev_fd;              /* file desc for IB dev */
    int ucmd_ib_dev_fd;             /* Another open file descriptor for IB dev
                                     * used for encapusulating user commands
                                     * through GET_CONTEXT IB command */

    uint32_t ucx_flags;
    int ucx_caps[USD_CAP_MAX];          /* device capablities */

    pthread_mutex_t ucx_mutex;          /* protect intr_list */
    LIST_HEAD(intr_head, usd_cq_comp_intr) ucx_intr_list;

    /* Remove these after moving ud_attrs here */
    int event_fd;
    unsigned num_comp_vectors;
};

/*
 * Instance of a device opened by user
 */
struct usd_device {
    struct usd_context *ud_ctx;

    uint32_t ud_flags;
    struct usd_device_attrs ud_attrs;   /* TODO move this to usd_ctx */

    /* VFs we have associated with this device */
    struct usd_vf *ud_vf_list;

    /* PD for this device */
    uint32_t ud_pd_handle;

    /* destination related */
    int ud_arp_sockfd;              /* for ARP */
    TAILQ_HEAD(, usd_dest_req) ud_pending_reqs;
    TAILQ_HEAD(, usd_dest_req) ud_completed_reqs;

    TAILQ_ENTRY(usd_device) ud_link;

    struct usd_grp_vect_map grp_vect_map;
};

/*
 * Registered memory region
 */
struct usd_mr {
    struct usd_device *umr_dev;
    void *umr_vaddr;
    uint32_t umr_handle;
    uint32_t umr_lkey;
    uint32_t umr_rkey;
    size_t umr_length;
};

/*
 * Information about the PCI virtual function
 */
struct usd_vf {
    uint32_t vf_id;
    int vf_refcnt;
    struct vnic_dev_bar vf_bar0;
    size_t vf_bar_map_len;
    struct vnic_dev *vf_vdev;
    struct vnic_dev_iomap_info iomaps[RES_TYPE_MAX];

    /* Will also protect the devcmd region */
    pthread_mutex_t vf_lock;
    struct usd_vf *vf_next;
    struct usd_vf *vf_prev;
};

/*
 * Holding place for information about a VF
 */
struct usd_vf_info {
    uint32_t vi_vfid;
    dma_addr_t vi_bar_bus_addr;
    uint32_t vi_bar_len;
    size_t vi_barhead_len;
    struct usnic_vnic_barres_info barres[RES_TYPE_MAX];
};

/*
 * Internal representation of a filter
 */
struct usd_qp_filter {
    enum usd_filter_type qf_type;
    union {
        struct {
            int u_sockfd;
        } qf_udp;
    } qf_filter;
};

/*
 * Definitions and structures about queues
 */

/*
 * this is used to keep track of what has been allocated and/or
 * initialized to assist with teardown of partially completed queues
 */
enum usd_qstate {
    USD_QS_FILTER_ALLOC = (1 << 0),
    USD_QS_VERBS_CREATED = (1 << 1),
    USD_QS_VF_MAPPED = (1 << 2),
    USD_QS_VNIC_ALLOCATED = (1 << 3),
    USD_QS_VNIC_INITIALIZED = (1 << 4),
    USD_QS_READY = (1 << 5)
};

struct usd_cq_impl {
    struct usd_cq ucq_cq;
    struct usd_device *ucq_dev;
    struct usd_vf *ucq_vf;

    uint32_t ucq_state;

    struct vnic_cq ucq_vnic_cq;

    void *ucq_desc_ring;
    uint32_t ucq_next_desc;
    uint32_t ucq_last_color;

    uint32_t ucq_index;
    uint32_t ucq_num_entries;
    uint32_t ucq_cqe_mask;
    uint32_t ucq_color_shift;
    uint32_t ucq_handle;

    int comp_fd;
    int comp_vec;
    int comp_req_notify;
    int intr_offset;
    struct usd_cq_comp_intr *ucq_intr;

    struct usd_rq **ucq_rq_map;
    struct usd_wq **ucq_wq_map;
};
#define to_cqi(CQ) ((struct usd_cq_impl *)(CQ))
#define to_usdcq(CQ) (&(CQ)->ucq_cq)

struct usd_rq {
    struct usd_cq_impl *urq_cq;
    uint32_t urq_state;

    uint32_t urq_index;
    uint32_t urq_num_entries;
    struct vnic_rq urq_vnic_rq;

    void **urq_context;

    char *urq_rxbuf;
    char **urq_post_addr;
    uint32_t urq_recv_credits;  /* number of available descriptors */
    struct rq_enet_desc *urq_desc_ring;
    struct rq_enet_desc *urq_next_desc;
    uint32_t urq_post_index;    /* next rxbuf to post */
    uint32_t urq_post_index_mask;
    uint32_t urq_last_comp;
    uint32_t urq_accum_bytes;

    uint32_t urq_num_rxbuf;
    uint32_t urq_rxbuf_size;
};

struct usd_wq_post_info {
    void *wp_context;
    uint32_t wp_len;
};

struct usd_wq {
    struct usd_cq_impl *uwq_cq;
    uint32_t uwq_state;
    struct vnic_wq uwq_vnic_wq;

    uint32_t uwq_index;
    uint32_t uwq_num_entries;
    uint32_t uwq_send_credits;
    struct wq_enet_desc *uwq_desc_ring;
    struct wq_enet_desc *uwq_next_desc;
    uint32_t uwq_post_index;
    uint32_t uwq_post_index_mask;
    uint32_t uwq_last_comp;

    uint8_t *uwq_copybuf;
    struct usd_wq_post_info *uwq_post_info;

    /* used only for PIO QPs */
    void *pio_v_wq_addr;
    uint64_t pio_p_wq_addr;
    char *pio_v_pkt_buf;
    uint64_t pio_p_pkt_buf;
};

struct usd_qp_impl {
    struct usd_qp uq_qp;        /* user's view of QP */

    struct usd_device *uq_dev;
    struct usd_vf *uq_vf;

    struct usd_qp_attrs uq_attrs;

    uint32_t uq_qp_handle;      /* IB QP handle */
    uint32_t uq_qp_num;

    /* primary filter for this QP */
    struct usd_qp_filter uq_filter;

    struct usd_wq uq_wq;
    struct usd_rq uq_rq;
};
#define to_qpi(Q) ((struct usd_qp_impl *)(Q))
#define to_usdqp(Q) (&(Q)->uq_qp)

struct usd_dest {
    union {
        struct {
            struct usd_udp_hdr u_hdr;
        } ds_udp;
    } ds_dest;
};

extern struct usd_qp_ops usd_qp_ops_ud_udp;
extern struct usd_qp_ops usd_qp_ops_ud_pio_udp;
extern struct usd_qp_ops usd_qp_ops_ud_raw;
#endif /* _USD_H_ */
