www.pudn.com > PCIÇý¶¯ÊµÀý.zip > PCI_SAMPLE.C


/////////////////////////////////////////////////////////////////////////////// 
// 
//    (C) Copyright 1995 - 1997 OSR Open Systems Resources, Inc. 
//    All Rights Reserved 
// 
//    This sofware is supplied for instructional purposes only. 
// 
//    OSR Open Systems Resources, Inc. (OSR) expressly disclaims any warranty 
//    for this software.  THIS SOFTWARE IS PROVIDED  "AS IS" WITHOUT WARRANTY 
//    OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, 
//    THE IMPLIED WARRANTIES OF MECHANTABILITY OR FITNESS FOR A PARTICULAR 
//    PURPOSE.  THE ENTIRE RISK ARISING FROM THE USE OF THIS SOFTWARE REMAINS 
//    WITH YOU.  OSR's entire liability and your exclusive remedy shall not 
//    exceed the price paid for this material.  In no event shall OSR or its 
//    suppliers be liable for any damages whatsoever (including, without 
//    limitation, damages for loss of business profit, business interruption, 
//    loss of business information, or any other pecuniary loss) arising out 
//    of the use or inability to use this software, even if OSR has been 
//    advised of the possibility of such damages.  Because some states/ 
//    jurisdictions do not allow the exclusion or limitation of liability for 
//    consequential or incidental damages, the above limitation may not apply 
//    to you. 
// 
//    OSR Open Systems Resources, Inc. 
//    105 Route 101A Suite 19 
//    Amherst, NH 03031  (603) 595-6500 FAX: (603) 595-6503 
//    email bugs to: bugs@osr.com 
// 
// 
//    This driver is the example Busmaster DMA device driver that 
//    accompanies the book Windows NT Device Driver Development, by 
//    Peter Viscarola and W. Anthony Mason, (c) 1998 OSR Open Systems 
//    Resources, Inc. and published by MacMillan Technical Publishing 
//    ISBN 1578700582.   
// 
//    MODULE: 
// 
//        PCI_SAMPLE.C 
// 
//    ABSTRACT: 
// 
//      This file contains the initial entry point for the OSR Sample 
//      PCI Busmaster DMA device driver for the AMCC 5933 chip. 
// 
//    AUTHOR(S): 
// 
//        OSR Open Systems Resources, Inc. 
//  
//    REVISION:    
// 
//          V1.1    Fix to typo in OsrWrite() to correctly set cancel 
//                  routine to NULL when completing a request that's 
//                  been cancelled very early in its processing. 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
 
//#include  
#include "osr-pci.h" 
 
// 
// Forward Declarations 
// 
NTSTATUS 
DriverEntry(PDRIVER_OBJECT DriverObj, PUNICODE_STRING RegistryPath); 
static VOID OsrUnload(PDRIVER_OBJECT DriverObject); 
static VOID OsrReturnPool(PPCI_COMMON_CONFIG  configInfo, PDEVICE_DESCRIPTION 
            deviceDescription,  PCM_RESOURCE_LIST resources); 
 
