diff options
Diffstat (limited to 'audio/simpleaudiosample/Source/Main/adapter.cpp')
| -rw-r--r-- | audio/simpleaudiosample/Source/Main/adapter.cpp | 844 |
1 files changed, 844 insertions, 0 deletions
diff --git a/audio/simpleaudiosample/Source/Main/adapter.cpp b/audio/simpleaudiosample/Source/Main/adapter.cpp new file mode 100644 index 00000000..105f7fde --- /dev/null +++ b/audio/simpleaudiosample/Source/Main/adapter.cpp @@ -0,0 +1,844 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + adapter.cpp + +Abstract: + + Setup and miniport installation. No resources are used by simple audio sample. + This sample is to demonstrate how to develop a full featured audio miniport driver. +--*/ + +#pragma warning (disable : 4127) + +// +// All the GUIDS for all the miniports end up in this object. +// +#define PUT_GUIDS_HERE + +#include "definitions.h" +#include "endpoints.h" +#include "minipairs.h" + +typedef void (*fnPcDriverUnload) (PDRIVER_OBJECT); +fnPcDriverUnload gPCDriverUnloadRoutine = NULL; +extern "C" DRIVER_UNLOAD DriverUnload; + +//----------------------------------------------------------------------------- +// Referenced forward. +//----------------------------------------------------------------------------- + +DRIVER_ADD_DEVICE AddDevice; + +NTSTATUS +StartDevice +( + _In_ PDEVICE_OBJECT, + _In_ PIRP, + _In_ PRESOURCELIST +); + +_Dispatch_type_(IRP_MJ_PNP) +DRIVER_DISPATCH PnpHandler; + +// +// Rendering streams are saved to a file by default. Use the registry value +// DoNotCreateDataFiles (DWORD) > 0 to override this default. +// +DWORD g_DoNotCreateDataFiles = 0; // default is off. +DWORD g_DisableToneGenerator = 0; // default is to generate tones. +UNICODE_STRING g_RegistryPath; // This is used to store the registry settings path for the driver + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +#pragma code_seg("PAGE") +void ReleaseRegistryStringBuffer() +{ + if (g_RegistryPath.Buffer != NULL) + { + ExFreePool(g_RegistryPath.Buffer); + g_RegistryPath.Buffer = NULL; + g_RegistryPath.Length = 0; + g_RegistryPath.MaximumLength = 0; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +extern "C" +void DriverUnload +( + _In_ PDRIVER_OBJECT DriverObject +) +/*++ + +Routine Description: + + Our driver unload routine. This just frees the WDF driver object. + +Arguments: + + DriverObject - pointer to the driver object + +Environment: + + PASSIVE_LEVEL + +--*/ +{ + PAGED_CODE(); + + DPF(D_TERSE, ("[DriverUnload]")); + + ReleaseRegistryStringBuffer(); + + if (DriverObject == NULL) + { + goto Done; + } + + // + // Invoke first the port unload. + // + if (gPCDriverUnloadRoutine != NULL) + { + gPCDriverUnloadRoutine(DriverObject); + } + + // + // Unload WDF driver object. + // + if (WdfGetDriver() != NULL) + { + WdfDriverMiniportUnload(WdfGetDriver()); + } +Done: + return; +} + +//============================================================================= +#pragma code_seg("INIT") +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +CopyRegistrySettingsPath( + _In_ PUNICODE_STRING RegistryPath +) +/*++ + +Routine Description: + +Copies the following registry path to a global variable. + +\REGISTRY\MACHINE\SYSTEM\ControlSetxxx\Services\<driver>\Parameters + +Arguments: + +RegistryPath - Registry path passed to DriverEntry + +Returns: + +NTSTATUS - SUCCESS if able to configure the framework + +--*/ + +{ + // Initializing the unicode string, so that if it is not allocated it will not be deallocated too. + RtlInitUnicodeString(&g_RegistryPath, NULL); + + g_RegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR); + + g_RegistryPath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, g_RegistryPath.MaximumLength, MINADAPTER_POOLTAG); + + if (g_RegistryPath.Buffer == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlAppendUnicodeToString(&g_RegistryPath, RegistryPath->Buffer); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("INIT") +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +GetRegistrySettings( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Initialize Driver Framework settings from the driver + specific registry settings under + + \REGISTRY\MACHINE\SYSTEM\ControlSetxxx\Services\<driver>\Parameters + +Arguments: + + RegistryPath - Registry path passed to DriverEntry + +Returns: + + NTSTATUS - SUCCESS if able to configure the framework + +--*/ + +{ + NTSTATUS ntStatus; + UNICODE_STRING parametersPath; + RTL_QUERY_REGISTRY_TABLE paramTable[] = { + // QueryRoutine Flags Name EntryContext DefaultType DefaultData DefaultLength + { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"DoNotCreateDataFiles", &g_DoNotCreateDataFiles, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &g_DoNotCreateDataFiles, sizeof(ULONG)}, + { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"DisableToneGenerator", &g_DisableToneGenerator, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &g_DisableToneGenerator, sizeof(ULONG)}, + { NULL, 0, NULL, NULL, 0, NULL, 0} + }; + + DPF(D_TERSE, ("[GetRegistrySettings]")); + + PAGED_CODE(); + + RtlInitUnicodeString(¶metersPath, NULL); + + parametersPath.MaximumLength = + RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(WCHAR); + + parametersPath.Buffer = (PWCH) ExAllocatePool2(POOL_FLAG_PAGED, parametersPath.MaximumLength, MINADAPTER_POOLTAG); + if (parametersPath.Buffer == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlAppendUnicodeToString(¶metersPath, RegistryPath->Buffer); + RtlAppendUnicodeToString(¶metersPath, L"\\Parameters"); + + ntStatus = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + parametersPath.Buffer, + ¶mTable[0], + NULL, + NULL + ); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RtlQueryRegistryValues failed, using default values, 0x%x", ntStatus)); + // + // Don't return error because we will operate with default values. + // + } + + // + // Dump settings. + // + DPF(D_VERBOSE, ("DoNotCreateDataFiles: %u", g_DoNotCreateDataFiles)); + DPF(D_VERBOSE, ("DisableToneGenerator: %u", g_DisableToneGenerator)); + + // + // Cleanup. + // + ExFreePool(parametersPath.Buffer); + + return STATUS_SUCCESS; +} + +#pragma code_seg("INIT") +extern "C" DRIVER_INITIALIZE DriverEntry; +extern "C" NTSTATUS +DriverEntry +( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPathName +) +{ +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + + All audio adapter drivers can use this code without change. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ + NTSTATUS ntStatus; + WDF_DRIVER_CONFIG config; + + DPF(D_TERSE, ("[DriverEntry]")); + + // Copy registry Path name in a global variable to be used by modules inside driver. + // !! NOTE !! Inside this function we are initializing the registrypath, so we MUST NOT add any failing calls + // before the following call. + ntStatus = CopyRegistrySettingsPath(RegistryPathName); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Registry path copy error 0x%x", ntStatus)), + Done); + + // + // Get registry configuration. + // + ntStatus = GetRegistrySettings(RegistryPathName); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Registry Configuration error 0x%x", ntStatus)), + Done); + + WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); + // + // Set WdfDriverInitNoDispatchOverride flag to tell the framework + // not to provide dispatch routines for the driver. In other words, + // the framework must not intercept IRPs that the I/O manager has + // directed to the driver. In this case, they will be handled by Audio + // port driver. + // + config.DriverInitFlags |= WdfDriverInitNoDispatchOverride; + config.DriverPoolTag = MINADAPTER_POOLTAG; + + ntStatus = WdfDriverCreate(DriverObject, + RegistryPathName, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + WDF_NO_HANDLE); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("WdfDriverCreate failed, 0x%x", ntStatus)), + Done); + + // + // Tell the class driver to initialize the driver. + // + ntStatus = PcInitializeAdapterDriver(DriverObject, + RegistryPathName, + (PDRIVER_ADD_DEVICE)AddDevice); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("PcInitializeAdapterDriver failed, 0x%x", ntStatus)), + Done); + + // + // To intercept stop/remove/surprise-remove. + // + DriverObject->MajorFunction[IRP_MJ_PNP] = PnpHandler; + + // + // Hook the port class unload function + // + gPCDriverUnloadRoutine = DriverObject->DriverUnload; + DriverObject->DriverUnload = DriverUnload; + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + if (!NT_SUCCESS(ntStatus)) + { + if (WdfGetDriver() != NULL) + { + WdfDriverMiniportUnload(WdfGetDriver()); + } + + ReleaseRegistryStringBuffer(); + } + + return ntStatus; +} // DriverEntry + +#pragma code_seg() +// disable prefast warning 28152 because +// DO_DEVICE_INITIALIZING is cleared in PcAddAdapterDevice +#pragma warning(disable:28152) +#pragma code_seg("PAGE") +//============================================================================= +NTSTATUS AddDevice +( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT PhysicalDeviceObject +) +/*++ + +Routine Description: + + The Plug & Play subsystem is handing us a brand new PDO, for which we + (by means of INF registration) have been asked to provide a driver. + + We need to determine if we need to be in the driver stack for the device. + Create a function device object to attach to the stack + Initialize that device object + Return status success. + + All audio adapter drivers can use this code without change. + +Arguments: + + DriverObject - pointer to a driver object + + PhysicalDeviceObject - pointer to a device object created by the + underlying bus driver. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus; + ULONG maxObjects; + + DPF(D_TERSE, ("[AddDevice]")); + + maxObjects = g_MaxMiniports; + + // Tell the class driver to add the device. + // + ntStatus = + PcAddAdapterDevice + ( + DriverObject, + PhysicalDeviceObject, + PCPFNSTARTDEVICE(StartDevice), + maxObjects, + 0 + ); + + return ntStatus; +} // AddDevice + +#pragma code_seg() +NTSTATUS +_IRQL_requires_max_(DISPATCH_LEVEL) +PowerControlCallback +( + _In_ LPCGUID PowerControlCode, + _In_opt_ PVOID InBuffer, + _In_ SIZE_T InBufferSize, + _Out_writes_bytes_to_(OutBufferSize, *BytesReturned) PVOID OutBuffer, + _In_ SIZE_T OutBufferSize, + _Out_opt_ PSIZE_T BytesReturned, + _In_opt_ PVOID Context +) +{ + UNREFERENCED_PARAMETER(PowerControlCode); + UNREFERENCED_PARAMETER(InBuffer); + UNREFERENCED_PARAMETER(InBufferSize); + UNREFERENCED_PARAMETER(OutBuffer); + UNREFERENCED_PARAMETER(OutBufferSize); + UNREFERENCED_PARAMETER(BytesReturned); + UNREFERENCED_PARAMETER(Context); + + return STATUS_NOT_IMPLEMENTED; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallEndpointRenderFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon, + _In_ PENDPOINT_MINIPAIR _pAeMiniports + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopology = NULL; + PUNKNOWN unknownWave = NULL; + PPORTCLSETWHELPER pPortClsEtwHelper = NULL; +#ifdef _USE_IPortClsRuntimePower + PPORTCLSRUNTIMEPOWER pPortClsRuntimePower = NULL; +#endif // _USE_IPortClsRuntimePower + PPORTCLSStreamResourceManager pPortClsResMgr = NULL; + PPORTCLSStreamResourceManager2 pPortClsResMgr2 = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_pDeviceObject); + + ntStatus = _pAdapterCommon->InstallEndpointFilters( + _pIrp, + _pAeMiniports, + NULL, + &unknownTopology, + &unknownWave, + NULL, NULL); + + if (unknownWave) // IID_IPortClsEtwHelper and IID_IPortClsRuntimePower interfaces are only exposed on the WaveRT port. + { + ntStatus = unknownWave->QueryInterface (IID_IPortClsEtwHelper, (PVOID *)&pPortClsEtwHelper); + if (NT_SUCCESS(ntStatus)) + { + _pAdapterCommon->SetEtwHelper(pPortClsEtwHelper); + ASSERT(pPortClsEtwHelper != NULL); + pPortClsEtwHelper->Release(); + } + +#ifdef _USE_IPortClsRuntimePower + // Let's get the runtime power interface on PortCls. + ntStatus = unknownWave->QueryInterface(IID_IPortClsRuntimePower, (PVOID *)&pPortClsRuntimePower); + if (NT_SUCCESS(ntStatus)) + { + // This interface would typically be stashed away for later use. Instead, + // let's just send an empty control with GUID_NULL. + NTSTATUS ntStatusTest = + pPortClsRuntimePower->SendPowerControl + ( + _pDeviceObject, + &GUID_NULL, + NULL, + 0, + NULL, + 0, + NULL + ); + + if (NT_SUCCESS(ntStatusTest) || STATUS_NOT_IMPLEMENTED == ntStatusTest || STATUS_NOT_SUPPORTED == ntStatusTest) + { + ntStatus = pPortClsRuntimePower->RegisterPowerControlCallback(_pDeviceObject, &PowerControlCallback, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pPortClsRuntimePower->UnregisterPowerControlCallback(_pDeviceObject); + } + } + else + { + ntStatus = ntStatusTest; + } + + pPortClsRuntimePower->Release(); + } +#endif // _USE_IPortClsRuntimePower + + // + // Test: add and remove current thread as streaming audio resource. + // In a real driver you should only add interrupts and driver-owned threads + // (i.e., do NOT add the current thread as streaming resource). + // + // testing IPortClsStreamResourceManager: + ntStatus = unknownWave->QueryInterface(IID_IPortClsStreamResourceManager, (PVOID *)&pPortClsResMgr); + if (NT_SUCCESS(ntStatus)) + { + PCSTREAMRESOURCE_DESCRIPTOR res; + PCSTREAMRESOURCE hRes = NULL; + PDEVICE_OBJECT pdo = NULL; + + PcGetPhysicalDeviceObject(_pDeviceObject, &pdo); + PCSTREAMRESOURCE_DESCRIPTOR_INIT(&res); + res.Pdo = pdo; + res.Type = ePcStreamResourceThread; + res.Resource.Thread = PsGetCurrentThread(); + + NTSTATUS ntStatusTest = pPortClsResMgr->AddStreamResource(NULL, &res, &hRes); + if (NT_SUCCESS(ntStatusTest)) + { + pPortClsResMgr->RemoveStreamResource(hRes); + hRes = NULL; + } + + pPortClsResMgr->Release(); + pPortClsResMgr = NULL; + } + + // testing IPortClsStreamResourceManager2: + ntStatus = unknownWave->QueryInterface(IID_IPortClsStreamResourceManager2, (PVOID *)&pPortClsResMgr2); + if (NT_SUCCESS(ntStatus)) + { + PCSTREAMRESOURCE_DESCRIPTOR res; + PCSTREAMRESOURCE hRes = NULL; + PDEVICE_OBJECT pdo = NULL; + + PcGetPhysicalDeviceObject(_pDeviceObject, &pdo); + PCSTREAMRESOURCE_DESCRIPTOR_INIT(&res); + res.Pdo = pdo; + res.Type = ePcStreamResourceThread; + res.Resource.Thread = PsGetCurrentThread(); + + NTSTATUS ntStatusTest = pPortClsResMgr2->AddStreamResource2(pdo, NULL, &res, &hRes); + if (NT_SUCCESS(ntStatusTest)) + { + pPortClsResMgr2->RemoveStreamResource(hRes); + hRes = NULL; + } + + pPortClsResMgr2->Release(); + pPortClsResMgr2 = NULL; + } + } + + SAFE_RELEASE(unknownTopology); + SAFE_RELEASE(unknownWave); + + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallAllRenderFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon + ) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR* ppAeMiniports = g_RenderEndpoints; + + PAGED_CODE(); + + for(ULONG i = 0; i < g_cRenderEndpoints; ++i, ++ppAeMiniports) + { + ntStatus = InstallEndpointRenderFilters(_pDeviceObject, _pIrp, _pAdapterCommon, *ppAeMiniports); + IF_FAILED_JUMP(ntStatus, Exit); + } + + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallEndpointCaptureFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon, + _In_ PENDPOINT_MINIPAIR _pAeMiniports +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_pDeviceObject); + + ntStatus = _pAdapterCommon->InstallEndpointFilters( + _pIrp, + _pAeMiniports, + NULL, + NULL, + NULL, + NULL, NULL); + + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallAllCaptureFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon +) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR* ppAeMiniports = g_CaptureEndpoints; + + PAGED_CODE(); + + for (ULONG i = 0; i < g_cCaptureEndpoints; ++i, ++ppAeMiniports) + { + ntStatus = InstallEndpointCaptureFilters(_pDeviceObject, _pIrp, _pAdapterCommon, *ppAeMiniports); + IF_FAILED_JUMP(ntStatus, Exit); + } + + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +StartDevice +( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_ PRESOURCELIST ResourceList +) +{ +/*++ + +Routine Description: + + This function is called by the operating system when the device is + started. + It is responsible for starting the miniports. This code is specific to + the adapter because it calls out miniports for functions that are specific + to the adapter. + +Arguments: + + DeviceObject - pointer to the driver object + + Irp - pointer to the irp + + ResourceList - pointer to the resource list assigned by PnP manager + +Return Value: + + NT status code. + +--*/ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(DeviceObject); + ASSERT(Irp); + ASSERT(ResourceList); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + PADAPTERCOMMON pAdapterCommon = NULL; + PUNKNOWN pUnknownCommon = NULL; + PortClassDeviceContext* pExtension = static_cast<PortClassDeviceContext*>(DeviceObject->DeviceExtension); + + DPF_ENTER(("[StartDevice]")); + + // + // create a new adapter common object + // + ntStatus = NewAdapterCommon( + &pUnknownCommon, + IID_IAdapterCommon, + NULL, + POOL_FLAG_NON_PAGED + ); + IF_FAILED_JUMP(ntStatus, Exit); + + ntStatus = pUnknownCommon->QueryInterface( IID_IAdapterCommon,(PVOID *) &pAdapterCommon); + IF_FAILED_JUMP(ntStatus, Exit); + + ntStatus = pAdapterCommon->Init(DeviceObject); + IF_FAILED_JUMP(ntStatus, Exit); + + // + // register with PortCls for power-management services + ntStatus = PcRegisterAdapterPowerManagement( PUNKNOWN(pAdapterCommon), DeviceObject); + IF_FAILED_JUMP(ntStatus, Exit); + + // + // Install wave+topology filters for render devices + // + ntStatus = InstallAllRenderFilters(DeviceObject, Irp, pAdapterCommon); + IF_FAILED_JUMP(ntStatus, Exit); + + // + // Install wave+topology filters for capture devices + // + ntStatus = InstallAllCaptureFilters(DeviceObject, Irp, pAdapterCommon); + IF_FAILED_JUMP(ntStatus, Exit); + +Exit: + + // + // Stash the adapter common object in the device extension so + // we can access it for cleanup on stop/removal. + // + if (pAdapterCommon) + { + ASSERT(pExtension != NULL); + pExtension->m_pCommon = pAdapterCommon; + } + + // + // Release the adapter IUnknown interface. + // + SAFE_RELEASE(pUnknownCommon); + + return ntStatus; +} // StartDevice + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PnpHandler +( + _In_ DEVICE_OBJECT *_DeviceObject, + _Inout_ IRP *_Irp +) +/*++ + +Routine Description: + + Handles PnP IRPs + +Arguments: + + _DeviceObject - Functional Device object pointer. + + _Irp - The Irp being passed + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + IO_STACK_LOCATION *stack; + PortClassDeviceContext *ext; + + // Documented https://msdn.microsoft.com/en-us/library/windows/hardware/ff544039(v=vs.85).aspx + // This method will be called in IRQL PASSIVE_LEVEL +#pragma warning(suppress: 28118) + PAGED_CODE(); + + ASSERT(_DeviceObject); + ASSERT(_Irp); + + // + // Check for the REMOVE_DEVICE irp. If we're being unloaded, + // uninstantiate our devices and release the adapter common + // object. + // + stack = IoGetCurrentIrpStackLocation(_Irp); + + switch (stack->MinorFunction) + { + case IRP_MN_REMOVE_DEVICE: + case IRP_MN_SURPRISE_REMOVAL: + case IRP_MN_STOP_DEVICE: + ext = static_cast<PortClassDeviceContext*>(_DeviceObject->DeviceExtension); + + if (ext->m_pCommon != NULL) + { + ext->m_pCommon->Cleanup(); + + ext->m_pCommon->Release(); + ext->m_pCommon = NULL; + } + break; + + default: + break; + } + + ntStatus = PcDispatchIrp(_DeviceObject, _Irp); + + return ntStatus; +} + +#pragma code_seg() + |
