/*
 * Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/**
 * @file ol_htt_tx_api.h
 * @brief Specify the tx HTT API functions called by the host data SW.
 * @details
 *  This file declares the HTT API functions that are specifically
 *  related to transmit processing.
 *  In particular, the methods of the abstract HTT tx descriptor are
 *  specified.
 */
#ifndef _OL_HTT_TX_API__H_
#define _OL_HTT_TX_API__H_

//#include <osapi_linux.h>    /* u_int16_t, etc. */
#include <osdep.h>      /* u_int16_t, etc. */
#include <adf_nbuf.h>   /* adf_nbuf_t */
#include <ol_cfg.h>     /* wlan_frm_fmt */

#include <htt.h>        /* needed by inline functions */
#include <adf_net_types.h>
#include <ol_htt_api.h> /* htt_pdev_handle */
#include <htt_types.h>
#include <vos_trace.h>

/* Remove these macros when they get added to htt.h. */
#ifndef HTT_TX_DESC_EXTENSION_GET
#define HTT_TX_DESC_EXTENSION_OFFSET_BYTES 0
#define HTT_TX_DESC_EXTENSION_OFFSET_DWORD 0
#define HTT_TX_DESC_EXTENSION_M        0x10000000
#define HTT_TX_DESC_EXTENSION_S        28

#define HTT_TX_DESC_EXTENSION_GET(_var) \
    (((_var) & HTT_TX_DESC_EXTENSION_M) >> HTT_TX_DESC_EXTENSION_S)
#define HTT_TX_DESC_EXTENSION_SET(_var, _val)            \
    do {                                                 \
        HTT_CHECK_SET_VAL(HTT_TX_DESC_EXTENSION, _val);  \
        ((_var) |= ((_val) << HTT_TX_DESC_EXTENSION_S)); \
    } while (0)
#endif

/*================ meta-info about tx MSDUs =================================*/

/*
 * For simplicity, use the IEEE 802.11 frame type values.
 */
enum htt_frm_type {
	htt_frm_type_mgmt = 0,
	htt_frm_type_ctrl = 1,
	htt_frm_type_data = 2
};

/*
 * For simplicity, use the IEEE 802.11 frame sub-type values.
 */
enum htt_frm_subtype {
    htt_frm_subtype_mgmt_assoc_req = 0,
    htt_frm_subtype_mgmt_assoc_resp = 1,
    htt_frm_subtype_mgmt_reassoc_req = 2,
    htt_frm_subtype_mgmt_reassoc_resp = 3,
    htt_frm_subtype_mgmt_probe_req = 4,
    htt_frm_subtype_mgmt_probe_resp = 5,
    htt_frm_subtype_mgmt_timing_adv = 6,
    htt_frm_subtype_mgmt_beacon = 8,
    htt_frm_subtype_mgmt_atim = 9,
    htt_frm_subtype_mgmt_disassoc = 10,
    htt_frm_subtype_mgmt_auth = 11,
    htt_frm_subtype_mgmt_deauth = 12,
    htt_frm_subtype_mgmt_action = 13,
    htt_frm_subtype_mgmt_action_no_ack = 14,

    htt_frm_subtype_data_data = 0,
    htt_frm_subtype_data_data_cf_ack = 1,
    htt_frm_subtype_data_data_cf_poll = 2,
    htt_frm_subtype_data_data_cf_ack_cf_poll = 3,
    htt_frm_subtype_data_null = 4,
    htt_frm_subtype_data_cf_ack = 5,
    htt_frm_subtype_data_cf_poll = 6,
    htt_frm_subtype_data_cf_ack_cf_poll = 7,
    htt_frm_subtype_data_QoS_data = 8,
    htt_frm_subtype_data_QoS_data_cf_ack = 9,
    htt_frm_subtype_data_QoS_data_cf_poll = 10,
    htt_frm_subtype_data_QoS_data_cf_ack_cf_poll = 11,
    htt_frm_subtype_data_QoS_null = 12,
    htt_frm_subtype_data_QoS_cf_poll = 14,
    htt_frm_subtype_data_QoS_cf_ack_cf_poll = 15,
};

