//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       chanpdo.c
//
//--------------------------------------------------------------------------

#include "pciidex.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ChannelStartDevice)
#pragma alloc_text(PAGE, ChannelQueryStopRemoveDevice)
#pragma alloc_text(PAGE, ChannelRemoveDevice)
#pragma alloc_text(PAGE, ChannelStopDevice)
#pragma alloc_text(PAGE, ChannelStopChannel)
#pragma alloc_text(PAGE, ChannelQueryId)
#pragma alloc_text(PAGE, ChannelBuildDeviceId)
#pragma alloc_text(PAGE, ChannelBuildInstanceId)
#pragma alloc_text(PAGE, ChannelBuildCompatibleId)
#pragma alloc_text(PAGE, ChannelBuildHardwareId)
#pragma alloc_text(PAGE, ChannelQueryCapabitilies)
#pragma alloc_text(PAGE, ChannelQueryResources)
#pragma alloc_text(PAGE, ChannelQueryResourceRequirements)
#pragma alloc_text(PAGE, ChannelInternalDeviceIoControl)
#pragma alloc_text(PAGE, ChannelQueryText)
#pragma alloc_text(PAGE, PciIdeChannelQueryInterface)
#pragma alloc_text(PAGE, ChannelQueryDeviceRelations)
#pragma alloc_text(PAGE, ChannelUsageNotification)
#pragma alloc_text(PAGE, ChannelQueryPnPDeviceState)

#pragma alloc_text(NONPAGE, ChannelGetPdoExtension)
#pragma alloc_text(NONPAGE, ChannelUpdatePdoState)
#pragma alloc_text(NONPAGE, PciIdeChannelTransferModeSelect)
#pragma alloc_text(NONPAGE, PciIdeChannelTransferModeInterface)

#endif // ALLOC_PRAGMA


PCHANPDO_EXTENSION
ChannelGetPdoExtension(
    PDEVICE_OBJECT DeviceObject
    )
{
    KIRQL currentIrql;
    PCHANPDO_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
    PKSPIN_LOCK spinLock;


    spinLock = &pdoExtension->SpinLock;
    KeAcquireSpinLock(spinLock, &currentIrql);

    if ((pdoExtension->PdoState & PDOS_DEADMEAT) &&
        (pdoExtension->PdoState & PDOS_REMOVED)) {

        pdoExtension = NULL;
    }

    KeReleaseSpinLock(spinLock, currentIrql);

    return pdoExtension;
}

ULONG
ChannelUpdatePdoState(
    PCHANPDO_EXTENSION PdoExtension,
    ULONG SetFlags,
    ULONG ClearFlags
    )
{
    ULONG pdoState;
    KIRQL currentIrql;

    ASSERT (PdoExtension);

    KeAcquireSpinLock(&PdoExtension->SpinLock, &currentIrql);

    SETMASK (PdoExtension->PdoState, SetFlags);
    CLRMASK (PdoExtension->PdoState, ClearFlags);
    pdoState = PdoExtension->PdoState;

    KeReleaseSpinLock(&PdoExtension->SpinLock, currentIrql);

    return pdoState;
}



NTSTATUS
ChannelStartDevice (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    PCHANPDO_EXTENSION pdoExtension;
    NTSTATUS status;
    IDE_CHANNEL_STATE channelState;

    PAGED_CODE();

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        status = STATUS_SUCCESS;

        //
        // always keep native mode started
        //

        if (pdoExtension->ParentDeviceExtension->
            NativeMode[pdoExtension->ChannelNumber] == FALSE) {

            channelState = PciIdeChannelEnabled (
                               pdoExtension->ParentDeviceExtension,
                               pdoExtension->ChannelNumber
                               );
            //
            // ISSUE: we should free the resources assigned.
            //
            //ASSERT(channelState != ChannelDisabled);

            if (channelState == ChannelStateUnknown) {

                //
                // we don't really know if this channel
                // is acutally enabled
                //
                // we will do our empty channel test
                //

                PIO_STACK_LOCATION thisIrpSp;
                IDE_RESOURCE ideResource;
                PCM_PARTIAL_RESOURCE_DESCRIPTOR irqPartialDescriptors;
                IDE_REGISTERS_1  baseIoAddress1;
                IDE_REGISTERS_2  baseIoAddress2;
                ULONG baseIoAddressLength1;
                ULONG baseIoAddressLength2;
                ULONG maxIdeDevice;
                PCM_RESOURCE_LIST resourceList;

                thisIrpSp = IoGetCurrentIrpStackLocation( Irp );

                //
                // legacy mode channel gets its the start device irp
                //
                resourceList = thisIrpSp->Parameters.StartDevice.AllocatedResourcesTranslated;

                status = DigestResourceList (
                             &ideResource,
                             resourceList,
                             &irqPartialDescriptors
                             );

                if (NT_SUCCESS(status)) {

                    AtapiBuildIoAddress (
                        ideResource.TranslatedCommandBaseAddress,
                        ideResource.TranslatedControlBaseAddress,
                        &baseIoAddress1,
                        &baseIoAddress2,
                        &baseIoAddressLength1,
                        &baseIoAddressLength2,
                        &maxIdeDevice,
                        NULL
                        );

                    if (IdePortChannelEmpty (
                            &baseIoAddress1,
                            &baseIoAddress2,
                            maxIdeDevice)) {

                        //
                        // upgrade its state to "disabled"
                        //
                        channelState = ChannelDisabled;

                    } else {

                        channelState = ChannelEnabled;
                    }

                    //
                    // don't need the io resource anymore
                    // unmap io space if nesscessary
                    //
                    if ((ideResource.CommandBaseAddressSpace == MEMORY_SPACE) &&
                        (ideResource.TranslatedCommandBaseAddress)) {

                        MmUnmapIoSpace (
                            ideResource.TranslatedCommandBaseAddress,
                            baseIoAddressLength1
                            );
                    }
                    if ((ideResource.ControlBaseAddressSpace == MEMORY_SPACE) &&
                        (ideResource.TranslatedControlBaseAddress)) {

                        MmUnmapIoSpace (
                            ideResource.TranslatedControlBaseAddress,
                            baseIoAddressLength2
                            );
                    }

                }
                if (channelState == ChannelDisabled) {

                    pdoExtension->EmptyChannel = TRUE;

                    //
                    // channel looks empty
                    // change our resource requirement to free our irq for other devices
                    //
                    if (irqPartialDescriptors) {
                        SETMASK (pdoExtension->PnPDeviceState, PNP_DEVICE_FAILED | PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED);
                        IoInvalidateDeviceState (DeviceObject);
                    }
                } else {

                    pdoExtension->EmptyChannel = FALSE;
                }
            }
        }

        if (NT_SUCCESS(status)) {

            //
            // grab the DmaDetectionLevel from the registry
            // default is DdlFirmwareOk
            //
            pdoExtension->DmaDetectionLevel = DdlFirmwareOk;
            status = PciIdeXGetDeviceParameter (
                       pdoExtension->DeviceObject,
                       DMA_DETECTION_LEVEL_REG_KEY,
                       (PULONG)&pdoExtension->DmaDetectionLevel
                       );

            status = BusMasterInitialize (pdoExtension);
        }
    }

    if (NT_SUCCESS(status)) {

        //
        // get the firmware initialized DMA capable bits
        //
        if (pdoExtension->BmRegister) {

            pdoExtension->BootBmStatus = READ_PORT_UCHAR (&pdoExtension->BmRegister->Status);
        }

        ChannelUpdatePdoState (
            pdoExtension,
            PDOS_STARTED,
            PDOS_DEADMEAT | PDOS_STOPPED | PDOS_REMOVED
            );



#if DBG
        {
           ULONG data;
           USHORT vendorId =0;
           USHORT deviceId = 0;
           PVOID deviceExtension;

           data = 0;
           deviceExtension = pdoExtension->ParentDeviceExtension->VendorSpecificDeviceEntension;

           PciIdeXGetBusData (
               deviceExtension,
               &vendorId,
               FIELD_OFFSET(PCI_COMMON_CONFIG, VendorID),
               sizeof(vendorId)
               );

           PciIdeXGetBusData (
               deviceExtension,
               &deviceId,
               FIELD_OFFSET(PCI_COMMON_CONFIG, DeviceID),
               sizeof(deviceId)
               );

           if (vendorId == 0x8086) {

               data = 0;
               PciIdeXGetBusData (
                   deviceExtension,
                   &data,
                   0x40,    // IDETIM0
                   2
                   );

                PciIdeXSaveDeviceParameter (
                    deviceExtension,
                    L"Old IDETIM0",
                    data
                    );

                data = 0;
                PciIdeXGetBusData (
                    deviceExtension,
                    &data,
                    0x42,    // IDETIM1
                    2
                    );

                 PciIdeXSaveDeviceParameter (
                     deviceExtension,
                     L"Old IDETIM1",
                     data
                     );

                if (deviceId != 0x1230) {       // !PIIX

                    data = 0;
                    PciIdeXGetBusData (
                        deviceExtension,
                        &data,
                        0x44,
                        1
                        );

                    PciIdeXSaveDeviceParameter (
                        deviceExtension,
                        L"Old SIDETIM",
                        data
                        );
                }

                if (deviceId == 0x7111) {

                    USHORT t;

                    data = 0;
                    PciIdeXGetBusData (
                        deviceExtension,
                        &data,
                        0x48,
                        1
                        );

                    PciIdeXSaveDeviceParameter (
                        deviceExtension,
                        L"Old SDMACTL",
                        data
                        );

                    data = 0;
                    PciIdeXGetBusData (
                        deviceExtension,
                        &data,
                        0x4a, //SDMATIM0
                        1
                        );

                    PciIdeXSaveDeviceParameter (
                        deviceExtension,
                        L"Old SDMATIM0",
                        data
                        );

                    data = 0;
                    PciIdeXGetBusData (
                        deviceExtension,
                        &data,
                        0x4b, //SDMATIM1
                        1
                        );

                    PciIdeXSaveDeviceParameter (
                        deviceExtension,
                        L"Old SDMATIM1",
                        data
                        );
                }
           }
        }
#endif // DBG
    }



    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return status;
} // ChannelStartDevice

