/*++ 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 #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(; pOpWeight; } // 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(; pOpWeight; if (u < partsum) { break; // Found it! } } ASSERT(pOpOutcome; } 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 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