/*++

Copyright (c) 1995 Microsoft Corporation

Module Name:

    initunlo.c

Abstract:

    Code to handle child PDO

Author:

    Brian Lieuallen   02/12/98

Environment:

    Kernel mode

Revision History :

--*/

#include "precomp.h"


//#define MODEM_INSTANCE_ID  L"0000"

#define DEVICE_ID  L"MODEMWAVE\\0"

#define HARDWARE_ID_PREFIX  L"MODEMWAVE\\"

#define WAVE_EXTENSION      L"\\Wave"

#define CHILD_DEVICE_TEXT L"Modem Audio Device"


NTSTATUS
ModemGetVolatileWaveKey(
    IN PDEVICE_OBJECT   Pdo,
    OUT HANDLE          *Handle
    );


NTSTATUS
ModemCreateWaveDriverRegValue(
    IN PDEVICE_OBJECT   Pdo,
    IN ULONG            Started
    );

NTSTATUS
RemoveWaveDriverRegKeyValue(
    PDEVICE_OBJECT    Pdo
    );


#pragma alloc_text(PAGE,CreateChildPdo)
#pragma alloc_text(PAGE,ModemPdoPnp)
#pragma alloc_text(PAGE,ModemPdoPower)
#pragma alloc_text(PAGE,ModemPdoWmi)
#pragma alloc_text(PAGE,ModemGetVolatileWaveKey)
#pragma alloc_text(PAGE,ModemCreateWaveDriverRegValue)
#pragma alloc_text(PAGE,RemoveWaveDriverRegKeyValue)

//#define DUPLEX_ONLY 1

NTSTATUS
CreateChildPdo(
    PDEVICE_EXTENSION   DeviceExtension
    )

