/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    DBG.C

Abstract:

    This module contains debug only code for USB Hub driver

Author:

    jdunn

Environment:

    kernel mode only

Notes:


Revision History:

    11-5-96 : created

--*/


#include <wdm.h>
#ifdef WMI_SUPPORT
#include <wmilib.h>
#endif /* WMI_SUPPORT */

#include "stdarg.h"
#include "stdio.h"
#include "usbhub.h"

#ifdef MAX_DEBUG
#define DEBUG_HEAP
#endif

typedef struct _HEAP_TAG_BUFFER {
    ULONG Sig;
    ULONG Length;
} HEAP_TAG_BUFFER, *PHEAP_TAG_BUFFER;


#if DBG 

// this flag causes us to write a ' in the format string 
// so that the string goes to the NTKERN buffer
// this trick causes problems with driver verifier on NT
// and the trace buffer isn't in NT anyway 
ULONG USBH_W98_Debug_Trace = 
#ifdef NTKERN_TRACE
1;
#else 
0;
#endif

VOID
USBH_Assert(
    IN PVOID FailedAssertion,
    IN PVOID FileName,
    IN ULONG LineNumber,
    IN PCHAR Message
    )
/*++

Routine Description:

    Debug Assert function. 

Arguments:

    DeviceObject - pointer to a device object

    Irp          - pointer to an I/O Request Packet

Return Value:


--*/
{
#ifdef NTKERN  
    // this makes the compiler generate a ret
    ULONG stop = 1;
    
assert_loop:
#endif
    // just call the NT assert function and stop
    // in the debugger.
    RtlAssert( FailedAssertion, FileName, LineNumber, Message );

    // loop here to prevent users from going past
    // are assert before we can look at it
#ifdef NTKERN    
    DBGBREAK();
    if (stop) {
        goto assert_loop;
    }        
#endif
    return;
}


ULONG
_cdecl
USBH_KdPrintX(
    ULONG l,
    PCH Format,
    ...
    )
{
    va_list list;
    int i;
    int arg[6];
    
    if (USBH_Debug_Trace_Level >= l) {    
        if (l <= 1) {
            if (USBH_W98_Debug_Trace) {             
                //override trace buffer
#ifdef USBHUB20  
                DbgPrint("USBHUB20.SYS: ");
#else
                DbgPrint("USBHUB.SYS: ");
#endif                
                *Format = ' ';
            } else {
#ifdef USBHUB20  
                DbgPrint("USBHUB20.SYS: ");
#else
                DbgPrint("USBHUB.SYS: ");
#endif                 
            }
        } else {
#ifdef USBHUB20 
            DbgPrint("USBHUB20.SYS: ");
#else
            DbgPrint("USBHUB.SYS: ");
#endif             
        }
        va_start(list, Format);
        for (i=0; i<6; i++) 
            arg[i] = va_arg(list, int);
        
        DbgPrint(Format, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]);    
    } 

    return 0;
}


VOID
UsbhWarning(
    PDEVICE_EXTENSION_PORT DeviceExtensionPort,
    PUCHAR Message,
    BOOLEAN DebugBreak
    )
{                                                                                               
    DbgPrint("USBHUB: Warning **************************************************************\n");
    if (DeviceExtensionPort) {
        DbgPrint("Device PID %04.4x, VID %04.4x\n",     
                 DeviceExtensionPort->DeviceDescriptor.idProduct, 
                 DeviceExtensionPort->DeviceDescriptor.idVendor); 
    }
    DbgPrint("%s", Message);

    DbgPrint("******************************************************************************\n");

    if (DebugBreak) {
        DBGBREAK();
    }
      
}


