|
|
/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
fake.c
Abstract:
Fake versions of various external calls (ndis, ip...). Used for debugging and component testing only.
To enable, define ARPDBG_FAKE_APIS in ccdefs.h
Revision History:
Who When What -------- -------- ---------------------------------------------- josephj 03-22-98 Created
Notes:
--*/ #include <precomp.h>
#if ARPDBG_FAKE_APIS
//
// File-specific debugging defaults.
//
#define TM_CURRENT TM_FAKE
//=========================================================================
// L O C A L P R O T O T Y P E S
//=========================================================================
#if RM_EXTRA_CHECKING
#define LOCKROOTOBJ(_pHdr, _pSR) \
RmWriteLockObject( \ (_pHdr)->pRootObject, \ 0, \ (_pSR) \ ) #else // !RM_EXTRA_CHECKING
#define LOCKROOTOBJ(_pHdr, _pSR) \
RmWriteLockObject( \ (_pHdr)->pRootObject, \ (_pSR) \ ) #endif // !RM_EXTRA_CHECKING
#define UNLOCKROOTOBJ(_pHdr, _pSR) \
RmUnlockObject( \ (_pHdr)->pRootObject, \ (_pSR) \ )
typedef VOID (*PFN_FAKE_COMPLETIONCALLBACK)( struct _FAKETASK *pFTask );
// This task structure holds a union of the information required for the completions
// of the various apis that are being faked.
//
typedef struct _FAKETASK { RM_TASK TskHdr;
// Client's context to pass back
//
PVOID pClientContext;
// Client object the call is associated.
//
PRM_OBJECT_HEADER pOwningObject;
// The status to report in the asynchronous completion fn.
//
NDIS_STATUS Status;
// Milliseconds to delay before calling the async completion fn.
//
UINT DelayMs;
// Wheather to call the completion fn at DPC or PASSIVE IRQL level.
//
INT fDpc;
// This is used solely to switch to DPC level when asynchronously
// calling the completion callback.
//
NDIS_SPIN_LOCK NdisLock;
// This is used solely to wait DelayMs ms if required.
//
NDIS_TIMER Timer;
// This is used solely to switch to a different (and PASSIVE) context.
//
NDIS_WORK_ITEM WorkItem;
// This is used only for fake NdisClMakeCall
//
PCO_CALL_PARAMETERS CallParameters;
// This is used only for fake NdisCoSendPackets
//
PNDIS_PACKET pNdisPacket;
// The actual completion callback function;
//
PFN_FAKE_COMPLETIONCALLBACK pfnCompletionCallback;
} FAKETASK;
VOID arpFakeMakeCallCompletionCallback( struct _FAKETASK *pFTask );
VOID arpFakeCloseCallCompletionCallback( struct _FAKETASK *pFTask );
VOID arpFakeSendPacketsCompletionCallback( struct _FAKETASK *pFTask );
NDIS_STATUS arpDbgAllocateFakeTask( IN PRM_OBJECT_HEADER pParentObject, IN PFN_RM_TASK_HANDLER pfnHandler, IN UINT Timeout, IN const char * szDescription, OUT PRM_TASK *ppTask, IN PRM_STACK_RECORD pSR );
VOID arpDbgFakeTaskDelete( PRM_OBJECT_HEADER pObj, PRM_STACK_RECORD psr );
RM_STATIC_OBJECT_INFO FakeTasks_StaticInfo = { 0, // TypeUID
0, // TypeFlags
"FAKE Task", // TypeName
0, // Timeout
NULL, // pfnCreate
arpDbgFakeTaskDelete, // pfnDelete
NULL, // pfnVerifier
0, // length of resource table
NULL // Resource Table
};
NDIS_STATUS arpDbgFakeCompletionTask( IN struct _RM_TASK * pTask, IN RM_TASK_OPERATION Code, IN UINT_PTR UserParam, // Unused
IN PRM_STACK_RECORD pSR );
//=========================================================================
// F A K E N D I S E N T R Y P O I N T S
//=========================================================================
NDIS_STATUS arpDbgFakeNdisClMakeCall( IN NDIS_HANDLE NdisVcHandle, IN OUT PCO_CALL_PARAMETERS CallParameters, IN NDIS_HANDLE ProtocolPartyContext OPTIONAL, OUT PNDIS_HANDLE NdisPartyHandle, OPTIONAL IN PRM_OBJECT_HEADER pOwningObject, IN PVOID pClientContext, IN PRM_STACK_RECORD pSR ) /*++
Routine Description:
Fake version of NdisClMakeCall.
--*/ { ENTER("FakeNdisClMakeCall", 0x3d4195ae) NDIS_STATUS Status; NDIS_STATUS AsyncStatus; UINT DelayMs; DBGMARK(0xced41a61); RM_ASSERT_NOLOCKS(pSR); ASSERT(NdisPartyHandle==NULL); ASSERT(ProtocolPartyContext==NULL); ASSERT(NdisVcHandle != NULL);
do { static OUTCOME_PROBABILITY StatusOutcomes[] = { {NDIS_STATUS_SUCCESS, 1}, // Return NDIS_STATUS_SUCCESS
{NDIS_STATUS_FAILURE, 1} // Return NDIS_STATUS_FAILURE
};
static OUTCOME_PROBABILITY DelayMsOutcomes[] = { {0, 5}, // Delay 0ms, etc...
{10, 5}, {100, 5}, {1000, 1}, {10000, 1} };
static OUTCOME_PROBABILITY AsyncOutcomes[] = { {TRUE, 1}, // Complete Async
{FALSE, 1} // Complete Sync
}; static OUTCOME_PROBABILITY DpcOutcomes[] = { {TRUE, 1}, // Complete at DPC level
{FALSE, 1} // Complete at PASSIVE level
};
FAKETASK *pMCTask;
// We serialize calls to arpGenRandomInt by claiming the root object's
// lock...
//
LOCKROOTOBJ(pOwningObject, pSR);
// Get the status we're supposed to return.
//
Status = AsyncStatus = (NDIS_STATUS) arpGenRandomInt( StatusOutcomes, ARRAY_LENGTH(StatusOutcomes) );
// Determine if we're to return synchronously or complete
// asynchronously...
//
if (!arpGenRandomInt(AsyncOutcomes, ARRAY_LENGTH(AsyncOutcomes))) { // We're to return synchronously.
//
UNLOCKROOTOBJ(pOwningObject, pSR); break; }
//
// We're to complete asynchronously...
//
DelayMs = arpGenRandomInt( DelayMsOutcomes, ARRAY_LENGTH(DelayMsOutcomes) );
if (DelayMs == 0) { // We're to immediately indicatie async completion...
// (we don't mess with IRQ levels if we're returning here..)
//
UNLOCKROOTOBJ(pOwningObject, pSR); ArpCoMakeCallComplete( AsyncStatus, pClientContext, NULL, CallParameters ); Status = NDIS_STATUS_PENDING; break; }
// We're to indicate status sometime in the future -- in a different context.
// Start a task to do this...
//
Status = arpDbgAllocateFakeTask( pOwningObject, // pParentObject,
arpDbgFakeCompletionTask, // pfnHandler,
0, // Timeout,
"Task:Fake NdisClMakeCall", // szDescription,
&(PRM_TASK) pMCTask, pSR ); if (FAIL(Status)) { // Couldn't allocate task. Call callback right away...
//
UNLOCKROOTOBJ(pOwningObject, pSR); ArpCoMakeCallComplete( AsyncStatus, pClientContext, NULL, CallParameters ); Status = NDIS_STATUS_PENDING; break; }
// Initialize pMCTask...
//
pMCTask->pClientContext = pClientContext; pMCTask->pOwningObject = pOwningObject; pMCTask->Status = AsyncStatus; pMCTask->DelayMs = DelayMs; pMCTask->fDpc = arpGenRandomInt( DpcOutcomes, ARRAY_LENGTH(DpcOutcomes) ); pMCTask->CallParameters = CallParameters; pMCTask->pfnCompletionCallback = arpFakeMakeCallCompletionCallback;
UNLOCKROOTOBJ(pOwningObject, pSR);
(void) RmStartTask( &pMCTask->TskHdr, 0, // UserParam (unused)
pSR );
Status = NDIS_STATUS_PENDING;
} while (FALSE);
RM_ASSERT_NOLOCKS(pSR); EXIT();
return Status; }
NDIS_STATUS arpDbgFakeNdisClCloseCall( IN NDIS_HANDLE NdisVcHandle, IN NDIS_HANDLE NdisPartyHandle OPTIONAL, IN PVOID Buffer OPTIONAL, IN UINT Size, OPTIONAL IN PRM_OBJECT_HEADER pOwningObject, IN PVOID pClientContext, IN PRM_STACK_RECORD pSR ) /*++
Routine Description:
Fake version of NdisClCloseCall.
--*/ { ENTER("FakeNdisClCloseCall", 0x7d8bbd3c) NDIS_STATUS Status; NDIS_STATUS AsyncStatus; UINT DelayMs; DBGMARK(0x228fac3a); RM_ASSERT_NOLOCKS(pSR); ASSERT(NdisPartyHandle==NULL); ASSERT(NdisVcHandle != NULL);
do {
static OUTCOME_PROBABILITY DelayMsOutcomes[] = { {0, 5}, // Delay 0ms, etc...
{10, 5}, {100, 5}, {1000, 1}, {10000, 1} };
static OUTCOME_PROBABILITY AsyncOutcomes[] = { {TRUE, 1}, // Complete Async
{FALSE, 1} // Complete Sync
}; static OUTCOME_PROBABILITY DpcOutcomes[] = { {TRUE, 1}, // Complete at DPC level
{FALSE, 1} // Complete at PASSIVE level
};
FAKETASK *pCCTask;
// We serialize calls to arpGenRandomInt by claiming the root object's
// lock...
//
LOCKROOTOBJ(pOwningObject, pSR);
// Get the status we're supposed to return.
//
Status = AsyncStatus = NDIS_STATUS_SUCCESS; // We never fail this call.
// Determine if we're to return synchronously or complete
// asynchronously...
//
if (!arpGenRandomInt(AsyncOutcomes, ARRAY_LENGTH(AsyncOutcomes))) { // We're to return synchronously.
//
UNLOCKROOTOBJ(pOwningObject, pSR); break; }
//
// We're to complete asynchronously...
//
DelayMs = arpGenRandomInt( DelayMsOutcomes, ARRAY_LENGTH(DelayMsOutcomes) );
if (DelayMs == 0) { // We're to immediately indicatie async completion...
// (we don't mess with IRQ levels if we're returning here..)
//
UNLOCKROOTOBJ(pOwningObject, pSR); ArpCoCloseCallComplete( AsyncStatus, pClientContext, NULL ); Status = NDIS_STATUS_PENDING; break; }
Status = arpDbgAllocateFakeTask( pOwningObject, // pParentObject,
arpDbgFakeCompletionTask, // pfnHandler,
0, // Timeout,
"Task:Fake NdisClCloseCall", // szDescription,
&(PRM_TASK) pCCTask, pSR ); if (FAIL(Status)) { // Couldn't alloc task; lets call callback right now and return pending.
//
UNLOCKROOTOBJ(pOwningObject, pSR); ArpCoCloseCallComplete( AsyncStatus, pClientContext, NULL ); Status = NDIS_STATUS_PENDING; break; }
// Initialize pCCTask...
//
pCCTask->pClientContext = pClientContext; pCCTask->pOwningObject = pOwningObject; pCCTask->Status = AsyncStatus; pCCTask->DelayMs = DelayMs; pCCTask->fDpc = arpGenRandomInt( DpcOutcomes, ARRAY_LENGTH(DpcOutcomes) ); pCCTask->pfnCompletionCallback = arpFakeCloseCallCompletionCallback;
UNLOCKROOTOBJ(pOwningObject, pSR);
(void) RmStartTask( &pCCTask->TskHdr, 0, // UserParam (unused)
pSR );
Status = NDIS_STATUS_PENDING;
} while (FALSE);
RM_ASSERT_NOLOCKS(pSR); EXIT();
return Status; }
VOID arpDbgFakeNdisCoSendPackets( IN NDIS_HANDLE NdisVcHandle, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets, IN PRM_OBJECT_HEADER pOwningObject, IN PVOID pClientContext ) /*++
Routine Description:
Fake version of NdisCoSendPackets.
--*/ { ENTER("FakeNdisCoSendPackets", 0x98c6a8aa) NDIS_STATUS Status; NDIS_STATUS AsyncStatus; UINT DelayMs; RM_DECLARE_STACK_RECORD(sr) DBGMARK(0x3be1b902); ASSERT(NumberOfPackets==1);
do { static OUTCOME_PROBABILITY StatusOutcomes[] = { {NDIS_STATUS_SUCCESS, 1}, // Return NDIS_STATUS_SUCCESS
{NDIS_STATUS_FAILURE, 1} // Return NDIS_STATUS_FAILURE
};
static OUTCOME_PROBABILITY DelayMsOutcomes[] = { {0, 5}, // Delay 0ms, etc...
{10, 5}, {100, 5}, {1000, 1}, {10000, 1} }; static OUTCOME_PROBABILITY DpcOutcomes[] = { {TRUE, 1}, // Complete at DPC level
{FALSE, 1} // Complete at PASSIVE level
};
FAKETASK *pSPTask;
// We serialize calls to arpGenRandomInt by claiming the root object's
// lock...
//
LOCKROOTOBJ(pOwningObject, &sr);
// Get the status we're supposed to return.
//
Status = AsyncStatus = (NDIS_STATUS) arpGenRandomInt( StatusOutcomes, ARRAY_LENGTH(StatusOutcomes) );
// Compute the delay amount.
//
DelayMs = arpGenRandomInt( DelayMsOutcomes, ARRAY_LENGTH(DelayMsOutcomes) ); if (DelayMs == 0) { UNLOCKROOTOBJ(pOwningObject, &sr); // We're to immediately indicatie async completion...
// (we don't mess with IRQ levels if we're returning here..)
//
ArpCoSendComplete( AsyncStatus, pClientContext, *PacketArray ); break; }
//
// Nonzero delay -- start task to complete this.
//
Status = arpDbgAllocateFakeTask( pOwningObject, // pParentObject,
arpDbgFakeCompletionTask, // pfnHandler,
0, // Timeout,
"Task:Fake NdisCoSendPackets", // szDescription,
&(PRM_TASK) pSPTask, &sr ); if (FAIL(Status)) { UNLOCKROOTOBJ(pOwningObject, &sr); // Fail...
//
ArpCoSendComplete( AsyncStatus, pClientContext, *PacketArray ); break; }
// Initialize pSPTask...
//
pSPTask->pClientContext = pClientContext; pSPTask->pOwningObject = pOwningObject; pSPTask->Status = AsyncStatus; pSPTask->DelayMs = DelayMs; pSPTask->fDpc = arpGenRandomInt( DpcOutcomes, ARRAY_LENGTH(DpcOutcomes) ); pSPTask->pNdisPacket = *PacketArray; pSPTask->pfnCompletionCallback = arpFakeSendPacketsCompletionCallback;
UNLOCKROOTOBJ(pOwningObject, &sr);
(void) RmStartTask( &pSPTask->TskHdr, 0, // UserParam (unused)
&sr );
Status = NDIS_STATUS_PENDING;
} while (FALSE);
RM_ASSERT_NOLOCKS(&sr); EXIT();
}
NDIS_STATUS arpDbgFakeCompletionTask( IN struct _RM_TASK * pTask, IN RM_TASK_OPERATION Code, IN UINT_PTR UserParam, IN PRM_STACK_RECORD pSR ) /*++
Routine Description:
This task is to complete the fake api asynchronously after the specified delay, with the specified status, and at the specified IRQL (passive/dpc).
Arguments: UserParam for (Code == RM_TASKOP_START) : unused
--*/ { NDIS_STATUS Status = NDIS_STATUS_FAILURE; FAKETASK * pFTask = (FAKETASK *) pTask; ENTER("FakeCompletionTask", 0xc319c5c2)
// Following are the list of pending states for this task.
//
enum { PEND_ResumedAfterDelay, PEND_SwitchedToAsync, };
switch(Code) {
case RM_TASKOP_START: {
TR_WARN(( "START: Delay=%lu; fDpc=%lu; Status=%lu\n", pFTask->DelayMs, pFTask->fDpc, pFTask->Status ));
if (pFTask->DelayMs!=0) { // Non-zero delay -- let's resume after the delay...
//
RmSuspendTask(pTask, PEND_ResumedAfterDelay, pSR);
RmResumeTaskDelayed( pTask, 0, pFTask->DelayMs, &pFTask->Timer, pSR ); } else { // No delay is requested. Switch to async right away...
//
RmSuspendTask(pTask, PEND_SwitchedToAsync, pSR);
RmResumeTaskAsync( pTask, 0, &pFTask->WorkItem, pSR ); }
RM_ASSERT_NOLOCKS(pSR); Status = NDIS_STATUS_PENDING;
} break;
case RM_TASKOP_PENDCOMPLETE: {
switch(RM_PEND_CODE(pTask)) { case PEND_ResumedAfterDelay: { // We've waited around for pFTask->DelayMs ms; Now
// switch to passive...
//
RmSuspendTask(pTask, PEND_SwitchedToAsync, pSR);
RmResumeTaskAsync( pTask, 0, &pFTask->WorkItem, pSR ); Status = NDIS_STATUS_PENDING; } break;
case PEND_SwitchedToAsync: { //
// We should now be at PASSIVE IRQL.
// Call the completion routine either at DPC or PASSIVE irql.
//
if (pFTask->fDpc) { // We need to call the routine at DPC level.
//
NdisAllocateSpinLock(&pFTask->NdisLock); NdisAcquireSpinLock(&pFTask->NdisLock); }
// Call the completion routine.
//
pFTask->pfnCompletionCallback(pFTask);
// If required, release the lock we held earlier.
//
if (pFTask->fDpc) { NdisReleaseSpinLock(&pFTask->NdisLock); } Status = pFTask->Status; } // end case PEND_OnStart
break;
default: { ASSERTEX(!"Unknown pend op", pTask); } break;
} // end switch(RM_PEND_CODE(pTask))
} // case RM_TASKOP_PENDCOMPLETE
break;
case RM_TASKOP_END: { Status = (NDIS_STATUS) UserParam;
} break;
default: { ASSERTEX(!"Unexpected task op", pTask); } break;
} // switch (Code)
RM_ASSERT_NOLOCKS(pSR); EXIT()
return Status; }
NDIS_STATUS arpDbgAllocateFakeTask( IN PRM_OBJECT_HEADER pParentObject, IN PFN_RM_TASK_HANDLER pfnHandler, IN UINT Timeout, IN const char * szDescription, OUT PRM_TASK *ppTask, IN PRM_STACK_RECORD pSR ) /*++
Routine Description:
Allocates and initializes a task of subtype FAKETASK.
Arguments:
pParentObject - Object that is to be the parent of the allocated task. pfnHandler - The task handler for the task. Timeout - Unused. szDescription - Text describing this task. ppTask - Place to store pointer to the new task.
Return Value:
NDIS_STATUS_SUCCESS if we could allocate and initialize the task. NDIS_STATUS_RESOURCES otherwise --*/ { FAKETASK *pFTask; NDIS_STATUS Status; ARP_ALLOCSTRUCT(pFTask, MTAG_DBGINFO); Status = NDIS_STATUS_RESOURCES; *ppTask = NULL;
if (pFTask != NULL) {
RmInitializeTask( &(pFTask->TskHdr), pParentObject, pfnHandler, &FakeTasks_StaticInfo, szDescription, Timeout, pSR ); *ppTask = &(pFTask->TskHdr); Status = NDIS_STATUS_SUCCESS; }
return Status; }
VOID arpDbgFakeTaskDelete ( PRM_OBJECT_HEADER pObj, PRM_STACK_RECORD psr ) /*++
Routine Description:
Actually free the specified task.
Arguments:
pObj - Actually the task to be freed.
--*/ { ARP_FREE(pObj); }
INT arpGenRandomInt( OUTCOME_PROBABILITY *rgOutcomes, UINT cOutcomes ) /*++
Routine Description:
Generate a new sample given the specified probability distribution.
Arguments:
rgOutcomes - Array of outcomes from which to select the random sample. cOutcomes - Number of elements in the above array.
Return Value:
Random integer
--*/ { ULONG u, sum, partsum; OUTCOME_PROBABILITY *pOp, *pOpEnd;
// Get a nicely-random number.
//
u = ran1x();
// Run through weights, computing the sum of weights...
//
pOp = pOpEnd = rgOutcomes; pOpEnd += cOutcomes; sum=0; for(; pOp<pOpEnd; pOp++) { sum += pOp->Weight; }
// It's really meaningless to pass in a pPD with zero sum of weights.
// We return 0 in this case.
//
if (sum == 0) return 0; // EARLY RETURN
// Make u range from 0..sum-1 inclusive
//
u ^= u>>16; // Get more randomness in the lower 16 bits for mod below...
u %= sum;
// Now go through the array of outcomes, computing the partial sum (partsum)
// of weigths, and picking the FIRST outcome at array position X such that
// u < partsum.
//
partsum=0; pOp = pOpEnd = rgOutcomes; pOpEnd += cOutcomes; for(; pOp<pOpEnd; pOp++) { partsum += pOp->Weight; if (u < partsum) { break; // Found it!
} }
ASSERT(pOp<pOpEnd);
return pOp->Outcome; }
static long g_idum;
unsigned long ran1x(void) /*++
Routine Description:
Closely based on ran1() from "Numerical Recipes in C." ISBN 0 521 43108 5 (except that it returns unsigned long instead of float, and uses g_idum instead of input arg long *idum).
Pretty uniform and uncorrelated from sample to sample; also individual bits are pretty random. We need these properties.
Return Value:
Random unsigned integer.
--*/ { #define IA 16807
#define IM RAN1X_MAX
#define IQ 127773
#define IR 2836
#define NTAB 32
#define NDIV (1+(IM-1)/NTAB)
int j; long k; static long iy=0; static long iv[NTAB];
if (g_idum <= 0 || !iy) { //
// Initialization code... (I'm not really sure if iy or g_idum can
// go to zero in the course of operation, so I'm leaving this
// initialization code here instead of moving it to sranx1x).
//
if (-g_idum < 1) { g_idum = 1; } else { g_idum = -g_idum; } for (j=NTAB+7;j>=0;j--) { k = g_idum/IQ; g_idum = IA*(g_idum-k*IQ)-IR*k; if (g_idum<0) { g_idum += IM; } if (j<NTAB) { iv[j] = g_idum; } } iy=iv[0]; }
k=g_idum/IQ; g_idum=IA*(g_idum-k*IQ)-IR*k; if (g_idum<0) { g_idum += IM; } j = iy/NDIV; iy = iv[j]; iv[j] = g_idum;
// iy ranges from 1 .. (IM-1)
//
return (unsigned long) iy; }
void sran1x( unsigned long seed ) /*++
Routine Description:
Sets the seed used by ran1x.
--*/ { g_idum = (long) seed;
//
// Make sure the seed is -ve, to trigger ran1x initialization code above.
//
if (g_idum > 0) { g_idum = -g_idum; } if (g_idum==0) { g_idum = -1; } }
VOID arpFakeMakeCallCompletionCallback( struct _FAKETASK *pFTask ) /*++
Routine Description:
Calls ARP's makecall completion callback.
Arguments:
pFTask - Task in whose context this callback is to be made. The task contains information used in calling the makecall completion callback. --*/ { // Call the make call completion routine.
//
ArpCoMakeCallComplete( pFTask->Status, (NDIS_HANDLE) pFTask->pClientContext, NULL, pFTask->CallParameters ); }
VOID arpFakeCloseCallCompletionCallback( struct _FAKETASK *pFTask ) /*++
Routine Description:
Calls ARP's closecall completion callback.
Arguments:
pFTask - Task in whose context this callback is to be made. The task contains information used in calling the closecall completion callback. --*/ { ArpCoCloseCallComplete( pFTask->Status, (NDIS_HANDLE) pFTask->pClientContext, NULL ); }
VOID arpFakeSendPacketsCompletionCallback( struct _FAKETASK *pFTask ) /*++
Routine Description:
Calls ARP's cosendpackets completion callback.
Arguments:
pFTask - Task in whose context this callback is to be made. The task contains information used in calling the cosendpackets completion callback. --*/ { ArpCoSendComplete( pFTask->Status, (NDIS_HANDLE) pFTask->pClientContext, pFTask->pNdisPacket ); }
#endif // ARPDBG_FAKE_APIS
|