/*	Traps.c - Trap generator and handler.
*
*******************************************************************************
* Copyright (c) 1996-97 Digi International Inc., All Rights Reserved          *
*                                                                             *
* This  software  contains  proprietary  and  confidential   information   of *
* Digi International  Inc.   By  accepting  transfer  of this copy, Recipient *
* agrees to retain this software in  confidence,  to  prevent  disclosure  to *
* others,  and  to make no  use of this software other than that for which it *
* was delivered.  This is an unpublished copyright work  Digi   International *
* Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is *
* strictly prohibited.                                                        *
*                                                                             *
* Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc. *
* Confidential  when   combined   with   the   aggregated  modules  for  this *
* product) OBJECT CODE ONLY SOURCE MATERIALS                                  *
*                                                                             *
* (C) COPYRIGHT Digi International Inc. 1996-97                               *
*                                                                             *
* All Rights Reserved                                                         *
*                                                                             *
* US   Government   Users   Restricted   Rights   -   Use,   duplication   or *
* disclosure  restricted by GSA ADP Schedule Contract with Digi International *
* Inc.                                                                        *
*                                                                             *
*******************************************************************************
*
*/

#include "DS_RAS.h"
#include "Traps.h"
#include "List.h"
#include "Queue.h"
#include "mgmtshim.h"
#include "mgmthlc.h"

typedef struct
{
	uint32 iBoard;
}
TraceHandle_t;

typedef struct
{
	TraceHandle_t th;
	LIST lstTraceObjects;
}
TraceList_t;

typedef struct
{
	size_t iOffsetToGeneric1;
	size_t iOffsetToGeneric2;
}
TraceInstanceData_t;

typedef struct
{
	uint16 const nCommand;
	uint16 const cb;
	size_t const iOffsetToTraceObject;
	TraceInstanceData_t tid;
}
TraceRequest_t;

typedef struct
{
	TraceInstanceData_t tid;
}
TraceResponse_t;

typedef struct
{
	TraceRequest_t treq;
	TraceResponse_t trsp;
}
TraceInfo_t;

typedef struct
{
	UINT const eType;
	TraceInfo_t ti;
}
TraceInfoEntry_t;

typedef struct
{
	TrapData_t td;
	TRAP_HANDLER pfnTrapHandler;
}
TraceData_t;

typedef struct
{
	TrapObject_t to;
	TraceInfo_t const * pti;
	LIST lstTraceData;
}
TraceObject_t;

static TraceInfoEntry_t const atie[] = 
{
	{
		GET_DS1_STATUS,
		{
			{ SET_DS1_STATUS_TRC, sizeof (trc_ds1_status_request_t), offsetof(trc_ds1_status_request_t, status), { TRUE, offsetof(trc_ds1_status_request_t, port) } },
			{ { TRUE, offsetof(trc_ds1_status_request_t, port) } }
		}
	}
};

static CRITICAL_SECTION mcsTraceAccess = { 0 };
static LIST mlstTrace = { 0 };

static CRITICAL_SECTION mcsTrapAccess = { 0 };
static QUEUE mqTraps = { 0 };

static HANDLE mhHaveTraps = NULL;
static BOOL mfTrapHandlerActive = FALSE;

static TraceList_t * GetTraceList(TraceHandle_t const * const pth)
{
	TraceList_t * ptl = Find(&mlstTrace, pth, sizeof *pth, offsetof(TraceList_t, th));

	if (ptl == NULL)
	{
		TraceList_t tl = { 0 };

		tl.th = *pth;
		ptl = Add(&mlstTrace, &tl, sizeof tl);
	}

	return ptl;
}

static TraceInfo_t const * FindTraceInfo(UINT const eType)
{
	TraceInfo_t const * pti = NULL;

	int i;

	for (i = 0; i < ElementCount(atie); i++)
	{
		TraceInfoEntry_t const * const ptie = &atie[i];

		if (eType == ptie->eType)
		{
			pti = &ptie->ti;
			break;
		}
	}

	return pti;
}

BOOL InitTrapHandler(HANDLE const hHaveTraps)
{
	mhHaveTraps = hHaveTraps;

	InitializeCriticalSection(&mcsTraceAccess);
	InitializeCriticalSection(&mcsTrapAccess);

	return TRUE;
}

static int TraceCallback(uint8 *TrcData, uint32 TrcLen, TRCARG arg)
{
	return 0;
}

static void CALLBACK IndicateTrace(void * const pvData, void * const pvUserData)
{
	TraceData_t const * const ptd = pvData;
	BYTE * const pbData = pvUserData;
	size_t i;

	for (i = 0; i < ptd->td.cb; i++)
		pbData[ptd->td.iOffset + 1] = 1;
}

static void CALLBACK SetTrace(void * const pvData, void const * const pvUserData)
{
	TraceObject_t * const pto = pvData;
	TraceHandle_t const * const pth = pvUserData;
	uint16 const cbData = pto->pti->treq.cb;
	BYTE * const pbData = malloc(cbData);

	if (pvData == NULL) goto Error;

	memset(pbData, 0, cbData);
	if (pto->to.iGeneric1 != 0)
	{
		uint32 * const pGeneric1 = (uint32 *) (pbData + pto->pti->treq.tid.iOffsetToGeneric1);

		*pGeneric1 = pto->to.iGeneric1;
	}
	if (pto->to.iGeneric2 != 0)
	{
		uint32 * const pGeneric2 = (uint32 *) (pbData + pto->pti->treq.tid.iOffsetToGeneric2);

		*pGeneric2 = pto->to.iGeneric2;
	}
	ForAll(&pto->lstTraceData, IndicateTrace, pvData);

	MgmtTrace(pto->to.iBoard, pto->pti->treq.nCommand, pbData, cbData, NULL, 0, TraceCallback, (TRCARG) pth);

Error:

	NULL;
}

