/**
 *   @brief This function processes the received buffer
 *
 *   @param pmadapter A pointer to mlan_adapter
 *   @param pmbuf     A pointer to the received buffer
 *
 *   @return        MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
mlan_status
wlan_handle_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf)
{
	mlan_status ret = MLAN_STATUS_SUCCESS;
	pmlan_private priv = MNULL;
	RxPD *prx_pd;
#ifdef DEBUG_LEVEL1
	t_u32 sec = 0, usec = 0;
#endif

	ENTER();

	prx_pd = (RxPD *) (pmbuf->pbuf + pmbuf->data_offset);
	/* Get the BSS number from RxPD, get corresponding priv */
	priv = wlan_get_priv_by_id(pmadapter, prx_pd->bss_num & BSS_NUM_MASK,
				   prx_pd->bss_type);
	if (!priv)
		priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY);
	pmbuf->bss_index = priv->bss_index;
	PRINTM_GET_SYS_TIME(MDATA, &sec, &usec);
	PRINTM_NETINTF(MDATA, priv);
	PRINTM(MDATA, "%lu.%06lu : Data <= FW\n", sec, usec);
	ret = priv->ops.process_rx_packet(pmadapter, pmbuf);

	LEAVE();
	return ret;
}
/**
 *  @brief Aggregate multiple packets into one single AMSDU packet
 *
 *  @param priv     A pointer to mlan_private structure
 *  @param pra_list Pointer to the RA List table containing the pointers
 *                  to packets.
 *  @param headroom Any interface specific headroom that may be need. TxPD
 *                  will be formed leaving this headroom.
 *  @param ptrindex Pointer index
 *
 *  @return     Final packet size or MLAN_STATUS_FAILURE
 */