VOID 
UsbhInfo(
    PDEVICE_EXTENSION_HUB DeviceExtensionHub
    )
{
    PUSB_HUB_DESCRIPTOR hubDescriptor;
    ULONG i;

    hubDescriptor = DeviceExtensionHub->HubDescriptor;
    
    USBH_KdPrint((1, "'*****************************************************\n"));
    
    if (USBH_HubIsBusPowered(DeviceExtensionHub->FunctionalDeviceObject,
                             DeviceExtensionHub->ConfigurationDescriptor)) {
        USBH_KdPrint((1, "'*** Hub VID %04.4x PID %04.4x is BUS POWERED \n",
                DeviceExtensionHub->DeviceDescriptor.idVendor,
                DeviceExtensionHub->DeviceDescriptor.idProduct));                             
    } else {
        USBH_KdPrint((1, "'*** Hub VID %04.4x PID %04.4x is SELF POWERED \n",
                DeviceExtensionHub->DeviceDescriptor.idVendor,
                DeviceExtensionHub->DeviceDescriptor.idProduct));
    }                
    USBH_KdPrint((1, "'*** has %d ports\n", 
            hubDescriptor->bNumberOfPorts));
    if (HUB_IS_GANG_POWER_SWITCHED(hubDescriptor->wHubCharacteristics)) {
        USBH_KdPrint((1,"'*** is 'gang power switched'\n"));
    } else if (HUB_IS_NOT_POWER_SWITCHED(hubDescriptor->wHubCharacteristics)) {
        USBH_KdPrint((1,"'*** is 'not power switched'\n"));
    } else if (HUB_IS_PORT_POWER_SWITCHED(hubDescriptor->wHubCharacteristics)) {
        USBH_KdPrint((1,"'*** is 'port power switched'\n"));
    } else {
        TEST_TRAP();
    }

    for (i=0; i< hubDescriptor->bNumberOfPorts; i++) {
    
        if (PORT_ALWAYS_POWER_SWITCHED(hubDescriptor, i+1)) {
            USBH_KdPrint((1,"'*** port (%d) is power switched\n", i+1));    
        }
        
        if (PORT_DEVICE_NOT_REMOVABLE(hubDescriptor, i+1)) {
            USBH_KdPrint((1,"'*** port (%d) device is not removable\n", i+1));    
        }
    }
    
    USBH_KdPrint((1, "'*****************************************************\n"));    
}        


PVOID
UsbhGetHeap(
    IN POOL_TYPE PoolType,
    IN ULONG NumberOfBytes,
    IN ULONG Signature,
    IN PLONG TotalAllocatedHeapSpace
    )
/*++

Routine Description:

    Debug routine, used to debug heap problems.  We are using this since 
    most NT debug functions are not supported by NTKERN.
    
Arguments:

    PoolType - pool type passed to ExAllocatePool
    
    NumberOfBytes - number of bytes for item

    Signature - four byte signature supplied by caller

    TotalAllocatedHeapSpace - pointer to variable where client stores
                the total accumulated heap space allocated.

Return Value:

    pointer to allocated memory

--*/
{
    PUCHAR p;
#ifdef DEBUG_HEAP
    PHEAP_TAG_BUFFER tagBuffer;
    
    // we call ExAllocatePoolWithTag but no tag will be added
    // when running under NTKERN

    p = (PUCHAR) ExAllocatePoolWithTag(PoolType,
                                       NumberOfBytes + sizeof(HEAP_TAG_BUFFER)*2,
                                       Signature);

    if (p) {
        tagBuffer = (PHEAP_TAG_BUFFER) p;
        tagBuffer->Sig = Signature;
        tagBuffer->Length = NumberOfBytes;        
        p += sizeof(HEAP_TAG_BUFFER);
        *TotalAllocatedHeapSpace += NumberOfBytes;

        tagBuffer = (PHEAP_TAG_BUFFER) (p + NumberOfBytes);
        tagBuffer->Sig = Signature;
        tagBuffer->Length = NumberOfBytes;     
    }                                            

//    LOGENTRY(LOG_MISC, (PUCHAR) &Signature, 0, 0, 0);
//    LOGENTRY(LOG_MISC, "GetH", p, NumberOfBytes, stk[1] & 0x00FFFFFF);
#else    
    p = (PUCHAR) ExAllocatePoolWithTag(PoolType,
                                       NumberOfBytes,
                                       Signature);

#endif /* DEBUG_HEAP */                
    return p;
}


VOID
UsbhRetHeap(
    IN PVOID P,
    IN ULONG Signature,
    IN PLONG TotalAllocatedHeapSpace
    )