enum htt_ofdm_datarate {            // Value    MBPS    Modulation  Coding
    htt_ofdm_datarate_6_mbps = 0,   // 0        6       BPSK        1/2
    htt_ofdm_datarate_9_mbps = 1,   // 1        9       BPSK        3/4
    htt_ofdm_datarate_12_mbps = 2,  // 2        12      QPSK        1/2
    htt_ofdm_datarate_18_mbps = 3,  // 3        18      QPSK        3/4
    htt_ofdm_datarate_24_mbps = 4,  // 4        24      16-QAM      1/2
    htt_ofdm_datarate_36_mbps = 5,  // 5        36      16-QAM      3/4
    htt_ofdm_datarate_48_mbps = 6,  // 6        48      64-QAM      1/2
    htt_ofdm_datarate_54_mbps = 7,  // 7        54      64-QAM      3/4
    htt_ofdm_datarate_max = 7,
};

/**
 * @brief TX control header
 * @details
 *  When sending an OCB packet, the user application has
 *  the option of including the following struct following an ethernet header
 *  with the proto field set to 0x8151. This struct includes various TX
 *  paramaters including the TX power and MCS.
 */
PREPACK struct ocb_tx_ctrl_hdr_t {
    /* The version must be 1. */
    A_UINT16 version;
    A_UINT16 length;
    A_UINT16 channel_freq;

    /* flags */
    union {
        struct {
            A_UINT16
                /* bit 0: if set, tx pwr spec is valid */
                valid_pwr:            1,
                /* bit 1: if set, tx MCS mask spec is valid */
                valid_datarate:       1,
                /* bit 2: if set, tx retries spec is valid */
                valid_retries:        1,
                /* bit 3: if set, chain mask is valid */
                valid_chain_mask:     1,
                /* bit 4: if set, tx expire TSF spec is valid*/
                valid_expire_tsf:     1,
                /* bit 5: if set, TID is valid */
                valid_tid:            1,
                /* bit 6: if set, tx frame with 802.3 ethernet header */
                valid_eth_mode:       1,
                /* bits 15:7 - unused, set to 0x0 */
                reserved0_15_7:       9;
        };
        A_UINT16 all_flags;
    };

    /* TX expiry time (TSF) LSBs */
    A_UINT32 expire_tsf_lo;

    /* TX expiry time (TSF) MSBs */
    A_UINT32 expire_tsf_hi;

    /* pwr -
     * Specify what power the tx frame needs to be transmitted at.
     * The power a signed (two's complement) value is in units of 0.5 dBm.
     * The value needs to be appropriately sign-extended when extracting
     * the value from the message and storing it in a variable that is
     * larger than A_INT8.
     * If the transmission uses multiple tx chains, this power spec is
     * the total transmit power, assuming incoherent combination of
     * per-chain power to produce the total power.
     */
    A_INT8 pwr;

    /* datarate -
     * The desired modulation and coding scheme.
     *
     * VALUE    DATA RATE   MODULATION  CODING RATE
     *          @ 20 MHz
     *          (MBPS)
     * 0        6           BPSK        1/2
     * 1        9           BPSK        3/4
     * 2        12          QPSK        1/2
     * 3        18          QPSK        3/4
     * 4        24          16-QAM      1/2
     * 5        36          16-QAM      3/4
     * 6        48          64-QAM      1/2
     * 7        54          64-QAM      3/4
     */
    A_UINT8 datarate;

    /* retry_limit -
     * Specify the maximum number of transmissions, including the
     * initial transmission, to attempt before giving up if no ack
     * is received.
     * If the tx rate is specified, then all retries shall use the
     * same rate as the initial transmission.
     * If no tx rate is specified, the target can choose whether to
     * retain the original rate during the retransmissions, or to
     * fall back to a more robust rate.
     */
    A_UINT8 retry_limit;

    /* Chain mask - specify which chains to transmit from. */
    A_UINT8 chain_mask;

    /* Extended Traffic ID (0-15) */
    A_UINT8 ext_tid;

    /* Ensure that the size of the structure is a multiple of 4. */
    A_UINT8 reserved[3];

} POSTPACK;

/**
 * @brief tx MSDU meta-data that HTT may use to program the FW/HW tx descriptor
 */