NTSTATUS
ChannelQueryStopRemoveDevice (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    NTSTATUS           status;
    PCHANPDO_EXTENSION pdoExtension;

    pdoExtension = ChannelGetPdoExtension(DeviceObject);

    if (pdoExtension) {

        //
        // Check the paging path count for this device.
        //

        if (pdoExtension->PagingPathCount ||
            pdoExtension->CrashDumpPathCount) {
            status = STATUS_UNSUCCESSFUL;
        } else {
            status = STATUS_SUCCESS;
        }

    } else {

        status = STATUS_NO_SUCH_DEVICE;
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return status;

} // ChannelQueryStopRemoveDevice

NTSTATUS
ChannelRemoveDevice (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    PCHANPDO_EXTENSION pdoExtension;
    NTSTATUS status;
    PDEVICE_OBJECT AttacheePdo;
    BOOLEAN removeFromParent;
    BOOLEAN callIoDeleteDevice;

    PAGED_CODE();

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension) {

        PIO_STACK_LOCATION thisIrpSp;
        ULONG actionFlag;

        thisIrpSp = IoGetCurrentIrpStackLocation(Irp);

        status = ChannelStopChannel (pdoExtension);
        ASSERT (NT_SUCCESS(status));

        if (thisIrpSp->MinorFunction == IRP_MN_REMOVE_DEVICE) {

            if (pdoExtension->PdoState & PDOS_DEADMEAT) {

               actionFlag = PDOS_REMOVED;
               removeFromParent = TRUE;
               callIoDeleteDevice = TRUE;

            } else {
               actionFlag = PDOS_DISABLED_BY_USER;
               removeFromParent = FALSE;
               callIoDeleteDevice = FALSE;
            }

        } else {

            actionFlag = PDOS_SURPRISE_REMOVED;
            removeFromParent = FALSE;
            callIoDeleteDevice = FALSE;
        }

        ChannelUpdatePdoState (
            pdoExtension,
            actionFlag,
            PDOS_STARTED | PDOS_STOPPED
            );

        if (removeFromParent) {

            PCTRLFDO_EXTENSION  fdoExtension;

            fdoExtension = pdoExtension->ParentDeviceExtension;

            fdoExtension->ChildDeviceExtension[pdoExtension->ChannelNumber] = NULL;

            if (callIoDeleteDevice) {

                IoDeleteDevice (pdoExtension->DeviceObject);
            }
        }

        if (pdoExtension->NeedToCallIoInvalidateDeviceRelations) {

            pdoExtension->NeedToCallIoInvalidateDeviceRelations = FALSE;
            IoInvalidateDeviceRelations (
                pdoExtension->ParentDeviceExtension->AttacheePdo,
                BusRelations
                );
        }

        status = STATUS_SUCCESS;

    } else {

        status = STATUS_NO_SUCH_DEVICE;
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return status;
} // ChannelRemoveDevice

NTSTATUS
ChannelStopDevice (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    PCHANPDO_EXTENSION pdoExtension;
    NTSTATUS status;

    PAGED_CODE();

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        status = ChannelStopChannel (pdoExtension);
        ASSERT (NT_SUCCESS(status));

        ChannelUpdatePdoState (
            pdoExtension,
            PDOS_STOPPED,
            PDOS_STARTED
            );

        status = STATUS_SUCCESS;
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return status;
} // ChannelRemoveDevice

NTSTATUS
ChannelStopChannel (
    PCHANPDO_EXTENSION PdoExtension
    )
{
    NTSTATUS status;

    PAGED_CODE();

    status = BusMasterUninitialize (PdoExtension);
    ASSERT (NT_SUCCESS(status));

    return STATUS_SUCCESS;
} // ChannelStopChannel


NTSTATUS
ChannelQueryId (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    PIO_STACK_LOCATION  thisIrpSp;
    PCHANPDO_EXTENSION  pdoExtension;
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    PWSTR idString = NULL;

    PAGED_CODE();

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        thisIrpSp = IoGetCurrentIrpStackLocation( Irp );
        switch (thisIrpSp->Parameters.QueryId.IdType) {

            case BusQueryDeviceID:

                //
                // Caller wants the bus ID of this device.
                //

                idString = ChannelBuildDeviceId (pdoExtension);
                break;

            case BusQueryInstanceID:

                //
                // Caller wants the unique id of the device
                //

                idString = ChannelBuildInstanceId (pdoExtension);
                break;

            case BusQueryCompatibleIDs:

                //
                // Caller wants the unique id of the device
                //

                idString = ChannelBuildCompatibleId (pdoExtension);
                break;

            case BusQueryHardwareIDs:

                //
                // Caller wants the unique id of the device
                //

                idString = ChannelBuildHardwareId (pdoExtension);
                break;

            default:
                idString = NULL;
                DebugPrint ((1, "pciide: QueryID type %d not supported\n", thisIrpSp->Parameters.QueryId.IdType));
                status = STATUS_NOT_SUPPORTED;
                break;
        }
    }

    if( idString != NULL ){
        Irp->IoStatus.Information = (ULONG_PTR) idString;
        status = STATUS_SUCCESS;
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return status;
} // ChannelQueryId

PWSTR
ChannelBuildDeviceId(
    IN PCHANPDO_EXTENSION pdoExtension
    )
{
    PWSTR       idString;
    ULONG       idStringBufLen;
    NTSTATUS    status;
    WCHAR       deviceIdFormat[] = L"PCIIDE\\IDEChannel";

    PAGED_CODE();

    idStringBufLen = ( wcslen( deviceIdFormat ) + 1 ) * sizeof( WCHAR );
    idString = ExAllocatePool( PagedPool, idStringBufLen );
    if( idString == NULL ){

        return NULL;
    }

    //
    // Form the string and return it.
    //

    wcscpy( idString,
              deviceIdFormat);

    return idString;
} // ChannelBuildDeviceId

PWSTR
ChannelBuildInstanceId(
    IN PCHANPDO_EXTENSION pdoExtension
    )
{
    PWSTR       idString;
    ULONG       idStringBufLen;
    NTSTATUS    status;
    WCHAR       instanceIdFormat[] = L"%d";

    PAGED_CODE();

    //
    // The maximum number of digits that a ulong can hold is 10.
    // So the id len should be 10 + 1 (for '\0')
    //
    idStringBufLen = 11 * sizeof( WCHAR );
    idString = ExAllocatePool (PagedPool, idStringBufLen);

    if( idString) {

        //
        // Form the string and return it.
        //
        swprintf( idString,
                  instanceIdFormat,
                  pdoExtension->ChannelNumber);
    }


    return idString;

} // ChannelBuildInstanceId

//
// Multi-string Compatible IDs
//
WCHAR ChannelCompatibleId[] = {
    L"*PNP0600"
    };
//
// internal Compatible IDs
//
PWCHAR ChannelInternalCompatibleId[MAX_IDE_CHANNEL] = {
    L"Primary_IDE_Channel",
    L"Secondary_IDE_Channel"
    };

PWSTR
ChannelBuildCompatibleId(
    IN PCHANPDO_EXTENSION pdoExtension
    )
{
    PWSTR idString;
    ULONG idStringBufLen;
    ULONG i;

    PAGED_CODE();

    idStringBufLen = sizeof(ChannelCompatibleId);
    idString = ExAllocatePool (PagedPool, idStringBufLen + sizeof (WCHAR) * 2);
    if( idString == NULL ){

        return NULL;
    }

    RtlCopyMemory (
        idString,
        ChannelCompatibleId,
        idStringBufLen
        );
    idString[idStringBufLen/2 + 0] = L'\0';
    idString[idStringBufLen/2 + 1] = L'\0';

    return idString;
} // ChannelBuildCompatibleId

PWSTR
ChannelBuildHardwareId(
    IN PCHANPDO_EXTENSION pdoExtension
    )
{
    NTSTATUS status;

    struct {
        USHORT  VendorId;
        USHORT  DeviceId;
    } hwRawId;

    PWSTR vendorIdString;
    PWSTR deviceIdString;
    WCHAR vendorId[10];
    WCHAR deviceId[10];

    PWSTR idString;
    ULONG idStringBufLen;

    ULONG stringLen;
    ULONG internalIdLen;

    PAGED_CODE();

    status = PciIdeBusData (
                 pdoExtension->ParentDeviceExtension,
                 &hwRawId,
                 0,
                 sizeof (hwRawId),
                 TRUE
                 );
    if (!NT_SUCCESS(status)) {

        return NULL;
    }

    vendorIdString = NULL;
    deviceIdString = NULL;

    switch (hwRawId.VendorId) {
        case 0x8086:
            vendorIdString = L"Intel";

            switch (hwRawId.DeviceId) {

                case 0x1230:
                    deviceIdString = L"PIIX";
                    break;

                case 0x7010:
                    deviceIdString = L"PIIX3";
                    break;

                case 0x7111:
                    deviceIdString = L"PIIX4";
                    break;
            }
            break;

        case 0x1095:
            vendorIdString = L"CMD";
            break;

        case 0x10b9:
            vendorIdString = L"ALi";
            break;

        case 0x1039:
            vendorIdString = L"SiS";
            break;

        case 0x0e11:
            vendorIdString = L"Compaq";
            break;

        case 0x10ad:
            vendorIdString = L"WinBond";
            break;
    }

    if (vendorIdString == NULL) {

        swprintf (vendorId,
                  L"%04x",
                  hwRawId.VendorId);

        vendorIdString = vendorId;
    }

    if (deviceIdString == NULL) {

        swprintf (deviceId,
                  L"%04x",
                  hwRawId.DeviceId);

        deviceIdString = deviceId;
    }

    idStringBufLen = (256 * sizeof (WCHAR));
    idStringBufLen += sizeof(ChannelCompatibleId);
    idString = ExAllocatePool( PagedPool, idStringBufLen);
    if( idString == NULL ){

        return NULL;
    }

    //
    // Form the string and return it.
    //
    swprintf (idString,
              L"%ws-%ws",
              vendorIdString,
              deviceIdString
              );
    stringLen = wcslen(idString);
    idString[stringLen] = L'\0';
    stringLen++;

    //
    // internal HW id
    //
    internalIdLen = wcslen(ChannelInternalCompatibleId[pdoExtension->ChannelNumber]);
    RtlCopyMemory (
        idString + stringLen,
        ChannelInternalCompatibleId[pdoExtension->ChannelNumber],
        internalIdLen * sizeof (WCHAR)
        );
    stringLen += internalIdLen;
    idString[stringLen] = L'\0';
    stringLen++;

    //
    // generic HW id
    //
    RtlCopyMemory (
        idString + stringLen,
        ChannelCompatibleId,
        sizeof(ChannelCompatibleId)
        );
    stringLen += sizeof(ChannelCompatibleId) / sizeof(WCHAR);
    idString[stringLen] = L'\0';
    stringLen++;
    idString[stringLen] = L'\0';
    stringLen++;

    return idString;
} // ChannelBuildHardwareId

NTSTATUS
ChannelQueryCapabitilies (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    PIO_STACK_LOCATION      thisIrpSp;
    PCHANPDO_EXTENSION      pdoExtension;
    PDEVICE_CAPABILITIES    capabilities;
    NTSTATUS                status;

    PAGED_CODE();

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        DEVICE_CAPABILITIES parentDeviceCapabilities;

        status = IdeGetDeviceCapabilities(
                     pdoExtension->ParentDeviceExtension->AttacheePdo,
                     &parentDeviceCapabilities);

        if (NT_SUCCESS(status)) {

            thisIrpSp    = IoGetCurrentIrpStackLocation( Irp );
            capabilities = thisIrpSp->Parameters.DeviceCapabilities.Capabilities;

            *capabilities = parentDeviceCapabilities;

            capabilities->UniqueID          = FALSE;
            capabilities->Address           = pdoExtension->ChannelNumber;
        }
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest (Irp, IO_NO_INCREMENT);
    return status;
} // ChannelQueryCapabitilies


NTSTATUS
ChannelQueryResources(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    PCHANPDO_EXTENSION              pdoExtension;
    PCTRLFDO_EXTENSION              fdoExtension;
    PIO_STACK_LOCATION              thisIrpSp;
    NTSTATUS                        status;
    ULONG                           resourceListSize;
    PCM_RESOURCE_LIST               resourceList;
    PCM_FULL_RESOURCE_DESCRIPTOR    fullResourceList;
    PCM_PARTIAL_RESOURCE_LIST       partialResourceList;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptors;
    IDE_CHANNEL_STATE 				channelState;

    PAGED_CODE();

    resourceList = NULL;
    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        thisIrpSp = IoGetCurrentIrpStackLocation( Irp );
        fdoExtension = pdoExtension->ParentDeviceExtension;

        if (fdoExtension->NativeMode[pdoExtension->ChannelNumber]) {

            //
            // Don't make up resources for native mode controller
            // PCI bus driver should find them all
            //
            resourceList = NULL;
            status = STATUS_SUCCESS;
            goto GetOut;
        }

		//
		// Don't claim resources if the channel is disabled
		//
        channelState = PciIdeChannelEnabled (
										pdoExtension->ParentDeviceExtension,
										pdoExtension->ChannelNumber
										);
		if (channelState == ChannelDisabled) {

			resourceList = NULL;
			status = STATUS_SUCCESS;
			goto GetOut;
		}

        //
        // TEMP CODE for the time without a real PCI driver.
		// Actually pciidex should do this
        //

        resourceListSize = sizeof (CM_RESOURCE_LIST) - sizeof (CM_FULL_RESOURCE_DESCRIPTOR) +
                             FULL_RESOURCE_LIST_SIZE(3);   // primary IO (2) + IRQ
        resourceList = ExAllocatePool (PagedPool, resourceListSize);
        if (resourceList == NULL) {
            status = STATUS_NO_MEMORY;
            goto GetOut;
        }

        RtlZeroMemory(resourceList, resourceListSize);

        resourceList->Count = 1;
        fullResourceList = resourceList->List;
        partialResourceList = &(fullResourceList->PartialResourceList);
        partialResourceList->Count = 0;
        partialDescriptors = partialResourceList->PartialDescriptors;

        fullResourceList->InterfaceType = Isa;
        fullResourceList->BusNumber = 0;

        if (pdoExtension->ChannelNumber == 0) {

            if (!fdoExtension->PdoCmdRegResourceFound[0]) {

                partialDescriptors[partialResourceList->Count].Type                  = CmResourceTypePort;
                partialDescriptors[partialResourceList->Count].ShareDisposition      = CmResourceShareDeviceExclusive;
                partialDescriptors[partialResourceList->Count].Flags                 = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
                partialDescriptors[partialResourceList->Count].u.Port.Start.QuadPart = 0x1f0;
                partialDescriptors[partialResourceList->Count].u.Port.Length         = 8;

                partialResourceList->Count++;
            }

            if (!fdoExtension->PdoCtrlRegResourceFound[0]) {

                partialDescriptors[partialResourceList->Count].Type                  = CmResourceTypePort;
                partialDescriptors[partialResourceList->Count].ShareDisposition      = CmResourceShareDeviceExclusive;
                partialDescriptors[partialResourceList->Count].Flags                 = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
                partialDescriptors[partialResourceList->Count].u.Port.Start.QuadPart = 0x3f6;
                partialDescriptors[partialResourceList->Count].u.Port.Length         = 1;

                partialResourceList->Count++;
            }

            if (!fdoExtension->PdoInterruptResourceFound[0]) {

                partialDescriptors[partialResourceList->Count].Type                  = CmResourceTypeInterrupt;
                partialDescriptors[partialResourceList->Count].ShareDisposition      = CmResourceShareDeviceExclusive;
                partialDescriptors[partialResourceList->Count].Flags                 = CM_RESOURCE_INTERRUPT_LATCHED;
                partialDescriptors[partialResourceList->Count].u.Interrupt.Level     = 14;
                partialDescriptors[partialResourceList->Count].u.Interrupt.Vector    = 14;
                partialDescriptors[partialResourceList->Count].u.Interrupt.Affinity  = 0x1;  // ISSUE: 08/28/2000: To be looked into.

                partialResourceList->Count++;
            }

        } else { // if (pdoExtension->ChannelNumber == 1)

            if (!fdoExtension->PdoCmdRegResourceFound[1]) {

                partialDescriptors[partialResourceList->Count].Type                  = CmResourceTypePort;
                partialDescriptors[partialResourceList->Count].ShareDisposition      = CmResourceShareDeviceExclusive;
                partialDescriptors[partialResourceList->Count].Flags                 = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
                partialDescriptors[partialResourceList->Count].u.Port.Start.QuadPart = 0x170;
                partialDescriptors[partialResourceList->Count].u.Port.Length         = 8;

                partialResourceList->Count++;
            }

            if (!fdoExtension->PdoCtrlRegResourceFound[1]) {

                partialDescriptors[partialResourceList->Count].Type                  = CmResourceTypePort;
                partialDescriptors[partialResourceList->Count].ShareDisposition      = CmResourceShareDeviceExclusive;
                partialDescriptors[partialResourceList->Count].Flags                 = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
                partialDescriptors[partialResourceList->Count].u.Port.Start.QuadPart = 0x376;
                partialDescriptors[partialResourceList->Count].u.Port.Length         = 1;

                partialResourceList->Count++;
            }

            if (!fdoExtension->PdoInterruptResourceFound[1]) {

                partialDescriptors[partialResourceList->Count].Type                  = CmResourceTypeInterrupt;
                partialDescriptors[partialResourceList->Count].ShareDisposition      = CmResourceShareDeviceExclusive;
                partialDescriptors[partialResourceList->Count].Flags                 = CM_RESOURCE_INTERRUPT_LATCHED;
                partialDescriptors[partialResourceList->Count].u.Interrupt.Level     = 15;
                partialDescriptors[partialResourceList->Count].u.Interrupt.Vector    = 15;
                partialDescriptors[partialResourceList->Count].u.Interrupt.Affinity  = 0x1;  // ISSUE: 08/28/2000: To be Looked into

                partialResourceList->Count++;
            }
        }

        if (!partialResourceList->Count) {

            ExFreePool (resourceList);
            resourceList = NULL;
        }

        status = STATUS_SUCCESS;
    }

GetOut:

    Irp->IoStatus.Information = (ULONG_PTR) resourceList;
    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return status;
} // ChannelQueryResources

