/*
 * txPort.c
 *
 * Copyright(c) 1998 - 2010 Texas Instruments. All rights reserved.      
 * All rights reserved.                                                  
 *                                                                       
 * 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.                                                      
 *  * Neither the name Texas Instruments nor the names of its            
 *    contributors may be used to endorse or promote products derived    
 *    from this software without specific prior written permission.      
 *                                                                       
 * 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  
 * OWNER 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.
 */


/****************************************************************************
 *
 *   MODULE:  txPort.c
 *   
 *   PURPOSE: Multiplexes between the management and data queues.
 * 
 *	 DESCRIPTION:  
 *   ============
 * 		The Tx port state machine multiplexes between the management and data queues 
 *		according to the management queues requests.
 *
 ****************************************************************************/

#define __FILE_ID__  FILE_ID_62
#include "commonTypes.h"
#include "tidef.h"
#include "osApi.h"
#include "report.h"
#include "DataCtrl_Api.h"
#include "DrvMainModules.h"


typedef enum
{
    MUX_MGMT_QUEUES,	/* The management queues have access to the Tx path. */
    MUX_DATA_QUEUES		/* The data queues have access to the Tx path. */
} EQueuesMuxState;

typedef enum
{
	QUEUE_ACTION_NONE,
	QUEUE_ACTION_STOP,
	QUEUE_ACTION_WAKE
} EQueueAction;

/* The txPort module object. */
typedef struct 
{
	TI_HANDLE		hOs;
	TI_HANDLE		hReport;
	TI_HANDLE		hTxDataQ;
	TI_HANDLE		hTxMgmtQ;

	EQueuesMuxState queuesMuxState;
	TI_BOOL			txSuspended;
	TI_BOOL			mgmtQueueEnabled;
	TI_BOOL			dataQueueEnabled;
} TTxPortObj;

/* 
 * The txPort local functions:
 */
static void updateQueuesStates(TTxPortObj *pTxPort);

/****************************************************************************
 *                      txPort_Create()
 ****************************************************************************
 * DESCRIPTION:	Create the txPort module object 
 * 
 * INPUTS:	None
 * 
 * OUTPUT:	None
 * 
 * RETURNS:	The Created object
 ****************************************************************************/
TI_HANDLE txPort_create(TI_HANDLE hOs)
{
	TTxPortObj *pTxPort;

	pTxPort = os_memoryAlloc(hOs, sizeof(TTxPortObj));
	if (pTxPort == NULL)
		return NULL;

	os_memoryZero(hOs, pTxPort, sizeof(TTxPortObj));

	pTxPort->hOs = hOs;

	return( (TI_HANDLE)pTxPort );
}


/****************************************************************************
 *                      txPort_unLoad()
 ****************************************************************************
 * DESCRIPTION:	Unload the txPort module object 
 * 
 * INPUTS:	hTxPort - The object to free
 * 
 * OUTPUT:	None
 * 
 * RETURNS:	TI_OK 
 ****************************************************************************/
TI_STATUS txPort_unLoad(TI_HANDLE hTxPort)
{
	TTxPortObj *pTxPort = (TTxPortObj *)hTxPort;

	if (pTxPort)
		os_memoryFree(pTxPort->hOs, pTxPort, sizeof(TTxPortObj));

	return TI_OK;
}


/****************************************************************************
 *                      txPort_init()
 ****************************************************************************
 * DESCRIPTION:	Configure the txPort module object 
 * 
 * INPUTS:	The needed TI handles
 * 
 * OUTPUT:	None
 * 
 * RETURNS:	void
 ****************************************************************************/
void txPort_init (TStadHandlesList *pStadHandles)
{
	TTxPortObj *pTxPort = (TTxPortObj *)(pStadHandles->hTxPort);

	pTxPort->hReport  = pStadHandles->hReport;
	pTxPort->hTxDataQ = pStadHandles->hTxDataQ;
	pTxPort->hTxMgmtQ = pStadHandles->hTxMgmtQ;

	pTxPort->queuesMuxState	  = MUX_MGMT_QUEUES;
	pTxPort->txSuspended	  = TI_FALSE;
	pTxPort->mgmtQueueEnabled = TI_TRUE;
	pTxPort->dataQueueEnabled = TI_FALSE;
}


/****************************************************************************
 *                      txPort_enableData()
 ****************************************************************************
 * DESCRIPTION:	Called by the txMgmtQueue SM when the Tx path CAN be used by the
 *				  data-queues (i.e. it's not needed for mgmt). Update the queues accordingly.
 ****************************************************************************/
void txPort_enableData(TI_HANDLE hTxPort)
{
	TTxPortObj *pTxPort = (TTxPortObj *)hTxPort;

	pTxPort->queuesMuxState = MUX_DATA_QUEUES;
	updateQueuesStates(pTxPort);
} 