struct htt_msdu_info_t {
    /* the info sub-struct specifies the characteristics of the MSDU */
    struct {
        u_int16_t ethertype;
        #define HTT_INVALID_PEER_ID 0xffff
        u_int16_t peer_id;
        u_int8_t vdev_id;
        u_int8_t ext_tid;
        /*
         * l2_hdr_type - L2 format (802.3, native WiFi 802.11, or raw 802.11)
         * Based on attach-time configuration, the tx frames provided by the
         * OS to the tx data SW are expected to be either 802.3 format or
         * the "native WiFi" variant of 802.11 format.
         * Internally, the driver may also inject tx frames into the tx
         * datapath, and these frames may be either 802.3 format, or 802.11
         * "raw" format, with no further 802.11 encapsulation needed.
         * The tx frames are tagged with their frame format, so the target
         * FW/HW will know how to interpret the packet's encapsulation
         * headers when doing tx classification, and what form of 802.11
         * header encapsulation is needed, if any.
         */
        u_int8_t l2_hdr_type; /* enum htt_pkt_type */
        /*
         * frame_type - is the tx frame management or data?
         * Just to avoid confusion, the enum values for this frame type field
         * use the 802.11 frame type values, although it is unexpected for
         * control frames to be sent through the host data path.
         */
        u_int8_t frame_type; /* enum htt_frm_type */
        /*
         * frame subtype - this field specifies the sub-type of management
         * frames
         * Just to avoid confusion, the enum values for this frame subtype
         * field use the 802.11 management frame subtype values.
         */
        u_int8_t frame_subtype; /* enum htt_frm_subtype */
        u_int8_t is_unicast;

        /* dest_addr is not currently used.
         * It could be used as an input to a Tx BD (Riva tx descriptor)
         * signature computation.
        u_int8_t *dest_addr;
         */

        u_int8_t l3_hdr_offset; // w.r.t. adf_nbuf_data(msdu), in bytes

        /* l4_hdr_offset is not currently used.
         * It could be used to specify to a TCP/UDP checksum computation
         * engine where the TCP/UDP header starts.
        u_int8_t l4_hdr_offset; // w.r.t. adf_nbuf_data(msdu), in bytes
         */
    } info;
    /* the action sub-struct specifies how to process the MSDU */
    struct {
        u_int8_t use_6mbps; /* mgmt frames: option to force 6 Mbps rate */
        u_int8_t do_encrypt;
        u_int8_t do_tx_complete;
        u_int8_t tx_comp_req;

        /*
         * cksum_offload - Specify whether checksum offload is enabled or not
         * Target FW uses this flag to turn on HW checksumming
         * 0x0 - No checksum offload
         * 0x1 - L3 header checksum only
         * 0x2 - L4 checksum only
         * 0x3 - L3 header checksum + L4 checksum
         */
        adf_nbuf_tx_cksum_t cksum_offload;
    } action;
};

static inline void
htt_msdu_info_dump(struct htt_msdu_info_t *msdu_info)
{
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "HTT MSDU info object (%pK)\n", msdu_info);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  ethertype: %#x\n", msdu_info->info.ethertype);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  peer_id: %d\n", msdu_info->info.peer_id);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  vdev_id: %d\n", msdu_info->info.vdev_id);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  ext_tid: %d\n", msdu_info->info.ext_tid);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  l2_hdr_type: %d\n", msdu_info->info.l2_hdr_type);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  frame_type: %d\n", msdu_info->info.frame_type);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  frame_subtype: %d\n", msdu_info->info.frame_subtype);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  is_unicast: %u\n", msdu_info->info.is_unicast);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  l3_hdr_offset: %u\n", msdu_info->info.l3_hdr_offset);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  use 6 Mbps: %d\n", msdu_info->action.use_6mbps);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  do_encrypt: %d\n",  msdu_info->action.do_encrypt);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  do_tx_complete: %d\n", msdu_info->action.do_tx_complete);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  is_unicast: %u\n", msdu_info->info.is_unicast);
    VOS_TRACE(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_INFO_LOW,
        "  is_unicast: %u\n", msdu_info->info.is_unicast);
}


/*================ tx completion message field access methods ===============*/