NTSTATUS
ChannelQueryResourceRequirements(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    PIO_STACK_LOCATION              thisIrpSp;
    PCHANPDO_EXTENSION              pdoExtension;
    PCTRLFDO_EXTENSION              fdoExtension;
    NTSTATUS                        status;

    PIO_RESOURCE_REQUIREMENTS_LIST  requirementsList;
    PIO_RESOURCE_LIST               resourceList;
    PIO_RESOURCE_DESCRIPTOR         resourceDescriptor;
    ULONG                           requirementsListSize;

    BOOLEAN                         reportIrq;
    IDE_CHANNEL_STATE 				channelState;

    PAGED_CODE();

    requirementsList = NULL;
    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        thisIrpSp = IoGetCurrentIrpStackLocation( Irp );
        fdoExtension = pdoExtension->ParentDeviceExtension;

        if (fdoExtension->NativeMode[pdoExtension->ChannelNumber]) {

            //
            // Don't make up resources for native mode controller
            // PCI bus driver should find them all
            //
            requirementsList = NULL;
            status = STATUS_SUCCESS;
            goto GetOut;
        }

        //
        // legacy controller
        //
        channelState = PciIdeChannelEnabled (
										pdoExtension->ParentDeviceExtension,
										pdoExtension->ChannelNumber
										);

		//
		// Don't claim resources if the channel is disabled
		//
        if (channelState == ChannelStateUnknown ) {

            if (pdoExtension->EmptyChannel) {

                reportIrq = FALSE;

            } else {

                reportIrq = TRUE;
            }
        } else if (channelState == ChannelDisabled) {

            requirementsList = NULL;
            status = STATUS_SUCCESS;
            goto GetOut;
		}

        //
        // TEMP CODE for the time without a real PCI driver.
		// pciidex should do this.
        //

        requirementsListSize = sizeof (IO_RESOURCE_REQUIREMENTS_LIST) +
                               sizeof (IO_RESOURCE_DESCRIPTOR) * (3 - 1);
        requirementsList = ExAllocatePool (PagedPool, requirementsListSize);
        if( requirementsList == NULL ){
            status = STATUS_NO_MEMORY;
            goto GetOut;
        }

        RtlZeroMemory(requirementsList, requirementsListSize);

        requirementsList->ListSize          = requirementsListSize;
        requirementsList->InterfaceType     = Isa;
        requirementsList->BusNumber         = 0;    // ISSUE: 08/30/2000
        requirementsList->SlotNumber        = 0;
        requirementsList->AlternativeLists  = 1;

        resourceList            = requirementsList->List;
        resourceList->Version   = 1;
        resourceList->Revision  = 1;
        resourceList->Count     = 0;

        resourceDescriptor = resourceList->Descriptors;

        if (pdoExtension->ChannelNumber == 0) {

            if (!fdoExtension->PdoCmdRegResourceFound[0]) {

                resourceDescriptor[resourceList->Count].Option           = IO_RESOURCE_PREFERRED;
                resourceDescriptor[resourceList->Count].Type             = CmResourceTypePort;
                resourceDescriptor[resourceList->Count].ShareDisposition = CmResourceShareDeviceExclusive;
                resourceDescriptor[resourceList->Count].Flags            = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
                resourceDescriptor[resourceList->Count].u.Port.Length    = 8;
                resourceDescriptor[resourceList->Count].u.Port.Alignment = 1;
                resourceDescriptor[resourceList->Count].u.Port.MinimumAddress.QuadPart = 0x1f0;
                resourceDescriptor[resourceList->Count].u.Port.MaximumAddress.QuadPart = 0x1f7;

                resourceList->Count++;
            }

            if (!fdoExtension->PdoCtrlRegResourceFound[0]) {

                resourceDescriptor[resourceList->Count].Option           = IO_RESOURCE_PREFERRED;
                resourceDescriptor[resourceList->Count].Type             = CmResourceTypePort;
                resourceDescriptor[resourceList->Count].ShareDisposition = CmResourceShareDeviceExclusive;
                resourceDescriptor[resourceList->Count].Flags            = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
                resourceDescriptor[resourceList->Count].u.Port.Length    = 1;
                resourceDescriptor[resourceList->Count].u.Port.Alignment = 1;
                resourceDescriptor[resourceList->Count].u.Port.MinimumAddress.QuadPart = 0x3f6;
                resourceDescriptor[resourceList->Count].u.Port.MaximumAddress.QuadPart = 0x3f6;

                resourceList->Count++;
            }

            if (!fdoExtension->PdoInterruptResourceFound[0] && reportIrq) {

                resourceDescriptor[resourceList->Count].Option           = IO_RESOURCE_PREFERRED;
                resourceDescriptor[resourceList->Count].Type             = CmResourceTypeInterrupt;
                resourceDescriptor[resourceList->Count].ShareDisposition = CmResourceShareDeviceExclusive;
                resourceDescriptor[resourceList->Count].Flags            = CM_RESOURCE_INTERRUPT_LATCHED;
                resourceDescriptor[resourceList->Count].u.Interrupt.MinimumVector = 0xe;
                resourceDescriptor[resourceList->Count].u.Interrupt.MaximumVector = 0xe;

                resourceList->Count++;
            }

        } else { // if (pdoExtension->ChannelNumber == 1)

            if (!fdoExtension->PdoCmdRegResourceFound[1]) {

                resourceDescriptor[resourceList->Count].Option           = IO_RESOURCE_PREFERRED;
                resourceDescriptor[resourceList->Count].Type             = CmResourceTypePort;
                resourceDescriptor[resourceList->Count].ShareDisposition = CmResourceShareDeviceExclusive;
                resourceDescriptor[resourceList->Count].Flags            = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
                resourceDescriptor[resourceList->Count].u.Port.Length    = 8;
                resourceDescriptor[resourceList->Count].u.Port.Alignment = 1;
                resourceDescriptor[resourceList->Count].u.Port.MinimumAddress.QuadPart = 0x170;
                resourceDescriptor[resourceList->Count].u.Port.MaximumAddress.QuadPart = 0x177;

                resourceList->Count++;
            }

            if (!fdoExtension->PdoCtrlRegResourceFound[1]) {

                resourceDescriptor[resourceList->Count].Option           = IO_RESOURCE_PREFERRED;
                resourceDescriptor[resourceList->Count].Type             = CmResourceTypePort;
                resourceDescriptor[resourceList->Count].ShareDisposition = CmResourceShareDeviceExclusive;
                resourceDescriptor[resourceList->Count].Flags            = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
                resourceDescriptor[resourceList->Count].u.Port.Length    = 1;
                resourceDescriptor[resourceList->Count].u.Port.Alignment = 1;
                resourceDescriptor[resourceList->Count].u.Port.MinimumAddress.QuadPart = 0x376;
                resourceDescriptor[resourceList->Count].u.Port.MaximumAddress.QuadPart = 0x376;

                resourceList->Count++;
            }

            if (!fdoExtension->PdoInterruptResourceFound[1] && reportIrq) {

                resourceDescriptor[resourceList->Count].Option           = IO_RESOURCE_PREFERRED;
                resourceDescriptor[resourceList->Count].Type             = CmResourceTypeInterrupt;
                resourceDescriptor[resourceList->Count].ShareDisposition = CmResourceShareDeviceExclusive;
                resourceDescriptor[resourceList->Count].Flags            = CM_RESOURCE_INTERRUPT_LATCHED;
                resourceDescriptor[resourceList->Count].u.Interrupt.MinimumVector = 0xf;
                resourceDescriptor[resourceList->Count].u.Interrupt.MaximumVector = 0xf;

                resourceList->Count++;
            }
        }

        if (!resourceList->Count) {

            ExFreePool (requirementsList);
            requirementsList = NULL;
        }

        status = STATUS_SUCCESS;
    }