#if DBG 
static VOID OsrPrintResourceList(PCM_RESOURCE_LIST); 
static VOID OsrPrintConfig(PPCI_COMMON_CONFIG  configInfo); 
#endif 
 
 
// 
// The following pragma allows the DriverEntry code to be discarded once 
// initialization is completed 
// 
#pragma alloc_text(INIT,DriverEntry) 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  DriverEntry 
// 
//      This routine is called by NT when the driver is first loaded.  It is the 
//    responsibility of this routine to find it's device and create whatever 
//    device objects it needs. 
// 
//  INPUTS: 
// 
//      DriverObj - Address of the DRIVER_OBJECT created by NT for this driver. 
// 
//      RegistryPath - UNICODE_STRING which represents this drivers KEY in the 
//                   Registry.   
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      STATUS_SUCCESS. Otherwise an error indicating why the driver could not 
//                    Load. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS 
DriverEntry(PDRIVER_OBJECT DriverObj, PUNICODE_STRING RegistryPath) 
{ 
    NTSTATUS code; 
    PPCI_COMMON_CONFIG  configInfo = NULL; 
    ULONG busNumber; 
    ULONG deviceNumber; 
    ULONG AddressSpace; 
    PCI_SLOT_NUMBER slotNumber; 
    ULONG length; 
    BOOLEAN moreBuses; 
    BOOLEAN adapterFound; 
    ULONG index; 
    ULONG addressSpace; 
    PHYSICAL_ADDRESS address; 
    POSR_DEVICE_EXT devExt; 
    PDEVICE_OBJECT devObj; 
    UNICODE_STRING devName, linkName; 
    PDEVICE_DESCRIPTION deviceDescription = NULL; 
    PCM_RESOURCE_LIST resources = NULL; 
    ULONG interruptLevel; 
    ULONG interruptVector; 
    ULONG mappedSystemVector; 
    PHYSICAL_ADDRESS portStart; 
    ULONG portLength; 
    KIRQL irql; 
    KAFFINITY affinity; 
     
    DbgPrint("\nOSR PCI Sample Driver -- Compiled %s %s\n",__DATE__, __TIME__); 
    DbgPrint("(c) 1997 OSR Open Systems Resources, Inc.\n\n"); 
 
    // 
    // Establish dispatch entry points for the functions we support 
    // 
 
    DriverObj->MajorFunction[IRP_MJ_CREATE]         =  OsrCreateClose; 
    DriverObj->MajorFunction[IRP_MJ_CLOSE]          =  OsrCreateClose; 
 
    DriverObj->MajorFunction[IRP_MJ_READ]           =  OsrRead; 
    DriverObj->MajorFunction[IRP_MJ_WRITE]          =  OsrWrite; 
    DriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] =  OsrDeviceControl; 
     
    // 
    // Unload function 
    // 
    DriverObj->DriverUnload = OsrUnload; 
 
     
    // 
    // Allocate space for a configuration information structure 
    // 
    configInfo = ExAllocatePoolWithTag(PagedPool, 
                                       sizeof(PCI_COMMON_CONFIG), 
                                       'pRSO'); 
 
    if (!configInfo) { 
 
        // 
        // Clean up the mess 
        // 
        OsrReturnPool(configInfo, deviceDescription, resources); 
 
        // 
        // Indicate load failure to the I/O manager 
        // 
        return(STATUS_INSUFFICIENT_RESOURCES); 
    } 
 
    // 
    // Search for our device on all the PCI busses in the system 
    // 
    // We do this by ennumerating the configuration information for each 
    // slot on each PCI bus in the system, until we find a device with 
    // our Vendor ID and Device ID. 
    // 
    // Since this sample driver supports only a single card, we stop looking as 
    // soon as we find one device.  Drivers that support multiple cards would 
    // ennumerate all the busses and slots, finding as many devices as exist. 
    // 
    // Since our device is not a PCI "multifunction" device, we don't search 
    // each function in each slot. 
    // 
    // 
    adapterFound = FALSE; 
    moreBuses = TRUE; 
 
    for (busNumber = 0; !adapterFound && moreBuses; busNumber++) { 
     
        // 
        // Ennumerate all the devices on this bus 
        // 
        for (deviceNumber = 0;  
             !adapterFound && deviceNumber < PCI_MAX_DEVICES; 
             deviceNumber++)  { 
 
            // 
            // For PCI buses, the logical slot number is a PCI_SLOT_NUMBER 
            // structure, comprising a combination of the device number and the 
            // function number on the card.  The Reserved section MUST be set 
            // to zero. 
            // 
            // Note that since we're not a multifunction device, we only look 
            // at FunctionNumber 0 in each slot. 
            // 
            slotNumber.u.bits.Reserved = 0; 
            slotNumber.u.bits.DeviceNumber = deviceNumber; 
            slotNumber.u.bits.FunctionNumber = 0; 
             
            // 
            // Get the configuration space for the adapter in this slot 
            // 
            length = HalGetBusData(PCIConfiguration, 
                                   busNumber, 
                                   slotNumber.u.AsULONG, 
                                   configInfo, 
                                   sizeof(PCI_COMMON_CONFIG) ); 
 
            // 
            // A return value of zero indicates no more PCI buses on the system 
            //                        
            if (length == 0) { 
#if DBG 
                DbgPrint("Reached end of PCI bus list\n"); 
#endif 
                moreBuses = FALSE; 
                break; 
            } 
 
            // 
            // If there's nothing in this slot, PCI_INVALID_VENDORID is returned 
            // as the vendor ID.  If this is the case, just continue running 
            // the bus. 
            // 
            if (configInfo->VendorID == PCI_INVALID_VENDORID)  { 
                 
                continue; 
            } 
 
            // 
            // Dump out information about the device found. 
            // 
#if DBG 
            DbgPrint("Found a PCI device at Bus %d. Device %d.\n", 
                          busNumber, 
                          deviceNumber); 
 
            DbgPrint("Vendor id = 0x%0x, Device id = 0x%0x\n",  
                          (int)configInfo->VendorID, 
                          (int)configInfo->DeviceID); 
#endif   // DBG 
             
            // 
            // Is this the PCI device for which we've been searching?  It is  
            // if both the vendor and device ID match 
            // 
            if ( (configInfo->VendorID == OSR_PCI_VID) && 
                  (configInfo->DeviceID == OSR_PCI_DID) )  { 
                     
                ULONG index; 
                 
                // 
                // FOUND IT!  No need to keep searching.  We support only 
                // one device. 
                // 
                adapterFound = TRUE; 
 
#if DBG 
                DbgPrint("*** Found OUR PCI ADAPTER ***"); 
                 
                // 
                // Just for the sake of interest, let's dump some of the 
                // configuration information for our device. 
                // 
                OsrPrintConfig(configInfo); 
#endif 
            } 
 
        } 
 
    } 
 
    // 
    // If we didn't find our adapter, we bail out here, thereby aborting 
    // the driver load process.  The I/O Manager will delete our driver 
    // object. 
    // 
    if (!adapterFound) { 
 
#if DBG 
        DbgPrint("OSR PCI Sample device was not found!? -- EXITING.\n"); 
#endif 
        // 
        // Clean up the mess 
        // 
        OsrReturnPool(configInfo, deviceDescription, resources); 
 
        // 
        // Indicate load failure to the I/O manager; driver image is deleted... 
        // 
        return(STATUS_NO_SUCH_DEVICE); 
    } 
     
    // 
    // *************************************************************** 
    // 
    // Hooray!  We've found our device! 
    // 
    // Lets create a device object for it 
    // 
 
    // 
    // Initialize the UNICODE device name.  This will be the "native NT" name 
    // for our device. 
    // 
    RtlInitUnicodeString(&devName, L"\\Device\\OSRPCI"); 
 
    // 
    // Ask the I/O Manager to create the device object and 
    // device extension 
    // 
    code = IoCreateDevice(DriverObj, 
                          sizeof(OSR_DEVICE_EXT), 
                          &devName, 
                          FILE_DEVICE_OSR, 
                          0,        
                          FALSE, 
                          &devObj); 
 
         
    if(!NT_SUCCESS(code))  { 
         
#if DBG 
        DbgPrint("IoCreateDevice failed.  Status = 0x%0x\n", code); 
#endif 
        return(STATUS_UNSUCCESSFUL); 
    }     
 
    // 
    // Get a pointer to our device extension 
    // 
    devExt = (POSR_DEVICE_EXT)devObj->DeviceExtension; 
     
    // 
    // Zero out the device extension.  While not strictly necessary 
    // (the documentation says the device extension is zeroed) it's 
    // better to be safe. 
    // 
    RtlZeroMemory(devExt, sizeof(OSR_DEVICE_EXT)); 
 
    // 
    // Save the device object pointer away for future reference 
    // 
    devExt->DeviceObject = devObj; 
 
    // 
    // Store the bus and slot number away for the device we found 
    // 
    devExt->BusNumber = busNumber-1; 
    devExt->SlotNumber = slotNumber; 
 
    // 
    // Next, make the device accessible from user-mode applications. 
    // Note that this name can be either the same or different from 
    // the native "kernel mode" name of the device object, given above. 
    // 
    RtlInitUnicodeString(&linkName, L"\\??\\OSRPCI"); 
 
    code = IoCreateSymbolicLink(&linkName, &devName); 
     
    if (!NT_SUCCESS(code)) 
    { 
 
#if DBG 
        DbgPrint("IoCreateSymbolicLink failed.  Status = 0x%x\n", code); 
#endif 
        // 
        // Clean up the mess 
        // 
        OsrReturnPool(configInfo, deviceDescription, resources); 
 
        OsrUnload(DriverObj); 
 
        // 
        // Indicate load failure to the I/O manager; driver image is deleted... 
        // 
        return(code); 
    } 
 
 
    // 
    // Initialize our IRP queues 
    // 
    InitializeListHead(&devExt->ReadQueue); 
    InitializeListHead(&devExt->WriteQueue); 
     
    // 
    // Initialize our Spin Locks 
    // 
    KeInitializeSpinLock(&devExt->ReadQueueLock); 
    KeInitializeSpinLock(&devExt->WriteQueueLock); 
     
    // 
    // Ask the I/O Manager to use describe user read/write buffers using MDLs 
    // 
    devObj->Flags |= DO_DIRECT_IO; 
 
    // 
    // Next, get the HAL to tell us about the resources the device will use.  
    // These resources include ports, shared memory regions, interrupts, and 
    // the like.  The resources will be resevered for us in the registry (so 
    // we do not have to call either IoReportResourceUsage or IoAssignResources). 
    // 
    code = HalAssignSlotResources(RegistryPath, 
                                  NULL, 
                                  DriverObj, 
                                  devObj, 
                                  PCIBus, 
                                  devExt->BusNumber, 
                                  devExt->SlotNumber.u.AsULONG, 
                                  &resources); 
 
    // 
    // On return from this call, all resources are identified and assigned for 
    // use by our device. 
    // 
    if (!NT_SUCCESS(code)) { 
 
        // 
        // log an appropriate error string. 
        // 
#if DBG 
        DbgPrint("HalAssignSlotResourced failed!  Status = 0x%0x", code); 
#endif         
        // 
        // Clean up the mess 
        // 
        OsrReturnPool(configInfo, deviceDescription, resources); 
 
        OsrUnload(DriverObj); 
 
        // 
        // Indicate load failure to the I/O manager 
        // 
        return(code); 
    } 
 