/**
 * @brief Look up the descriptor ID of the nth MSDU from a tx completion msg.
 * @details
 *  A tx completion message tells the host that the target is done
 *  transmitting a series of MSDUs.  The message uses a descriptor ID
 *  to identify each such MSDU.  This function/macro is used to
 *  find the ID of one such MSDU referenced by the tx completion message.
 *
 * @param iterator - tx completion message context provided by HTT to the
 *      tx completion message handler.  This abstract reference to the
 *      HTT tx completion message's payload allows the data SW's tx
 *      completion handler to not care about the format of the HTT
 *      tx completion message.
 * @param num - (zero-based) index to specify a single MSDU within the
 *      series of MSDUs referenced by the tx completion message
 * @return descriptor ID for the specified MSDU
 */
u_int16_t
htt_tx_compl_desc_id(void *iterator, int num);


/*========================= tx descriptor operations ========================*/


/**
 * @brief Allocate a HTT abstract tx descriptor.
 * @details
 *  Allocate a HTT abstract tx descriptor from a pool within "consistent"
 *  memory, which is accessible by HIF and/or MAC DMA as well as by the
 *  host CPU.
 *  It is expected that the tx datapath will allocate HTT tx descriptors
 *  and link them with datapath SW tx descriptors up front as the driver
 *  is loaded.  Thereafter, the link from datapath SW tx descriptor to
 *  HTT tx descriptor will be maintained until the driver is unloaded.
 *
 * @param htt_pdev - handle to the HTT instance making the allocation
 * @param[OUT] paddr_lo - physical address of the HTT descriptor
 * @return success -> descriptor handle, -OR- failure -> NULL
 */
void *
htt_tx_desc_alloc(htt_pdev_handle htt_pdev, u_int32_t *paddr_lo);

/**
 * @brief Free a HTT abstract tx descriptor.
 *
 * @param htt_pdev - handle to the HTT instance that made the allocation
 * @param htt_tx_desc - the descriptor to free
 */
void
htt_tx_desc_free(htt_pdev_handle htt_pdev, void *htt_tx_desc);

/**
* @brief Discard all tx frames in the process of being downloaded.
* @details
* This function dicards any tx frames queued in HTT or the layers
* under HTT.
* The download completion callback is invoked on these frames.
*
* @param htt_pdev - handle to the HTT instance
*/
#if defined(CONFIG_HL_SUPPORT)
#define htt_tx_pending_discard(pdev) /* no-op */
#else
void
htt_tx_pending_discard(htt_pdev_handle pdev);
#endif

/**
 * @brief Download a MSDU descriptor and (a portion of) the MSDU payload.
 * @details
 *  This function is used within LL systems to download a tx descriptor and
 *  the initial portion of the tx MSDU payload, and within HL systems to
 *  download the tx descriptor and the entire tx MSDU payload.
 *  The HTT layer determines internally how much of the tx descriptor
 *  actually needs to be downloaded. In particular, the HTT layer does not
 *  download the fragmentation descriptor, and only for the LL case downloads
 *  the physical address of the fragmentation descriptor.
 *  In HL systems, the tx descriptor and the entire frame are downloaded.
 *  In LL systems, only the tx descriptor and the header of the frame are
 *  downloaded.  To determine how much of the tx frame to download, this
 *  function assumes the tx frame is the default frame type, as specified
 *  by ol_cfg_frame_type.  "Raw" frames need to be transmitted through the
 *  alternate htt_tx_send_nonstd function.
 *  The tx descriptor has already been attached to the adf_nbuf object during
 *  a preceding call to htt_tx_desc_init.
 *
 * @param htt_pdev - the handle of the physical device sending the tx data
 * @param msdu - the frame being transmitted
 * @param msdu_id - unique ID for the frame being transmitted
 * @return 0 -> success, -OR- 1 -> failure
 */
int
htt_tx_send_std(
    htt_pdev_handle htt_pdev,
    adf_nbuf_t msdu,
    u_int16_t msdu_id);

/**
 * @brief Download a Batch Of Tx MSDUs
 * @details
 *     Each MSDU already has the MSDU ID stored in the headroom of the
 *     netbuf data buffer, and has the HTT tx descriptor already attached
 *     as a prefix fragment to the netbuf.
 *
 * @param htt_pdev - the handle of the physical device sending the tx data
 * @param head_msdu - the MSDU Head for Tx batch being transmitted
 * @param num_msdus - The total Number of MSDU's provided for batch tx
 * @return null-terminated linked-list of unaccepted frames
 */