GetOut:

    Irp->IoStatus.Information = (ULONG_PTR) requirementsList;
    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return status;
} // ChannelQueryResourceRequirements


NTSTATUS
ChannelInternalDeviceIoControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    PIO_STACK_LOCATION              thisIrpSp;
    PCHANPDO_EXTENSION              pdoExtension;
    NTSTATUS                        status;

    PAGED_CODE();

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        thisIrpSp    = IoGetCurrentIrpStackLocation(Irp);

        switch (thisIrpSp->Parameters.DeviceIoControl.IoControlCode) {

            //
            // TEMP CODE for the time without a real PCI driver.
			// pciidex knows about the resources.
            //
            case IOCTL_IDE_GET_RESOURCES_ALLOCATED:
                {
                PCTRLFDO_EXTENSION              fdoExtension;
                ULONG                           resourceListSize;
                PCM_RESOURCE_LIST               resourceList;
                PCM_FULL_RESOURCE_DESCRIPTOR    fullResourceList;
                PCM_PARTIAL_RESOURCE_LIST       partialResourceList;
                PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptors;
                ULONG                           channel;

                channel = pdoExtension->ChannelNumber;
                fdoExtension = pdoExtension->ParentDeviceExtension;

                resourceListSize = fdoExtension->PdoResourceListSize[channel];

                //
                // have the callee allocate the buffer. 
                //
                resourceList = (PCM_RESOURCE_LIST) Irp->AssociatedIrp.SystemBuffer;
                ASSERT(resourceList);

                RtlCopyMemory (
                    resourceList,
                    fdoExtension->PdoResourceList[channel],
                    resourceListSize);

                Irp->IoStatus.Information = resourceListSize;
                status = STATUS_SUCCESS;
                }
                break;

            default:
                DebugPrint ((1,
                             "PciIde, Channel PDO got Unknown IoControlCode 0x%x\n",
                             thisIrpSp->Parameters.DeviceIoControl.IoControlCode));
                status = STATUS_INVALID_PARAMETER;
                break;
        }
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest (Irp, IO_NO_INCREMENT);

    return status;
} // ChannelInternalDeviceIoControl