#if DBG 
    // 
    // For curiosity and debugging purposes, display the resources that were 
    // allocated for our use. 
    // 
    OsrPrintResourceList(resources); 
 
    portStart.LowPart = 0; 
    portLength = 0; 
    interruptLevel = 0; 
    interruptVector = 0; 
 
#endif 
     
    // 
    // Decode the returned resources 
    // 
    // For our device, we know to expect an interrupt resource, and ONE set 
    // I/O space port resources.  We expect, and we attempt to handle, no 
    // other resources. 
    // 
    for (index = 0; index < resources->Count; index++)  { 
        ULONG index2; 
         
        for (index2 = 0; 
             index2 < resources->List[index].PartialResourceList.Count; 
             index2++) { 
 
            PCM_PARTIAL_RESOURCE_DESCRIPTOR prd; 
             
            prd = &resources->List[index].PartialResourceList.PartialDescriptors[index2]; 
 
            switch (prd->Type) { 
                 
                case CmResourceTypePort: 
 
                    // 
                    // Our port resources are in I/O space.  And our port 
                    // space is 64. bytes long. 
                    // 
                    ASSERT(prd->Flags == CM_RESOURCE_PORT_IO); 
                    ASSERT(prd->u.Port.Length == 64); 
 
                    portStart.HighPart = 0; 
                    portStart.LowPart  =  prd->u.Port.Start.LowPart; 
                    portLength =  prd->u.Port.Length; 
                    break; 
 
                case CmResourceTypeInterrupt: 
 
                    // 
                    // PCI interrupts are level sensitive 
                    // 
                    ASSERT(prd->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE); 
 
                    interruptLevel = prd->u.Interrupt.Level; 
                    interruptVector = prd->u.Interrupt.Vector; 
                    break; 
 
#if DBG 
                case CmResourceTypeMemory: 
                    DbgPrint("...Unexpected memory resource!\n"); 
                    break; 
 
                case CmResourceTypeDma: 
                    DbgPrint("...Unexpected DMA resource!\n"); 
                    break; 
 
                case CmResourceTypeDeviceSpecific: 
                    DbgPrint("...Unexpected device specific resource!\n"); 
                    break; 
#endif 
                default: 
                    DbgPrint("Unexpected and unknown resource type\n"); 
                    break; 
            } 
             
        } 
 
    } 
 
    // 
    // We NEED the interrupt info AND one port 
    // 
    ASSERT(interruptLevel && interruptVector && portStart.LowPart && portLength); 
 
    // 
    // Indciate that the resigsters are in port space on this card 
    // 
    addressSpace = 0x01; 
 
    // 
    // Get the HAL to translate our bus-relative port base address, to an 
    // unambiguous address to be used for device access.  If we're returned 
    // FALSE, the call failed. 
    // 
    if (!HalTranslateBusAddress(PCIBus, 
                               devExt->BusNumber, 
                               portStart, 
                               &addressSpace, 
                               &address))  { 
        // 
        // Print an appropriate error string. 
        // 
#if DBG 
        DbgPrint("HalAssignSlotResourced failed\n"); 
#endif 
 
        // 
        // Clean up the mess 
        // 
        OsrReturnPool(configInfo, deviceDescription, resources); 
 
        OsrUnload(DriverObj); 
 
        // 
        // 
        return(STATUS_UNSUCCESSFUL); 
    } 
     
    // 
    // If this card's I/O space registers actually appear in memory space on 
    // this processor, we need to map them with some kernel virtual addresses 
    // so we can access them. This may be the case on, for example, some RISC 
    // processors. NOTE HOWEVER, that regardless of the addressSpace returned, 
    // since the registers on the card are in PORT I/O space, we will ALWAYS 
    // access them in the driver using WRITE_PORT_xxx and READ_PORT_xxx. 
    // 
    if (addressSpace == 0x0) { 
        ULONG lengthInBytes; 
 
#if DBG 
        DbgPrint("Address space for port is MEMORY\n"); 
#endif 
 
        lengthInBytes = 
           resources->List[0].PartialResourceList.PartialDescriptors[1].u.Port.Length; 
 
        // 
        // Maps a potentially 64 bit physical address to a 32 bit virtual address. 
        // 
        devExt->AmccBaseRegisterAddress = MmMapIoSpace(address, 
                                                       lengthInBytes, 
                                                       FALSE); 
 
    } else { 
 
        ASSERT(addressSpace == 0x01); 
 
#if DBG 
        DbgPrint("Address space is port\n"); 
#endif 
        // 
        // Port I/O space is only 16 bits long, so we can grab the low 32 bits 
        // of the address only. 
        // 
        devExt->AmccBaseRegisterAddress = (PULONG)address.LowPart; 
 
    } 
     