int
wlan_11n_aggregate_pkt(mlan_private * priv, raListTbl * pra_list,
		       int headroom, int ptrindex)
{
	int pkt_size = 0;
	pmlan_adapter pmadapter = priv->adapter;
	mlan_buffer *pmbuf_aggr, *pmbuf_src;
	t_u8 *data;
	int pad = 0;
	mlan_status ret = MLAN_STATUS_SUCCESS;
#ifdef DEBUG_LEVEL1
	t_u32 sec = 0, usec = 0;
#endif
	mlan_tx_param tx_param;
#ifdef STA_SUPPORT
	TxPD *ptx_pd = MNULL;
#endif
	t_u32 max_amsdu_size = MIN(pra_list->max_amsdu, pmadapter->tx_buf_size);
	ENTER();

	PRINTM(MDAT_D, "Handling Aggr packet\n");

	pmbuf_src =
		(pmlan_buffer) util_peek_list(pmadapter->pmoal_handle,
					      &pra_list->buf_head, MNULL,
					      MNULL);
	if (pmbuf_src) {
		pmbuf_aggr = wlan_alloc_mlan_buffer(pmadapter,
						    pmadapter->tx_buf_size, 0,
						    MOAL_MALLOC_BUFFER);
		if (!pmbuf_aggr) {
			PRINTM(MERROR, "Error allocating mlan_buffer\n");
			pmadapter->callbacks.moal_spin_unlock(pmadapter->
							      pmoal_handle,
							      priv->wmm.
							      ra_list_spinlock);
			LEAVE();
			return MLAN_STATUS_FAILURE;
		}

		data = pmbuf_aggr->pbuf + headroom;
		pmbuf_aggr->bss_index = pmbuf_src->bss_index;
		pmbuf_aggr->buf_type = pmbuf_src->buf_type;
		pmbuf_aggr->priority = pmbuf_src->priority;
		pmbuf_aggr->pbuf = data;
		pmbuf_aggr->data_offset = 0;
		pmbuf_aggr->in_ts_sec = pmbuf_src->in_ts_sec;
		pmbuf_aggr->in_ts_usec = pmbuf_src->in_ts_usec;
		if (pmbuf_src->flags & MLAN_BUF_FLAG_TDLS)
			pmbuf_aggr->flags |= MLAN_BUF_FLAG_TDLS;

		/* Form AMSDU */
		wlan_11n_form_amsdu_txpd(priv, pmbuf_aggr);
		pkt_size = sizeof(TxPD);
#ifdef STA_SUPPORT
		if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA)
			ptx_pd = (TxPD *) pmbuf_aggr->pbuf;
#endif
	} else {
		pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
						      priv->wmm.
						      ra_list_spinlock);
		goto exit;
	}

	while (pmbuf_src && ((pkt_size + (pmbuf_src->data_len + LLC_SNAP_LEN)
			      + headroom) <= max_amsdu_size)) {

		pmbuf_src = (pmlan_buffer)
			util_dequeue_list(pmadapter->pmoal_handle,
					  &pra_list->buf_head, MNULL, MNULL);

		pra_list->total_pkts--;

		/* decrement for every PDU taken from the list */
		priv->wmm.pkts_queued[ptrindex]--;
		util_scalar_decrement(pmadapter->pmoal_handle,
				      &priv->wmm.tx_pkts_queued, MNULL, MNULL);

		pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
						      priv->wmm.
						      ra_list_spinlock);

		if (pmbuf_src->flags & MLAN_BUF_FLAG_TCP_ACK)
			pmadapter->callbacks.moal_tcp_ack_tx_ind(pmadapter->
								 pmoal_handle,
								 pmbuf_src);

		pkt_size += wlan_11n_form_amsdu_pkt(pmadapter,
						    (data + pkt_size),
						    pmbuf_src->pbuf +
						    pmbuf_src->data_offset,
						    pmbuf_src->data_len, &pad);

		DBG_HEXDUMP(MDAT_D, "pmbuf_src", pmbuf_src,
			    sizeof(mlan_buffer));
		wlan_write_data_complete(pmadapter, pmbuf_src,
					 MLAN_STATUS_SUCCESS);

		pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
						    priv->wmm.ra_list_spinlock);

		if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) {
			pmadapter->callbacks.moal_spin_unlock(pmadapter->
							      pmoal_handle,
							      priv->wmm.
							      ra_list_spinlock);
			LEAVE();
			return MLAN_STATUS_FAILURE;
		}

		pmbuf_src =
			(pmlan_buffer) util_peek_list(pmadapter->pmoal_handle,
						      &pra_list->buf_head,
						      MNULL, MNULL);
	}

	pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
					      priv->wmm.ra_list_spinlock);

	/* Last AMSDU packet does not need padding */
	pkt_size -= pad;
	pmbuf_aggr->data_len = pkt_size;
	wlan_11n_update_pktlen_amsdu_txpd(priv, pmbuf_aggr);
	pmbuf_aggr->data_len += headroom;
	pmbuf_aggr->pbuf = data - headroom;
	tx_param.next_pkt_len = ((pmbuf_src) ?
				 pmbuf_src->data_len + sizeof(TxPD) : 0);
	ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA,
				     pmbuf_aggr, &tx_param);
	switch (ret) {
	case MLAN_STATUS_RESOURCE:
		pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
						    priv->wmm.ra_list_spinlock);

		if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) {
			pmadapter->callbacks.moal_spin_unlock(pmadapter->
							      pmoal_handle,
							      priv->wmm.
							      ra_list_spinlock);
			pmbuf_aggr->status_code = MLAN_ERROR_PKT_INVALID;
			wlan_write_data_complete(pmadapter, pmbuf_aggr,
						 MLAN_STATUS_FAILURE);
			LEAVE();
			return MLAN_STATUS_FAILURE;
		}
#ifdef STA_SUPPORT
		/* reset tx_lock_flag */
		if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) &&
		    pmadapter->pps_uapsd_mode &&
		    (pmadapter->tx_lock_flag == MTRUE)) {
			pmadapter->tx_lock_flag = MFALSE;
			ptx_pd->flags = 0;
		}
#endif
		util_enqueue_list_head(pmadapter->pmoal_handle,
				       &pra_list->buf_head,
				       (pmlan_linked_list) pmbuf_aggr, MNULL,
				       MNULL);

		pra_list->total_pkts++;

		/* add back only one: aggregated packet is requeued as one */
		priv->wmm.pkts_queued[ptrindex]++;
		util_scalar_increment(pmadapter->pmoal_handle,
				      &priv->wmm.tx_pkts_queued, MNULL, MNULL);
		pmbuf_aggr->flags |= MLAN_BUF_FLAG_REQUEUED_PKT;
		pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
						      priv->wmm.
						      ra_list_spinlock);
		PRINTM(MINFO, "MLAN_STATUS_RESOURCE is returned\n");
		pmbuf_aggr->status_code = MLAN_ERROR_PKT_INVALID;
		break;
	case MLAN_STATUS_FAILURE:
		pmadapter->data_sent = MFALSE;
		PRINTM(MERROR, "Error: host_to_card failed: 0x%X\n", ret);
		pmbuf_aggr->status_code = MLAN_ERROR_DATA_TX_FAIL;
		pmadapter->dbg.num_tx_host_to_card_failure++;
		wlan_write_data_complete(pmadapter, pmbuf_aggr, ret);
		goto exit;
	case MLAN_STATUS_PENDING:
		break;
	case MLAN_STATUS_SUCCESS:
		wlan_write_data_complete(pmadapter, pmbuf_aggr, ret);
		break;
	default:
		break;
	}
	if (ret != MLAN_STATUS_RESOURCE) {
		pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
						    priv->wmm.ra_list_spinlock);
		if (wlan_is_ralist_valid(priv, pra_list, ptrindex)) {
			priv->wmm.packets_out[ptrindex]++;
			priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list;
		}
		pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur =
			pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur->
			pnext;
		pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
						      priv->wmm.
						      ra_list_spinlock);
	}
	PRINTM_GET_SYS_TIME(MDATA, &sec, &usec);
	PRINTM_NETINTF(MDATA, priv);
	PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec);