adf_nbuf_t
htt_tx_send_batch(
    htt_pdev_handle htt_pdev,
    adf_nbuf_t head_msdu,
    int num_msdus);



/* The htt scheduler for queued packets in htt
 * htt when unable to send to HTC because of lack of resource
 * forms a nbuf queue which is flushed when tx completion event from
 * target is recieved
 */

void
htt_tx_sched(htt_pdev_handle pdev);


/**
 * @brief Same as htt_tx_send_std, but can handle raw frames.
 */
int
htt_tx_send_nonstd(
    htt_pdev_handle htt_pdev,
    adf_nbuf_t msdu,
    u_int16_t msdu_id,
    enum htt_pkt_type pkt_type);

/**
 * @brief Initialize the tx descriptor.
 * @details
 *  This function initializes the tx descriptor.
 *  The values for the standard tx descriptor fields are provided as
 *  function arguments.  Non-standard descriptor fields, which don't
 *  have function arguments to specify their value, are set to zero.
 *  An exception to this initialization of non-standard fields to zero
 *  is the "extended TID" field, which is initialized to the "invalid"
 *  value (0x1f).
 *
 * @param pdev - the handle of the physical device sending the tx data
 * @param htt_tx_desc - abstract handle to the tx descriptor
 * @param htt_tx_desc_paddr_lo - physical address of the HTT tx descriptor
 * @param desc_id - ID to tag the descriptor with.
 *      The target FW uses this ID to identify to the host which MSDUs
 *      the target is referring to in its tx completion / postpone / drop
 *      messages.
 *      This ID is abstract - it is only interpreted inside the host
 *      tx datapath SW.  In practice, though, the ID is an index into an
 *      array of tx descriptor structs.
 *      This ID is used for both HL and LL systems, since in both systems
 *      the target may need to refer to a particular MSDU to explicitly tell
 *      the host when it may free the MSDU descriptor and network buffer.
 * @param msdu - the MSDU that is being prepared for transmission
 * @param msdu_info - tx MSDU meta-data
 */

/*
 * Provide a constant to specify the offset of the HTT portion of the
 * HTT tx descriptor, to avoid having to export the descriptor defintion.
 * The htt module checks internally that this exported offset is consistent
 * with the private tx descriptor definition.
 *
 * Similarly, export a definition of the HTT tx descriptor size, and then
 * check internally that this exported constant matches the private tx
 * descriptor definition.
 */
