// // Copyright (c) 1998-1999, Microsoft Corporation, all rights reserved // // util.c // // IEEE1394 mini-port/call-manager driver // // General utility routines // // 12/28/1998 JosephJ Created, adapted from the l2tp sources. // #include "precomp.h" // Debug counts of oddities that should not be happening. // ULONG g_ulAllocTwFailures = 0; //----------------------------------------------------------------------------- // Local prototypes (alphabetically) //----------------------------------------------------------------------------- ULONG atoul( IN CHAR* pszNumber ); VOID ReversePsz( IN OUT CHAR* psz ); VOID TunnelWork( IN NDIS_WORK_ITEM* pWork, IN VOID* pContext ); VOID ultoa( IN ULONG ul, OUT CHAR* pszBuf ); //----------------------------------------------------------------------------- // General utility routines (alphabetically) //----------------------------------------------------------------------------- CHAR* nicStrDup( IN CHAR* psz ) // Return a duplicate of 'psz'. Caller must eventually call FREE_NONPAGED // on the returned string. // { return nicStrDupSized( psz, strlen( psz ), 0 ); } CHAR* nicStrDupNdisString( IN NDIS_STRING* pNdisString ) // Returns null-terminated ASCII copy of the NDIS_STRING 'pNdisString' // Caller must eventually call FREE_NONPAGED on the returned string. // { CHAR* pszDup; pszDup = ALLOC_NONPAGED( pNdisString->Length + 1, MTAG_UTIL ); if (pszDup) { NdisZeroMemory( pszDup, pNdisString->Length + 1 ); if (pNdisString->Length) { NdisMoveMemory( pszDup, pNdisString->Buffer, pNdisString->Length ); } // NDIS_STRING is UNICODE_STRING on NT but need the corresponding // ASCII (not multi-byte ANSI) value from NDIS_STRING on any system. // If it looks like a Unicode string then "convert" it by picking out // every other byte, hopefully all the non-zero ones. This is not // foolproof, but then Unicode doesn't convert to ASCII in any // foolproof way. // if (pNdisString->Length > 1 && pszDup[ 1 ] == '\0') { USHORT i; for (i = 0; i * 2 < pNdisString->Length; ++i) { pszDup[ i ] = pszDup[ i * 2 ]; } pszDup[ i ] = '\0'; } } return pszDup; } CHAR* nicStrDupSized( IN CHAR* psz, IN ULONG ulLength, IN ULONG ulExtra ) // Return a duplicate of the first 'ulLength' bytes of 'psz' followed by a // null character and 'ulExtra' extra bytes, or NULL on error. Caller // must eventually call FREE_NONPAGED on the returned string. // { CHAR* pszDup; pszDup = ALLOC_NONPAGED( ulLength + 1 + ulExtra, MTAG_UTIL ); if (pszDup) { if (ulLength) { NdisMoveMemory( pszDup, psz, ulLength ); } pszDup[ ulLength ] = '\0'; } return pszDup; } //----------------------------------------------------------------------------- // Local utility routines (alphabetically) //----------------------------------------------------------------------------- ULONG atoul( IN CHAR* pszNumber ) // Convert string of digits 'pszNumber' to it's ULONG value. // { ULONG ulResult; ulResult = 0; while (*pszNumber && *pszNumber >= '0' && *pszNumber <= '9') { ulResult *= 10; ulResult += *pszNumber - '0'; ++pszNumber; } return ulResult; } #if DBG VOID ReversePsz( IN OUT CHAR* psz ) // Reverse the order of the characters in 'psz' in place. // { CHAR* pchLeft; CHAR* pchRight; pchLeft = psz; pchRight = psz + strlen( psz ) - 1; while (pchLeft < pchRight) { CHAR ch; ch = *pchLeft; *pchLeft = *pchRight; *pchRight = *pchLeft; ++pchLeft; --pchRight; } } #endif #if DBG VOID ultoa( IN ULONG ul, OUT CHAR* pszBuf ) // Convert 'ul' to null-terminated string form in caller's 'pszBuf'. It's // caller job to make sure 'pszBuf' is long enough to hold the returned // string. // { CHAR* pch; pch = pszBuf; do { *pch++ = (CHAR )((ul % 10) + '0'); ul /= 10; } while (ul); *pch = '\0'; ReversePsz( pszBuf ); } #endif VOID nicSetFlags( 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 nicClearFlags( 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 nicReadFlags( IN ULONG* pulFlags ) // Read the value of '*pulFlags' as an interlocked operation. // { return *pulFlags; } // // Reference And Dereference functions taken directly from Ndis // BOOLEAN nicReferenceRef( IN PREF RefP ) /*++ Routine Description: Adds a reference to an object. Arguments: RefP - A pointer to the REFERENCE portion of the object. Return Value: TRUE if the reference was added. FALSE if the object was closing. --*/ { BOOLEAN rc = TRUE; KIRQL OldIrql; TRACE( TL_V, TM_Ref, ( "nicReferenceRef, %.8x", RefP ) ); // NdisAcquireSpinLock (&RefP->SpinLock); if (RefP->Closing) { rc = FALSE; } else { NdisInterlockedIncrement (&RefP->ReferenceCount); } // NdisReleaseSpinLock (&RefP->SpinLock); TRACE( TL_V, TM_Ref, ( "nicReferenceRef, Bool %.2x, Ref %d", rc, RefP->ReferenceCount ) ); return(rc); } BOOLEAN nicDereferenceRef( IN PREF RefP ) /*++ Routine Description: Removes a reference to an object. Arguments: RefP - A pointer to the REFERENCE portion of the object. Return Value: TRUE if the reference count is now 0. FALSE otherwise. --*/ { BOOLEAN rc = FALSE; KIRQL OldIrql; TRACE( TL_V, TM_Ref, ( "==>nicDeReferenceRef, %x", RefP ) ); // NdisAcquireSpinLock (&RefP->SpinLock); NdisInterlockedDecrement (&RefP->ReferenceCount); if (RefP->ReferenceCount == 0) { rc = TRUE; NdisSetEvent (&RefP->RefZeroEvent); } if ((signed long)RefP->ReferenceCount < 0) { ASSERT ( !"Ref Has Gone BELOW ZERO"); } // NdisReleaseSpinLock (&RefP->SpinLock); TRACE( TL_V, TM_Ref, ( "<==nicDeReferenceRef, %.2x, RefCount %d", rc, RefP->ReferenceCount ) ); return(rc); } VOID nicInitializeRef( IN PREF RefP ) /*++ Routine Description: Initialize a reference count structure. Arguments: RefP - The structure to be initialized. Return Value: None. --*/ { TRACE( TL_V, TM_Ref, ( "==>nicInitializeRef, %.8x", RefP ) ); RefP->Closing = FALSE; RefP->ReferenceCount = 1; // NdisAllocateSpinLock (&RefP->SpinLock); NdisInitializeEvent (&RefP->RefZeroEvent); TRACE( TL_V, TM_Ref, ( "<==nicInitializeRef, %.8x", RefP ) ); } BOOLEAN nicCloseRef( IN PREF RefP ) /*++ Routine Description: Closes a reference count structure. Arguments: RefP - The structure to be closed. Return Value: FALSE if it was already closing. TRUE otherwise. --*/ { KIRQL OldIrql; BOOLEAN rc = TRUE; TRACE( TL_N, TM_Ref, ( "==>ndisCloseRef, %.8x", RefP ) ); // NdisAcquireSpinLock (&RefP->SpinLock); if (RefP->Closing) { rc = FALSE; } else RefP->Closing = TRUE; // NdisReleaseSpinLock (&RefP->SpinLock); TRACE( TL_N, TM_Ref, ( "<==ndisCloseRef, %.8x, RefCount %.8x", RefP, RefP->ReferenceCount ) ); return(rc); } // // // These are self expanatory Pdo Reference functions // which will be turned into macros once we have functionality // working // BOOLEAN nicReferenceRemoteNode ( IN REMOTE_NODE *pPdoCb, IN PCHAR pDebugPrint ) /*++ Routine Description: Arguments: Return Value: --*/ { BOOLEAN bRefClosing = FALSE; bRefClosing = nicReferenceRef (&pPdoCb->Ref); TRACE( TL_V, TM_RemRef, ( "**nicReferenceRemoteNode pPdoCb %x, to %d, %s, ret %x ", pPdoCb, pPdoCb->Ref.ReferenceCount, pDebugPrint, bRefClosing ) ); return bRefClosing ; } BOOLEAN nicDereferenceRemoteNode ( IN REMOTE_NODE *pPdoCb, IN PCHAR pDebugPrint ) /*++ Routine Description: Arguments: Return Value: --*/ { TRACE( TL_V, TM_RemRef, ( "**nicDereferenceRemoteNode %x to %d , %s", pPdoCb , pPdoCb->Ref.ReferenceCount -1, pDebugPrint ) ); return nicDereferenceRef (&pPdoCb->Ref); } VOID nicInitalizeRefRemoteNode( IN REMOTE_NODE *pPdoCb ) /*++ Routine Description: Closes Ref on the remote node Arguments: IN REMOTE_NODE *pPdoCb - RemoteNode Return Value: None --*/ { TRACE( TL_N, TM_Ref, ( "**nicinitalizeRefPdoCb pPdoCb %.8x", pPdoCb ) ); nicInitializeRef (&pPdoCb->Ref); } BOOLEAN nicCloseRefRemoteNode( IN REMOTE_NODE *pPdoCb ) /*++ Routine Description: Closes Ref on the remote node Arguments: IN REMOTE_NODE *pPdoCb - RemoteNode Return Value: Return value of nicCloseRef --*/ { TRACE( TL_N, TM_Ref, ( "**nicClosePdoCb pPdoCb %.8x", pPdoCb ) ); return nicCloseRef (&pPdoCb->Ref); } NDIS_STATUS NtStatusToNdisStatus ( NTSTATUS NtStatus ) /*++ Routine Description: Dumps the packet , if the appropriate Debuglevels are set Arguments: NTSTATUS NtStatus - NtStatus to be converted Return Value: NdisStatus - NtStatus' corresponding NdisStatus --*/ { NDIS_STATUS NdisStatus; switch (NtStatus) { case STATUS_SUCCESS: { NdisStatus = NDIS_STATUS_SUCCESS; break; } case STATUS_UNSUCCESSFUL: { NdisStatus = NDIS_STATUS_FAILURE; break; } case STATUS_PENDING: { NdisStatus = NDIS_STATUS_PENDING; break; } case STATUS_INVALID_BUFFER_SIZE: { NdisStatus = NDIS_STATUS_INVALID_LENGTH; break; } case STATUS_INSUFFICIENT_RESOURCES: { NdisStatus = NDIS_STATUS_RESOURCES; break; } case STATUS_INVALID_GENERATION: { NdisStatus = NDIS_STATUS_DEST_OUT_OF_ORDER; break; } case STATUS_ALREADY_COMMITTED: { NdisStatus = NDIS_STATUS_RESOURCE_CONFLICT; break; } case STATUS_DEVICE_BUSY: { NdisStatus = NDIS_STATUS_MEDIA_BUSY; break; } case STATUS_INVALID_PARAMETER: { NdisStatus = NDIS_STATUS_INVALID_DATA; break; } case STATUS_DEVICE_DATA_ERROR: { NdisStatus = NDIS_STATUS_DEST_OUT_OF_ORDER; break; } case STATUS_TIMEOUT: { NdisStatus = NDIS_STATUS_FAILURE; break; } case STATUS_IO_DEVICE_ERROR: { NdisStatus = NDIS_STATUS_NETWORK_UNREACHABLE; break; } default: { NdisStatus = NDIS_STATUS_FAILURE; TRACE( TL_A, TM_Send, ( "Cause: Don't know, INVESTIGATE %x", NtStatus ) ); //ASSERT (0); } } return NdisStatus; } VOID PrintNdisPacket ( ULONG TM_Comp, PNDIS_PACKET pMyPacket ) /*++ Routine Description: Dumps the packet , if the appropriate Debuglevels are set Arguments: ULONG TM_Comp = Debug Component PNDIS_PACKET pMyPacket - Packet to be printed Return Value: None --*/ { PNDIS_BUFFER pPrintNdisBuffer = pMyPacket->Private.Head; // // Print out the complete NdisPacket . Change to DEBUG ONLY // while (pPrintNdisBuffer != NULL) { PVOID pPrintData = NdisBufferVirtualAddress(pPrintNdisBuffer); ULONG PrintLength = NdisBufferLength (pPrintNdisBuffer); TRACE( TL_D, TM_Comp, ( " pNdisbuffer %x, pData %x, Len %x", pPrintNdisBuffer, pPrintData, PrintLength) ); if (pPrintData != NULL) { DUMPB (TL_D, TM_Recv, pPrintData, PrintLength); } pPrintNdisBuffer = pPrintNdisBuffer->Next; } } VOID nicAllocatePacket( OUT PNDIS_STATUS pNdisStatus, OUT PNDIS_PACKET *ppNdisPacket, IN PNIC_PACKET_POOL pPacketPool ) /*++ Routine Description: Calls the ndis API to allocate a packet. On Win9X calls to allocate packets are serialized Arguments: pNdisStatus - pointer to NdisStatus *ppNdisPacket - Ndis packet Allocated by Ndis, pPacketPool - packet pool from which the packet is allocated Return Value: return value of the call to Ndis --*/ { KIRQL OldIrql; #ifdef Win9X #if PACKETPOOL_LOCK KeAcquireSpinLock(&pPacketPool->Lock, &OldIrql); #endif #endif NdisAllocatePacket (pNdisStatus, ppNdisPacket, pPacketPool->Handle ); #ifdef Win9X #if PACKETPOOL_LOCK KeReleaseSpinLock(&pPacketPool->Lock, OldIrql); #endif #endif if (*pNdisStatus == NDIS_STATUS_SUCCESS) { PRSVD pRsvd = NULL; PINDICATE_RSVD pIndicateRsvd = NULL; pRsvd =(PRSVD)((*ppNdisPacket)->ProtocolReserved); pIndicateRsvd = &pRsvd->IndicateRsvd; pIndicateRsvd->Tag = NIC1394_TAG_ALLOCATED; NdisInterlockedIncrement (&pPacketPool->AllocatedPackets); } else { *ppNdisPacket = NULL; nicIncrementMallocFailure(); } } VOID nicFreePacket( IN PNDIS_PACKET pNdisPacket, IN PNIC_PACKET_POOL pPacketPool ) /*++ Routine Description: Free the packet and decrements the outstanding Packet count. All calls are serialized on Win9x Arguments: IN PNDIS_PACKET pNdisPacket - Packet to be freed IN PNIC_PACKET_POOL pPacketPool - PacketPool to which the packet belongs Return Value: None --*/ { KIRQL OldIrql; PRSVD pRsvd = NULL; PINDICATE_RSVD pIndicateRsvd = NULL; pRsvd =(PRSVD)(pNdisPacket->ProtocolReserved); pIndicateRsvd = &pRsvd->IndicateRsvd; pIndicateRsvd->Tag = NIC1394_TAG_FREED; #ifdef Win9X #if PACKETPOOL_LOCK KeAcquireSpinLock(&pPacketPool->Lock, &OldIrql); #endif #endif NdisInterlockedDecrement (&pPacketPool->AllocatedPackets); NdisFreePacket (pNdisPacket); #ifdef Win9X #if PACKETPOOL_LOCK KeReleaseSpinLock(&pPacketPool->Lock, OldIrql); #endif #endif } VOID nicFreePacketPool ( IN PNIC_PACKET_POOL pPacketPool ) /*++ Routine Description: frees the packet pool after waiting for the outstanding packet count to go to zero Arguments: IN PNIC_PACKET_POOL pPacketPool - PacketPool which is to be freed Return Value: None --*/ { ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); while (NdisPacketPoolUsage (pPacketPool->Handle)!=0) { TRACE( TL_V, TM_Cm, ( " Waiting PacketPool %x, AllocatedPackets %x", pPacketPool->Handle, pPacketPool->AllocatedPackets ) ); NdisMSleep (10000); } NdisFreePacketPool (pPacketPool->Handle); pPacketPool->Handle = NULL; ASSERT (pPacketPool->AllocatedPackets == 0); } VOID nicAcquireSpinLock ( IN PNIC_SPIN_LOCK pNicSpinLock, IN PUCHAR FileName, IN UINT LineNumber ) /*++ Routine Description: Acquires a spin lock and if the Dbg, then it will spew out the line and file Arguments: NIC_SPIN_LOCK - Lock to be acquired Return Value: None --*/ { PKTHREAD pThread; TRACE (TL_V, TM_Lock, ("Lock %x, Acquired by File %s, Line %x" , pNicSpinLock, FileName, LineNumber)) ; NdisAcquireSpinLock(&(pNicSpinLock->NdisLock)); #if TRACK_LOCKS pThread = KeGetCurrentThread(); pNicSpinLock->OwnerThread = pThread; NdisMoveMemory(pNicSpinLock->TouchedByFileName, FileName, LOCK_FILE_NAME_LEN); pNicSpinLock->TouchedByFileName[LOCK_FILE_NAME_LEN - 1] = 0x0; pNicSpinLock->TouchedInLineNumber = LineNumber; pNicSpinLock->IsAcquired++; #endif } VOID nicReleaseSpinLock ( IN PNIC_SPIN_LOCK pNicSpinLock, IN PUCHAR FileName, IN UINT LineNumber ) /*++ Routine Description: Release a spin lock and if Dbg is On, then it will spew out the line and file Arguments: pNicSpinLock - Lock to be Release FileName - File Name LineNumber - Line Return Value: None --*/ { PKTHREAD pThread; TRACE (TL_V, TM_Lock, ("Lock %x, Released by File %s, Line %x" , pNicSpinLock, FileName, LineNumber)) ; #if TRACK_LOCKS pThread = KeGetCurrentThread(); NdisMoveMemory(pNicSpinLock->TouchedByFileName, FileName, LOCK_FILE_NAME_LEN); pNicSpinLock->TouchedByFileName[LOCK_FILE_NAME_LEN - 1] = 0x0; pNicSpinLock->TouchedInLineNumber = LineNumber; pNicSpinLock->IsAcquired--; pNicSpinLock->OwnerThread = 0; #endif NdisReleaseSpinLock(&(pNicSpinLock->NdisLock)); } VOID nicInitializeNicSpinLock ( IN PNIC_SPIN_LOCK pNicSpinLock ) /*++ Routine Description: Initializes the lock in the SpinLock Arguments: pNicSpinLock - SpinLock Return Value: None --*/ { NdisAllocateSpinLock (&pNicSpinLock->NdisLock); } VOID nicFreeNicSpinLock ( IN PNIC_SPIN_LOCK pNicSpinLock ) /*++ Routine Description: Frees the spinlock Arguments: pNicSpinLock - SpinLock Return Value: None --*/ { ASSERT ((ULONG)pNicSpinLock->NdisLock.SpinLock == 0); NdisFreeSpinLock (&pNicSpinLock->NdisLock); } UINT nicGetSystemTime( VOID ) /*++ Returns system time in seconds. Since it's in seconds, we won't overflow unless the system has been up for over a 100 years :-) --*/ { LARGE_INTEGER Time; NdisGetCurrentSystemTime(&Time); Time.QuadPart /= 10000000; //100-nanoseconds to seconds. return Time.LowPart; } UINT nicGetSystemTimeMilliSeconds( VOID ) /*++ Returns system time in seconds. Since it's in seconds, we won't overflow unless the system has been up for over a 100 years :-) --*/ { LARGE_INTEGER Time; NdisGetCurrentSystemTime(&Time); Time.QuadPart /= 10000; //10-nanoseconds to seconds. return Time.LowPart; } ULONG SwapBytesUlong( IN ULONG Val) { return ((((Val) & 0x000000ff) << 24) | (((Val) & 0x0000ff00) << 8) | (((Val) & 0x00ff0000) >> 8) | (((Val) & 0xff000000) >> 24) ); } void nicTimeStamp( char *szFormatString, UINT Val ) /*++ Routine Description: Execute and print a time stamp Arguments: Return Value: --*/ { 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); } VOID nicDumpPkt ( IN PNDIS_PACKET pPacket, CHAR * str ) { PNDIS_BUFFER pBuffer; extern BOOLEAN g_ulNicDumpPacket ; if ( g_ulNicDumpPacket == FALSE) { return ; } pBuffer = pPacket->Private.Head; DbgPrint (str); DbgPrint ("Packet %p TotLen %x", pPacket, pPacket->Private.TotalLength); do { ULONG Length = nicNdisBufferLength (pBuffer); PUCHAR pVa = nicNdisBufferVirtualAddress (pBuffer); DbgPrint ("pBuffer %p, Len %x \n", pBuffer, Length); Dump( pVa, Length, 0, 1 ); pBuffer = pBuffer->Next; } while (pBuffer != NULL); } VOID nicDumpMdl ( IN PMDL pMdl, IN ULONG LengthToPrint, IN CHAR *str ) { ULONG MdlLength ; PUCHAR pVa; extern BOOLEAN g_ulNicDumpPacket ; if ( g_ulNicDumpPacket == FALSE ) { return; } MdlLength = MmGetMdlByteCount(pMdl); // // if Length is zero then use MdlLength // if (LengthToPrint == 0) { LengthToPrint = MdlLength; } // // Check for invalid length // if (MdlLength < LengthToPrint) { return; } pVa = MmGetSystemAddressForMdlSafe(pMdl,LowPagePriority ); if (pVa == NULL) { return; } DbgPrint (str); DbgPrint ("pMdl %p, Len %x\n", pMdl, LengthToPrint); Dump( pVa, LengthToPrint, 0, 1 ); }