I has been tested a driver. When I attempt to disable a driver, it receives IRP_MN_QUERY_REMOVE_DEVICE but never receives IRP_MN_REMOVE_DEVICE. When I compile a same driver for Windows XP PnP signalisation works fine. IRP_MN_REMOVE_DEVICE is received. I have no clue why Windows does not send me IRP_MN_REMOVE_DEVICE.
Here is a description on Microsoft pages, but there are no explanation, why Windows 10 hangs in remove-pending state.
https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/understanding-when-remove-irps-are-issued
https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/images/rem-irps.png
The IRP dispatching seems workable:
static NTSTATUS DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS status;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
DEVICEDATA *deviceData = (DEVICEDATA*)DeviceObject->DeviceExtension;
switch(stack->MinorFunction)
{
case IRP_MN_START_DEVICE:
_DbgPrintF(DEBUGLVL_VERBOSE, "IRP_MN_START_DEVICE");
if(deviceData->NumMemResources>0 || deviceData->NumIOResources>0 || deviceData->NumIRQResources>0)
{
DeallocateResources(deviceData); /* Double start condition!!! */
}
status = PnpStart(DeviceObject, Irp);
deviceData->DevicePnPState = Started;
break;
case IRP_MN_QUERY_STOP_DEVICE:
_DbgPrintF(DEBUGLVL_VERBOSE, "IRP_MN_QUERY_STOP_DEVICE");
deviceData->DevicePnPState = StopPending;
SendIrpSynchronously(deviceData->NextLowerDriver, Irp);
status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_REMOVE_DEVICE: /* warning before device removing */
_DbgPrintF(DEBUGLVL_VERBOSE, "IRP_MN_QUERY_REMOVE_DEVICE");
deviceData->DevicePnPState = RemovePending;
//IoSetDeviceInterfaceState(&deviceData->InterfaceName, FALSE); // Shutdown all outstanding interfaces.
//https://github.com/teeedubb/ScpServer/blob/master/ScpVBus/bus/buspdo.c
//https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/handling-an-irp-mn-query-remove-device-request
//SendIrpSynchronously(deviceData->NextLowerDriver, Irp);
//_DbgPrintF(DEBUGLVL_VERBOSE, "SendIrpSynchronously() finished");
//status = STATUS_SUCCESS;
//break;
//Irp->IoStatus.Status = STATUS_SUCCESS;
//IoCompleteRequest(Irp, IO_NO_INCREMENT);
//return STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation(Irp);
status = IoCallDriver(deviceData->NextLowerDriver, Irp);
_DbgfprintF(DEBUGLVL_VERBOSE, "query remove IoCallDriver status = %Xh\n",status);
return status;
//status = SendIrpAsync(deviceData->NextLowerDriver, Irp);
//_DbgfprintF(DEBUGLVL_VERBOSE, "status = %Xh\n",status);
//return status;
case IRP_MN_STOP_DEVICE:
_DbgPrintF(DEBUGLVL_VERBOSE, "IRP_MN_STOP_DEVICE");
deviceData->DevicePnPState = Stopped;
DeallocateResources(deviceData);
/* Pass the IRP down so that lower drivers can stop. */
Irp->IoStatus.Status = STATUS_SUCCESS; // A driver must set Irp->IoStatus.Status to STATUS_SUCCESS.
SendIrpAsync(deviceData->NextLowerDriver, Irp);
return STATUS_SUCCESS;
case IRP_MN_REMOVE_DEVICE:
_DbgPrintF(DEBUGLVL_VERBOSE, "IRP_MN_REMOVE_DEVICE");
deviceData->DevicePnPState = Deleted;
/* Disable the device interface */
IoSetDeviceInterfaceState(&deviceData->InterfaceName, FALSE);
DeallocateResources(deviceData);
Irp->IoStatus.Status = STATUS_SUCCESS; // A driver must set Irp->IoStatus.Status to STATUS_SUCCESS.
SendIrpAsync(deviceData->NextLowerDriver, Irp);
// delete our device, we have to do this after we send the request down
IoDetachDevice(deviceData->NextLowerDriver);
deviceData->NextLowerDriver = NULL;
IoDeleteDevice(DeviceObject);
return STATUS_SUCCESS;
// This could allow to unload and load driver without rebooting. Not tested yet.
case IRP_MN_QUERY_CAPABILITIES:
_DbgPrintF(DEBUGLVL_VERBOSE, "IRP_MN_QUERY_CAPABILITIES");
{
PDEVICE_CAPABILITIES DeviceCapabilities;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
//TRACE(TL_TRACE, ("t1394VDev_Pnp: IRP_MN_QUERY_CAPABILITIES\n"));
DeviceCapabilities = IrpSp->Parameters.DeviceCapabilities.Capabilities;
if(DeviceCapabilities)
DeviceCapabilities->SurpriseRemovalOK = TRUE;
SendIrpSynchronously(deviceData->NextLowerDriver, Irp);
status = STATUS_SUCCESS;
break;
}
case IRP_MN_QUERY_DEVICE_RELATIONS:
_DbgPrintF(DEBUGLVL_VERBOSE, "IRP_MN_QUERY_DEVICE_RELATIONS");
Irp->IoStatus.Status = STATUS_SUCCESS; // A driver must set Irp->IoStatus.Status to STATUS_SUCCESS.
IoSkipCurrentIrpStackLocation(Irp);
return(IoCallDriver(deviceData->NextLowerDriver, Irp));
//Irp->IoStatus.Status = status;
//IoCompleteRequest(Irp, IO_NO_INCREMENT);
//return STATUS_SUCCESS;
case IRP_MN_CANCEL_REMOVE_DEVICE:
_DbgPrintF(DEBUGLVL_VERBOSE, "IRP_MN_CANCEL_REMOVE_DEVICE");
IoSkipCurrentIrpStackLocation(Irp);
return(IoCallDriver(deviceData->NextLowerDriver, Irp));
default : // Asynchronous handling of unknown PnP request.
_DbgfprintF(DEBUGLVL_VERBOSE, "Unknown IRP received %u\n",stack->MinorFunction);
IoSkipCurrentIrpStackLocation(Irp);
return(IoCallDriver(deviceData->NextLowerDriver, Irp));
//break;
}
/* Signal OK and complete the IRP. */
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(status);
}