#define HTT_TX_DESC_VADDR_OFFSET 8
#define HTT_TX_DESC_SIZE 24
static inline
void
htt_tx_desc_init(
    htt_pdev_handle pdev,
    void *htt_tx_desc,
    u_int32_t htt_tx_desc_paddr_lo,
    u_int16_t msdu_id,
    adf_nbuf_t msdu,
    struct htt_msdu_info_t *msdu_info,
    struct ocb_tx_ctrl_hdr_t *tx_ctrl,
    u_int8_t is_dsrc)
{
    u_int32_t *word0, *word1, *word3;
    u_int32_t local_word0, local_word1, local_word3;
    struct htt_host_tx_desc_t *htt_host_tx_desc = (struct htt_host_tx_desc_t *)
        (((char *) htt_tx_desc) - HTT_TX_DESC_VADDR_OFFSET);
    bool desc_ext_required = (tx_ctrl && tx_ctrl->all_flags != 0);

    word0 = (u_int32_t *) htt_tx_desc;
    word1 = word0 + 1;
    /*
     * word2 is frag desc pointer
     * word3 is peer_id
     */
    word3 = word0 + 3; // Dword 3

    /*
     * HTT Tx Desc is in uncached memory. Used cached writes per word, to
     * reduce unnecessary memory access.
     */

    local_word0 = 0;
    HTT_H2T_MSG_TYPE_SET(local_word0, HTT_H2T_MSG_TYPE_TX_FRM);
    HTT_TX_DESC_PKT_TYPE_SET(local_word0, msdu_info->info.l2_hdr_type);
    HTT_TX_DESC_VDEV_ID_SET(local_word0, msdu_info->info.vdev_id);
    if (tx_ctrl && tx_ctrl->valid_tid) {
        HTT_TX_DESC_EXT_TID_SET(local_word0, tx_ctrl->ext_tid);
    } else {
        HTT_TX_DESC_EXT_TID_SET(local_word0, msdu_info->info.ext_tid);
    }
    HTT_TX_DESC_CKSUM_OFFLOAD_SET(local_word0, msdu_info->action.cksum_offload);
    HTT_TX_DESC_EXTENSION_SET(local_word0, desc_ext_required);
    if (pdev->cfg.is_high_latency)
        HTT_TX_DESC_TX_COMP_SET(local_word0, msdu_info->action.tx_comp_req);
    HTT_TX_DESC_NO_ENCRYPT_SET(local_word0, msdu_info->action.do_encrypt ? 0 : 1);
    *word0 = local_word0;

    local_word1 = 0;
    HTT_TX_DESC_FRM_LEN_SET(local_word1, adf_nbuf_len(msdu));
    HTT_TX_DESC_FRM_ID_SET(local_word1, msdu_id);

    *word1 = local_word1;

    /* Initialize peer_id to INVALID_PEER bcoz this is NOT Reinjection path*/
    local_word3 = HTT_INVALID_PEER;
    if (tx_ctrl && tx_ctrl->channel_freq) {
            HTT_TX_DESC_CHAN_FREQ_SET(local_word3, tx_ctrl->channel_freq);
    }
    *word3 = local_word3;

    /*
     *  If any of the tx control flags are set, then we need the extended
     *  HTT header.
     */
    if (desc_ext_required)
    {
        struct htt_tx_msdu_desc_ext_t local_desc_ext = {0};

        /*
         * Copy the info that was read from TX control header from the user
         * application to the extended HTT header.
         * First copy everything
         * to a local temp structure, and then copy everything to the
         * actual uncached structure in one go to save memory writes.
         */
        local_desc_ext.valid_pwr = tx_ctrl->valid_pwr;
        local_desc_ext.valid_mcs_mask = tx_ctrl->valid_datarate;
        local_desc_ext.valid_retries = tx_ctrl->valid_retries;
        local_desc_ext.valid_expire_tsf = tx_ctrl->valid_expire_tsf;
        local_desc_ext.valid_chainmask = tx_ctrl->valid_chain_mask;

        local_desc_ext.pwr = tx_ctrl->pwr;
        if (tx_ctrl->valid_datarate &&
            tx_ctrl->datarate <= htt_ofdm_datarate_max)
        local_desc_ext.mcs_mask = (1 << (tx_ctrl->datarate + 4));
        local_desc_ext.retry_limit = tx_ctrl->retry_limit;
        local_desc_ext.expire_tsf_lo = tx_ctrl->expire_tsf_lo;
        local_desc_ext.expire_tsf_hi = tx_ctrl->expire_tsf_hi;
        local_desc_ext.chain_mask = tx_ctrl->chain_mask;

        local_desc_ext.is_dsrc = (is_dsrc != 0);

        adf_nbuf_push_head(msdu, sizeof(local_desc_ext));
        adf_os_mem_copy(adf_nbuf_data(msdu), &local_desc_ext,
               sizeof(local_desc_ext));    }

    /*
     * Specify that the data provided by the OS is a bytestream,
     * and thus should not be byte-swapped during the HIF download
     * even if the host is big-endian.
     * There could be extra fragments added before the OS's fragments,
     * e.g. for TSO, so it's incorrect to clear the frag 0 wordstream flag.
     * Instead, clear the wordstream flag for the final fragment, which
     * is certain to be (one of the) fragment(s) provided by the OS.
     * Setting the flag for this final fragment suffices for specifying
     * all fragments provided by the OS rather than added by the driver.
     */
    adf_nbuf_set_frag_is_wordstream(msdu, adf_nbuf_get_num_frags(msdu) - 1, 0);

    /* store a link to the HTT tx descriptor within the netbuf */
    adf_nbuf_frag_push_head(
        msdu,
        HTT_TX_DESC_SIZE,
        (char *) htt_host_tx_desc, /* virtual addr */
        htt_tx_desc_paddr_lo, 0 /* phys addr MSBs - n/a */);

    /*
     * Indicate that the HTT header (and HTC header) is a meta-data
     * "wordstream", i.e. series of u_int32_t, rather than a data
     * bytestream.
     * This allows the HIF download to byteswap the HTT + HTC headers if
     * the host is big-endian, to convert to the target's little-endian
     * format.
     */
    adf_nbuf_set_frag_is_wordstream(msdu, 0, 1);
}