#if DBG 
    DbgPrint("OSRPCI Registers start at 0x%x", devExt->AmccBaseRegisterAddress); 
#endif 
    // 
    // Register our DpcforISR routine.  This is the routine which will 
    // be used to complete our interrupt processing. 
    // 
    IoInitializeDpcRequest(devObj, OsrDpcForIsr); 
     
    // 
    // Like port and memory addresses, interrupts also have to be "translated" 
    // from bus-specific values. 
    // 
    mappedSystemVector = HalGetInterruptVector(PCIBus, 
                                                devExt->BusNumber, 
                                                interruptLevel, 
                                                interruptVector, 
                                                &irql, 
                                                &affinity); 
#if DBG 
    DbgPrint("HalGetInterruptVector returned vector 0x%x, IRQL 0x%0x, Affinity 0x%0x\n", 
                    mappedSystemVector, 
                    irql, 
                    affinity); 
#endif 
 
    // 
    // Connect to interrupt from the device.  After this call, 
    // interrupts from the device will result in calls to our OsrHandleInterrupt 
    // function. 
    // 
    code = IoConnectInterrupt(&devExt->InterruptObject, 
                              OsrHandleInterrupt, 
                              devExt, 
                              NULL, 
                              mappedSystemVector, 
                              irql, 
                              irql, 
                              LevelSensitive, 
                              TRUE, 
                              affinity, 
                              FALSE); 
    if (!NT_SUCCESS(code))  { 
 
#if DBG 
        DbgPrint("IoConnectInterrupt failed with error 0x%x", code); 
#endif 
 
        // 
        // Clean up the mess 
        // 
        OsrReturnPool(configInfo, deviceDescription, resources); 
 
        OsrUnload(DriverObj); 
 
        // 
        // Indicate load failure to the I/O manager; driver image is deleted... 
        // 
        return(code); 
    } 
     
    // 
    // Now we reset the adapter card. 
    // 
    OsrResetAdapter(devObj, FALSE); 
 
    // 
    // Finally, we get our adapter objects from the HAL. When we do this, 
    // we describe out device to the HAL in pretty gory detail.   In return, 
    // he gives us a pointer to our adapter object, and tells us the maximum 
    // number of mapping registers we should ever use.  This is based on 
    // the maximum transfer size that we support, yet limited by the number 
    // of available map registers on HALs where real map registers are used. 
    //  
    // Note that since this device is capable of performing reads and writes 
    // in parallel we allocate TWO ADAPTER OBJECTS.  We use the same device 
    // description structure for both, since the device's READ DMA ability is 
    // identical to its WRITE DMA ability. 
    //  
    deviceDescription = ExAllocatePoolWithTag(PagedPool, 
                                            sizeof(DEVICE_DESCRIPTION), 'dRSO'); 
 
    // 
    // Important: Zero out the entire structure first! 
    // 
    RtlZeroMemory(deviceDescription, sizeof(DEVICE_DESCRIPTION)); 
 
    deviceDescription->Version = DEVICE_DESCRIPTION_VERSION; 
    deviceDescription->Master = TRUE; 
    deviceDescription->ScatterGather = FALSE; 
    deviceDescription->Dma32BitAddresses = TRUE; 
    deviceDescription->BusNumber = devExt->BusNumber; 
    deviceDescription->InterfaceType = PCIBus; 
 
    // 
    // Maximum size of transfer that we support on this device. 
    // 
    deviceDescription->MaximumLength = OSR_PCI_MAX_TXFER; 
 
    devExt->ReadAdapter = HalGetAdapter(deviceDescription, &devExt->ReadMapRegsGot); 
 
    if(!devExt->ReadAdapter)  { 
 
#if DBG 
        DbgPrint("HalGetAdapter for READ FAILED!!\n");     
#endif 
        // 
        // Clean up the mess 
        // 
        OsrReturnPool(configInfo, deviceDescription, resources); 
 
        OsrUnload(DriverObj); 
 
        return(STATUS_UNSUCCESSFUL); 
    } 
 
