/*++

Copyright (c) 1996  Microsoft Corporation

Module Name: 

    debug.c

Abstract

                        Debug/performance routines

Author:

    Ervin P.

Environment:

    Kernel mode only

Revision History:


--*/

#include "pch.h"

#if DBG

    // can poke this in the debugger to trap for warnings
    BOOLEAN dbgTrapOnWarn = FALSE;      

    BOOLEAN dbgTrapOnSS = FALSE;

    BOOLEAN dbgVerbose = FALSE;  

    BOOLEAN dbgInfo = TRUE;

    BOOLEAN dbgSkipSecurity = FALSE;

    BOOLEAN dbgTrapOnHiccup = FALSE;

    ULONG dbgLastEntry = 0;
    ULONG dbgInHidclass = 0;
    VOID DbgCommonEntryExit(BOOLEAN isEntering)
    {
        if (isEntering){
            dbgInHidclass++;
            #ifdef _X86_
                _asm nop
                _asm mov eax, [ebp+4]   /*  <- set breakpt here */
                _asm mov dbgLastEntry, eax
            #endif
        }
        else {
            dbgInHidclass--;
        }
    }

    VOID InitFdoExtDebugInfo(PHIDCLASS_DEVICE_EXTENSION hidclassExt)
    {
        FDO_EXTENSION *fdoExt = &hidclassExt->fdoExt;
        NTSTATUS status;
        ULONG actualLen;

        status = IoGetDeviceProperty(   hidclassExt->hidExt.PhysicalDeviceObject,
                                        DevicePropertyDriverKeyName,
                                        sizeof(fdoExt->dbgDriverKeyName),
                                        fdoExt->dbgDriverKeyName,
                                        &actualLen);

        if (!NT_SUCCESS(status)) {
            //
            // We couldn't get the driver key name.  This will happen during
            // textmode setup on NT, for example, when we're loaded as part of
            // bootstrapping the system (long before the device installer/class
            // installer have run).
            //
            // Simply initialize the driver key name field to an empty string.
            //
            *(fdoExt->dbgDriverKeyName) = L'\0';
        }
    }


        ULONG dbgMinInterruptDelta = 0x0fffffff;
        ULONG dbgMaxInterruptsPerSecond = 0;
        ULONG dbgShortestInt = 0x0fffffff;
        ULONG dbgLongestInt = 0;
        LARGE_INTEGER dbgLastIntStart = {0};
        ULONG dbgAveIntTime = 0;

        VOID DbgLogIntStart()
        {
                static ULONG dbgInterruptsThisSecond = 0;
                static ULONG dbgThisSecondStartTime = 0;

                LARGE_INTEGER timeNow;
                ULONG lastTimeMilliSec, timeNowMilliSec;

                KeQuerySystemTime(&timeNow);

                // convert from usec to millisec
                timeNowMilliSec = timeNow.LowPart/10000;
                lastTimeMilliSec = dbgLastIntStart.LowPart/10000;

                if (timeNow.HighPart == dbgLastIntStart.HighPart){
                        ULONG delta = timeNowMilliSec - lastTimeMilliSec;

                        if (delta < dbgMinInterruptDelta){
                                dbgMinInterruptDelta = delta;
                        }

                        if (timeNowMilliSec - dbgThisSecondStartTime < 1000){
                                dbgInterruptsThisSecond++;
                                if (dbgInterruptsThisSecond > dbgMaxInterruptsPerSecond){
                                        dbgMaxInterruptsPerSecond = dbgInterruptsThisSecond;
                                }
                        }
                        else {
                                dbgThisSecondStartTime = timeNowMilliSec;
                                dbgInterruptsThisSecond = 0;
                        }
                }
                else {
                        // this case is harder so skip it
                        dbgThisSecondStartTime = timeNowMilliSec;
                        dbgInterruptsThisSecond = 0;
                }

                dbgLastIntStart = timeNow;
        }

        VOID DbgLogIntEnd()
        {
            LARGE_INTEGER timeNow;

            KeQuerySystemTime(&timeNow);

            if (timeNow.HighPart == dbgLastIntStart.HighPart){ 
                    ULONG timeNowMilliSec = timeNow.LowPart/10000;
                    ULONG intStartTimeMilliSec = dbgLastIntStart.LowPart/10000;
                    ULONG delta = timeNowMilliSec - intStartTimeMilliSec;

                    if (delta < dbgShortestInt){
                        dbgShortestInt = delta;
                    }
                    else if (delta > dbgLongestInt){
                        dbgLongestInt = delta;
                    }

                    {
                        static ULONG dbgIntCount = 0;
                        static ULONG dbgTimeLast1000Ints = 0;

                        if (dbgIntCount < 1000){
                                dbgIntCount++;
                                dbgTimeLast1000Ints += delta;
                        }
                        else {
                                dbgAveIntTime = dbgTimeLast1000Ints/1000;
                                dbgTimeLast1000Ints = 0;
                                dbgIntCount = 0;
                        }
                    }

            }
            else {
                // This is harder so we just skip it
            }

        }


        NTSTATUS DbgTestGetDeviceStringCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
        {
        ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
                ExFreePool(Irp->UserBuffer);
                IoFreeIrp(Irp);
                return STATUS_MORE_PROCESSING_REQUIRED;
        }


        VOID DbgTestGetDeviceString(PFDO_EXTENSION fdoExt)
        {
            PIRP Irp;
            const ULONG inputLen = 200;

            Irp = IoAllocateIrp(fdoExt->fdo->StackSize, FALSE);
            if (Irp){
                Irp->UserBuffer = ALLOCATEPOOL(NonPagedPool, inputLen); 
                if (Irp->UserBuffer){
                    ULONG stringId = HID_STRING_ID_IMANUFACTURER;
                    ULONG languageId = 0x0409;      // English
                    PIO_STACK_LOCATION currentIrpSp = IoGetCurrentIrpStackLocation(Irp);

                    Irp->MdlAddress->MappedSystemVa = Irp->UserBuffer;
                    Irp->MdlAddress->MdlFlags |= MDL_SOURCE_IS_NONPAGED_POOL;
                    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;

                    currentIrpSp->Parameters.DeviceIoControl.OutputBufferLength = inputLen;

                    IoSetCompletionRoutine( Irp, 
                                            DbgTestGetDeviceStringCompletion, 
                                            (PVOID)NULL, 
                                            TRUE, 
                                            TRUE, 
                                            TRUE);

                    HidpGetDeviceString(fdoExt, Irp, stringId, languageId);
                }
            }
        }

        VOID DbgTestGetIndexedString(PFDO_EXTENSION fdoExt)
        {
            PIRP Irp;
            const ULONG inputLen = 200;

            Irp = IoAllocateIrp(fdoExt->fdo->StackSize, FALSE);
            if (Irp){
                Irp->UserBuffer = ALLOCATEPOOL(NonPagedPool, inputLen); 
                if (Irp->UserBuffer){
                    ULONG stringIndex = 1;      // ???
                    ULONG languageId = 0x0409;      // English
                    PIO_STACK_LOCATION currentIrpSp = IoGetCurrentIrpStackLocation(Irp);

                    Irp->MdlAddress->MappedSystemVa = Irp->UserBuffer;
                    Irp->MdlAddress->MdlFlags |= MDL_SOURCE_IS_NONPAGED_POOL;

                    currentIrpSp->Parameters.DeviceIoControl.InputBufferLength = inputLen;

                    IoSetCompletionRoutine( Irp, 
                                                                    DbgTestGetDeviceStringCompletion, 
                                                                    (PVOID)NULL, 
                                                                    TRUE, 
                                                                    TRUE, 
                                                                    TRUE);

                    HidpGetIndexedString(fdoExt, Irp, stringIndex, languageId);
                }
            }
        }

    #define DBG_MAX_DEVOBJ_RECORDS 100
    dbgDevObjRecord dbgDevObjs[DBG_MAX_DEVOBJ_RECORDS] = {0};

    VOID DbgRecordDevObj(PDEVICE_OBJECT devObj, PCHAR str)
    {
        ULONG i;   

        for (i = 0; i < DBG_MAX_DEVOBJ_RECORDS; i++){
            if (!ISPTR(dbgDevObjs[i].devObj)){
                break;
            }
            else if (dbgDevObjs[i].devObj == devObj){
                // already there
                break;
            }
        }       

        if ((i < DBG_MAX_DEVOBJ_RECORDS) && !dbgDevObjs[i].devObj){
            ULONG j;
            dbgDevObjs[i].devObj = devObj;
            for (j = 0; str[j] && (j < dbgDevObjRecord_STRINGSIZE); j++){
                dbgDevObjs[i].str[j] = str[j];
            }
        }
    }

    #define DBG_MAX_FEATURE_RECORDS 0x1000
    dbgFeatureRecord dbgFeatures[DBG_MAX_FEATURE_RECORDS] = {0};
    ULONG dbgFeatureFirstFreeIndex = 0;
    VOID DbgRecordReport(ULONG reportId, ULONG controlCode, BOOLEAN isComplete)
    {
        ULONG typeId;
        
        switch (controlCode){
        case IOCTL_HID_GET_FEATURE: typeId = (ULONG)'fteG'; break;
        case IOCTL_HID_SET_FEATURE: typeId = (ULONG)'fteS'; break;
        case IOCTL_HID_GET_INPUT_REPORT: typeId = (ULONG)'iteG'; break;
        case IOCTL_HID_SET_OUTPUT_REPORT: typeId = (ULONG)'oteS'; break;
            default:                    typeId = (ULONG)'xxxx'; TRAP; break;
        }

        if (isComplete){
            LONG i;
            // step back to find the report that got completed
            // assumes no overlapped calls to same feature
            ASSERT(dbgFeatureFirstFreeIndex > 0);
            i = dbgFeatureFirstFreeIndex-1;
            while ((i >= 0) && 
                   ((dbgFeatures[i].reportId != reportId) ||
                    (dbgFeatures[i].type != typeId)       ||
                    dbgFeatures[i].completed)){
                i--;
            }
            ASSERT(i >= 0);
            if (i >= 0){
                dbgFeatures[i].completed = 1;
            }
        }
        else {
            if (dbgFeatureFirstFreeIndex >= DBG_MAX_FEATURE_RECORDS){
                RtlZeroMemory(dbgFeatures, sizeof(dbgFeatures));
                dbgFeatureFirstFreeIndex = 0;
            }

            dbgFeatures[dbgFeatureFirstFreeIndex].marker = (ULONG)'taeF';
            dbgFeatures[dbgFeatureFirstFreeIndex].reportId = reportId;
            dbgFeatures[dbgFeatureFirstFreeIndex].type = typeId;
            dbgFeatures[dbgFeatureFirstFreeIndex].completed = 0;
            dbgFeatureFirstFreeIndex++;
        }

    }


    #define DBG_MAX_READ_RECORDS 0x1000
    dbgReadRecord dbgReads[DBG_MAX_READ_RECORDS] = {0};
    VOID DbgRecordRead(PIRP irp, ULONG length, ULONG reportId, ULONG completed)
    {
        LONG i;

        for (i = 0; 
            (i < DBG_MAX_READ_RECORDS) && 
            dbgReads[i].irpPtr && 
            ((dbgReads[i].irpPtr != (ULONG_PTR)irp) || dbgReads[i].completed);
             i++){
        }

        if (i < DBG_MAX_READ_RECORDS){
            if (dbgReads[i].irpPtr){
                ASSERT(dbgReads[i].irpPtr == (ULONG_PTR)irp);
                ASSERT(!dbgReads[i].completed);
                ASSERT(completed);
                dbgReads[i].length = length;
                dbgReads[i].reportId = reportId;
                dbgReads[i].completed = completed;
            }
            else {
                dbgReads[i].irpPtr = (ULONG_PTR)irp;
                dbgReads[i].length = length;
                dbgReads[i].reportId = reportId;
                dbgReads[i].completed = completed;
            }
        }

    }


    VOID DbgLogIrpMajor(ULONG_PTR irpPtr, ULONG majorFunc, ULONG isForCollectionPdo, ULONG isComplete, ULONG status)
    {

        if (dbgVerbose){
            char *funcName;

            switch (majorFunc){
                #undef MAKE_CASE
                #define MAKE_CASE(fnc) case fnc: funcName = #fnc; break;

                MAKE_CASE(IRP_MJ_CREATE)
                MAKE_CASE(IRP_MJ_CREATE_NAMED_PIPE)
                MAKE_CASE(IRP_MJ_CLOSE)
                MAKE_CASE(IRP_MJ_READ)
                MAKE_CASE(IRP_MJ_WRITE)
                MAKE_CASE(IRP_MJ_QUERY_INFORMATION)
                MAKE_CASE(IRP_MJ_SET_INFORMATION)
                MAKE_CASE(IRP_MJ_QUERY_EA)
                MAKE_CASE(IRP_MJ_SET_EA)
                MAKE_CASE(IRP_MJ_FLUSH_BUFFERS)
                MAKE_CASE(IRP_MJ_QUERY_VOLUME_INFORMATION)
                MAKE_CASE(IRP_MJ_SET_VOLUME_INFORMATION)
                MAKE_CASE(IRP_MJ_DIRECTORY_CONTROL)
                MAKE_CASE(IRP_MJ_FILE_SYSTEM_CONTROL)
                MAKE_CASE(IRP_MJ_DEVICE_CONTROL)
                MAKE_CASE(IRP_MJ_INTERNAL_DEVICE_CONTROL)
                MAKE_CASE(IRP_MJ_SHUTDOWN)
                MAKE_CASE(IRP_MJ_LOCK_CONTROL)
                MAKE_CASE(IRP_MJ_CLEANUP)
                MAKE_CASE(IRP_MJ_CREATE_MAILSLOT)
                MAKE_CASE(IRP_MJ_QUERY_SECURITY)
                MAKE_CASE(IRP_MJ_SET_SECURITY)
                MAKE_CASE(IRP_MJ_POWER)
                MAKE_CASE(IRP_MJ_SYSTEM_CONTROL)
                MAKE_CASE(IRP_MJ_DEVICE_CHANGE)
                MAKE_CASE(IRP_MJ_QUERY_QUOTA)
                MAKE_CASE(IRP_MJ_SET_QUOTA)
                MAKE_CASE(IRP_MJ_PNP)

                default: funcName = NULL;    break;
            }

            if (isComplete){
                if (funcName){
                    DBGOUT(("< %s for %s status=%xh (irp=%ph)",
                            funcName,
                            isForCollectionPdo ? "collection" : "device",
                            status,
                            irpPtr));
                }
                else {
                    DBGOUT(("< ????<majorFunc=%xh> for %s status=%xh (irp=%ph)",
                            majorFunc,
                            isForCollectionPdo ? "collection" : "device",
                            status,
                            irpPtr));
                }
            }
            else {
                if (funcName){
                    DBGOUT(("> %s (irp=%xh)", funcName, irpPtr));
                }
                else {
                    DBGOUT(("> ????<majorFunc=%xh> (irp=%xh)", majorFunc, irpPtr));
                }
            }
        }

    }



    #define DBG_MAX_PNP_IRP_RECORDS 0x1000
    dbgPnPIrpRecord dbgPnPIrps[DBG_MAX_PNP_IRP_RECORDS] = {0};

    VOID DbgLogPnpIrp(ULONG_PTR irpPtr, ULONG minorFunc, ULONG isForCollectionPdo, ULONG isComplete, ULONG status)
    {
        char *funcName;
        ULONG funcShortName;
        int i;

        switch (minorFunc){
            #undef MAKE_CASE
            #define MAKE_CASE(fnc) case fnc: funcName = #fnc; funcShortName = *(ULONG *)(funcName+7); break;

            MAKE_CASE(IRP_MN_START_DEVICE)
            MAKE_CASE(IRP_MN_QUERY_REMOVE_DEVICE)
            MAKE_CASE(IRP_MN_REMOVE_DEVICE)
            MAKE_CASE(IRP_MN_CANCEL_REMOVE_DEVICE)
            MAKE_CASE(IRP_MN_STOP_DEVICE)
            MAKE_CASE(IRP_MN_QUERY_STOP_DEVICE)
            MAKE_CASE(IRP_MN_CANCEL_STOP_DEVICE)
            MAKE_CASE(IRP_MN_QUERY_DEVICE_RELATIONS)
            MAKE_CASE(IRP_MN_QUERY_INTERFACE)
            MAKE_CASE(IRP_MN_QUERY_CAPABILITIES)
            MAKE_CASE(IRP_MN_QUERY_RESOURCES)
            MAKE_CASE(IRP_MN_QUERY_RESOURCE_REQUIREMENTS)
            MAKE_CASE(IRP_MN_QUERY_DEVICE_TEXT)
            MAKE_CASE(IRP_MN_READ_CONFIG)
            MAKE_CASE(IRP_MN_WRITE_CONFIG)
            MAKE_CASE(IRP_MN_EJECT)
            MAKE_CASE(IRP_MN_SET_LOCK)
            MAKE_CASE(IRP_MN_QUERY_ID)
            MAKE_CASE(IRP_MN_QUERY_PNP_DEVICE_STATE)
            MAKE_CASE(IRP_MN_QUERY_BUS_INFORMATION)
            MAKE_CASE(IRP_MN_DEVICE_USAGE_NOTIFICATION)
            MAKE_CASE(IRP_MN_SURPRISE_REMOVAL)

#ifndef IRP_MN_QUERY_LEGACY_BUS_INFORMATION
#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18
#endif // IRP_MN_QUERY_LEGACY_BUS_INFORMATION
            MAKE_CASE(IRP_MN_QUERY_LEGACY_BUS_INFORMATION)

            default: funcName = NULL; funcShortName = (ULONG)'\?\?\?\?'; break;
        }

        if (dbgVerbose){
            if (isComplete){
                if (funcName){
                    DBGOUT((" < %s for %s status=%xh (irp=%ph)", 
                            funcName, 
                            isForCollectionPdo ? "collection" : "device",
                            status,
                            irpPtr));
                }
                else {
                    DBGOUT((" < ?? <minorFunc=%xh> for %s status=%xh (irp=%ph)", 
                            minorFunc, 
                            isForCollectionPdo ? "collection" : "device",
                            status,
                            irpPtr));
                }
            }
            else {
                if (funcName){
                    DBGOUT((" > %s for %s (irp=%xh)", 
                            funcName, 
                            isForCollectionPdo ? "collection" : "device",
                            irpPtr));
                }
                else {
                    DBGOUT((" > ?? <minorFunc=%xh> for %s (irp=%xh)", 
                            minorFunc, 
                            isForCollectionPdo ? "collection" : "device",
                            irpPtr));
                }
            }
        }

        if (isComplete){
            for (i = 0; (i < DBG_MAX_PNP_IRP_RECORDS) && dbgPnPIrps[i].irpPtr; i++){
                if ((dbgPnPIrps[i].irpPtr == irpPtr) &&
                    ((dbgPnPIrps[i].status == 0xFFFFFFFF) || (dbgPnPIrps[i].status == STATUS_PENDING))){
                    dbgPnPIrps[i].status = status;
                    break;
                }

            }        
        }
        else {
            for (i = 0; i < DBG_MAX_PNP_IRP_RECORDS; i++){
                if (!dbgPnPIrps[i].irpPtr){
                    dbgPnPIrps[i].irpPtr = irpPtr;
                    dbgPnPIrps[i].func = funcShortName;
                    dbgPnPIrps[i].isForCollectionPdo = isForCollectionPdo;
                    dbgPnPIrps[i].status = 0xFFFFFFFF;
                    break;                  
                }
            }
        }


    }


    VOID DbgLogPowerIrp(PVOID devExt, UCHAR minorFunc, ULONG isClientPdo, ULONG isComplete, PCHAR type, ULONG powerState, ULONG status)
    {
        char *funcName;

        switch (minorFunc){
            #undef MAKE_CASE
            #define MAKE_CASE(fnc) case fnc: funcName = #fnc; break;

            MAKE_CASE(IRP_MN_WAIT_WAKE)
            MAKE_CASE(IRP_MN_POWER_SEQUENCE)
            MAKE_CASE(IRP_MN_SET_POWER)
            MAKE_CASE(IRP_MN_QUERY_POWER)

            default: funcName = "????"; break;
        }
        
        
        if (dbgVerbose){
            if (isComplete){
                DBGOUT((" < %s for %s(ext=%ph) status=%xh ", 
                        funcName, 
                        isClientPdo ? "collection" : "device",
                        devExt,
                        status));
            }
            else if (minorFunc == IRP_MN_SET_POWER){
                DBGOUT((" > %s for %s(ext=%ph) type=%s, powerState=%ph", 
                        funcName, 
                        isClientPdo ? "collection" : "device",
                        devExt,
                        type,
                        powerState));
            }
            else {
                DBGOUT((" > %s for %s(ext=%ph) ", 
                        funcName, 
                        isClientPdo ? "collection" : "device",
                        devExt));
            }
        }


    }



    #define DBG_MAX_REPORT_RECORDS 0x100
    dbgReportRecord dbgReportRecords[DBG_MAX_REPORT_RECORDS] = { 0 };
    ULONG dbgCurrentReportRecord = 0;

    VOID DbgLogReport(ULONG collectionNumber, ULONG numRecipients, ULONG numPending, ULONG numFailed, PUCHAR report, ULONG reportLength)
    {
        ASSERT(dbgCurrentReportRecord <= DBG_MAX_REPORT_RECORDS);
        
        if (dbgCurrentReportRecord == DBG_MAX_REPORT_RECORDS){
            RtlZeroMemory(dbgReportRecords, DBG_MAX_REPORT_RECORDS*sizeof(dbgReportRecord)); 
            dbgCurrentReportRecord = 0;
        }

        dbgReportRecords[dbgCurrentReportRecord].collectionNumber = (UCHAR)collectionNumber;
        dbgReportRecords[dbgCurrentReportRecord].numRecipients = (UCHAR)numRecipients;
        if (reportLength > sizeof(dbgReportRecords[dbgCurrentReportRecord].reportBytes)){
            reportLength = sizeof(dbgReportRecords[dbgCurrentReportRecord].reportBytes);
        }
        RtlCopyMemory((PUCHAR)dbgReportRecords[dbgCurrentReportRecord].reportBytes, report, reportLength);

        dbgCurrentReportRecord++;

        if (dbgVerbose){
            ULONG i;

            DBGOUT(("Report (cltn #%d, %d recipients; %d pending, %d failed):", collectionNumber, numRecipients, numPending, numFailed));
            DbgPrint("'\t report bytes: \t");
            for (i = 0; i < reportLength; i++){
                DbgPrint("%02x ", report[i]);
            }
            DbgPrint("\n");
        }
    }


    VOID DbgLogIoctl(ULONG_PTR fdo, ULONG ioControlCode, ULONG status)
    {
        if (dbgVerbose){
            PCHAR ioctlStr;

            switch (ioControlCode){
                #undef MAKE_CASE
                #define MAKE_CASE(ioctl) case ioctl: ioctlStr = #ioctl; break;

                MAKE_CASE(IOCTL_HID_GET_DRIVER_CONFIG)
                MAKE_CASE(IOCTL_HID_SET_DRIVER_CONFIG)
                MAKE_CASE(IOCTL_HID_GET_POLL_FREQUENCY_MSEC)
                MAKE_CASE(IOCTL_HID_SET_POLL_FREQUENCY_MSEC)
                MAKE_CASE(IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS)
                MAKE_CASE(IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS)
                MAKE_CASE(IOCTL_HID_GET_COLLECTION_INFORMATION)
                MAKE_CASE(IOCTL_HID_GET_COLLECTION_DESCRIPTOR)
                MAKE_CASE(IOCTL_HID_FLUSH_QUEUE)
                MAKE_CASE(IOCTL_HID_SET_FEATURE)
                MAKE_CASE(IOCTL_HID_GET_FEATURE)
                MAKE_CASE(IOCTL_GET_PHYSICAL_DESCRIPTOR)
                MAKE_CASE(IOCTL_HID_GET_HARDWARE_ID)
                MAKE_CASE(IOCTL_HID_GET_MANUFACTURER_STRING)
                MAKE_CASE(IOCTL_HID_GET_PRODUCT_STRING)
                MAKE_CASE(IOCTL_HID_GET_SERIALNUMBER_STRING)
                MAKE_CASE(IOCTL_HID_GET_INDEXED_STRING)
                MAKE_CASE(IOCTL_INTERNAL_HID_SET_BLUESCREEN)

                default: ioctlStr = "???"; break;
            }

            DBGOUT(("IOCTL %s (%xh) status=%xh (fdo=%ph)", 
                    ioctlStr, ioControlCode, status, fdo));
        }
    }

#endif