/**
 * @brief Set a flag to indicate that the MSDU in question was postponed.
 * @details
 *  In systems in which the host retains its tx frame until the target sends
 *  a tx completion, the target has the option of discarding it's copy of
 *  the tx descriptor (and frame, for HL) and sending a "postpone" message
 *  to the host, to inform the host that it must eventually download the
 *  tx descriptor (and frame, for HL).
 *  Before the host downloads the postponed tx desc/frame again, it will use
 *  this function to set a flag in the HTT tx descriptor indicating that this
 *  is a re-send of a postponed frame, rather than a new frame.  The target
 *  uses this flag to keep the correct order between re-sent and new tx frames.
 *  This function is relevant for LL systems.
 *
 * @param pdev - the handle of the physical device sending the tx data
 * @param desc - abstract handle to the tx descriptor
 */
void
htt_tx_desc_flag_postponed(htt_pdev_handle pdev, void *desc);

/**
 * @brief Set a flag to tell the target that more tx downloads are en route.
 * @details
 *  At times, particularly in response to a U-APSD trigger in a HL system, the
 *  host will download multiple tx descriptors (+ frames, in HL) in a batch.
 *  The host will use this function to set a "more" flag in the initial
 *  and interior frames of the batch, to tell the target that more tx frame
 *  downloads within the batch are imminent.
 *
 * @param pdev - the handle of the physical device sending the tx data
 * @param desc - abstract handle to the tx descriptor
 */
void
htt_tx_desc_flag_batch_more(htt_pdev_handle pdev, void *desc);

/**
 * @brief Specify the number of fragments in the fragmentation descriptor.
 * @details
 *  Specify the number of fragments within the MSDU, i.e. the number of
 *  elements within the fragmentation descriptor.
 *  For LL, this is used to terminate the list of fragments used by the
 *  HW's tx MAC DMA.
 *  For HL, this is used to terminate the list of fragments provided to
 *  HTC for download.
 *
 * @param pdev - the handle of the physical device sending the tx data
 * @param desc - abstract handle to the tx descriptor
 * @param num_frags - the number of fragments comprising the MSDU
 */
static inline
void
htt_tx_desc_num_frags(htt_pdev_handle pdev, void *desc, u_int32_t num_frags)
{
    /*
     * Set the element after the valid frag elems to 0x0,
     * to terminate the list of fragments.
     */
    *((u_int32_t *)
        (((char *) desc) + HTT_TX_DESC_LEN + num_frags * 8)) = 0;
}

/**
 * @brief Specify the location and size of a fragment of a tx MSDU.
 * @details
 *  In LL systems, the tx MAC DMA needs to know how the MSDU is constructed
 *  from fragments.
 *  In LL and HL systems, the HIF's download DMA to the target (LL: tx desc
 *  + header of tx payload; HL: tx desc + entire tx payload) needs to know
 *  where to find the fragments to download.
 *  The tx data SW uses this function to specify the location and size of
 *  each of the MSDU's fragments.
 *
 * @param pdev - the handle of the physical device sending the tx data
 * @param desc - abstract handle to the HTT tx descriptor
 * @param frag_num - which fragment is being specified (zero-based indexing)
 * @param frag_phys_addr - DMA/physical address of the fragment
 * @param frag_len - number of bytes within the fragment
 */
static inline
void
htt_tx_desc_frag(
    htt_pdev_handle pdev,
    void *desc,
    int frag_num,
    u_int32_t frag_phys_addr,
    u_int16_t frag_len)
{
    u_int32_t *word =
        (u_int32_t *) (((char *) desc) + HTT_TX_DESC_LEN + frag_num * 8);
    *word = frag_phys_addr;
    word++;
    *word = frag_len;
}