#if DBG 
    DbgPrint("Map Registers got for read = %d.\n", devExt->ReadMapRegsGot); 
#endif 
 
    // 
    // Get ANOTHER Adapter Object for use with WRITE transfers 
    // 
    devExt->WriteAdapter = HalGetAdapter(deviceDescription, &devExt->WriteMapRegsGot); 
 
    if(!devExt->WriteAdapter)  { 
 
#if DBG 
        DbgPrint("HalGetAdapter for WRITE FAILED!!\n");     
#endif 
        // 
        // Clean up the mess 
        // 
        OsrReturnPool(configInfo, deviceDescription, resources); 
 
        OsrUnload(DriverObj); 
 
        return(STATUS_UNSUCCESSFUL); 
    } 
 
#if DBG 
    DbgPrint("Map Registers got for write = %d.\n", devExt->WriteMapRegsGot); 
#endif 
 
    // 
    // Initialize the watchdog timer.  This timer causes a "soft reset" 
    // on the device, in a particular direction, if an in-progress IRP 
    // hangs around too long. 
    // 
    IoInitializeTimer(devObj, OsrWatchdogTimer, NULL); 
    IoStartTimer(devObj); 
 
    // 
    // Return the memory allocated from pool during initialization 
    // 
    ExFreePool(configInfo); 
    ExFreePool(deviceDescription);     
     
    // 
    // Don't forget to free the CM_RESOURCE_LIST structure! 
    // Even though HalAssignSlotResources(...) allocated it, we're 
    // responsible for freeing it!! 
    // 
    ExFreePool(resources);  
 
#if DBG 
    DbgPrint("DriverEntry: done\n"); 