{
    NTSTATUS   Status;

    ACCESS_MASK AccessMask = FILE_ALL_ACCESS;
    HANDLE      hKey;

    PAGED_CODE();

    if (DeviceExtension->ChildPdo != NULL) {

        D_PNP(DbgPrint("MODEM: CreateChildPdo: already have child\n");)

        return STATUS_SUCCESS;
    }


    if (DeviceExtension->Removing) {

        D_PNP(DbgPrint("MODEM: CreateChildPdo: Removing\n");)

        return STATUS_SUCCESS;
    }


    Status=IoOpenDeviceRegistryKey(
        DeviceExtension->Pdo,
        PLUGPLAY_REGKEY_DRIVER,
        AccessMask,
        &hKey
        );

    if (NT_SUCCESS(Status)) {
        //
        //  opened the key
        //
        RTL_QUERY_REGISTRY_TABLE ParamTable[5];

        UNICODE_STRING           HardwareId;
        ULONG                    DuplexSupport=0;
        GUID                     PermanentGuid;

        PWSTR      Parameters = L"WaveDriver";
        PWSTR      Id         = L"WaveHardwareID";
        PWSTR      Duplex     = L"DuplexSupport";

        RtlInitUnicodeString(&HardwareId,NULL);

        HardwareId.MaximumLength=256*sizeof(WCHAR);

        RtlZeroMemory(
            ParamTable,
            sizeof(ParamTable)
            );

        //
        // Entry for the modem reg properties
        //

        ParamTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
        ParamTable[0].Name = L"PermanentGuid";
        ParamTable[0].EntryContext = &PermanentGuid;
        ParamTable[0].DefaultType = REG_NONE;
        ParamTable[0].DefaultLength = 0;//sizeof(localProp);

        //
        // Note that rtlqueryregistryvalues has a real hack
        // way of getting binary data.  We also have to add
        // the *negative* length that we want to the beginning
        // of the buffer.
        //
        *(PLONG)&PermanentGuid = -((LONG)sizeof(PermanentGuid));



        //
        //  change to the parmeters key
        //

        ParamTable[1].QueryRoutine = NULL;
        ParamTable[1].Flags = RTL_QUERY_REGISTRY_SUBKEY;
        ParamTable[1].Name = Parameters;

        //
        //  Get the hardware id
        //

        ParamTable[2].QueryRoutine = NULL;
        ParamTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED |
                              RTL_QUERY_REGISTRY_NOEXPAND |
                              RTL_QUERY_REGISTRY_DIRECT;

        ParamTable[2].Name = Id;
        ParamTable[2].EntryContext = (PVOID)&HardwareId;
        ParamTable[2].DefaultType = 0;

        //
        //  see if it supports duplex mode
        //

        ParamTable[3].QueryRoutine = NULL;
        ParamTable[3].Flags = RTL_QUERY_REGISTRY_NOEXPAND |
                              RTL_QUERY_REGISTRY_DIRECT;

        ParamTable[3].Name = Duplex;
        ParamTable[3].EntryContext = (PVOID)&DuplexSupport;
        ParamTable[3].DefaultType = REG_DWORD;


        Status=RtlQueryRegistryValues(
                   RTL_REGISTRY_HANDLE,
                   hKey,
                   ParamTable,
                   NULL,
                   NULL
                   );



        if (NT_SUCCESS(Status)) {
            //
            //  got the values
            //
#ifdef DUPLEX_ONLY
            if (DuplexSupport != 0) {
#endif
                //
                //  it supports full duplex
                //
                PDEVICE_OBJECT    NewPdo;

                Status = IoCreateDevice(
                             DeviceExtension->DeviceObject->DriverObject,
                             sizeof(PDO_DEVICE_EXTENSION),
                             NULL,
                             FILE_DEVICE_BUS_EXTENDER,
                             FILE_AUTOGENERATED_DEVICE_NAME,
                             FALSE,
                             &NewPdo
                             );

                if (NT_SUCCESS(Status)) {
                    //
                    //  got the device
                    //
                    PPDO_DEVICE_EXTENSION   PdoExtension=NewPdo->DeviceExtension;

                    PdoExtension->DoType=DO_TYPE_PDO;

                    PdoExtension->ParentFdo=DeviceExtension->DeviceObject;
                    PdoExtension->ParentPdo=DeviceExtension->Pdo;

                    //
                    //  save hardware id string
                    //
                    PdoExtension->HardwareId=HardwareId;

                    PdoExtension->UnEnumerated=FALSE;

                    PdoExtension->DuplexSupport=DuplexSupport;

                    RtlCopyMemory(&PdoExtension->PermanentGuid,&PermanentGuid,sizeof(GUID));

                    DeviceExtension->ChildPdo=NewPdo;

//                    ObReferenceObject(DeviceExtension->ChildPdo);

                    NewPdo->Flags |= DO_POWER_PAGABLE;

                    NewPdo->Flags &= ~DO_DEVICE_INITIALIZING;
#ifdef WAVE_KEY
                    ModemCreateWaveDriverRegValue(
                        DeviceExtension->Pdo,
                        0
                        );
#endif

                } else {

                    D_PNP(DbgPrint("MODEM: CreateChildPdo: IoCreateDevice() failed %08lx\n",Status);)

                }
#ifdef DUPLEX_ONLY
            } else {
                D_PNP(DbgPrint("MODEM: CreateChildPdo: Not a duplex modem\n");)
            }
#endif
        } else {

            D_PNP(DbgPrint("MODEM: CreateChildPdo: could not query reg, %08lx\n",Status);)
        }

        ZwClose(hKey);

    } else {

        D_PNP(DbgPrint("MODEM: CreateChildPdo: could not open driver key, %08lx\n",Status);)
    }

    return Status;

}



NTSTATUS
ModemPdoPnp (
    IN PDEVICE_OBJECT       DeviceObject,
    IN PIRP                 Irp
    )