NTSTATUS
ChannelQueryText (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    PIO_STACK_LOCATION  thisIrpSp;
    PCHANPDO_EXTENSION  pdoExtension;
    PWCHAR              returnString;
    ANSI_STRING         ansiString;
    UNICODE_STRING      unicodeString;
    ULONG               stringLen;
    NTSTATUS            status;

    PAGED_CODE();

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        thisIrpSp    = IoGetCurrentIrpStackLocation (Irp);

        returnString = NULL;
        Irp->IoStatus.Information = 0;

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

            PMESSAGE_RESOURCE_ENTRY messageEntry;

            status = RtlFindMessage(pdoExtension->DriverObject->DriverStart,
                                    11,
                                    LANG_NEUTRAL,
                                    PCIIDEX_IDE_CHANNEL,
                                    &messageEntry);

            if (!NT_SUCCESS(status)) {

                returnString = NULL;

            } else {

                if (messageEntry->Flags & MESSAGE_RESOURCE_UNICODE) {

                    //
                    // Our caller wants a copy they can free, also we need to
                    // strip the trailing CR/LF.  The Length field of the
                    // message structure includes both the header and the
                    // actual text.
                    //
                    // Note: The message resource entry length will always be a
                    // multiple of 4 bytes in length.  The 2 byte null terminator
                    // could be in either the last or second last WCHAR position.
                    //

                    ULONG textLength;

                    textLength = messageEntry->Length -
                                 FIELD_OFFSET(MESSAGE_RESOURCE_ENTRY, Text) -
                                 2 * sizeof(WCHAR);

                    returnString = (PWCHAR)(messageEntry->Text);
                    if (returnString[textLength / sizeof(WCHAR)] == 0) {
                        textLength -= sizeof(WCHAR);
                    }

                    returnString = ExAllocatePool(PagedPool, textLength);

                    if (returnString) {

                        //
                        // Copy the text except for the CR/LF/NULL
                        //

                        textLength -= sizeof(WCHAR);
                        RtlCopyMemory(returnString, messageEntry->Text, textLength);

                        //
                        // New NULL terminator.
                        //

                        returnString[textLength / sizeof(WCHAR)] = 0;
                    }

                } else {

                    //
                    // RtlFindMessage returns a string?   Wierd.
                    //

                    ANSI_STRING    ansiDescription;
                    UNICODE_STRING unicodeDescription;

                    RtlInitAnsiString(&ansiDescription, messageEntry->Text);

                    //
                    // Strip CR/LF off the end of the string.
                    //

                    ansiDescription.Length -= 2;

                    //
                    // Turn it all into a unicode string so we can grab the buffer
                    // and return that to our caller.
                    //

                    status = RtlAnsiStringToUnicodeString(
                                 &unicodeDescription,
                                 &ansiDescription,
                                 TRUE
                                 );

                    returnString = unicodeDescription.Buffer;
                }
            }
        } else if (thisIrpSp->Parameters.QueryDeviceText.DeviceTextType == DeviceTextLocationInformation) {

            stringLen = 100;

            returnString = ExAllocatePool (
                               PagedPool,
                               stringLen
                               );

            if (returnString) {

                swprintf(returnString, L"%ws Channel",
                         ((pdoExtension->ChannelNumber == 0) ? L"Primary" :
                                                               L"Secondary"));

                RtlInitUnicodeString (&unicodeString, returnString);

                //
                // null terminate it
                //
                unicodeString.Buffer[unicodeString.Length/sizeof(WCHAR) + 0] = L'\0';
            }
        }

        Irp->IoStatus.Information = (ULONG_PTR) returnString;
        if (Irp->IoStatus.Information) {

            status = STATUS_SUCCESS;
        } else {

            //
            // return the original error code
            //
            status = Irp->IoStatus.Status;
        }
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return status;

} // ChannelQueryText


