/**
 *  @brief Add TxPD to AMSDU header
 *
 *  @param priv     A pointer to mlan_private structure
 *  @param mbuf		Pointer to buffer where the TxPD will be formed
 *
 *  @return		N/A
 */
static void
wlan_11n_form_amsdu_txpd(mlan_private * priv, mlan_buffer * mbuf)
{
	TxPD *ptx_pd;
	mlan_adapter *pmadapter = priv->adapter;

	ENTER();

	ptx_pd = (TxPD *) mbuf->pbuf;
	memset(pmadapter, ptx_pd, 0, sizeof(TxPD));

	/*
	 * Original priority has been overwritten
	 */
	ptx_pd->priority = (t_u8) mbuf->priority;
	ptx_pd->pkt_delay_2ms =
		wlan_wmm_compute_driver_packet_delay(priv, mbuf);
	ptx_pd->bss_num = GET_BSS_NUM(priv);
	ptx_pd->bss_type = priv->bss_type;
	/* Always zero as the data is followed by TxPD */
	ptx_pd->tx_pkt_offset = sizeof(TxPD);
	ptx_pd->tx_pkt_type = PKT_TYPE_AMSDU;
	if (mbuf->flags & MLAN_BUF_FLAG_TDLS)
		ptx_pd->flags = MRVDRV_TxPD_FLAGS_TDLS_PACKET;
	if (ptx_pd->tx_control == 0)
		/* TxCtrl set by user or default */
		ptx_pd->tx_control = priv->pkt_tx_ctrl;

	endian_convert_TxPD(ptx_pd);

	LEAVE();
}
/**
 *  @brief This function fill the txpd for tx packet
 *
 *  @param priv	   A pointer to mlan_private structure
 *  @param pmbuf   A pointer to the mlan_buffer for process
 *
 *  @return        headptr or MNULL
 */
t_void *
wlan_ops_uap_process_txpd(IN t_void *priv, IN pmlan_buffer pmbuf)
{
	pmlan_private pmpriv = (pmlan_private)priv;
	UapTxPD *plocal_tx_pd;
	t_u8 *head_ptr = MNULL;
	t_u32 pkt_type;
	t_u32 tx_control;
	ENTER();

	if (!pmbuf->data_len) {
		PRINTM(MERROR, "uAP Tx Error: Invalid packet length: %d\n",
		       pmbuf->data_len);
		pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID;
		goto done;
	}
	if (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) {
		memcpy(pmpriv->adapter, &pkt_type,
		       pmbuf->pbuf + pmbuf->data_offset, sizeof(pkt_type));
		memcpy(pmpriv->adapter, &tx_control,
		       pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type),
		       sizeof(tx_control));
		pmbuf->data_offset += sizeof(pkt_type) + sizeof(tx_control);
		pmbuf->data_len -= sizeof(pkt_type) + sizeof(tx_control);
	}
	if (pmbuf->data_offset < (sizeof(UapTxPD) + INTF_HEADER_LEN +
				  DMA_ALIGNMENT)) {
		PRINTM(MERROR,
		       "not enough space for UapTxPD: headroom=%d pkt_len=%d, required=%d\n",
		       pmbuf->data_offset, pmbuf->data_len,
		       sizeof(UapTxPD) + INTF_HEADER_LEN + DMA_ALIGNMENT);
		DBG_HEXDUMP(MDAT_D, "drop pkt",
			    pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len);
		pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID;
		goto done;
	}

	/* head_ptr should be aligned */
	head_ptr =
		pmbuf->pbuf + pmbuf->data_offset - sizeof(UapTxPD) -
		INTF_HEADER_LEN;
	head_ptr = (t_u8 *)((t_ptr)head_ptr & ~((t_ptr)(DMA_ALIGNMENT - 1)));

	plocal_tx_pd = (UapTxPD *)(head_ptr + INTF_HEADER_LEN);
	memset(pmpriv->adapter, plocal_tx_pd, 0, sizeof(UapTxPD));

	/* Set the BSS number to TxPD */
	plocal_tx_pd->bss_num = GET_BSS_NUM(pmpriv);
	plocal_tx_pd->bss_type = pmpriv->bss_type;

	plocal_tx_pd->tx_pkt_length = (t_u16)pmbuf->data_len;

	plocal_tx_pd->priority = (t_u8)pmbuf->priority;
	plocal_tx_pd->pkt_delay_2ms =
		wlan_wmm_compute_driver_packet_delay(pmpriv, pmbuf);

	if (plocal_tx_pd->priority <
	    NELEMENTS(pmpriv->wmm.user_pri_pkt_tx_ctrl))
		/*
		 * Set the priority specific tx_control field, setting of 0 will
		 *   cause the default value to be used later in this function
		 */
		plocal_tx_pd->tx_control
			=
			pmpriv->wmm.user_pri_pkt_tx_ctrl[plocal_tx_pd->
							 priority];

	if (pmbuf->flags & MLAN_BUF_FLAG_TX_STATUS) {
		plocal_tx_pd->tx_token_id = (t_u8)pmbuf->tx_seq_num;
		plocal_tx_pd->flags |= MRVDRV_TxPD_FLAGS_TX_PACKET_STATUS;
	}

	/* Offset of actual data */
	plocal_tx_pd->tx_pkt_offset =
		(t_u16)((t_ptr)pmbuf->pbuf + pmbuf->data_offset -
			(t_ptr)plocal_tx_pd);

	if (!plocal_tx_pd->tx_control) {
		/* TxCtrl set by user or default */
		plocal_tx_pd->tx_control = pmpriv->pkt_tx_ctrl;
	}

	if (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) {
		plocal_tx_pd->tx_pkt_type = (t_u16)pkt_type;
		plocal_tx_pd->tx_control = tx_control;
	}
	uap_endian_convert_TxPD(plocal_tx_pd);

	/* Adjust the data offset and length to include TxPD in pmbuf */
	pmbuf->data_len += pmbuf->data_offset;
	pmbuf->data_offset = (t_u32)((t_ptr)head_ptr - (t_ptr)pmbuf->pbuf);
	pmbuf->data_len -= pmbuf->data_offset;