void htt_tx_desc_frags_table_set(
    htt_pdev_handle pdev,
    void *desc,
    u_int32_t paddr,
    int reset);

/**
 * @brief Specify the type and subtype of a tx frame.
 *
 * @param pdev - the handle of the physical device sending the tx data
 * @param type - format of the MSDU (802.3, native WiFi, raw, or mgmt)
 * @param sub_type - sub_type (relevant for raw frames)
 */
static inline
void
htt_tx_desc_type(
    htt_pdev_handle pdev,
    void *htt_tx_desc,
    enum wlan_frm_fmt type,
    u_int8_t sub_type)
{
    u_int32_t *word0;

    word0 = (u_int32_t *) htt_tx_desc;
    /* clear old values */
    *word0 &= ~(HTT_TX_DESC_PKT_TYPE_M | HTT_TX_DESC_PKT_SUBTYPE_M);
    /* write new values */
    HTT_TX_DESC_PKT_TYPE_SET(*word0, type);
    HTT_TX_DESC_PKT_SUBTYPE_SET(*word0, sub_type);
}

/***** TX MGMT DESC management APIs ****/

/* Number of mgmt descriptors in the pool */
#define HTT_MAX_NUM_MGMT_DESCS 32

/** htt_tx_mgmt_desc_pool_alloc
 * @description - allocates the memory for mgmt frame descriptors
 * @param  - htt pdev object
 * @param  - num of descriptors to be allocated in the pool
 */
void
htt_tx_mgmt_desc_pool_alloc(struct htt_pdev_t *pdev, A_UINT32 num_elems);

/** htt_tx_mgmt_desc_alloc
 * @description - reserves a mgmt descriptor from the pool
 * @param  - htt pdev object
 * @param  - pointer to variable to hold the allocated desc id
 * @param  - pointer to the mamangement from UMAC
 * @return - pointer the allocated mgmt descriptor
 */
adf_nbuf_t
htt_tx_mgmt_desc_alloc(struct htt_pdev_t *pdev, A_UINT32 *desc_id, adf_nbuf_t mgmt_frm);

/** htt_tx_mgmt_desc_free
 * @description - releases the management descriptor back to the pool
 * @param  - htt pdev object
 * @param  - descriptor ID
 */
void
htt_tx_mgmt_desc_free(struct htt_pdev_t *pdev, A_UINT8 desc_id, A_UINT32 status);

/** htt_tx_mgmt_desc_pool_free
 * @description - releases all the resources allocated for mgmt desc pool
 * @param  - htt pdev object
 */
void
htt_tx_mgmt_desc_pool_free(struct htt_pdev_t *pdev);


/**
 * @brief Provide a buffer to store a 802.11 header added by SW tx encap
 *
 * @param htt_tx_desc - which frame the 802.11 header is being added to
 * @param new_l2_hdr_size - how large the buffer needs to be
 */
#define htt_tx_desc_mpdu_header(htt_tx_desc, new_l2_hdr_size) /*NULL*/

/**
 * @brief How many tx credits would be consumed by the specified tx frame.
 *
 * @param msdu - the tx frame in question
 * @return number of credits used for this tx frame
 */
#define htt_tx_msdu_credit(msdu) 1 /* 1 credit per buffer */



#ifdef HTT_DBG
void
htt_tx_desc_display(void *tx_desc);
#else
#define htt_tx_desc_display(tx_desc)
#endif

static inline
void htt_tx_desc_set_peer_id(void *htt_tx_desc, u_int16_t peer_id)
{
    u_int16_t *peer_id_field_ptr;

    peer_id_field_ptr = (u_int16_t *)
        (htt_tx_desc + HTT_TX_DESC_PEERID_DESC_PADDR_OFFSET_BYTES);

    *peer_id_field_ptr = peer_id;
}
static inline
void htt_tx_desc_set_chanfreq(void *htt_tx_desc, u_int16_t chanfreq)
{
    u_int16_t *chanfreq_field_ptr;

    chanfreq_field_ptr = (u_int16_t *)
        (htt_tx_desc + HTT_TX_DESC_CHAN_FREQ_OFFSET_BYTES);

    *chanfreq_field_ptr = chanfreq;
}

#endif /* _OL_HTT_TX_API__H_ */