#endif 
     
    return(STATUS_SUCCESS); 
     
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  OsrUnload 
// 
//      This routine is our dynamic unload entry point.  We are called here when 
//    the OS wants to unload our driver.  It is our responsibility to release any 
//    resources we allocated. 
// 
//  INPUTS: 
// 
//      DriverObj - Address of our DRIVER_OBJECT. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      None. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
//    No doubt we pool leak at this entry point by not properly returning everything. 
// 
/////////////////////////////////////////////////////////////////////////////// 
static VOID 
OsrUnload(PDRIVER_OBJECT DriverObject) 
{ 
    POSR_DEVICE_EXT devExt; 
    PDEVICE_OBJECT devObj; 
    IO_RESOURCE_REQUIREMENTS_LIST reqList; 
    NTSTATUS code; 
    UNICODE_STRING linkName; 
    CM_RESOURCE_LIST returnResources; 
    BOOLEAN conflictDetected; 
             
#if DBG 
    DbgPrint("SAMPLE-PCI: UNLOAD called.\n"); 
#endif 
 
    // 
    // For THIS driver, there will only ever be a single device object. 
    // Because of this, we just get it from the DriverObj.  If this were 
    // a multiple device driver, we would do this in a while loop... 
    // 
    devObj = DriverObject->DeviceObject; 
 
    if (!devObj) { 
 
        return; 
    } 
 
    devExt= (POSR_DEVICE_EXT)devObj->DeviceExtension; 
 
    RtlInitUnicodeString(&linkName, L"\\??\\OSRPCI"); 
 
    IoDeleteSymbolicLink(&linkName); 
 
    // 
    // Reset the adapter card 
    // 
    OsrResetAdapter(devObj, FALSE); 
 
    if (devExt->InterruptObject) { 
        // 
        // Disconnect the interrupt. 
        // 
        IoDisconnectInterrupt(devExt->InterruptObject); 
    } 
 
    // 
    // Set up a "special" reasource list, that indicates NO RESOURCES. 
    // This will result in our resouces being returned. 
    // 
    returnResources.Count = 0; 
 
    code = IoReportResourceUsage(NULL, 
                                DriverObject, 
                                NULL, 
                                0, 
                                devObj, 
                                &returnResources, 
                                sizeof(returnResources), 
                                FALSE, 
                                &conflictDetected); 
 
    if(!NT_SUCCESS(code))  { 
         
#if DBG 
        DbgPrint("UNLOAD: IoReportResourceUsage failed.  Status = 0x%0x\n", 
                        code); 
#endif 
    }                           
 
    // 
    // Stop the watchdog timer 
    // 
    IoStopTimer(devObj); 
      
    // 
    // Delete the device object 
    // 
    IoDeleteDevice(devObj); 
} 
 
#if DBG 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  OsrPrintConfig 
// 
//      This routine is called to print out the PCI configuration information for 
//    our device.    
// 
//  INPUTS: 
// 
//      configInfo - Address of the PCI_COMMON_CONFIG information for our device. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      None. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
//    We only use this for debugging purposes. 
// 
/////////////////////////////////////////////////////////////////////////////// 
static VOID 
OsrPrintConfig(PPCI_COMMON_CONFIG  configInfo) 
{ 
    ULONG index; 
 
    DbgPrint("Displaying PCI Configuration Information\n"); 
    DbgPrint("\tRevisionID is 0x%x\n", (int)configInfo->RevisionID); 
    DbgPrint("\tProgIf is 0x%x\n", (int) configInfo->ProgIf); 
    DbgPrint("\tSubClass is 0x%x\n", (int) configInfo->SubClass); 
    DbgPrint("\tBaseClass is 0x%x\n", (int) configInfo->BaseClass); 
    DbgPrint("\tCacheLineSize is 0x%x\n", (int) configInfo->CacheLineSize); 
    DbgPrint("\tLatencyTimer is 0x%x\n", (int) configInfo->LatencyTimer); 
    DbgPrint("\tHeaderType is 0x%x\n", (int) configInfo->HeaderType); 
    DbgPrint("\tBIST is 0x%x\n", (int) configInfo->HeaderType); 
 
    for (index = 0; index < PCI_TYPE0_ADDRESSES; index++)  { 
 
        DbgPrint("\tBaseAddresses[%d] is 0x%x\n",index, configInfo->u.type0.BaseAddresses[index]); 
 
    } 
 
    DbgPrint("\tROMBaseAddress is 0x%x\n", configInfo->u.type0.ROMBaseAddress); 
    DbgPrint("\tInterruptLine is 0x%x\n", configInfo->u.type0.InterruptLine); 
    DbgPrint("\tInterruptPin is 0x%x\n", configInfo->u.type0.InterruptPin); 
    DbgPrint("****************************\n"); 
 
}     
 
 
// 
// Some static string tables we use as part of debugging 
// 
static PSTR CmResourceTypeStrings[] =  
{ 
    "CmResourceTypeNull", 
    "CmResourceTypePort", 
    "CmResourceTypeInterrupt", 
    "CmResourceTypeMemory", 
    "CmResourceTypeDma", 
    "CmResourceTypeDeviceSpecific", 
    "CmResourceTypeMaximum" 
}; 
 