NTSTATUS
PciIdeChannelQueryInterface (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    PIO_STACK_LOCATION          thisIrpSp;
    PCHANPDO_EXTENSION          pdoExtension;
    NTSTATUS                    status;

    PAGED_CODE();

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        status = Irp->IoStatus.Status;
        thisIrpSp = IoGetCurrentIrpStackLocation( Irp );

        if (RtlEqualMemory(&GUID_PCIIDE_BUSMASTER_INTERFACE,
                thisIrpSp->Parameters.QueryInterface.InterfaceType,
                sizeof(GUID)) &&
            (thisIrpSp->Parameters.QueryInterface.Size >=
                sizeof(PCIIDE_BUSMASTER_INTERFACE))) {

            //
            // The query is for an busmaster interface
            //
            status = BmQueryInterface (
                         pdoExtension,
                         (PPCIIDE_BUSMASTER_INTERFACE) thisIrpSp->Parameters.QueryInterface.Interface
                         );

        } else if (RtlEqualMemory(&GUID_PCIIDE_SYNC_ACCESS_INTERFACE,
                       thisIrpSp->Parameters.QueryInterface.InterfaceType,
                       sizeof(GUID)) &&
                  (thisIrpSp->Parameters.QueryInterface.Size >=
                       sizeof(PCIIDE_SYNC_ACCESS_INTERFACE))) {

            //
            // The query is for dual ide channel sync access interface
            //
            status = PciIdeQuerySyncAccessInterface (
                         pdoExtension,
                         (PPCIIDE_SYNC_ACCESS_INTERFACE) thisIrpSp->Parameters.QueryInterface.Interface
                         );

        } else if (RtlEqualMemory(&GUID_PCIIDE_XFER_MODE_INTERFACE,
                       thisIrpSp->Parameters.QueryInterface.InterfaceType,
                       sizeof(GUID)) &&
                  (thisIrpSp->Parameters.QueryInterface.Size >=
                       sizeof(PCIIDE_XFER_MODE_INTERFACE))) {

            //
            // The query is for dual ide channel sync access interface
            //
            status = PciIdeChannelTransferModeInterface (
                         pdoExtension,
                         (PPCIIDE_XFER_MODE_INTERFACE) thisIrpSp->Parameters.QueryInterface.Interface
                         );

#ifdef ENABLE_NATIVE_MODE

        } else if (RtlEqualMemory(&GUID_PCIIDE_INTERRUPT_INTERFACE,
                       thisIrpSp->Parameters.QueryInterface.InterfaceType,
                       sizeof(GUID)) &&
                  (thisIrpSp->Parameters.QueryInterface.Size >=
                       sizeof(PCIIDE_INTERRUPT_INTERFACE))) {

            //
            // The query is for the channel interrupt interface
            //
            status = PciIdeChannelInterruptInterface (
                         pdoExtension,
                         (PPCIIDE_INTERRUPT_INTERFACE) thisIrpSp->Parameters.QueryInterface.Interface
                         );
#endif

        } else if (RtlEqualMemory(&GUID_PCIIDE_REQUEST_PROPER_RESOURCES,
                       thisIrpSp->Parameters.QueryInterface.InterfaceType,
                       sizeof(GUID)) &&
                  (thisIrpSp->Parameters.QueryInterface.Size >=
                       sizeof(PCIIDE_REQUEST_PROPER_RESOURCES))) {

            //
            // The query is for dual ide channel sync access interface
            //
            *((PCIIDE_REQUEST_PROPER_RESOURCES *) thisIrpSp->Parameters.QueryInterface.Interface) =
                PciIdeChannelRequestProperResources;
            status = STATUS_SUCCESS;
        }
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return status;
} // PciIdeChannelQueryInterface

#ifdef ENABLE_NATIVE_MODE

NTSTATUS
PciIdeInterruptControl (
	IN PVOID Context,
	IN ULONG DisConnect
	)
