/*++ Copyright (c) 1998-1999 Microsoft Corporation Module Name: util.c Abstract: ATMEPVC - utilities Author: Revision History: Who When What -------- -------- ---- ADube 03-23-00 created, . --*/ #include "precomp.h" #pragma hdrstop #if DO_TIMESTAMPS void epvcTimeStamp( char *szFormatString, UINT Val ) { UINT Minutes; UINT Seconds; UINT Milliseconds; LARGE_INTEGER Time; NdisGetCurrentSystemTime(&Time); Time.QuadPart /= 10000; //10-nanoseconds to milliseconds. Milliseconds = Time.LowPart; // don't care about highpart. Seconds = Milliseconds/1000; Milliseconds %= 1000; Minutes = Seconds/60; Seconds %= 60; DbgPrint( szFormatString, Minutes, Seconds, Milliseconds, Val); } #endif // DO_TIMESTAMPS //------------------------------------------------------------------------ // // // Task Data structures and functions begin here // // // //----------------------------------------------------------------------// // // EpvcTasks_StaticInfo contains static information about // objects of type EPVC_TASK; // RM_STATIC_OBJECT_INFO EpvcTasks_StaticInfo = { 0, // TypeUID 0, // TypeFlags "ATM Epvc Task", // TypeName 0, // Timeout NULL, // pfnCreate epvcTaskDelete, // pfnDelete NULL, // LockVerifier 0, // length of resource table NULL // Resource Table }; VOID epvcTaskDelete ( PRM_OBJECT_HEADER pObj, PRM_STACK_RECORD psr ) /*++ Routine Description: Free an object of type EPVC_TASK. Arguments: pHdr - Actually a pointer to the EPVC_TASK to be deleted. --*/ { EPVC_FREE(pObj); } NDIS_STATUS epvcAllocateTask( IN PRM_OBJECT_HEADER pParentObject, IN PFN_RM_TASK_HANDLER pfnHandler, IN UINT Timeout, IN const char * szDescription, OPTIONAL OUT PRM_TASK *ppTask, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Allocates and initializes a task of subtype ARP1394_TASK. 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 --*/ { EPVC_TASK *pATask; NDIS_STATUS Status; Status = EPVC_ALLOCSTRUCT(pATask, TAG_TASK); // TODO use lookaside lists. *ppTask = NULL; if (pATask != NULL && (FAIL(Status)== FALSE)) { EPVC_ZEROSTRUCT(pATask); RmInitializeTask( &(pATask->TskHdr), pParentObject, pfnHandler, &EpvcTasks_StaticInfo, szDescription, Timeout, pSR ); *ppTask = &(pATask->TskHdr); Status = NDIS_STATUS_SUCCESS; } else { Status = NDIS_STATUS_RESOURCES; } return Status; } NDIS_STATUS epvcAllocateTaskUsingLookasideList( IN PRM_OBJECT_HEADER pParentObject, IN PEPVC_NPAGED_LOOKASIDE_LIST pList, IN PFN_RM_TASK_HANDLER pfnHandler, IN UINT Timeout, IN const char * szDescription, OPTIONAL OUT PRM_TASK *ppTask, IN PRM_STACK_RECORD pSR ) /*++ Routine Description: Allocates and initializes a task of subtype ARP1394_TASK. 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 --*/ { EPVC_TASK *pATask; NDIS_STATUS Status; pATask = epvcGetLookasideBuffer (pList); Status = EPVC_ALLOCSTRUCT(pATask, TAG_TASK); // TODO use lookaside lists. *ppTask = NULL; if (pATask != NULL && (FAIL(Status)== FALSE)) { EPVC_ZEROSTRUCT(pATask); RmInitializeTask( &(pATask->TskHdr), pParentObject, pfnHandler, &EpvcTasks_StaticInfo, szDescription, Timeout, pSR ); *ppTask = &(pATask->TskHdr); Status = NDIS_STATUS_SUCCESS; } else { Status = NDIS_STATUS_RESOURCES; } return Status; } VOID epvcSetPrimaryAdapterTask( PEPVC_ADAPTER pAdapter, // LOCKIN LOCKOUT PRM_TASK pTask, ULONG PrimaryState, PRM_STACK_RECORD pSR ) { ENTER("epvcSetPrimaryAdapterTask", 0x49c9e2d5) RM_ASSERT_OBJLOCKED(&pAdapter->Hdr, pSR); ASSERT(pAdapter->bind.pPrimaryTask==NULL); #if DBG // Veriy that this is a valid primary task. Also verify that PrimaryState // is a valid primary state. // { PFN_RM_TASK_HANDLER pfn = pTask->pfnHandler; ASSERT( ((pfn == epvcTaskInitializeAdapter) && (PrimaryState == EPVC_AD_PS_INITING)) || ((pfn == epvcTaskShutdownAdapter) && (PrimaryState == EPVC_AD_PS_DEINITING)) ); } #endif // DBG // // Although it's tempting to put pTask as entity1 and pRask->Hdr.szDescption as // entity2 below, we specify NULL for both so that we can be sure that ONLY one // primary task can be active at any one time. // DBG_ADDASSOC( &pAdapter->Hdr, NULL, // Entity1 NULL, // Entity2 EPVC_ASSOC_AD_PRIMARY_TASK, " Primary task\n", pSR ); pAdapter->bind.pPrimaryTask = pTask; SET_AD_PRIMARY_STATE(pAdapter, PrimaryState); EXIT() } VOID epvcClearPrimaryAdapterTask( PEPVC_ADAPTER pAdapter, // LOCKIN LOCKOUT PRM_TASK pTask, ULONG PrimaryState, PRM_STACK_RECORD pSR ) { ENTER("epvcClearPrimaryAdapterTask", 0x593087b1) RM_ASSERT_OBJLOCKED(&pAdapter->Hdr, pSR); ASSERT(pAdapter->bind.pPrimaryTask==pTask); // Veriy that PrimaryState is a valid primary state. // ASSERT( (PrimaryState == EPVC_AD_PS_INITED) || (PrimaryState == EPVC_AD_PS_FAILEDINIT) || (PrimaryState == EPVC_AD_PS_DEINITED) ); // Delete the association added when setting the primary IF task // DBG_DELASSOC( &pAdapter->Hdr, NULL, NULL, EPVC_ASSOC_AD_PRIMARY_TASK, pSR ); pAdapter->bind.pPrimaryTask = NULL; SET_AD_PRIMARY_STATE(pAdapter, PrimaryState); EXIT() } VOID epvcSetSecondaryAdapterTask( PEPVC_ADAPTER pAdapter, // LOCKIN LOCKOUT PRM_TASK pTask, ULONG SecondaryState, PRM_STACK_RECORD pSR ) { ENTER("epvcSetSecondaryAdapterTask", 0x56bbb567) RM_ASSERT_OBJLOCKED(&pAdapter->Hdr, pSR); if (pAdapter->bind.pSecondaryTask != NULL) { ASSERT(FALSE); return; // EARLY RETURN } #if DBG // Veriy that this is a valid act/deact task. Also verify that SecondaryState // is a valid state. // { PFN_RM_TASK_HANDLER pfn = pTask->pfnHandler; ASSERT( ((pfn == epvcTaskActivateAdapter) && (SecondaryState == EPVC_AD_AS_ACTIVATING)) || ((pfn == epvcTaskDeactivateAdapter) && (SecondaryState == EPVC_AD_AS_DEACTIVATING)) ); } #endif // DBG // // Although it's tempting to put pTask as entity1 and pRask->Hdr.szDescption as // entity2 below, we specify NULL for both so that we can be sure that ONLY one // primary task can be active at any one time. // DBG_ADDASSOC( &pAdapter->Hdr, NULL, // Entity1 NULL, // Entity2 EPVC_ASSOC_ACTDEACT_AD_TASK, " Secondary task\n", pSR ); pAdapter->bind.pSecondaryTask = pTask; SET_AD_ACTIVE_STATE(pAdapter, SecondaryState); EXIT() } VOID epvcClearSecondaryAdapterTask( PEPVC_ADAPTER pAdapter, // LOCKIN LOCKOUT PRM_TASK pTask, ULONG SecondaryState, PRM_STACK_RECORD pSR ) { ENTER("epvcClearSecondaryAdapterTask", 0x698552bd) RM_ASSERT_OBJLOCKED(&pAdapter->Hdr, pSR); if (pAdapter->bind.pSecondaryTask != pTask) { ASSERT(FALSE); return; // EARLY RETURN } // Veriy that SecondaryState is a valid primary state. // ASSERT( (SecondaryState == EPVC_AD_AS_ACTIVATED) || (SecondaryState == EPVC_AD_AS_FAILEDACTIVATE) || (SecondaryState == EPVC_AD_AS_DEACTIVATED) ); // Delete the association added when setting the primary IF task // DBG_DELASSOC( &pAdapter->Hdr, NULL, NULL, EPVC_ASSOC_ACTDEACT_AD_TASK, pSR ); pAdapter->bind.pSecondaryTask = NULL; SET_AD_ACTIVE_STATE(pAdapter, SecondaryState); EXIT() } NDIS_STATUS epvcCopyUnicodeString( OUT PNDIS_STRING pDest, IN PNDIS_STRING pSrc, BOOLEAN fUpCase ) /*++ Routine Description: Copy the contents of unicode string pSrc into pDest. pDest->Buffer is allocated using NdisAllocateMemoryWithTag; Caller is responsible for freeing it. EXTRA EXTRA EXTRA: This make sure the destination is NULL terminated. IPAddInterface expects the Unicode string passed in to be NULL terminated. Return Value: NDIS_STATUS_SUCCESS on success; NDIS failure status on failure. --*/ { USHORT Length = pSrc->Length; PWCHAR pwStr; epvcAllocateMemoryWithTag(&pwStr, Length+sizeof(WCHAR), MTAG_STRING); EPVC_ZEROSTRUCT(pDest); if (pwStr == NULL) { return NDIS_STATUS_RESOURCES; } else { pDest->Length = Length; pDest->MaximumLength = Length+sizeof(WCHAR); pDest->Buffer = pwStr; { NdisMoveMemory(pwStr, pSrc->Buffer, Length); if (Length & 0x1) { ((PUCHAR)pwStr)[Length] = 0; } else { pwStr[Length/sizeof(*pwStr)] = 0; } } return NDIS_STATUS_SUCCESS; } } VOID epvcSetFlags( IN OUT ULONG* pulFlags, IN ULONG ulMask ) // Set 'ulMask' bits in '*pulFlags' flags as an interlocked operation. // { ULONG ulFlags; ULONG ulNewFlags; do { ulFlags = *pulFlags; ulNewFlags = ulFlags | ulMask; } while (InterlockedCompareExchange( pulFlags, ulNewFlags, ulFlags ) != (LONG )ulFlags); } VOID epvcClearFlags( IN OUT ULONG* pulFlags, IN ULONG ulMask ) // Set 'ulMask' bits in '*pulFlags' flags as an interlocked operation. // { ULONG ulFlags; ULONG ulNewFlags; do { ulFlags = *pulFlags; ulNewFlags = ulFlags & ~(ulMask); } while (InterlockedCompareExchange( pulFlags, ulNewFlags, ulFlags ) != (LONG )ulFlags); } ULONG epvcReadFlags( IN ULONG* pulFlags ) // Read the value of '*pulFlags' as an interlocked operation. // { return *pulFlags; } BOOLEAN epvcIsThisTaskPrimary ( PRM_TASK pTask, PRM_TASK* ppLocation ) { BOOLEAN fIsThisTaskPrimary = FALSE; ASSERT (*ppLocation != pTask); if (*ppLocation == NULL) { *ppLocation = pTask; return TRUE; } else { return FALSE; } } VOID epvcClearPrimaryTask ( PRM_TASK* ppLocation ) { *ppLocation = NULL; } #if DBG VOID CheckList( IN LIST_ENTRY* pList, IN BOOLEAN fShowLinks ) // Tries to detect corruption in list 'pList', printing verbose linkage // output if 'fShowLinks' is set. // { LIST_ENTRY* pLink; ULONG ul; ul = 0; for (pLink = pList->Flink; pLink != pList; pLink = pLink->Flink) { if (fShowLinks) { DbgPrint( "EPVC: CheckList($%p) Flink(%d)=$%p\n", pList, ul, pLink ); } ++ul; } for (pLink = pList->Blink; pLink != pList; pLink = pLink->Blink) { if (fShowLinks) { DbgPrint( "EPVC: CheckList($%p) Blink(%d)=$%p\n", pList, ul, pLink ); } --ul; } if (ul) { DbgBreakPoint(); } } #endif #if DBG VOID Dump( IN CHAR* p, IN ULONG cb, IN BOOLEAN fAddress, IN ULONG ulGroup ) // Hex dump 'cb' bytes starting at 'p' grouping 'ulGroup' bytes together. // For example, with 'ulGroup' of 1, 2, and 4: // // 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| // 0000 0000 0000 0000 0000 0000 0000 0000 |................| // 00000000 00000000 00000000 00000000 |................| // // If 'fAddress' is true, the memory address dumped is prepended to each // line. // { while (cb) { INT cbLine; cbLine = (cb < DUMP_BytesPerLine) ? cb : DUMP_BytesPerLine; DumpLine( p, cbLine, fAddress, ulGroup ); cb -= cbLine; p += cbLine; } } #endif #if DBG VOID DumpLine( IN CHAR* p, IN ULONG cb, IN BOOLEAN fAddress, IN ULONG ulGroup ) { CHAR* pszDigits = "0123456789ABCDEF"; CHAR szHex[ ((2 + 1) * DUMP_BytesPerLine) + 1 ]; CHAR* pszHex = szHex; CHAR szAscii[ DUMP_BytesPerLine + 1 ]; CHAR* pszAscii = szAscii; ULONG ulGrouped = 0; if (fAddress) DbgPrint( "EPVC: %p: ", p ); else DbgPrint( "EPVC: " ); while (cb) { *pszHex++ = pszDigits[ ((UCHAR )*p) / 16 ]; *pszHex++ = pszDigits[ ((UCHAR )*p) % 16 ]; if (++ulGrouped >= ulGroup) { *pszHex++ = ' '; ulGrouped = 0; } *pszAscii++ = (*p >= 32 && *p < 128) ? *p : '.'; ++p; --cb; } *pszHex = '\0'; *pszAscii = '\0'; DbgPrint( "%-*s|%-*s|\n", (2 * DUMP_BytesPerLine) + (DUMP_BytesPerLine / ulGroup), szHex, DUMP_BytesPerLine, szAscii ); } #endif VOID epvcInitializeLookasideList( IN OUT PEPVC_NPAGED_LOOKASIDE_LIST pLookasideList, ULONG Size, ULONG Tag, USHORT Depth ) /*++ Routine Description: Allocates and initializes a epvc Lookaside list Arguments: Return Value: --*/ { TRACE( TL_T, TM_Mp, ( "==> epvcInitializeLookasideList pLookaside List %x, size %x, Tag %x, Depth %x, ", pLookasideList, Size, Tag, Depth) ); NdisInitializeNPagedLookasideList( &pLookasideList->List, NULL, //Allocate NULL, // Free 0, // Flags Size, Tag, Depth ); // Depth pLookasideList->Size = Size; pLookasideList->bIsAllocated = TRUE; TRACE( TL_T, TM_Mp, ( "<== epvcInitializeLookasideList " ) ); } VOID epvcDeleteLookasideList ( IN OUT PEPVC_NPAGED_LOOKASIDE_LIST pLookasideList ) /*++ Routine Description: Deletes a lookaside list, only if it has been allocated Arguments: Return Value: --*/ { TRACE( TL_T, TM_Mp, ( "==> epvcDeleteLookasideList pLookaside List %x",pLookasideList ) ); if (pLookasideList && pLookasideList->bIsAllocated == TRUE) { while (pLookasideList->OutstandingPackets != 0) { NdisMSleep( 10000); } NdisDeleteNPagedLookasideList (&pLookasideList->List); } TRACE( TL_T, TM_Mp, ( "<== epvcDeleteLookasideList pLookaside List %x", pLookasideList) ); } PVOID epvcGetLookasideBuffer( IN PEPVC_NPAGED_LOOKASIDE_LIST pLookasideList ) // Function Description: // Allocate an buffer from the lookaside list. // will be changed to a macro // // // // Arguments // Lookaside list - from which the buffer is allocated // // // Return Value: // Return buffer can be NULL // { PVOID pLocalBuffer = NULL; TRACE( TL_T, TM_Send, ( "==>epvcGetLookasideBuffer pList %x",pLookasideList) ); ASSERT (pLookasideList != NULL); // // Optimize the lookaside list code path // pLocalBuffer = NdisAllocateFromNPagedLookasideList (&pLookasideList->List); if (pLocalBuffer != NULL) { NdisZeroMemory (pLocalBuffer, pLookasideList->Size); NdisInterlockedIncrement (&pLookasideList->OutstandingPackets); } else { epvcIncrementMallocFailure(); } TRACE( TL_T, TM_Send, ( "<==epvcGetLookasideBuffer, %x", pLocalBuffer ) ); return pLocalBuffer ; } VOID epvcFreeToNPagedLookasideList ( IN PEPVC_NPAGED_LOOKASIDE_LIST pLookasideList, IN PVOID pBuffer ) // Function Description: // Return the local buffer to the lookaside list // // Atguments // Lookaside list and its buffer // Return Value: // None { TRACE( TL_T, TM_Send, ( "==> epvcFreeToNPagedLookasideList , Lookaside list %x, plocalbuffer %x",pLookasideList, pBuffer ) ); NdisFreeToNPagedLookasideList (&pLookasideList->List, pBuffer); NdisInterlockedDecrement (&pLookasideList->OutstandingPackets); TRACE( TL_T, TM_Send, ( "<== epvcFreeToNPagedLookasideList ") ); }