static PSTR CmShareDispositionStrings[] =  
{ 
    "CmResourceShareUndetermined", 
    "CmResourceShareDeviceExclusive", 
    "CmResourceShareDriverExclusive", 
    "CmResourceShareShared" 
}; 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  OsrPrintResourceList 
// 
//      This routine is called to print out the Resource descriptor list containing 
//    the resources allocated for our device by NT. 
// 
//  INPUTS: 
// 
//      Resources - Address of the CM_RESOURCE_LIST information for our device. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      None. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
//    We only use this for debugging purposes. 
// 
/////////////////////////////////////////////////////////////////////////////// 
static VOID 
OsrPrintResourceList(PCM_RESOURCE_LIST Resources) 
{ 
    ULONG index, index2; 
         
    DbgPrint("%d. resource descriptor list(s) returned\n", Resources->Count); 
 
    for (index = 0; index < Resources->Count; index++)  { 
         
        DbgPrint("\t[%d] Interface Type 0x%x\n", 
                index, Resources->List[index].InterfaceType); 
        DbgPrint("\t[%d] BusNumber 0x%x\n", 
                index, Resources->List[index].BusNumber); 
        DbgPrint("\t[%d] Version 0x%x\n", 
                index, Resources->List[index].PartialResourceList.Version); 
        DbgPrint("\t[%d] Revision 0x%x\n", 
                index, Resources->List[index].PartialResourceList.Revision); 
         
        DbgPrint("\t[%d] Partial Resource Descriptors %d.\n", 
                index, Resources->List[index].PartialResourceList.Count); 
        for (index2 = 0; 
            index2 < Resources->List[index].PartialResourceList.Count; 
            index2++)  { 
 
            PCM_PARTIAL_RESOURCE_DESCRIPTOR prd; // Too much to type! 
             
            prd = &Resources->List[index].PartialResourceList.PartialDescriptors[index2]; 
 
            DbgPrint("\t\t[%d] Type 0x%x (%s)\n", 
                    index2, prd->Type, CmResourceTypeStrings[prd->Type]); 
            DbgPrint("\t\t[%d] Share Disposition 0x%x (%s)\n", 
                    index2, prd->ShareDisposition, 
                    CmShareDispositionStrings[prd->ShareDisposition]); 
            DbgPrint("\t\t[%d] Flags 0x%x\n", index2, prd->Flags); 
            DbgPrint("\t\t[%d] Raw 0x%x %x %x\n", 
                    index2, prd->u.DeviceSpecificData.DataSize, 
                    prd->u.DeviceSpecificData.Reserved1, 
                    prd->u.DeviceSpecificData.Reserved2); 
 
            switch (prd->Type) { 
                 
                case CmResourceTypePort: 
                    if (prd->Flags == CM_RESOURCE_PORT_MEMORY) 
                        DbgPrint("\t\t[%d] port memory starting at 0x%x length 0x%x\n",  
                                      index2, prd->u.Port.Start.LowPart, 
                                      prd->u.Port.Length); 
                    if (prd->Flags == CM_RESOURCE_PORT_IO) 
                       DbgPrint("\t\t[%d] port i/o starting at 0x%x length 0x%x\n",  
                                     index2, prd->u.Port.Start.LowPart, 
                                     prd->u.Port.Length); 
                    break; 
 
                case CmResourceTypeInterrupt: 
                    if (prd->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) 
                        DbgPrint("\t\t[%d] level interrupt at lvl 0x%x vector 0x%x affinity 0x%x\n",  
                                     index2, prd->u.Interrupt.Level, 
                                     prd->u.Interrupt.Vector, 
                                     prd->u.Interrupt.Affinity); 
                    if (prd->Flags == CM_RESOURCE_INTERRUPT_LATCHED) 
                        DbgPrint("\t\t[%d] latched interrupt at lvl 0x%x vector 0x%x affinity 0x%x\n",  
                                     index2, prd->u.Interrupt.Level, 
                                     prd->u.Interrupt.Vector, 
                                     prd->u.Interrupt.Affinity); 
                    break; 
 
                case CmResourceTypeMemory: 
                    if (prd->Flags == CM_RESOURCE_MEMORY_READ_WRITE) 
                        DbgPrint("\t\t[%d] r/w memory starting at 0x%x length 0x%x\n", 
                                 index2, prd->u.Memory.Start.LowPart, 
                                 prd->u.Memory.Length); 
                    if (prd->Flags & CM_RESOURCE_MEMORY_READ_ONLY) 
                        DbgPrint("\t\t[%d] r/o memory starting at 0x%x length 0x%x\n", 
                                 index2, prd->u.Memory.Start.LowPart, 
                                 prd->u.Memory.Length); 
                    if (prd->Flags & CM_RESOURCE_MEMORY_WRITE_ONLY) 
                        DbgPrint("\t\t[%d] w/o memory starting at 0x%x length 0x%x\n", 
                                 index2, prd->u.Memory.Start.LowPart, 
                                 prd->u.Memory.Length); 
                    break; 
 
                case CmResourceTypeDma: 
                    DbgPrint("\t\t[%d] DMA on channel 0x%x\n", 
                        index2, prd->u.Dma.Channel); 
                    break; 
 
                case CmResourceTypeDeviceSpecific: 
                    DbgPrint("\t\t[%d] Device specific data at 0x%x length 0x%x\n", 
                                 index2, 
                                 ((ULONG) &prd->u.DeviceSpecificData.Reserved2) + (ULONG)sizeof(ULONG), 
                                 prd->u.DeviceSpecificData.DataSize); 
                    break; 
 
                default: 
                    // 
                    // Say what?!!  Unknown resource type.  Something is pretty wierd here. 
                    // 
                    DbgPrint("Unknown resource type 0x%x\n", prd->Type); 
                    break; 
            } 
             
        } 
            DbgPrint("\t[%d] ***** End dump ******\n", index); 
    } 
 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  OsrPrintIntcsr 
// 
//      This routine is called to print out a descriptive view of the bits set 
//    in the IntCsr register. 
// 
//  INPUTS: 
// 
//      Intcsr - Contents of the IntCsr register. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      None. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL >= IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
//    We only use this for debugging purposes.  It is called by the ISR as well 
//    as by the read and write paths. 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID 
OsrPrintIntcsr(ULONG Intcsr)  
{ 
 
    if(Intcsr & AMCC_INT_OUT_FIFO_CTRL)  { 
         
        DbgPrint("\tOut FIFO Ctrl\n"); 
    } 
 
 
    if(Intcsr & AMCC_INT_IN_FIFO_CTRL)  { 
         
        DbgPrint("\tIn FIFO Ctrl\n"); 
    } 
 
    if(Intcsr & AMCC_INT_FIFO_ADVA_BYTE0)  { 
         
        DbgPrint("\tADVA BYTE0\n"); 
    } 
 
    if(Intcsr & AMCC_INT_FIFO_ADVA_BYTE1)  { 
         
        DbgPrint("\tADVA BYTE1\n"); 
    } 
 
    if(Intcsr & AMCC_INT_FIFO_ADVA_BYTE2)  { 
         
        DbgPrint("\tADVA BYTE2\n"); 
    } 
 
    if(Intcsr & AMCC_INT_FIFO_ADVA_BYTE3)  { 
         
        DbgPrint("\tADVA BYTE3\n"); 
    } 
 
    if(Intcsr & AMCC_INT_FIFO_ADVP_BYTE0)  { 
         
        DbgPrint("\tADVP BYTE0\n"); 
    } 
 
    if(Intcsr & AMCC_INT_FIFO_ADVP_BYTE1)  { 
        
        DbgPrint("\tADVP BYTE1\n"); 
    } 
 
    if(Intcsr & AMCC_INT_FIFO_ADVP_BYTE2)  { 
         
        DbgPrint("\tADVP BYTE2\n"); 
    } 
 
    if(Intcsr & AMCC_INT_FIFO_ADVP_BYTE3)  { 
         
        DbgPrint("\tADVP BYTE3\n"); 
    } 
 
    if(Intcsr & AMCC_INT_ENDIAN_16BIT)  { 
         
        DbgPrint("\tENDIAN 16BIT\n"); 
    } 
 
    if(Intcsr & AMCC_INT_ENDIAN_32BIT)  { 
         
        DbgPrint("\tENDIAN 32BIT\n"); 
    } 
 
    if(Intcsr & AMCC_INT_ENDIAN_64BIT)  { 
         
        DbgPrint("\tENDIAN 64BIT\n"); 
    } 
 
    if(Intcsr & AMCC_INT_INTERRUPTED)  { 
         
        DbgPrint("\tINTERRUPTED\n"); 
    } 
 
    if(Intcsr & AMCC_INT_RESERVED22)  { 
         
        DbgPrint("\tRESERVED 22\n"); 
    } 
 
    if(Intcsr & AMCC_INT_TARG_ABORT)  { 
         
        DbgPrint("\tTARG_ABORT\n"); 
    } 
 
    if(Intcsr & AMCC_INT_MAST_ABORT)  { 
         
       DbgPrint("\tMAST_ABORT\n"); 
    } 
 
    if(Intcsr & AMCC_INT_READ_COMP)  { 
         
       DbgPrint("\tREAD_COMP\n"); 
    } 
 
    if(Intcsr & AMCC_INT_WRITE_COMP)  { 
         
       DbgPrint("\tWRITE_COMP\n"); 
    } 
 
    if(Intcsr & AMCC_INT_INMBX_ACK)  { 
         
       DbgPrint("\tINMBX_ACK\n"); 
    } 
 
    if(Intcsr & AMCC_INT_OUTMBX_ACK)  { 
         
       DbgPrint("\tOUTMBX_ACK\n"); 
    } 
 
    if(Intcsr & AMCC_INT_INT_ON_READ)  { 
         
       DbgPrint("\tINT_ON_READ\n"); 
    } 
 
    if(Intcsr & AMCC_INT_INT_ON_WRITE)  { 
         
       DbgPrint("\tINT_ON_WRITE\n"); 
    } 
 
    if(Intcsr & AMCC_INT_RESERVED13)  { 
         
       DbgPrint("\tRESERVED13\n"); 
    } 
 
    if(Intcsr & AMCC_INT_ENABLE_OUTMBX_INT)  { 
         
       DbgPrint("\tENABLE_OUTMBX_INT\n"); 
    } 
 
    if(Intcsr & AMCC_INT_ENABLE_INMBX_INT)  { 
         
       DbgPrint("\tENABLE_INMBX_INT\n"); 
    } 
 
 
}     
 
#endif      // DBG 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  OsrReturnPool 
// 
//      This routine is called to delete the memory assiged to the Driver for 
//    configInfo, DeviceDescription and Resources. 
// 
//  INPUTS: 
// 
//      ConfigInfo - Address of the PCI_COMMON_CONFIG information to delete. 
//     
//    DeviceDescription - Address of the DEVICE_DESCRIPTION information to delete. 
// 
//    Resources - Address of the CM_RESOURCE_LIST to delete. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      None. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL >= IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
static VOID OsrReturnPool(PPCI_COMMON_CONFIG  ConfigInfo, PDEVICE_DESCRIPTION 
            DeviceDescription,  PCM_RESOURCE_LIST Resources) 
{ 
     
    if(ConfigInfo) 
        ExFreePool(ConfigInfo); 
 
    if(DeviceDescription) 
        ExFreePool(DeviceDescription); 
 
    if(Resources) 
        ExFreePool(Resources); 
}