done:
	LEAVE();
	return head_ptr;
}
/** 
 *  @brief This function fill the txpd for tx packet  
 *  
 *  @param priv	   A pointer to mlan_private structure
 *  @param pmbuf   A pointer to the mlan_buffer for process
 *
 *  @return 	   headptr or MNULL
 */
t_void *
wlan_ops_sta_process_txpd(IN t_void * priv, IN pmlan_buffer pmbuf)
{
    mlan_private *pmpriv = (mlan_private *) priv;
    pmlan_adapter pmadapter = pmpriv->adapter;
    TxPD *plocal_tx_pd;
    t_u8 *head_ptr = MNULL;

    ENTER();

    if (!pmbuf->data_len) {
        PRINTM(MERROR, "STA Tx Error: Invalid packet length: %d\n",
               pmbuf->data_len);
        pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID;
        goto done;
    }

    if (pmbuf->data_offset < (sizeof(TxPD) + INTF_HEADER_LEN + DMA_ALIGNMENT)) {
        PRINTM(MERROR, "not enough space for TxPD: %d\n", pmbuf->data_len);
        pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID;
        goto done;
    }

    /* head_ptr should be aligned */
    head_ptr =
        pmbuf->pbuf + pmbuf->data_offset - sizeof(TxPD) - INTF_HEADER_LEN;
    head_ptr = (t_u8 *) ((t_ptr) head_ptr & ~((t_ptr) (DMA_ALIGNMENT - 1)));

    plocal_tx_pd = (TxPD *) (head_ptr + INTF_HEADER_LEN);
    memset(pmadapter, plocal_tx_pd, 0, sizeof(TxPD));
    /* Set the BSS number to TxPD */
    plocal_tx_pd->bss_num = GET_BSS_NUM(pmpriv);
    plocal_tx_pd->bss_type = pmpriv->bss_type;

    plocal_tx_pd->tx_pkt_length = (t_u16) pmbuf->data_len;

    plocal_tx_pd->priority = (t_u8) pmbuf->priority;
    plocal_tx_pd->pkt_delay_2ms =
        wlan_wmm_compute_driver_packet_delay(pmpriv, pmbuf);

    if (plocal_tx_pd->priority < NELEMENTS(pmpriv->wmm.user_pri_pkt_tx_ctrl))
        /* 
         * Set the priority specific tx_control field, setting of 0 will
         *   cause the default value to be used later in this function
         */
        plocal_tx_pd->tx_control
            = pmpriv->wmm.user_pri_pkt_tx_ctrl[plocal_tx_pd->priority];
    if (pmadapter->pps_uapsd_mode) {
        if (MTRUE == wlan_check_last_packet_indication(pmpriv)) {
            pmadapter->tx_lock_flag = MTRUE;
            plocal_tx_pd->flags = MRVDRV_TxPD_POWER_MGMT_LAST_PACKET;
        }
    }

    /* Offset of actual data */
    plocal_tx_pd->tx_pkt_offset =
        (t_u16) ((t_ptr) pmbuf->pbuf + pmbuf->data_offset -
                 (t_ptr) plocal_tx_pd);

    if (!plocal_tx_pd->tx_control) {
        /* TxCtrl set by user or default */
        plocal_tx_pd->tx_control = pmpriv->pkt_tx_ctrl;
    }

    endian_convert_TxPD(plocal_tx_pd);

    /* Adjust the data offset and length to include TxPD in pmbuf */
    pmbuf->data_len += pmbuf->data_offset;
    pmbuf->data_offset = (t_u32) (head_ptr - pmbuf->pbuf);
    pmbuf->data_len -= pmbuf->data_offset;

  done:
    LEAVE();
    return head_ptr;
}
/** 
 *  @brief This function tells firmware to send a NULL data packet.
 *  
 *  @param priv     A pointer to mlan_private structure
 *  @param flags    Transmit Pkt Flags
 *
 *  @return 	    MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise failure
 */
