www.pudn.com > 9054Src.rar > Driver.c
/******************************************************************************* * Copyright (c) 2006 PLX Technology, Inc. * * PLX Technology Inc. licenses this software under specific terms and * conditions. Use of any of the software or derviatives thereof in any * product without a PLX Technology chip is strictly prohibited. * * PLX Technology, Inc. provides this software AS IS, WITHOUT ANY WARRANTY, * EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. PLX makes no guarantee * or representations regarding the use of, or the results of the use of, * the software and documentation in terms of correctness, accuracy, * reliability, currentness, or otherwise; and you rely on the software, * documentation and results solely at your own risk. * * IN NO EVENT SHALL PLX BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS, * LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES * OF ANY KIND. IN NO EVENT SHALL PLX'S TOTAL LIABILITY EXCEED THE SUM * PAID TO PLX FOR THE PRODUCT LICENSED HEREUNDER. * ******************************************************************************/ /****************************************************************************** * * File Name: * * Driver.c * * Description: * * Contains driver entry and cleanup routines as well as device * resource initialization. * * Revision History: * * 03-01-06 : PCI SDK v4.40 * *****************************************************************************/ #include#include "CommonApi.h" #include "Dispatch.h" #include "Driver.h" #include "GlobalVars.h" #include "PciSupport.h" #include "PlugPlay.h" #include "PlxInterrupt.h" #include "Power.h" #include "SupportFunc.h" #if defined(ALLOC_PRAGMA) && !defined(PLX_DEBUG) #pragma alloc_text(INIT, DriverEntry) #endif /****************************************************************************** * * Function : DriverEntry * * Description: Entry point for the driver * ******************************************************************************/ NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath ) { S8 MinorVersion; WCHAR Win9x_RegistryPath[100]; BOOLEAN bVersionFound; NTSTATUS status; UNICODE_STRING Win9x_RegistryPath_Unicode; REGISTRY_INFORMATION RegistryInfo; DebugPrintf_NoInfo(("\n")); DebugPrintf(("<========================================================>\n")); DebugPrintf(( "PLX Driver v%d.%d%d - built on %s %s\n", PLX_SDK_VERSION_MAJOR, PLX_SDK_VERSION_MINOR, PLX_SDK_VERSION_REVISION, __DATE__, __TIME__ )); DebugPrintf(( "Supports WDM v%x.%x\n", WDM_MAJORVERSION, WDM_MINORVERSION )); // Set starting version MinorVersion = 0x41; // Determine the highest WDM version supported do { // Decrement to lower version MinorVersion--; bVersionFound = IoIsWdmVersionAvailable( 0x01, // Just check for version 1.x for now MinorVersion ); } while ((bVersionFound == FALSE) && (MinorVersion >= 0)); // Display possible OS version DebugPrintf(("OS supports WDM 1.%02x ", MinorVersion)); if (MinorVersion >= 0x30) { DebugPrintf_NoInfo(("(Windows 2003 Server or higher)\n")); } else if (MinorVersion >= 0x20) { DebugPrintf_NoInfo(("(Windows XP or higher)\n")); } else if (MinorVersion >= 0x10) { DebugPrintf_NoInfo(("(Windows 2000 or higher)\n")); } else { DebugPrintf_NoInfo(("(Windows 98 or higher)\n")); // Use the centralized PLX registry path for driver options swprintf( Win9x_RegistryPath, WIN9X_REGISTRY_PATH_UNICODE L"\\" PLX_DRIVER_NAME_UNICODE ); RtlInitUnicodeString( &Win9x_RegistryPath_Unicode, Win9x_RegistryPath ); // Override assigned path pRegistryPath = &Win9x_RegistryPath_Unicode; } DebugPrintf(( "Driver Registry path = \"%ws\"\n", pRegistryPath->Buffer )); // Get configuration information from registry PlxRegistryInformationGet( pRegistryPath, &RegistryInfo ); // Fill in the appropriate dispatch handlers pDriverObject->DriverUnload = DriverUnload; pDriverObject->MajorFunction[IRP_MJ_CREATE] = Dispatch_Create; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = Dispatch_Close; pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = Dispatch_Cleanup; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Dispatch_IoControl; pDriverObject->MajorFunction[IRP_MJ_PNP] = Dispatch_Pnp; pDriverObject->MajorFunction[IRP_MJ_POWER] = Dispatch_Power; pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = Dispatch_SystemControl; pDriverObject->DriverExtension->AddDevice = AddDevice; // Initialize device count Gbl_DeviceCount = 0; // Initialize Common buffer pointer pGbl_CommonBuffer = NULL; // Set common buffer requested size Gbl_CommonBufferSize = RegistryInfo.CommonBufferSize; // Set cacheability of physical buffers Gbl_PhysicalMemoryCacheable = RegistryInfo.PhysicalMemoryCacheable; return STATUS_SUCCESS; } /****************************************************************************** * * Function : DriverUnload * * Description: Unload the driver * ******************************************************************************/ VOID DriverUnload( PDRIVER_OBJECT pDriverObject ) { DebugPrintf_NoInfo(("\n")); DebugPrintf(("Unloading Driver...\n")); DebugPrintf(("...Driver unloaded\n")); } /****************************************************************************** * * Function : AddDevice * * Description: Add a new device object to the driver * ******************************************************************************/ NTSTATUS AddDevice( PDRIVER_OBJECT pDriverObject, PDEVICE_OBJECT pdo ) { U8 i; WCHAR DeviceName[PLX_MAX_NAME_LENGTH]; WCHAR DeviceLinkName[PLX_MAX_NAME_LENGTH]; NTSTATUS status; POWER_STATE PowerState; UNICODE_STRING DeviceName_Unicode; UNICODE_STRING DeviceLinkName_Unicode; PDEVICE_OBJECT fdo; DEVICE_EXTENSION *pdx; // Build a device name and attempt to create it for (i=0; i < 64; i++) { swprintf( DeviceName, L"\\Device\\" PLX_DRIVER_NAME_UNICODE L"-%d", i ); RtlInitUnicodeString( &DeviceName_Unicode, DeviceName ); // Create the device object status = IoCreateDevice( pDriverObject, sizeof(DEVICE_EXTENSION), &DeviceName_Unicode, FILE_DEVICE_UNKNOWN, 0, FALSE, // Shared by applications &fdo ); // IoCreateDevice() will fail if the same object already exists if (NT_SUCCESS(status)) { break; } } // Check if the creation succeeded if ( !NT_SUCCESS(status) ) { DebugPrintf(("ERROR - Unable to create Device\n")); return status; } DebugPrintf_NoInfo(("\n")); DebugPrintf(( "Created Device (%ws)...\n", DeviceName )); // Link a Win32 name for user applications swprintf( DeviceLinkName, L"\\DosDevices\\" PLX_DRIVER_NAME_UNICODE L"-%d", i ); RtlInitUnicodeString( &DeviceLinkName_Unicode, DeviceLinkName ); DebugPrintf(( "Creating Win32 symbolic link (%ws)...\n", DeviceLinkName )); status = IoCreateSymbolicLink( &DeviceLinkName_Unicode, &DeviceName_Unicode ); if ( !NT_SUCCESS(status) ) { DebugPrintf(("WARNING - Unable to create symbolic link for Win32 Apps\n")); swprintf(DeviceLinkName, L""); } // // Initialize the device extension // pdx = fdo->DeviceExtension; // Clear device extension RtlZeroMemory( pdx, sizeof(DEVICE_EXTENSION) ); // Save the parent device object pdx->pDeviceObject = fdo; pdx->pPhysicalDeviceObject = pdo; pdx->usage = 1; // Locked until RemoveDevice sprintf( pdx->Device.SerialNumber, PLX_DRIVER_NAME "-%d", i ); wcscpy( pdx->LinkName, DeviceLinkName ); // Initialize PCI BAR variables for (i = 0 ; i < PCI_NUM_BARS_TYPE_00; i++) { pdx->PciBar[i].pVa = NULL; pdx->PciBar[i].Physical.QuadPart = 0; pdx->PciBar[i].Size = 0; pdx->PciBar[i].bIsIoSpace = FALSE; pdx->PciBar[i].pMdl = NULL; InitializeListHead( &(pdx->PciBar[i].List_Mappings) ); KeInitializeSpinLock( &(pdx->PciBar[i].Lock_MappingsList) ); } KeInitializeSpinLock( &(pdx->Lock_HwAccess) ); InitializeListHead( &(pdx->List_WaitObjects) ); KeInitializeSpinLock( &(pdx->Lock_WaitObjectsList) ); InitializeListHead( &(pdx->List_PhysicalMem) ); KeInitializeSpinLock( &(pdx->Lock_PhysicalMemList) ); InitializeListHead( &(pdx->List_BarMappings) ); KeInitializeSpinLock( &(pdx->Lock_BarMappingsList) ); // Initialize the interrupt DPC KeInitializeDpc( &(pdx->DpcForIsr), DpcForIsr, pdx ); #if defined(DMA_SUPPORT) // Initialize DMA management variables for (i = 0 ; i < NUMBER_OF_DMA_CHANNELS; i++) { pdx->DmaInfo[i].state = DmaStateClosed; pdx->DmaInfo[i].bLocalAddrConstant = FALSE; } KeInitializeSpinLock( &(pdx->Lock_DmaChannel) ); #endif // DMA_SUPPORT KeInitializeEvent( &pdx->evRemove, NotificationEvent, FALSE ); /* * Since we must pass PNP requests down to the next device object in the * chain (namely the physical device object created by the bus enumerator), * we have to remember what that device is. That's why we defined the * LowerDeviceObject member in our device extension. */ pdx->pLowerDeviceObject = IoAttachDeviceToDeviceStack( fdo, pdo ); DebugPrintf(( "Attached device to stack\n" " Functional DevObj: 0x%p\n" " Lower DevObj: 0x%p\n" " Physical DevObj: 0x%p\n", fdo, pdx->pLowerDeviceObject, pdo )); // Notify the power manager of the initial power state pdx->PowerState = PowerDeviceD0; // Start device in full power state PowerState.DeviceState = PowerDeviceD0; PoSetPowerState( fdo, DevicePowerState, PowerState ); // Indicate the I/O Manager buffer management method fdo->Flags |= DO_BUFFERED_IO; // Manually clear the Device Initialzing flag fdo->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; } /****************************************************************************** * * Function : RemoveDevice * * Description: Remove a functional device object * ******************************************************************************/ VOID RemoveDevice( PDEVICE_OBJECT fdo ) { NTSTATUS status; UNICODE_STRING DeviceLinkName_Unicode; DEVICE_EXTENSION *pdx; pdx = fdo->DeviceExtension; // Remove Win32 link name if (wcslen(pdx->LinkName) != 0) { DebugPrintf(( "Removing Win32 link (%ws)\n", pdx->LinkName )); RtlInitUnicodeString( &DeviceLinkName_Unicode, pdx->LinkName ); status = IoDeleteSymbolicLink( &DeviceLinkName_Unicode ); if ( !NT_SUCCESS(status) ) DebugPrintf(("WARNING - Unable to remove Win32 link\n")); } // Detach device from the device object stack if (pdx->pLowerDeviceObject) { IoDetachDevice( pdx->pLowerDeviceObject ); } DebugPrintf(("Deleting device object...\n")); // Delete the functional device object IoDeleteDevice( fdo ); } /****************************************************************************** * * Function : StartDevice * * Description: Start a device * ******************************************************************************/ NTSTATUS StartDevice( PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST ResourceListRaw, PCM_PARTIAL_RESOURCE_LIST ResourceList ) { U8 i; U8 BarIndex; U32 vector; KIRQL IrqL; BOOLEAN bIntPresent; NTSTATUS status; KAFFINITY affinity; KINTERRUPT_MODE mode; PLX_PHYSICAL_MEM PciMem; DEVICE_EXTENSION *pdx; PCI_COMMON_CONFIG PciRegs; PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceRaw; PCM_PARTIAL_RESOURCE_DESCRIPTOR Resource; pdx = fdo->DeviceExtension; bIntPresent = FALSE; ResourceRaw = ResourceListRaw->PartialDescriptors; Resource = ResourceList->PartialDescriptors; // Get the PCI base addresses PciRegisterBufferRead( fdo, 0, &PciRegs, sizeof(PCI_COMMON_CONFIG) ); DebugPrintf(( "Resource list contains %d descriptors\n", ResourceListRaw->Count )); for (i = 0; i < ResourceListRaw->Count; ++i, ++Resource, ++ResourceRaw) { DebugPrintf_NoInfo((" Resource %02d\n", i)); switch (ResourceRaw->Type) { case CmResourceTypeInterrupt: bIntPresent = TRUE; IrqL = (KIRQL) Resource->u.Interrupt.Level; vector = Resource->u.Interrupt.Vector; affinity = Resource->u.Interrupt.Affinity; DebugPrintf_NoInfo(( " Type : Interrupt\n" " Vector : 0x%02x (Translated = 0x%02x)\n" " IRQL : 0x%02x (Translated = 0x%02x)\n" " Affinity : 0x%08x\n", ResourceRaw->u.Interrupt.Vector, vector, ResourceRaw->u.Interrupt.Level, IrqL, ResourceRaw->u.Interrupt.Affinity )); if (ResourceRaw->Flags == CM_RESOURCE_INTERRUPT_LATCHED) { mode = Latched; DebugPrintf_NoInfo((" Mode : Latched\n")); } else { mode = LevelSensitive; DebugPrintf_NoInfo((" Mode : Level Sensitive\n")); } break; case CmResourceTypePort: DebugPrintf_NoInfo(( " Type : I/O Port\n" " Address : 0x%08lx (Translated = 0x%08lx)\n" " Size : 0x%08x ", ResourceRaw->u.Port.Start, Resource->u.Port.Start, ResourceRaw->u.Port.Length )); if (ResourceRaw->u.Port.Length >= (1 << 10)) { DebugPrintf_NoInfo(( "(%d Kb)\n", ResourceRaw->u.Port.Length >> 10 )); } else { DebugPrintf_NoInfo(( "(%d Bytes)\n", ResourceRaw->u.Port.Length )); } BarIndex = GetBarIndex( ResourceRaw->u.Port.Start, &PciRegs ); DebugPrintf_NoInfo(( " PCI BAR : " )); if (BarIndex != (U8)-1) { DebugPrintf_NoInfo(( "%d\n", BarIndex )); pdx->PciBar[BarIndex].Physical = ResourceRaw->u.Port.Start; pdx->PciBar[BarIndex].Size = ResourceRaw->u.Port.Length; pdx->PciBar[BarIndex].pVa = NULL; pdx->PciBar[BarIndex].bIsIoSpace = TRUE; } else { DebugPrintf_NoInfo(( "??\n" )); } break; case CmResourceTypeMemory: DebugPrintf_NoInfo(( " Type : Memory Space\n" " Address : 0x%08lx (Translated = 0x%08lx)\n" " Size : 0x%08x ", ResourceRaw->u.Memory.Start, Resource->u.Memory.Start, ResourceRaw->u.Memory.Length )); if (ResourceRaw->u.Memory.Length >= (1 << 10)) { DebugPrintf_NoInfo(( "(%d Kb)\n", ResourceRaw->u.Memory.Length >> 10 )); } else { DebugPrintf_NoInfo(( "(%d Bytes)\n", ResourceRaw->u.Memory.Length )); } BarIndex = GetBarIndex( ResourceRaw->u.Memory.Start, &PciRegs ); DebugPrintf_NoInfo(( " PCI BAR : " )); if (BarIndex != (U8)-1) { DebugPrintf_NoInfo(( "%d\n", BarIndex )); DebugPrintf_NoInfo((" Kernel VA: ")); // Record resources pdx->PciBar[BarIndex].Physical = ResourceRaw->u.Memory.Start; pdx->PciBar[BarIndex].Size = ResourceRaw->u.Memory.Length; pdx->PciBar[BarIndex].bIsIoSpace = FALSE; status = PlxPciBarResourceMap( pdx, BarIndex ); if ( NT_SUCCESS(status) ) { DebugPrintf_NoInfo(( "0x%p\n", pdx->PciBar[BarIndex].pVa )); } else { DebugPrintf_NoInfo(( "ERROR - Unable to map 0x%08lx ==> Kernel VA\n", ResourceRaw->u.Memory.Start )); } } else { DebugPrintf_NoInfo(( "??\n" )); } break; case CmResourceTypeNull: DebugPrintf_NoInfo((" Type: Null (unsupported)\n")); break; case CmResourceTypeDma: DebugPrintf_NoInfo((" Type: DMA (unsupported)\n")); break; case CmResourceTypeDeviceSpecific: DebugPrintf_NoInfo((" Type: Device Specific (unsupported)\n")); break; case CmResourceTypeBusNumber: DebugPrintf_NoInfo((" Type: Bus Number (unsupported)\n")); break; // NonArbitrated & ConfigData are currently #defined as the same number case CmResourceTypeConfigData: DebugPrintf_NoInfo((" Type: Non-Arbitrated or Config Data (unsupported)\n")); break; case CmResourceTypeDevicePrivate: DebugPrintf_NoInfo((" Type: Device Private Data (unsupported)\n")); break; case CmResourceTypePcCardConfig: DebugPrintf_NoInfo((" Type: PC Card Configuration (unsupported)\n")); break; case CmResourceTypeMfCardConfig: DebugPrintf_NoInfo((" Type: Multi-function Card Configuration (unsupported)\n")); break; default: DebugPrintf_NoInfo((" Type: ?Unknown Resource Type?\n")); break; } } // Make sure BAR 0 exists or the device can't be started if (pdx->PciBar[0].pVa == NULL) { ErrorPrintf(("ERROR - BAR 0 address not configured, unable to load driver\n")); return STATUS_INSUFFICIENT_RESOURCES; } // Record the Vendor and Device ID pdx->Device.VendorId = PciRegs.VendorID; pdx->Device.DeviceId = PciRegs.DeviceID; // Get bus and slot numbers of the device status = GetBusSlotNumber( pdx->pPhysicalDeviceObject, pdx, PciRegs.u.type0.BaseAddresses[0] ); if (!NT_SUCCESS(status)) { DebugPrintf(("WARNING - Unable to get bus and slot number\n")); } if (bIntPresent) { // Disable the PCI interrupt PlxChipPciInterruptDisable( pdx ); status = IoConnectInterrupt( &pdx->pInterruptObject, OnInterrupt, pdx, NULL, vector, IrqL, IrqL, mode, TRUE, affinity, FALSE ); if ( !NT_SUCCESS(status) ) { ErrorPrintf(( "ERROR - IoConnectInterrupt() failed, status = 0x%08x\n", status )); pdx->pInterruptObject = NULL; } else { DebugPrintf(("Connected to interrupt vector\n")); // Re-enable the PCI Interrupt KeSynchronizeExecution( pdx->pInterruptObject, PlxChipPciInterruptEnable, pdx ); } } else { DebugPrintf(("No interrupt found\n")); pdx->pInterruptObject = NULL; } // Allocate a DMA adapter object for physical memory allocations PlxDmaAdapterAllocate( pdx ); // If this is the first device, allocate buffers if (Gbl_DeviceCount == 0) { if (Gbl_CommonBufferSize != 0) { DebugPrintf(( "Allocating common buffer...\n" )); // Set requested size PciMem.Size = Gbl_CommonBufferSize; // Allocate common buffer PlxPciPhysicalMemoryAllocate( pdx, &PciMem, TRUE, // Smaller buffer is ok pdx // Assign owner as the device ); } /******************************************************** * Since this is the first device, make sure to invalidate * its removal relations. This forces the PnP Manager * to eventually query for removal relations before * attempting to remove the device. Devices added after * this one rely on it for the Common DMA and SGL buffers; * therefore, it can only be removed last. *******************************************************/ IoInvalidateDeviceRelations( pdx->pPhysicalDeviceObject, RemovalRelations ); } // Relay Common buffer properties to device if (pGbl_CommonBuffer != NULL) { PlxChipPostCommonBufferProperties( pdx, (U32)pGbl_CommonBuffer->BusPhysical, pGbl_CommonBuffer->Size ); } // Increment our device count InterlockedIncrement( &Gbl_DeviceCount ); return STATUS_SUCCESS; } /****************************************************************************** * * Function : StopDevice * * Description: Stop a device * ******************************************************************************/ VOID StopDevice( PDEVICE_OBJECT fdo ) { PLX_PHYSICAL_MEM PciMem; DEVICE_EXTENSION *pdx; pdx = fdo->DeviceExtension; // Free all interrupt resources if (pdx->pInterruptObject != NULL) { // Disable the PCI interrupt KeSynchronizeExecution( pdx->pInterruptObject, (PKSYNCHRONIZE_ROUTINE)PlxChipPciInterruptDisable, (PVOID)pdx ); // Remove the ISR IoDisconnectInterrupt( pdx->pInterruptObject ); pdx->pInterruptObject = NULL; } // Release the common buffer if this device owns it if (pGbl_CommonBuffer != NULL) { if (pGbl_CommonBuffer->pOwner == pdx) { // Release common buffer DebugPrintf(( "De-allocating Common Buffer...\n" )); // Prepare buffer properties for parameter PciMem.PhysicalAddr = pGbl_CommonBuffer->BusPhysical; PciMem.Size = pGbl_CommonBuffer->Size; // Release the buffer PlxPciPhysicalMemoryFree( pdx, &PciMem ); // Mark buffer as released pGbl_CommonBuffer = NULL; } } // Remove relayed Common buffer properties PlxChipPostCommonBufferProperties( pdx, 0, 0 ); // Release the DMA adapter object PlxDmaAdapterFree( pdx ); // Unmap I/O regions from kernel space (No local register access after this) PlxPciBarResourcesUnmap( pdx ); // Decrement our device count InterlockedDecrement( &Gbl_DeviceCount ); } /****************************************************************************** * * Function : PlxDmaAdapterAllocate * * Description: Allocate a DMA adapter object which will provide support for * later allocation of physically contiguous page-locked memory. * ******************************************************************************/ NTSTATUS PlxDmaAdapterAllocate( DEVICE_EXTENSION *pdx ) { ULONG NumMapRegisters; DEVICE_DESCRIPTION DeviceDescription; DebugPrintf(( "Allocating DMA Adapter object...\n" )); // Verify object not already created if (pdx->pDmaAdapter != NULL) { DebugPrintf(("ERROR - DMA Adapter object already exist, unable to allocate\n")); return STATUS_OBJECT_NAME_EXISTS; } // Clear device description RtlZeroMemory( &DeviceDescription, sizeof(DEVICE_DESCRIPTION) ); // Set device DMA properties DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION; DeviceDescription.Master = TRUE; // Device is bus master DeviceDescription.ScatterGather = TRUE; // Device supports SGL DeviceDescription.Dma32BitAddresses = TRUE; // Device supports 32-bit addressing DeviceDescription.Reserved1 = FALSE; // For future use, must be FALSE DeviceDescription.Dma64BitAddresses = FALSE; // Don't use 64-bit addressing DeviceDescription.InterfaceType = PCIBus; // Device is PCI DeviceDescription.MaximumLength = PHYS_MEM_MAX_SIZE_ALL; // OS will assign map register count NumMapRegisters = 0; // Allocate a DMA adapter object pdx->pDmaAdapter = IoGetDmaAdapter( pdx->pPhysicalDeviceObject, &DeviceDescription, &NumMapRegisters ); if (pdx->pDmaAdapter == NULL) { DebugPrintf(("ERROR - DMA Adapter allocation failed\n")); return STATUS_INSUFFICIENT_RESOURCES; } return STATUS_SUCCESS; } /****************************************************************************** * * Function : PlxDmaAdapterFree * * Description: Frees the device DMA adapter object * ******************************************************************************/ NTSTATUS PlxDmaAdapterFree( DEVICE_EXTENSION *pdx ) { DebugPrintf(( "Releasing DMA Adapter object...\n" )); // Verify DMA Adapter was created if (pdx->pDmaAdapter == NULL) { DebugPrintf(("ERROR - DMA Adapter object doesn't exist, unable to free object\n")); return STATUS_RESOURCE_TYPE_NOT_FOUND; } // Delete the DMA Adapter pdx->pDmaAdapter->DmaOperations->PutDmaAdapter( pdx->pDmaAdapter ); // DMA adapter object deleted, no longer available pdx->pDmaAdapter = NULL; return STATUS_SUCCESS; }