/*++
Routine Description:
    Handle requests from the PlugPlay system for the devices on the BUS

--*/
{
    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);

    PPDO_DEVICE_EXTENSION   PdoDeviceExtension=DeviceObject->DeviceExtension;

    PDEVICE_CAPABILITIES    deviceCapabilities;
    ULONG                   information;
    PWCHAR                  buffer=NULL, buffer2=NULL;
    ULONG                   length, i, j;
    NTSTATUS                status;

    PAGED_CODE ();

    status = Irp->IoStatus.Status;


    switch (IrpSp->MinorFunction) {
    case IRP_MN_QUERY_CAPABILITIES:

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_QUERY_CAPABILITIES\n");)


        //
        // Get the packet.
        //
        deviceCapabilities=IrpSp->Parameters.DeviceCapabilities.Capabilities;

        //
        // Set the capabilities.
        //

        deviceCapabilities->Version = 1;
        deviceCapabilities->Size = sizeof (DEVICE_CAPABILITIES);

        // We cannot wake the system.
        deviceCapabilities->SystemWake = PowerSystemUnspecified;
        deviceCapabilities->DeviceWake = PowerDeviceUnspecified;

        // We have no latencies
        deviceCapabilities->D1Latency = 0;
        deviceCapabilities->D2Latency = 0;
        deviceCapabilities->D3Latency = 0;

        // No locking or ejection
        deviceCapabilities->LockSupported = FALSE;
        deviceCapabilities->EjectSupported = FALSE;

        // Device can be physically removed.
        // Technically there is no physical device to remove, but this bus
        // driver can yank the PDO from the PlugPlay system, when ever it
        // receives an IOCTL_SERENUM_REMOVE_PORT device control command.
//        deviceCapabilities->Removable = TRUE;

        deviceCapabilities->SurpriseRemovalOK=TRUE;


        // not Docking device
        deviceCapabilities->DockDevice = FALSE;

        deviceCapabilities->UniqueID = TRUE;
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_QUERY_DEVICE_TEXT:

        if (IrpSp->Parameters.QueryDeviceText.DeviceTextType == DeviceTextDescription) {

            buffer=ALLOCATE_PAGED_POOL(sizeof(CHILD_DEVICE_TEXT));

            if (buffer != NULL) {

                RtlCopyMemory(buffer,CHILD_DEVICE_TEXT,sizeof(CHILD_DEVICE_TEXT));

                status=STATUS_SUCCESS;
                Irp->IoStatus.Information = (ULONG_PTR) buffer;
                break;

            } else {

                status=STATUS_INSUFFICIENT_RESOURCES;
            }
        }
        break;

    case IRP_MN_QUERY_ID:

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_QUERY_ID\n");)

        // Query the IDs of the device

        switch (IrpSp->Parameters.QueryId.IdType) {

            case BusQueryInstanceID: {
                //
                // Build an instance ID.  This is what PnP uses to tell if it has
                // seen this thing before or not.

                UNICODE_STRING    InstanceString;

                buffer=NULL;

                status=RtlStringFromGUID(
                         &PdoDeviceExtension->PermanentGuid,
                         &InstanceString
                         );

                if (NT_SUCCESS(status)) {

                    buffer = ALLOCATE_PAGED_POOL(InstanceString.Length+sizeof(WCHAR));

                    if (buffer) {

                        RtlZeroMemory(buffer,InstanceString.Length+sizeof(WCHAR));

                        RtlCopyMemory (buffer, InstanceString.Buffer, InstanceString.Length);

                    } else {

                        status=STATUS_NO_MEMORY;
                    }
                    RtlFreeUnicodeString(&InstanceString);
                }
                Irp->IoStatus.Status=status;
                Irp->IoStatus.Information = (ULONG_PTR) buffer;
                break;
            }

            case BusQueryDeviceID:
                //
                // return a WCHAR (null terminated) string describing the device
                // For PNP devices, it is exactly the same as the Hardware ID,
                //


                length=sizeof(DEVICE_ID);

                buffer = ALLOCATE_PAGED_POOL(length);

                if (buffer != NULL) {

                    status = STATUS_SUCCESS;
                    RtlZeroMemory(buffer, length);

                    RtlCopyMemory(
                        buffer,
                        DEVICE_ID,
                        length
                        );

                } else {

                    status=STATUS_INSUFFICIENT_RESOURCES;
                }
                Irp->IoStatus.Information = (ULONG_PTR) buffer;
                break;


            case BusQueryHardwareIDs:
                //
                // return a multi WCHAR (null terminated) string (null terminated)
                // array for use in matching hardare ids in inf files;
                //

                length = sizeof(HARDWARE_ID_PREFIX) +
                         PdoDeviceExtension->HardwareId.Length+
                         (sizeof(WCHAR)*2);

                buffer = ALLOCATE_PAGED_POOL(length);

                if (buffer != NULL) {

                    status = STATUS_SUCCESS;
                    RtlZeroMemory(buffer, length);

                    //
                    //  copy the prefix string in
                    //
                    RtlCopyMemory(
                        buffer,
                        HARDWARE_ID_PREFIX,
                        sizeof(HARDWARE_ID_PREFIX)-sizeof(UNICODE_NULL)
                        );

                    RtlCopyMemory (
                        buffer+((sizeof(HARDWARE_ID_PREFIX)-sizeof(UNICODE_NULL))/sizeof(WCHAR)),
                        PdoDeviceExtension->HardwareId.Buffer,
                        PdoDeviceExtension->HardwareId.Length
                        );

                } else {

                    status=STATUS_INSUFFICIENT_RESOURCES;
                }

                Irp->IoStatus.Information = (ULONG_PTR) buffer;
                break;


            case BusQueryCompatibleIDs:
                // The generic ids for installation of this pdo.


                length=sizeof(WCHAR)*2;

                buffer = ALLOCATE_PAGED_POOL(length);

                if (buffer != NULL) {

                    RtlZeroMemory (buffer,  length);
                    status = STATUS_SUCCESS;

                } else {

                    status=STATUS_INSUFFICIENT_RESOURCES;
                }

                Irp->IoStatus.Information = (ULONG_PTR) buffer;
                break;

            default:
                //
                //  not supported
                //
                break;

        }

        break;

    case IRP_MN_START_DEVICE: {

        PDEVICE_EXTENSION   FdoExtension=PdoDeviceExtension->ParentFdo->DeviceExtension;

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_START_DEVICE\n");)
#ifdef WAVE_KEY
        ModemCreateWaveDriverRegValue(
            FdoExtension->Pdo,
            1
            );
#endif

        status = STATUS_SUCCESS;

        break;
    }

    case IRP_MN_STOP_DEVICE:

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_STOP_DEVICE\n");)

        // Here we shut down the device.  The opposite of start.
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_SURPRISE_REMOVAL:

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_SURPRISE_REMOVAL\n");)

        status = STATUS_SUCCESS;
        break;


    case IRP_MN_REMOVE_DEVICE: {

            PDEVICE_EXTENSION   FdoExtension=PdoDeviceExtension->ParentFdo->DeviceExtension;

            D_PNP(DbgPrint("MODEM: PDO: IRP_MN_REMOVE_DEVICE: %08lx\n",DeviceObject);)
            //
            // The remove IRP code for a PDO uses the following steps:
            //
            KeEnterCriticalRegion();

            ExAcquireResourceExclusiveLite(
                &FdoExtension->OpenCloseResource,
                TRUE
                );

            if (PdoDeviceExtension->Deleted ) {
                //
                //  the PDO has already been deleted, Just return success
                //
                D_PNP(DbgPrint("MODEM: PDO: IRP_MN_REMOVE_DEVICE: already deleted %08lx\n",DeviceObject);)

                status=STATUS_SUCCESS;

            } else {
#ifdef WAVE_KEY
                ModemCreateWaveDriverRegValue(
                    FdoExtension->Pdo,
                    0
                    );
#endif

                if (PdoDeviceExtension->UnEnumerated) {
                    //
                    //  the parent is not emunerating, the child anymore
                    //
                    D_PNP(DbgPrint("MODEM: PDO: IRP_MN_REMOVE_DEVICE: NOT-Enumerated %08lx\n",DeviceObject);)

                    PdoDeviceExtension->DoType=DO_TYPE_DEL_PDO;

                    RtlFreeUnicodeString(&PdoDeviceExtension->HardwareId);

                    PdoDeviceExtension->Deleted=TRUE;

                    IoDeleteDevice(DeviceObject);

                    status=STATUS_SUCCESS;

                } else {
                    //
                    //  The parent is still enumerating, the child, leave it alone
                    //
                    D_PNP(DbgPrint("MODEM: PDO: IRP_MN_REMOVE_DEVICE: Still enumerated %08lx\n",DeviceObject);)

                    status=STATUS_SUCCESS;
                }
            }

            ExReleaseResourceLite(
                &FdoExtension->OpenCloseResource
                );

            KeLeaveCriticalRegion();
        }
        break;

    case IRP_MN_QUERY_STOP_DEVICE:

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_QUERY_STOP_DEVICE\n");)

        // No reason here why we can't stop the device.
        // If there were a reason we should speak now for answering success
        // here may result in a stop device irp.
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_CANCEL_STOP_DEVICE:

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_CANCEL_STOP_DEVICE\n");)
        //
        // The stop was canceled.  Whatever state we set, or resources we put
        // on hold in anticipation of the forcoming STOP device IRP should be
        // put back to normal.  Someone, in the long list of concerned parties,
        // has failed the stop device query.
        //
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_QUERY_REMOVE_DEVICE:

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_QUERY_REMOVE_DEVICE\n");)

        //
        // Just like Query Stop only now the impending doom is the remove irp
        //
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_CANCEL_REMOVE_DEVICE:

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_CANCEL_REMOVE_DEVICE\n");)
        //
        // Clean up a remove that did not go through, just like cancel STOP.
        //
        status = STATUS_SUCCESS;
        break;



    case IRP_MN_READ_CONFIG:

        D_PNP(DbgPrint("MODEM: PDO: IRP_MN_READ_CONFIG: Space=%d\n",IrpSp->Parameters.ReadWriteConfig.WhichSpace);)

        switch ( IrpSp->Parameters.ReadWriteConfig.WhichSpace ) {

            case READ_CONFIG_PERMANENT_GUID:

                if ((IrpSp->Parameters.ReadWriteConfig.Length >= sizeof(GUID))) {

                    RtlCopyMemory(
                        IrpSp->Parameters.ReadWriteConfig.Buffer,
                        &PdoDeviceExtension->PermanentGuid,
                        sizeof(GUID)
                        );

                    status = STATUS_SUCCESS;
                    Irp->IoStatus.Information=sizeof(GUID);
                    break;

                }
                status=STATUS_BUFFER_TOO_SMALL;
                break;

            case READ_CONFIG_DEVICE_NAME:  {

                ULONG    NameLength;
                ULONG    BufferLength;
                //
                //  Get the name of the modem device object
                //

                if (IrpSp->Parameters.ReadWriteConfig.Length > sizeof(WAVE_EXTENSION)) {

                    BufferLength=IrpSp->Parameters.ReadWriteConfig.Length - sizeof(WAVE_EXTENSION);

                    status=IoGetDeviceProperty(
                        PdoDeviceExtension->ParentPdo,
                        DevicePropertyPhysicalDeviceObjectName,
                        BufferLength,
                        IrpSp->Parameters.ReadWriteConfig.Buffer,
                        &NameLength
                        );


                    if (status == STATUS_SUCCESS) {

                        RtlCopyMemory(
                            ((PUCHAR)IrpSp->Parameters.ReadWriteConfig.Buffer)+NameLength-sizeof(UNICODE_NULL),
                            WAVE_EXTENSION,
                            sizeof(WAVE_EXTENSION)
                            );

                        Irp->IoStatus.Information=(NameLength-sizeof(UNICODE_NULL))+sizeof(WAVE_EXTENSION);

                    } else {

                        D_ERROR(DbgPrint("MODEM: PDO: ReadConfig: name failed\n");)
                    }
                }

                break;
            }

            case READ_CONFIG_NAME_SIZE:  {

                ULONG    NameLength;
                //
                //  Get the length of the modem device name
                //

                if (IrpSp->Parameters.ReadWriteConfig.Length >= sizeof(ULONG)) {

                    status=IoGetDeviceProperty(
                        PdoDeviceExtension->ParentPdo,
                        DevicePropertyPhysicalDeviceObjectName,
                        0,
                        NULL,
                        &NameLength
                        );

                    if (status == STATUS_BUFFER_TOO_SMALL) {

                        NameLength+=sizeof(WAVE_EXTENSION);

                        *(PULONG)(IrpSp->Parameters.ReadWriteConfig.Buffer)=NameLength;
                        Irp->IoStatus.Information=sizeof(ULONG);
                        status=STATUS_SUCCESS;
                    }

                } else {
                    D_ERROR(DbgPrint("MODEM: PDO: ReadConfig: name size failed\n");)
                    status=STATUS_BUFFER_TOO_SMALL;
                }

                break;
            }

            case READ_CONFIG_DUPLEX_SUPPORT:  {

                ULONG    NameLength;
                //
                //  Get the DuplexSupport value
                //

                if (IrpSp->Parameters.ReadWriteConfig.Length >= sizeof(ULONG)) {

                    *(PULONG)(IrpSp->Parameters.ReadWriteConfig.Buffer)=PdoDeviceExtension->DuplexSupport;
                    Irp->IoStatus.Information=sizeof(ULONG);
                    status=STATUS_SUCCESS;

                } else {

                    D_ERROR(DbgPrint("MODEM: PDO: ReadConfig: DuplexSupport size failed\n");)
                    status=STATUS_BUFFER_TOO_SMALL;
                }

                break;
            }

            default:

                break;
        }
        break;



    case IRP_MN_QUERY_DEVICE_RELATIONS: {

        PDEVICE_RELATIONS    CurrentRelations;

        switch (IrpSp->Parameters.QueryDeviceRelations.Type) {

            case TargetDeviceRelation:

                CurrentRelations=ALLOCATE_PAGED_POOL(sizeof(DEVICE_RELATIONS));

                if (CurrentRelations != NULL) {

                    ObReferenceObject(DeviceObject);
                    CurrentRelations->Objects[0]=DeviceObject;
                    CurrentRelations->Count=1;

                    Irp->IoStatus.Information=(ULONG_PTR)CurrentRelations;

                    status=STATUS_SUCCESS;

                }  else {

                    status=STATUS_INSUFFICIENT_RESOURCES;
                }

                break;

            default:

                break;

        }

        break;
    }

    default:
        //
        //  we aren't handling this irp
        //  just complete it
        //
        break;

    }

    //
    //  the irp has been handled in some way or the other
    //  complete it now
    //
    Irp->IoStatus.Status = status;
    IoCompleteRequest (Irp, IO_NO_INCREMENT);

    return status;
}