/*++
Description:

	Connects or disconnects the controller ISR. This intermediate function provides
	a clean interface to atapi. Since this is not a very frequently used function
	we can afford the extra call
	
Arguments :
	
	Context : Pdo extension
	Disconnect: 1 to disconnect and 0 to connect

	
Return Value:

	STATUS_SUCCESS : if the operation succeeds.	
	
--*/
{
	PCHANPDO_EXTENSION pdoExtension = Context;

	NTSTATUS status;

	//
	// Call the controller's interrupt control routine
	//
	status = ControllerInterruptControl(pdoExtension->ParentDeviceExtension,
										pdoExtension->ChannelNumber,
										DisConnect
										);

	return status;
}

NTSTATUS
PciIdeChannelInterruptInterface (
    IN PCHANPDO_EXTENSION PdoExtension,
    PPCIIDE_INTERRUPT_INTERFACE InterruptInterface
    )
{
	//
	// Return an interface only if we are in native mode.
	// Saves a few function calls on non native mode controllers
	//
	if (IsNativeMode(PdoExtension->ParentDeviceExtension)) {

		InterruptInterface->Context = PdoExtension;
		InterruptInterface->PciIdeInterruptControl = PciIdeInterruptControl;

		DebugPrint((1, "PciIdex: returing interrupt interface for channel %x\n", 
					PdoExtension->ChannelNumber));
	}

	return STATUS_SUCCESS;
}
#endif

NTSTATUS
PciIdeChannelTransferModeInterface (
    IN PCHANPDO_EXTENSION PdoExtension,
    PPCIIDE_XFER_MODE_INTERFACE XferMode
    )
{
    XferMode->TransferModeSelect = PciIdeChannelTransferModeSelect;
    XferMode->TransferModeTimingTable = PdoExtension->ParentDeviceExtension->
                                                                TransferModeTimingTable;
    XferMode->TransferModeTableLength = PdoExtension->ParentDeviceExtension->
                                                                TransferModeTableLength;
    XferMode->Context = PdoExtension;
    XferMode->VendorSpecificDeviceExtension=PdoExtension->
                    ParentDeviceExtension->VendorSpecificDeviceEntension;


    XferMode->UdmaModesSupported = PdoExtension->
                                    ParentDeviceExtension->
                                        ControllerProperties.PciIdeUdmaModesSupported;
    //
    //NULL is ok. checked in the IdePortDispatchRoutine
    //
    XferMode->UseDma = PdoExtension->
                            ParentDeviceExtension->
                                ControllerProperties.PciIdeUseDma;

    if (PdoExtension->
            ParentDeviceExtension->
                ControllerProperties.PciIdeTransferModeSelect) {

        //
        // Looks like the miniport fully support timing register programming
        //

        XferMode->SupportLevel = PciIdeFullXferModeSupport;

    } else {

        //
        // Looks like the miniport doens't support timing register programming
        //
        XferMode->SupportLevel = PciIdeBasicXferModeSupport;
    }

    //
    // This function can't fail
    //
    return STATUS_SUCCESS;
} // PciIdeChannelTransferModeInterface

NTSTATUS
PciIdeChannelTransferModeSelect (
    IN PCHANPDO_EXTENSION PdoExtension,
    PPCIIDE_TRANSFER_MODE_SELECT XferMode
    )
{
    ULONG i;
    NTSTATUS status;
    UCHAR    bmRawStatus;
    struct {
        USHORT  VendorID;
        USHORT  DeviceID;
    } pciId;

    //
    // check the registry for bus master mode
    // and overwrite the current if necessary
    //
    // if DMADetection = 0, clear current dma mode
    // if DMADetection = 1, set current mode
    // if DMADetection = 2, clear all current mode

    if (PdoExtension->DmaDetectionLevel == DdlPioOnly) {

        bmRawStatus = 0;

        for (i=0; i<MAX_IDE_DEVICE * MAX_IDE_LINE; i++) {

            XferMode->DeviceTransferModeSupported[i] &= PIO_SUPPORT;
            XferMode->DeviceTransferModeCurrent[i] &= PIO_SUPPORT;
        }

    } else if (PdoExtension->DmaDetectionLevel == DdlFirmwareOk) {

        if (PdoExtension->BmRegister) {

            //
            // get the firmware ok bits
            // the current value seems to be 0??
			//
            bmRawStatus = PdoExtension->BootBmStatus;

        } else {

            bmRawStatus = 0;
        }

    } else if (PdoExtension->DmaDetectionLevel == DdlAlways) {

        if (PdoExtension->BmRegister) {

            //
            // fake the firmware ok bits
            //
            bmRawStatus = BUSMASTER_DEVICE0_DMA_OK | BUSMASTER_DEVICE1_DMA_OK;

        } else {

            bmRawStatus = 0;
        }

    } else {

        bmRawStatus = 0;
    }

    //
    // in case there is no miniport support
    //
    status = STATUS_UNSUCCESSFUL;

    if (PdoExtension->DmaDetectionLevel != DdlPioOnly) {

        //
        // set up the channel number since the caller (atapi)
        // doesn't know how.
        //
        XferMode->Channel = PdoExtension->ChannelNumber;

        //
        // This decides whether UDMA modes > 2 should be supported or not
        // Currently impacts only the intel chipsets
        //
        XferMode->EnableUDMA66 = PdoExtension->ParentDeviceExtension->EnableUDMA66;

        if (PdoExtension->
                ParentDeviceExtension->
                    ControllerProperties.PciIdeTransferModeSelect) {

            status = (*PdoExtension->ParentDeviceExtension->ControllerProperties.PciIdeTransferModeSelect) (
                         PdoExtension->ParentDeviceExtension->VendorSpecificDeviceEntension,
                         XferMode
                         );
        }
    }

    DebugPrint((1, "Select in PCIIDEX: RawStatus=%x, current[0]=%x, current[1]=%x\n",
                bmRawStatus,
                XferMode->DeviceTransferModeCurrent[0],
                XferMode->DeviceTransferModeCurrent[1]));

    if (!NT_SUCCESS(status)) {

        status = STATUS_SUCCESS;

        if ((bmRawStatus & BUSMASTER_DEVICE0_DMA_OK) == 0) {

            XferMode->DeviceTransferModeSelected[0] = XferMode->DeviceTransferModeCurrent[0] & PIO_SUPPORT;
        } else {

            XferMode->DeviceTransferModeSelected[0] = XferMode->DeviceTransferModeCurrent[0];
        }

        if ((bmRawStatus & BUSMASTER_DEVICE1_DMA_OK) == 0) {

            XferMode->DeviceTransferModeSelected[1] = XferMode->DeviceTransferModeCurrent[1] & PIO_SUPPORT;
        } else {

            XferMode->DeviceTransferModeSelected[1] = XferMode->DeviceTransferModeCurrent[1];
        }

        for (i=0; i<MAX_IDE_DEVICE; i++) {

            DebugPrint((1, "Select in PCIIDEX: xfermode[%d]=%x\n",i,
                PdoExtension->ParentDeviceExtension->ControllerProperties.
                    SupportedTransferMode[PdoExtension->ChannelNumber][i]));


            if ((PdoExtension->ParentDeviceExtension->ControllerProperties.DefaultPIO == 1) && 
                (IS_DEFAULT(XferMode->UserChoiceTransferMode[i]))) {
                XferMode->DeviceTransferModeSelected[i] &= PIO_SUPPORT;
            }
            else  {
                XferMode->DeviceTransferModeSelected[i] &=
                    PdoExtension->ParentDeviceExtension->ControllerProperties.
                        SupportedTransferMode[PdoExtension->ChannelNumber][i];
            }
        }
    }

    return status;

} // PciIdeChannelTransferModeSelect