mlan_status
wlan_send_null_packet(pmlan_private priv, t_u8 flags)
{
    pmlan_adapter pmadapter = priv->adapter;
    TxPD *ptx_pd;
/* sizeof(TxPD) + Interface specific header */
#define NULL_PACKET_HDR 256
    t_u32 data_len = NULL_PACKET_HDR;
    pmlan_buffer pmbuf = MNULL;
    t_u8 *ptr;
    mlan_status ret = MLAN_STATUS_SUCCESS;
    t_u32 sec, usec;

    ENTER();

    if (pmadapter->surprise_removed == MTRUE) {
        ret = MLAN_STATUS_FAILURE;
        goto done;
    }

    if (priv->media_connected == MFALSE) {
        ret = MLAN_STATUS_FAILURE;
        goto done;
    }

    if (pmadapter->data_sent == MTRUE) {
        ret = MLAN_STATUS_FAILURE;
        goto done;
    }

    pmbuf = wlan_alloc_mlan_buffer(pmadapter, data_len, 0, MTRUE);
    if (!pmbuf) {
        ret = MLAN_STATUS_FAILURE;
        goto done;
    }
    memset(pmadapter, pmbuf->pbuf, 0, data_len);
    pmbuf->bss_index = priv->bss_index;
    pmbuf->buf_type = MLAN_BUF_TYPE_DATA;
    ptr = pmbuf->pbuf + pmbuf->data_offset;
    pmbuf->data_len = sizeof(TxPD) + INTF_HEADER_LEN;
    ptx_pd = (TxPD *) (ptr + INTF_HEADER_LEN);
    ptx_pd->tx_control = priv->pkt_tx_ctrl;
    ptx_pd->flags = flags;
    ptx_pd->priority = WMM_HIGHEST_PRIORITY;
    ptx_pd->tx_pkt_offset = sizeof(TxPD);
    /* Set the BSS number to TxPD */
    ptx_pd->bss_num = GET_BSS_NUM(priv);
    ptx_pd->bss_type = priv->bss_type;

    endian_convert_TxPD(ptx_pd);

    ret =
        pmadapter->callbacks.moal_write_data_async(pmadapter->pmoal_handle,
                                                   pmbuf, MLAN_USB_EP_DATA);

    switch (ret) {
    case MLAN_STATUS_RESOURCE:
        pmadapter->data_sent = MTRUE;
        /* Fall through FAILURE handling */
    case MLAN_STATUS_FAILURE:
        wlan_free_mlan_buffer(pmadapter, pmbuf);
        PRINTM(MERROR, "STA Tx Error: Failed to send NULL packet!\n");
        pmadapter->dbg.num_tx_host_to_card_failure++;
        goto done;
    case MLAN_STATUS_SUCCESS:
        wlan_free_mlan_buffer(pmadapter, pmbuf);
        PRINTM(MINFO, "STA Tx: Successfully send the NULL packet\n");
        pmadapter->tx_lock_flag = MTRUE;
        break;
    case MLAN_STATUS_PENDING:
        break;
    default:
        break;
    }

    pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec,
                                              &usec);
    PRINTM(MDATA, "%lu.%06lu : Null data => FW\n", sec, usec);
    DBG_HEXDUMP(MDAT_D, "Null data", ptr, sizeof(TxPD) + INTF_HEADER_LEN);
  done:
    LEAVE();
    return ret;
}