/*++

Routine Description:

    Debug routine, used to debug heap problems.  We are using this since 
    most NT debug functions are not supported by NTKERN.
    
Arguments:

    P - pointer to free

Return Value:

    none.

--*/
{
#ifdef DEBUG_HEAP
    PHEAP_TAG_BUFFER endTagBuffer;    
    PHEAP_TAG_BUFFER beginTagBuffer;

    USBH_ASSERT(P != 0);
    
    beginTagBuffer = (PHEAP_TAG_BUFFER) ((PUCHAR)P  - sizeof(HEAP_TAG_BUFFER));
    endTagBuffer = (PHEAP_TAG_BUFFER) ((PUCHAR)P + beginTagBuffer->Length);

    *TotalAllocatedHeapSpace -= beginTagBuffer->Length;

//    LOGENTRY(LOG_MISC, (PUCHAR) &Signature, 0, 0, 0);    
//    LOGENTRY(LOG_MISC, "RetH", P, tagBuffer->Length, stk[1] & 0x00FFFFFF);

    USBH_ASSERT(*TotalAllocatedHeapSpace >= 0);
    USBH_ASSERT(beginTagBuffer->Sig == Signature);
    USBH_ASSERT(endTagBuffer->Sig == Signature);
    USBH_ASSERT(endTagBuffer->Length == beginTagBuffer->Length);
    
    // fill the buffer with bad data
    RtlFillMemory(P, beginTagBuffer->Length, 0xff);
    beginTagBuffer->Sig = USBHUB_FREE_TAG;

    // free the original block
    ExFreePool(beginTagBuffer);    
#else
    ExFreePool(P);        
#endif /* DEBUG_HEAP */
}

#endif /* DBG */

#ifdef DEBUG_LOG

KSPIN_LOCK LogSpinLock;

struct USBH_LOG_ENTRY {
    CHAR         le_name[4];      // Identifying string
    ULONG_PTR    le_info1;        // entry specific info
    ULONG_PTR    le_info2;        // entry specific info
    ULONG_PTR    le_info3;        // entry specific info
}; /* USBD_LOG_ENTRY */


struct USBH_LOG_ENTRY *HubLStart = 0;    // No log yet
struct USBH_LOG_ENTRY *HubLPtr;
struct USBH_LOG_ENTRY *HubLEnd;
#ifdef PROFILE
ULONG LogMask = LOG_PROFILE;
#else 
ULONG LogMask = 0xFFFFFFFF;
#endif

VOID
USBH_Debug_LogEntry(
    IN ULONG Mask,
    IN CHAR *Name, 
    IN ULONG_PTR Info1, 
    IN ULONG_PTR Info2, 
    IN ULONG_PTR Info3
    )
/*++

Routine Description:

    Adds an Entry to USBH log.

Arguments:

Return Value:

    None.

--*/
{
    KIRQL irql;

    if (HubLStart == 0) {
        return;
    }        

    if ((Mask & LogMask) == 0) {
        return;
    }

    irql = KeGetCurrentIrql();
    if (irql < DISPATCH_LEVEL) {
        KeAcquireSpinLock(&LogSpinLock, &irql);
    } else {
        KeAcquireSpinLockAtDpcLevel(&LogSpinLock);
    }        
    
    if (HubLPtr > HubLStart) {
        HubLPtr -= 1;    // Decrement to next entry
    } else {
        HubLPtr = HubLEnd;
    }        

    if (irql < DISPATCH_LEVEL) {
        KeReleaseSpinLock(&LogSpinLock, irql);
    } else {
        KeReleaseSpinLockFromDpcLevel(&LogSpinLock);
    }        

    USBH_ASSERT(HubLPtr >= HubLStart);
    
    RtlCopyMemory(HubLPtr->le_name, Name, 4);
//    LPtr->le_ret = (stk[1] & 0x00ffffff) | (CurVMID()<<24);
    HubLPtr->le_info1 = Info1;
    HubLPtr->le_info2 = Info2;
    HubLPtr->le_info3 = Info3;

    return;
}


VOID
USBH_LogInit(
    )
/*++

Routine Description:

    Init the debug log - remember interesting information in a circular buffer

Arguments:
    
Return Value:

    None.

--*/
{
#ifdef MAX_DEBUG
    ULONG logSize = 4096*6;    
#else
    ULONG logSize = 4096*3;    
#endif

    
    KeInitializeSpinLock(&LogSpinLock);

    HubLStart = ExAllocatePoolWithTag(NonPagedPool, 
                                      logSize,
                                      USBHUB_HEAP_TAG); 

    if (HubLStart) {
        HubLPtr = HubLStart;

        // Point the end (and first entry) 1 entry from the end of the segment
        HubLEnd = HubLStart + (logSize / sizeof(struct USBH_LOG_ENTRY)) - 1;
    } else {
        USBH_KdBreak(("no mem for log!\n"));
    }

    return;
}

VOID
USBH_LogFree(
    )
/*++

Routine Description:

    Init the debug log - remember interesting information in a circular buffer

Arguments:
    
Return Value:

    None.

--*/
{
    if (HubLStart) {
        ExFreePool(HubLStart);
    }
}

#endif /* DEBUG_LOG */