NTSTATUS
ChannelQueryDeviceRelations (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    PIO_STACK_LOCATION  thisIrpSp;
    PDEVICE_RELATIONS   deviceRelations;
    NTSTATUS            status;

    PAGED_CODE();

    thisIrpSp = IoGetCurrentIrpStackLocation( Irp );

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

        case TargetDeviceRelation:

            deviceRelations = ExAllocatePool (NonPagedPool, sizeof(*deviceRelations));

            if (deviceRelations != NULL) {

                deviceRelations->Count = 1;
                deviceRelations->Objects[0] = DeviceObject;

                ObReferenceObjectByPointer(DeviceObject,
                                           0,
                                           0,
                                           KernelMode);

                Irp->IoStatus.Status = STATUS_SUCCESS;
                Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
            } else {

                Irp->IoStatus.Status = STATUS_NO_MEMORY;
                Irp->IoStatus.Information = 0;
            }
            break;
    }

    status = Irp->IoStatus.Status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return status;
} // ChannelQueryDeviceRelations

NTSTATUS
ChannelUsageNotification (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    NTSTATUS           status;
    PCHANPDO_EXTENSION pdoExtension;
    PIO_STACK_LOCATION irpSp;
    PDEVICE_OBJECT targetDeviceObject;
    IO_STATUS_BLOCK ioStatus;
    PULONG deviceUsageCount;

    PAGED_CODE();

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension == NULL) {

        status = STATUS_NO_SUCH_DEVICE;

    } else {

        irpSp = IoGetCurrentIrpStackLocation(Irp);

        if (irpSp->Parameters.UsageNotification.Type == DeviceUsageTypePaging) {

            //
            // Adjust the paging path count for this device.
            //
            deviceUsageCount = &pdoExtension->PagingPathCount;

            //
            // changing device state
            //
            SETMASK (pdoExtension->PnPDeviceState, PNP_DEVICE_NOT_DISABLEABLE);
            IoInvalidateDeviceState(pdoExtension->DeviceObject);

        } else if (irpSp->Parameters.UsageNotification.Type == DeviceUsageTypeHibernation) {

            //
            // Adjust the paging path count for this device.
            //
            deviceUsageCount = &pdoExtension->HiberPathCount;

        } else if (irpSp->Parameters.UsageNotification.Type == DeviceUsageTypeDumpFile) {

            //
            // Adjust the paging path count for this device.
            //
            deviceUsageCount = &pdoExtension->CrashDumpPathCount;

        } else {

            deviceUsageCount = NULL;
            DebugPrint ((0,
                         "PCIIDEX: Unknown IRP_MN_DEVICE_USAGE_NOTIFICATION type: 0x%x\n",
                         irpSp->Parameters.UsageNotification.Type));
        }

        //
        // get the top of parent's device stack
        //
        targetDeviceObject = IoGetAttachedDeviceReference(
                                 pdoExtension->
                                     ParentDeviceExtension->
                                         DeviceObject);

        ioStatus.Status = STATUS_NOT_SUPPORTED;
        status = PciIdeXSyncSendIrp (targetDeviceObject, irpSp, &ioStatus);

        ObDereferenceObject (targetDeviceObject);

        if (NT_SUCCESS(status)) {

            if (deviceUsageCount) {

                IoAdjustPagingPathCount (
                    deviceUsageCount,
                    irpSp->Parameters.UsageNotification.InPath
                    );
            }
        }
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return status;

} // ChannelUsageNotification

NTSTATUS
ChannelQueryPnPDeviceState (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    NTSTATUS status;
    PCHANPDO_EXTENSION pdoExtension;

    pdoExtension = ChannelGetPdoExtension(DeviceObject);

    if (pdoExtension) {

        PPNP_DEVICE_STATE deviceState;

        DebugPrint((2, "QUERY_DEVICE_STATE for PDOE 0x%x\n", pdoExtension));

        deviceState = (PPNP_DEVICE_STATE) &Irp->IoStatus.Information;
        SETMASK((*deviceState), pdoExtension->PnPDeviceState);

        CLRMASK (pdoExtension->PnPDeviceState, PNP_DEVICE_FAILED | PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED);

        status = STATUS_SUCCESS;

    } else {

        status = STATUS_DEVICE_DOES_NOT_EXIST;
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return status;
} // ChannelQueryPnPDeviceState


VOID
PciIdeChannelRequestProperResources(
    IN PDEVICE_OBJECT DeviceObject
    )
{
    PCHANPDO_EXTENSION pdoExtension;

    //
    // the FDO thinks the channel is not empty
    // anymore
    //
    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (pdoExtension) {
        pdoExtension->EmptyChannel = FALSE;
        SETMASK (pdoExtension->PnPDeviceState, PNP_DEVICE_FAILED | PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED);
        IoInvalidateDeviceState (DeviceObject);
    }
}

NTSTATUS
ChannelFilterResourceRequirements (
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    NTSTATUS status;
    PCHANPDO_EXTENSION pdoExtension;
    PIO_STACK_LOCATION thisIrpSp;
    ULONG             i, j;

    PIO_RESOURCE_REQUIREMENTS_LIST  requirementsListIn = NULL;
    PIO_RESOURCE_LIST               resourceListIn;
    PIO_RESOURCE_DESCRIPTOR         resourceDescriptorIn;

    PIO_RESOURCE_LIST               resourceListOut;
    PIO_RESOURCE_DESCRIPTOR         resourceDescriptorOut;

    ULONG newCount;

    PAGED_CODE();

    status = STATUS_NOT_SUPPORTED;

    //
    // the value will stay NULL if no filtering required
    //

#ifdef IDE_FILTER_PROMISE_TECH_RESOURCES
    if (NT_SUCCESS(ChannelFilterPromiseTechResourceRequirements (DeviceObject, Irp))) {
        goto getout;
    }
#endif // IDE_FILTER_PROMISE_TECH_RESOURCES

    pdoExtension = ChannelGetPdoExtension(DeviceObject);
    if (!pdoExtension) {
        goto getout;
    }


    //
    // filter out irq only if the channel is emtpy
    //
    if (!pdoExtension->EmptyChannel) {

        goto getout;
    }

    if (NT_SUCCESS(Irp->IoStatus.Status)) {
        //
        // already filtered.
        //
        requirementsListIn = (PIO_RESOURCE_REQUIREMENTS_LIST) Irp->IoStatus.Information;
    } else {
        thisIrpSp = IoGetCurrentIrpStackLocation(Irp);
        requirementsListIn = thisIrpSp->Parameters.FilterResourceRequirements.IoResourceRequirementList;
    }

    if (requirementsListIn == NULL) {
        goto getout;
    }

    if (requirementsListIn->AlternativeLists == 0) {
        goto getout;
    }

    resourceListIn = requirementsListIn->List;
    resourceListOut = resourceListIn;
    for (j=0; j<requirementsListIn->AlternativeLists; j++) {

        ULONG resCount;
        resourceDescriptorIn = resourceListIn->Descriptors;

        RtlMoveMemory (
           resourceListOut,
           resourceListIn,
           FIELD_OFFSET(IO_RESOURCE_LIST, Descriptors));
        resourceDescriptorOut = resourceListOut->Descriptors;

        resCount = resourceListIn->Count;
        for (i=newCount=0; i<resCount; i++) {

            if (resourceDescriptorIn[i].Type != CmResourceTypeInterrupt) {
                resourceDescriptorOut[newCount] = resourceDescriptorIn[i];
                newCount++;
            } else {
                status = STATUS_SUCCESS;
            }
        }
        resourceListIn = (PIO_RESOURCE_LIST) (resourceDescriptorIn + resCount);
        resourceListOut->Count = newCount;
        resourceListOut = (PIO_RESOURCE_LIST) (resourceDescriptorOut + newCount);
    }


getout:

    if (status != STATUS_NOT_SUPPORTED) {
        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = (ULONG_PTR) requirementsListIn;
    } else {
        status = Irp->IoStatus.Status;
    }
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return status;
}