exit:
	LEAVE();
	return pkt_size + headroom;
}
/**
 *  @brief This function checks the conditions and sends packet to device
 *
 *  @param priv	   A pointer to mlan_private structure
 *  @param pmbuf   A pointer to the mlan_buffer for process
 *  @param tx_param A pointer to mlan_tx_param structure
 *
 *  @return         MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise failure
 */
mlan_status
wlan_process_tx(pmlan_private priv, pmlan_buffer pmbuf,
		mlan_tx_param * tx_param)
{
	mlan_status ret = MLAN_STATUS_SUCCESS;
	pmlan_adapter pmadapter = priv->adapter;
	t_u8 *head_ptr = MNULL;
#ifdef DEBUG_LEVEL1
	t_u32 sec = 0, usec = 0;
#endif
#ifdef STA_SUPPORT
	TxPD *plocal_tx_pd = MNULL;
#endif

	ENTER();
	head_ptr = (t_u8 *) priv->ops.process_txpd(priv, pmbuf);
	if (!head_ptr) {
		pmbuf->status_code = MLAN_ERROR_PKT_INVALID;
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
#ifdef STA_SUPPORT
	if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA)
		plocal_tx_pd = (TxPD *) (head_ptr + INTF_HEADER_LEN);
#endif
	ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, pmbuf,
				     tx_param);
done:
	switch (ret) {
	case MLAN_STATUS_RESOURCE:
#ifdef STA_SUPPORT
		if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) &&
		    pmadapter->pps_uapsd_mode &&
		    (pmadapter->tx_lock_flag == MTRUE)) {
			pmadapter->tx_lock_flag = MFALSE;
			plocal_tx_pd->flags = 0;
		}
#endif
		PRINTM(MINFO, "MLAN_STATUS_RESOURCE is returned\n");
		break;
	case MLAN_STATUS_FAILURE:
		pmadapter->data_sent = MFALSE;
		PRINTM(MERROR, "Error: host_to_card failed: 0x%X\n", ret);
		pmadapter->dbg.num_tx_host_to_card_failure++;
		pmbuf->status_code = MLAN_ERROR_DATA_TX_FAIL;
		wlan_write_data_complete(pmadapter, pmbuf, ret);
		break;
	case MLAN_STATUS_PENDING:
		DBG_HEXDUMP(MDAT_D, "Tx", head_ptr + INTF_HEADER_LEN,
			    MIN(pmbuf->data_len + sizeof(TxPD),
				MAX_DATA_DUMP_LEN));
		break;
	case MLAN_STATUS_SUCCESS:
		DBG_HEXDUMP(MDAT_D, "Tx", head_ptr + INTF_HEADER_LEN,
			    MIN(pmbuf->data_len + sizeof(TxPD),
				MAX_DATA_DUMP_LEN));
		wlan_write_data_complete(pmadapter, pmbuf, ret);
		break;
	default:
		break;
	}

	if ((ret == MLAN_STATUS_SUCCESS) || (ret == MLAN_STATUS_PENDING)) {
		PRINTM_GET_SYS_TIME(MDATA, &sec, &usec);
		PRINTM_NETINTF(MDATA, priv);
		PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec);
	}
	LEAVE();
	return ret;
}
/** 
 *  @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;
#ifdef DEBUG_LEVEL1
    t_u32 sec, usec;
#endif

    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, MOAL_MALLOC_BUFFER);
    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 = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, pmbuf, MNULL);

    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;
    }

    PRINTM_GET_SYS_TIME(MDATA, &sec, &usec);
    PRINTM_NETINTF(MDATA, priv);
    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;
}