static void CALLBACK StartAllTrace(void * const pvData, void * const pvUserData)
{
	TraceList_t * const ptl = pvData;

	ForAll(&ptl->lstTraceObjects, SetTrace, &ptl->th);
}

static void CALLBACK StopTrace(void * const pvData, void const * const pvUserData)
{
	TraceObject_t * const pto = pvData;
	TraceHandle_t const * const pth = pvUserData;

	MgmtTrace(pto->to.iBoard, SET_MGMT_STOP_TRC, NULL, 0, NULL, 0, NULL, (TRCARG) pth);
}

static void CALLBACK StopAllTrace(void * const pvData, void * const pvUserData)
{
	TraceList_t * const ptl = pvData;

	ForAll(&ptl->lstTraceObjects, StopTrace, &ptl->th);
}

void StartTrapHandler(void)
{
	EnterCriticalSection(&mcsTraceAccess);
	{
		ForAll(&mlstTrace, StartAllTrace, NULL);
		mfTrapHandlerActive = TRUE;
	}
	LeaveCriticalSection(&mcsTraceAccess);
}

void StopTrapHandler(void)
{
	EnterCriticalSection(&mcsTrapAccess);
	{
		CloseHandle(mhHaveTraps);
		mhHaveTraps = NULL;

		EnterCriticalSection(&mcsTraceAccess);
		{
			ForAll(&mlstTrace, StopAllTrace, NULL);
			Clear(&mlstTrace);
			mfTrapHandlerActive = FALSE;
		}
		LeaveCriticalSection(&mcsTraceAccess);

		/* Clear the trap queue. */
		while (GetNextTrap() != NULL);
	}
	LeaveCriticalSection(&mcsTrapAccess);
}

static BOOL AddTraceObject(TraceList_t * const ptl, TrapObject_t const * const pto, TrapData_t const * const ptd, TRAP_HANDLER pfnTrapHandler)
{
	BOOL fSuccess = FALSE;
	TraceObject_t * ptrace = Find(&ptl->lstTraceObjects, pto, sizeof *pto, offsetof(TraceObject_t, to));

	if (ptrace == NULL)
	{
		TraceObject_t trace = { 0 };

		trace.to = *pto;

		trace.pti = FindTraceInfo(pto->eType);
		if (trace.pti == NULL) goto Error;

		ptrace = Add(&ptl->lstTraceObjects, &trace, sizeof trace);
		if (ptrace = NULL) goto Error;
	}

	{
		TraceData_t data;

		data.td = *ptd;
		data.pfnTrapHandler = pfnTrapHandler;

		if (Add(&ptrace->lstTraceData, &data, sizeof data) == NULL) goto Error;
	}

	if (mfTrapHandlerActive)
		SetTrace(ptrace, &ptl->th);

	fSuccess = TRUE;

Error:

	return fSuccess;
}

BOOL AddTrap(TrapObject_t const * const pto, TrapData_t const * const ptd, TRAP_HANDLER pfnTrapHandler)
{
	BOOL fSuccess = FALSE;

	EnterCriticalSection(&mcsTraceAccess);
	{
		TraceHandle_t th = { pto->iBoard };
		TraceList_t * const ptl = GetTraceList(&th);

		if (ptl == NULL) goto Error;

		if (!AddTraceObject(ptl, pto, ptd, pfnTrapHandler))
			goto Error;

		fSuccess = TRUE;

Error:

		NULL;
	}
	LeaveCriticalSection(&mcsTraceAccess);

	return fSuccess;
}

void RemoveTrap(TrapObject_t const * const pto, TrapData_t const * const ptd)
{
	EnterCriticalSection(&mcsTraceAccess);
	{
		TraceHandle_t th = { pto->iBoard };
		TraceList_t * const ptl = GetTraceList(&th);

		if (ptl != NULL)
		{
			TraceObject_t * ptrace = Find(&ptl->lstTraceObjects, pto, sizeof *pto, offsetof(TraceObject_t, to));

			if (ptrace)
			{
				TraceData_t const * const pdata = Find(&ptrace->lstTraceData, ptd, sizeof *ptd, offsetof(TraceData_t, td));

				if (pdata)
					Delete(&ptrace->lstTraceData, pdata);
			}
		}
	}
	LeaveCriticalSection(&mcsTraceAccess);
}

void TriggerTrap(Trap_t const * const pTrap)
{
	EnterCriticalSection(&mcsTrapAccess);
	{
		QueuePut(&mqTraps, pTrap, sizeof *pTrap);
	}
	LeaveCriticalSection(&mcsTrapAccess);
}

Trap_t const * GetNextTrap(void)
{
	BOOL fHaveTrap = FALSE;
	static Trap_t trap = { 0 };

	EnterCriticalSection(&mcsTrapAccess);
	{
		if (QueueGet(&mqTraps, &trap, sizeof trap))
			fHaveTrap = TRUE;
	}
	LeaveCriticalSection(&mcsTrapAccess);

	return (fHaveTrap ? &trap : NULL);
}