NTSTATUS
ModemPdoPower(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

{

    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS Status;

    PAGED_CODE();

    D_POWER(DbgPrint("MODEM: PDO: Power IRP, MN func=%d\n",irpSp->MinorFunction);)

    PoStartNextPowerIrp(Irp);
    
    if ((irpSp->MinorFunction == IRP_MN_SET_POWER)
            || (irpSp->MinorFunction == IRP_MN_QUERY_POWER))
    {
        Irp->IoStatus.Status = STATUS_SUCCESS;
    }
            
    Status = Irp->IoStatus.Status;
    IoCompleteRequest (Irp, IO_NO_INCREMENT);
    return Status;
}



NTSTATUS
ModemPdoWmi(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

{
    NTSTATUS   Status=Irp->IoStatus.Status;

    D_WMI(DbgPrint("MODEM: PDO: Wmi\n");)

    IoCompleteRequest (Irp, IO_NO_INCREMENT);

    return Status;
}


#ifdef WAVE_KEY

NTSTATUS
ModemGetVolatileWaveKey(
    IN PDEVICE_OBJECT   Pdo,
    OUT HANDLE          *Handle
    )
{

    NTSTATUS    Status;

    const ACCESS_MASK AccessMask = FILE_ALL_ACCESS;
    HANDLE      hKey;

    *Handle=NULL;


    Status=IoOpenDeviceRegistryKey(
        Pdo,
        PLUGPLAY_REGKEY_DRIVER,
        AccessMask,
        &hKey
        );

    if (NT_SUCCESS(Status)) {
        //
        //  opened the key
        //
        OBJECT_ATTRIBUTES    ObjectAttributes;
        UNICODE_STRING       SubKeyName;

        HANDLE               WaveSubKey;

        RtlInitUnicodeString(
            &SubKeyName,
            L"WaveDriver"
            );

        InitializeObjectAttributes(
            &ObjectAttributes,
            &SubKeyName,
            OBJ_CASE_INSENSITIVE,
            hKey,
            NULL
            );

        Status=ZwOpenKey(
            &WaveSubKey,
            AccessMask,
            &ObjectAttributes
            );

        if (NT_SUCCESS(Status)) {

            ULONG                Disposition;
            HANDLE               SubKey=NULL;

            RtlInitUnicodeString(
                &SubKeyName,
                L"Enumerated"
                );

            InitializeObjectAttributes(
                &ObjectAttributes,
                &SubKeyName,
                OBJ_CASE_INSENSITIVE,
                WaveSubKey,
                NULL
                );

            Status=ZwCreateKey(
                &SubKey,
                KEY_SET_VALUE,
                &ObjectAttributes,
                0,
                NULL,
                REG_OPTION_VOLATILE,
                &Disposition
                );

            if (NT_SUCCESS(Status)) {

                *Handle=SubKey;

            } else {

                D_ERROR(DbgPrint("MODEM: Failed to create Enumerated key %08lx\n",Status);)
            }

            ZwClose(WaveSubKey);

        } else {

            D_ERROR(DbgPrint("MODEM: Failed to Open Wavedriver key %08lx\n",Status);)
        }

        ZwClose(hKey);

    } else {

        D_ERROR(DbgPrint("MODEM: Failed to open device reg key %08lx\n",Status);)
    }

    return Status;
}


NTSTATUS
ModemCreateWaveDriverRegValue(
    IN PDEVICE_OBJECT   Pdo,
    IN ULONG            Started
    )

{
    NTSTATUS    Status;
    HANDLE      hKey;

    Status=ModemGetVolatileWaveKey(Pdo,&hKey);

    if (NT_SUCCESS(Status)) {
        //
        //  created or open the key
        //
        UNICODE_STRING    ValueName;

        RtlInitUnicodeString(
            &ValueName,
            L"Started"
            );

        Status=ZwSetValueKey(
            hKey,
            &ValueName,
            0,
            REG_DWORD,
            &Started,
            sizeof(Started)
            );

        if (!NT_SUCCESS(Status)) {

             D_ERROR(DbgPrint("MODEM: Failed to set value %08lx\n",Status);)
        }

        ZwClose(hKey);
    }

    return Status;
}



NTSTATUS
RemoveWaveDriverRegKeyValue(
    PDEVICE_OBJECT    Pdo
    )

{

    NTSTATUS    Status;

    HANDLE      hKey;

    Status=ModemGetVolatileWaveKey(Pdo,&hKey);

    if (NT_SUCCESS(Status)) {
        //
        //  created or open the key
        //
        ZwDeleteKey(hKey);
    }

    return Status;

}

#endif
