/* BEGIN_ICS_COPYRIGHT4 ****************************************

Copyright (c) 2015-2017, Intel Corporation

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 of Intel Corporation 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.

** END_ICS_COPYRIGHT4   ****************************************/

#include "bmamain.h"
#include "bmadebug.h"
#include "bma_provider.h"
#include "bspcommon/h/usrBootManager.h"
#include "bspcommon/ibml/h/icsApi.h"

//
// Debug params
//
#if ICS_LOGGING
_IB_DBG_PARAM_BLOCK(_DBG_LVL_ALL, _DBG_BREAK_ENABLE, BMA_TAG, "Bma", MOD_BMA, Bma);
#else
_IB_DBG_PARAM_BLOCK((_DBG_LVL_FATAL | _DBG_LVL_ERROR), _DBG_BREAK_ENABLE, BMA_TAG, "Bma");
#endif
 
//
// Global Info
//
BMA_GLOBAL_INFO  *g_BmaGlobalInfo;

//
// Stored settings
//
BMA_GLOBAL_CONFIG_PARAMETERS	g_BmaSettings = BMA_DEFAULT_SETTINGS;


//
// BmaLoad
//
// This routine is called by the VCA Init when the driver is first loaded. 
// This routine is to create and initialize the BMA agent global data structures.
//
// INPUTS:
//
//	ComponentInfo - Address of IBT_COMPONENT_INFO that needs to be updated
//	with this driver's specific handlers for add/remove of a channel adapter
//	device as well as the overall driver unload handler.
//
// OUTPUTS:
//
//	None.
//
// RETURNS:
//
//	FSUCCESS - Indicates a successful load.
//

FSTATUS
BmaLoad(
	IN IBT_COMPONENT_INFO		*ComponentInfo
	)
{
	FSTATUS						status = FSUCCESS;
	extern uint32 BmaDbg;
#ifdef IB_TRACE
	extern uint32 BmaTrace;
#endif


	_DBG_ENTER_LVL(_DBG_LVL_MAIN, BMALoad);
	_DBG_INIT;

	__DBG_LEVEL__ = BmaDbg;
#ifdef IB_TRACE
	__DBG_TRACE_LEVEL__ = BmaTrace;
#endif
#if defined(IB_DEBUG) || defined(DBG)
	MsgOut ("Bma:DebugFlags = 0x%8x\n", __DBG_LEVEL__);
#endif

#if defined(VXWORKS)
	_DBG_PRINT(_DBG_LVL_MAIN,  
	(" InfiniBand Baseboard Management Class agent. Built %s %s\n",\
	__DATE__, __TIME__ ));
#else
	_DBG_PRINT(_DBG_LVL_MAIN,  
	(" InfiniBand Baseboard Management Class agent. Built %s %s\n",\
	_DBG_PTR(__DATE__), _DBG_PTR(__TIME__) ));
#endif

	_TRC_REGISTER();

	//
    // Establish dispatch entry points for the functions supported.
	//

	MemoryClear( ComponentInfo, sizeof(IBT_COMPONENT_INFO) );

	ComponentInfo->AddDevice = BmaAddDevice;
	ComponentInfo->RemoveDevice = BmaRemoveDevice;
	ComponentInfo->Unload = BmaUnload;

	
	//
    // Read any registry parameters for the driver which may be present.
	//
	//BmaReadRegistryParameters();

	//
    // This function is called to initialize the GSA subsystem. This must be 
	// called only once and if the function does not return success, the 
	// caller must unload the component containing GSA or else not use the 
	// GSA functionality since GSA will not be available.
	//
	// We are being asked to do a one-time initialization. Here we initialize 
	// the list of channel adapters as well as initialize the list of service 
	// classes as well as initialize the spin locks guarding these lists.
	//


	//
	// Allocate space for Global data
	//

	g_BmaGlobalInfo = (BMA_GLOBAL_INFO*)MemoryAllocateAndClear(
									 sizeof(BMA_GLOBAL_INFO), FALSE, BMA_TAG );
	if ( NULL != g_BmaGlobalInfo )
	{
	
		//
		// initialize global data
		//

		g_BmaGlobalInfo->binitsuccess = FALSE; 

		SpinLockInitState( &g_BmaGlobalInfo->DevListLock );
		if( !SpinLockInit( &g_BmaGlobalInfo->DevListLock ) )
		{
			_DBG_ERROR(( "Unable to initialize spin locks!\n" ));
			status = FINSUFFICIENT_RESOURCES;
			goto fail_lock;
		}

		//
		// Recv Queue and Thread
		//
		CmdThreadInitState(&g_BmaGlobalInfo->RecvThread);
		if ( ! CmdThreadCreate(&g_BmaGlobalInfo->RecvThread,
				THREAD_PRI_HIGH, "Bma", (CMD_THREAD_CALLBACK)BmaThreadCallback,
				(CMD_THREAD_CALLBACK)BmaFreeCallback, (void*)g_BmaGlobalInfo))
		{
			_DBG_ERROR(("BmaMain: Unable to Create RecvThread\n"));
			status = FINSUFFICIENT_RESOURCES;
			goto fail_thread;
		}
		

		// initialize this general service class manager with GSI
		status = iba_gsi_register_class(MCLASS_BM, 
									IB_BM_CLASS_VERSION,
									GSI_REGISTER_RESPONDER,	// Register as a Responder
									FALSE,					// No SAR cap needed
									g_BmaGlobalInfo,		// Context
									BmaSendCallback,
									BmaRecvCallback,
									&g_BmaGlobalInfo->GsaHandle);
							
		if (status != FSUCCESS)
		{
			CmdThreadDestroy(&g_BmaGlobalInfo->RecvThread);
fail_thread:
			SpinLockDestroy( &g_BmaGlobalInfo->DevListLock );
fail_lock:
			MemoryDeallocate(g_BmaGlobalInfo);
			g_BmaGlobalInfo = NULL;

			_DBG_ERROR(("BmaMain: Unable to register with GSA!!! <status %d>\n", status));

			_DBG_LEAVE_EXT(_DBG_LVL_FUNC_TRACE);

			return status;
		}
 
	}
	else
	{
		status = FINSUFFICIENT_RESOURCES;
		_DBG_PRINT(_DBG_LVL_MAIN,("Not enough memory!\n"));
	}

    
	_DBG_LEAVE_LVL(_DBG_LVL_MAIN);
    
    return status;
}