/****************************************************************************
 *                      txPort_enableMgmt()
 ****************************************************************************
 * DESCRIPTION:	Called by the txMgmtQueue SM when the Tx path CAN'T be used by the
 *				  data-queues (i.e. it's needed for mgmt). Update the queues accordingly.
 ****************************************************************************/
void txPort_enableMgmt(TI_HANDLE hTxPort)
{
	TTxPortObj *pTxPort = (TTxPortObj *)hTxPort;

	pTxPort->queuesMuxState = MUX_MGMT_QUEUES;
	updateQueuesStates(pTxPort);
}


/****************************************************************************
 *                      txPort_suspendTx()
 ****************************************************************************
 * DESCRIPTION:	Used by STAD applications (e.g. recovery) to temporarily suspend the Tx path.
 ****************************************************************************/ 
void txPort_suspendTx(TI_HANDLE hTxPort)
{
	TTxPortObj *pTxPort = (TTxPortObj *)hTxPort;

	pTxPort->txSuspended = TI_TRUE;
	updateQueuesStates(pTxPort);
}


/****************************************************************************
 *                      txPort_resumeTx()
 ****************************************************************************
 * DESCRIPTION:	Used by STAD applications (e.g. recovery) to resume Tx path after suspended.
 ****************************************************************************/ 
void txPort_resumeTx(TI_HANDLE hTxPort)
{
	TTxPortObj *pTxPort = (TTxPortObj *)hTxPort;

	pTxPort->txSuspended = TI_FALSE;
	updateQueuesStates(pTxPort);
}


/****************************************************************************
 *                      updateQueuesStates()
 ****************************************************************************
 * DESCRIPTION:	 Switch the Data-Queue and Mgmt-Queue Tx on/off (stop/wake)
 *				   according to the current port conditions.
 ****************************************************************************/ 
static void updateQueuesStates (TTxPortObj *pTxPort)
{
	EQueueAction mgmtQueueAction = QUEUE_ACTION_NONE;
	EQueueAction dataQueueAction = QUEUE_ACTION_NONE;

	/* 
	 * If the Tx path is not suspended:
	 */
	if (!pTxPort->txSuspended)
	{
		/* If mgmt-queues should be enabled, set required actions (awake mgmt and stop data if needed). */
		if (pTxPort->queuesMuxState == MUX_MGMT_QUEUES)
		{
			if ( !pTxPort->mgmtQueueEnabled )
				mgmtQueueAction = QUEUE_ACTION_WAKE;
			if ( pTxPort->dataQueueEnabled )
				dataQueueAction = QUEUE_ACTION_STOP;
		}

		/* If data-queues should be enabled, set required actions (stop mgmt and awake data if needed). */
		else
		{
			if ( pTxPort->mgmtQueueEnabled )
				mgmtQueueAction = QUEUE_ACTION_STOP;
			if ( !pTxPort->dataQueueEnabled )
				dataQueueAction = QUEUE_ACTION_WAKE;
		}
	}

	/* 
	 * If the Tx path is not available (Xfer is busy or suspension is requested),
	 *   set required actions (stop mgmt and data if needed).
	 */
	else
	{
		if ( pTxPort->mgmtQueueEnabled )
			mgmtQueueAction = QUEUE_ACTION_STOP;
		if ( pTxPort->dataQueueEnabled )
			dataQueueAction = QUEUE_ACTION_STOP;
	}


#ifdef TI_DBG
	TRACE1(pTxPort->hReport, REPORT_SEVERITY_INFORMATION, ":  queuesMuxState = , TxSuspend = %d\n", pTxPort->txSuspended);
		
	TRACE2(pTxPort->hReport, REPORT_SEVERITY_INFORMATION, ":  PrevMgmtEnabled = %d,  PrevDataEnabled = %d, MgmtAction = , DataAction = \n", pTxPort->mgmtQueueEnabled, pTxPort->dataQueueEnabled);
#endif /* TI_DBG */

	/* 
	 * Execute the required actions. 
	 * Note: This is done at the end of this function because it may start a sequence that will call it again!!
	 *       Always do WAKE action after STOP action, since WAKE may lead to more activities!!
	 */
	if (mgmtQueueAction == QUEUE_ACTION_STOP)
	{
		pTxPort->mgmtQueueEnabled = TI_FALSE;
		txMgmtQ_StopAll (pTxPort->hTxMgmtQ);
	}
	if (dataQueueAction == QUEUE_ACTION_STOP)
	{
		pTxPort->dataQueueEnabled = TI_FALSE;
		txDataQ_StopAll (pTxPort->hTxDataQ);
	}
	if (mgmtQueueAction == QUEUE_ACTION_WAKE)
	{
		pTxPort->mgmtQueueEnabled = TI_TRUE;
		txMgmtQ_WakeAll (pTxPort->hTxMgmtQ);
	}
	if (dataQueueAction == QUEUE_ACTION_WAKE)
	{
		pTxPort->dataQueueEnabled = TI_TRUE;
		txDataQ_WakeAll (pTxPort->hTxDataQ);
	}
}