//
// BmaUnload
//
//	This routine should release resources allocated in the BmaLoad function.
//	Also, since their is a single instance of the service for all channel
//	adapters in the system, it will also release resources allocated in the
//	BmaAddDevice function. 
//
// INPUTS:
//
//	None.
//
// OUTPUTS:
//
//	None.
//
// RETURNS:
//
//	None
//
//
void
BmaUnload(void)
{

	_DBG_ENTER_LVL(_DBG_LVL_MAIN, BmaUnload);

	_TRC_UNREGISTER();

	if ( g_BmaGlobalInfo )
	{

		// Destroy the datagram pool
		if ( g_BmaGlobalInfo->DgrmPoolHandle )
			iba_gsi_destroy_dgrm_pool( g_BmaGlobalInfo->DgrmPoolHandle );

		// Deregister this class agent with the GSA	
		if ( g_BmaGlobalInfo->GsaHandle )
			iba_gsi_deregister_class(g_BmaGlobalInfo->GsaHandle);

		//
		// Recv Thread
		//
		CmdThreadDestroy(&g_BmaGlobalInfo->RecvThread);
				
		//
		// Ca List Members
		//
		while ( g_BmaGlobalInfo->DevList )
		{
			BmaRemoveDevice( g_BmaGlobalInfo->DevList->CaGuid, NULL );
		}

		//
		// Ca List Lock
		//
		SpinLockDestroy( &g_BmaGlobalInfo->DevListLock );


		//
		// Free up global memory
		//
		MemoryDeallocate( g_BmaGlobalInfo );
		g_BmaGlobalInfo = NULL;

	} // if g_BmaGlobalInfo

	_DBG_LEAVE_LVL(_DBG_LVL_MAIN);
  

}


//////////////////////////////////////////////////////////////////////////
// BmaAddDevice()
//
// This routine is called once for every new device added.
//
// INPUTS:
//		CaGuid - the channel adapters GUID
//
// OUTPUTS:
//
//	None.  Although function is defined to return context value, this
//	value is not set since their is nothing specifically allocated for
//	each new channel adapter, so nothing is done in the BmaRemoveDevice
//	function that needs the context.
//
// RETURNS:
//
//	FSUCCESS - Indicates a successful initialization.
//
//
FSTATUS
BmaAddDevice(
	IN	EUI64			CaGuid,
	OUT void			**Context
	)
{
	FSTATUS			status = FSUCCESS;
	uint32			BufferSize;
	BMA_DEV_INFO	*pDevInfo=NULL;

	_DBG_ENTER_LVL(_DBG_LVL_MAIN, BmaAddDevice);

	ASSERT(g_BmaGlobalInfo);

	if (!g_BmaGlobalInfo->binitsuccess)
	{
		// Create the datagram pool
		BufferSize = sizeof(MAD);
		status = iba_gsi_create_dgrm_pool(g_BmaGlobalInfo->GsaHandle,
								g_BmaSettings.MaxNDgrm,
								1,
								&BufferSize,
								0, //1,
								&g_BmaGlobalInfo->DgrmPoolHandle
								);
		if (status != FSUCCESS)
		{
			// Cleanup
			iba_gsi_deregister_class(g_BmaGlobalInfo->GsaHandle);

			MemoryDeallocate(g_BmaGlobalInfo);

			g_BmaGlobalInfo = NULL;

			_DBG_ERROR(("Unable to create BMA datagram pool!!! <status %d>\n", status));
		}
		else
		{
			g_BmaGlobalInfo->binitsuccess = TRUE;
		}

	}

	if (g_BmaGlobalInfo->binitsuccess)
	{
		pDevInfo = (BMA_DEV_INFO*)MemoryAllocateAndClear(sizeof(BMA_DEV_INFO), FALSE, BMA_TAG );

		if (pDevInfo)
		{
			IB_CA_ATTRIBUTES CaAttributes;

			pDevInfo->CaGuid = CaGuid;
			status = iba_query_ca_by_guid_alloc(CaGuid, &CaAttributes);
			if ( status == FSUCCESS )
			{
				pDevInfo->NumPorts = CaAttributes.Ports;
				pDevInfo->Port = (BMA_PORT_INFO*)
					MemoryAllocateAndClear(sizeof(BMA_PORT_INFO)*pDevInfo->NumPorts, FALSE, BMA_TAG);
				if (pDevInfo->Port)
				{
					IB_PORT_ATTRIBUTES *pPortAttr;
					unsigned i;

					for ( i = 0, pPortAttr = CaAttributes.PortAttributesList;
						  i < pDevInfo->NumPorts;
						  i++, pPortAttr = pPortAttr->Next )
					{
						// Traps disabled by default, Trap settings are all zero
						// BKey, BKeyProtect, BKeyViolations, and BKeyLease all init to zero
						pDevInfo->Port[i].Guid = pPortAttr->GUID;
					}

					MemoryDeallocate(CaAttributes.PortAttributesList);

					// Success, add this CA to the list
					SpinLockAcquire( &g_BmaGlobalInfo->DevListLock );
					pDevInfo->Next = g_BmaGlobalInfo->DevList;
					g_BmaGlobalInfo->DevList = pDevInfo;
					SpinLockRelease( &g_BmaGlobalInfo->DevListLock );
				}
				else
				{
					_DBG_ERROR(("MemAlloc failed for Port List!\n"));
					status = FINSUFFICIENT_MEMORY;
				}
			}
			else
			{
				_DBG_ERROR(("CA Query Failed!\n"));
				MemoryDeallocate(pDevInfo);
			}
		}
		else
		{
			_DBG_ERROR(("MemAlloc failed for pDevInfo!\n"));
			status = FINSUFFICIENT_MEMORY;
		}
	}

	_DBG_LEAVE_LVL(_DBG_LVL_MAIN);

	return status;

} // BmaAddDevice()


//////////////////////////////////////////////////////////////////////////
// BmaRemoveDevice()
//
// This routine is called once for every CA device being removed.  Nothing
//	is performed in this function.  All cleanup is performed in the BmaUnload
//	function.
//
// INPUTS:
//		CaGuid - the channel adapters GUID
//		Context - driver defined unique context for this CA
//
// OUTPUTS:
//
//	None.
//
// RETURNS:
//
//	FSUCCESS - Indicates a successful initialization.
//
//
FSTATUS
BmaRemoveDevice(
	IN	EUI64			CaGuid,
	IN void				*Context
	)
{
	FSTATUS			status = FSUCCESS;
	BMA_DEV_INFO	*pDevInfo=NULL;

	_DBG_ENTER_LVL(_DBG_LVL_MAIN, BmaRemoveDevice);

	ASSERT(g_BmaGlobalInfo);

	SpinLockAcquire( &g_BmaGlobalInfo->DevListLock );
	pDevInfo = g_BmaGlobalInfo->DevList;
	if ( g_BmaGlobalInfo->DevList->CaGuid == CaGuid )
	{
		g_BmaGlobalInfo->DevList = pDevInfo->Next;
	}
	else
	{
		while ( pDevInfo->Next )
		{
			if ( pDevInfo->Next->CaGuid == CaGuid )
			{
				BMA_DEV_INFO *Found = pDevInfo->Next;
				pDevInfo->Next = Found->Next;
				pDevInfo = Found;
				break;
			}
			pDevInfo = pDevInfo->Next;
		}
	}
	SpinLockRelease( &g_BmaGlobalInfo->DevListLock );

	if (pDevInfo && pDevInfo->CaGuid == CaGuid)
	{
		MemoryDeallocate( pDevInfo->Port );
		MemoryDeallocate( pDevInfo );
	}
	else
	{
		_DBG_ERROR(("Unable to find Ca with GUID 0x%"PRIx64"\n", CaGuid));
		status = FERROR;
	}

	_DBG_LEAVE_LVL(_DBG_LVL_MAIN);

	return status;

} // BmaRemoveDevice()
