// pnp.c // // // Requires DDK Only // File created on 2/2/2005 // #include "pch.h" #ifdef CPR_WMI_TRACE #include "pnp.tmh" #endif /////////////////////////////////////////////////////////////////////////////////////////////////// // CprPnpDispatch // Dispatch routine to handle PnP requests // // Arguments: // IN DeviceObject // pointer to our device object // // IN Irp // pointer to the PnP IRP // // Return Value: // NT status code // NTSTATUS CprPnpDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PCPR_DEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; NTSTATUS status; PDEVICE_CAPABILITIES deviceCapabilities; ULONG requestCount; ULONG index; PPNP_DEVICE_STATE deviceState; POWER_STATE powerState; __try { // Get our device extension from the device object deviceExtension = (PCPR_DEVICE_EXTENSION)DeviceObject->DeviceExtension; CprDebugPrint1(deviceExtension, DBG_PNP, DBG_TRACE, __FUNCTION__"++. IRP %p", Irp); CprDumpIrp(Irp, deviceExtension); // Get our current IRP stack location irpStack = IoGetCurrentIrpStackLocation(Irp); // Make sure we can accept IRPs if (!CprAcquireRemoveLock(deviceExtension)) { // Pass down. Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_WARN, __FUNCTION__"-- Lock Not Acquired. IRP %p STATUS %x", Irp, status); return status; } switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: // The device stack must be started from the bottom up. // So, we send the IRP down the stack and wait so that // all devices below ours have started before we process // this IRP and start ourselves. status = CprSubmitIrpSync(deviceExtension->LowerDeviceObject, Irp); if (!NT_SUCCESS(status)) { // Someone below us failed to start, so just complete with error break; } // Lower drivers have finished their start operation, so now // we process ours. // Start is an implicit power-up so // we set our device power state accordingly deviceExtension->DevicePowerState = PowerDeviceD0; powerState.DeviceState = deviceExtension->DevicePowerState; PoSetPowerState(deviceExtension->DeviceObject, DevicePowerState, powerState); status = CprGetDeviceCapabilities(deviceExtension); if (!NT_SUCCESS(status)) { CprFreeSerialResources(deviceExtension); break; } status = CprStartSerialDevice(deviceExtension, Irp); if (!NT_SUCCESS(status)) { CprFreeSerialResources(deviceExtension); break; } // Update our PnP state deviceExtension->PnpState = PnpStateStarted; // restart any stalled queues CprRestartQueues(deviceExtension); break; case IRP_MN_QUERY_STOP_DEVICE: if (CprIsStoppable(deviceExtension)) { // Device is stoppable. // pause io request processing CprStallQueues(deviceExtension); // Update our PnP state deviceExtension->PreviousPnpState = deviceExtension->PnpState; deviceExtension->PnpState = PnpStateStopPending; // We must set Irp->IoStatus.Status to STATUS_SUCCESS before // passing it down. Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); // Decrement the active I/O count CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; } else { // Pass down. Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"-- CPR not Stoppable. IRP %p STATUS %x", Irp, status); CprReleaseRemoveLock(deviceExtension); return status; } break; case IRP_MN_CANCEL_STOP_DEVICE: // Send this IRP down and wait for it to come back, // restart our stall fifo, // and process all the previously queued up IRPs. // First check to see whether we received a query before this // cancel. This could happen if someone above us failed a query // and passed down the subsequent cancel. if (deviceExtension->PnpState == PnpStateStopPending) { status = CprSubmitIrpSync(deviceExtension->LowerDeviceObject,Irp); if (NT_SUCCESS(status)) { // restore previous pnp state deviceExtension->PnpState = deviceExtension->PreviousPnpState; // restart the queues CprRestartQueues(deviceExtension); } else { // Somebody below us failed the cancel // this is a fatal error. ASSERTMSG("Cancel stop failed!", FALSE); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_ERR, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); } } else { // Spurious cancel so we just complete the request. // Pass down. Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; } break; case IRP_MN_STOP_DEVICE: // Mark the device as stopped. deviceExtension->PnpState = PnpStateStopped; // release our resources CprFreeSerialResources(deviceExtension); // send the request down, and we are done Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; case IRP_MN_QUERY_REMOVE_DEVICE: if (CprIsRemovable(deviceExtension)) { // pause io request processing CprStallQueues(deviceExtension); // Update our PnP state deviceExtension->PreviousPnpState = deviceExtension->PnpState; deviceExtension->PnpState = PnpStateRemovePending; // Now just send the request down and we are done Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); if (g_Data.BackdoorDevice != NULL) { PBACKDOOR_DEVICE_EXTENSION BackdoorDevExt = (PBACKDOOR_DEVICE_EXTENSION)g_Data.BackdoorDevice->DeviceExtension; BackdoorDevExt->numPorts--; } // decrement the I/O count CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; } else { // Our device is not removable, just fail the request // Pass down. Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; } break; case IRP_MN_CANCEL_REMOVE_DEVICE: // First check to see whether we have received a prior query // remove request. It could happen that we did not if // someone above us failed a query remove and passed down the // subsequent cancel remove request. if (PnpStateRemovePending == deviceExtension->PnpState) { status = CprSubmitIrpSync(deviceExtension->LowerDeviceObject, Irp); if (NT_SUCCESS(status)) { // restore pnp state, since remove was canceled deviceExtension->PnpState = deviceExtension->PreviousPnpState; // restart the queues CprRestartQueues(deviceExtension); } else { // Nobody can fail this IRP. This is a fatal error. ASSERTMSG("IRP_MN_CANCEL_REMOVE_DEVICE failed. Fatal error!", FALSE); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); } } else { // Spurious cancel remove request so we just complete it // Pass down. #if 0 Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; #else status = STATUS_SUCCESS; #endif } break; case IRP_MN_SURPRISE_REMOVAL: // The device has been unexpectedly removed from the machine // and is no longer available for I/O. // We must return device and memory resources, // disable interfaces. We will defer failing any outstanding // request to IRP_MN_REMOVE. // stall all the queues CprStallQueues(deviceExtension); // flush pending io list CprInvalidateIo(&deviceExtension->IoLock, STATUS_NO_SUCH_DEVICE); // update pnp state deviceExtension->PnpState = PnpStateSurpriseRemoved; // Return any resources acquired during device startup. CprFreeSerialResources(deviceExtension); // We must set Irp->IoStatus.Status to STATUS_SUCCESS before // passing it down. Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); if ((deviceExtension->portNumber > 0) && (deviceExtension->portNumber <= MAX_PORTS)) { PBACKDOOR_DEVICE_EXTENSION bdExt = (PBACKDOOR_DEVICE_EXTENSION)g_Data.BackdoorDevice->DeviceExtension; bdExt->serialDevice[deviceExtension->portNumber] = NULL; } // Adjust the active I/O count CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; case IRP_MN_REMOVE_DEVICE: // The Plug & Play system has dictated the removal of this device. We // have no choice but to detach and delete the device object. if (deviceExtension->PnpState != PnpStateSurpriseRemoved) { // flush pending io list CprInvalidateIo(&deviceExtension->IoLock, STATUS_NO_SUCH_DEVICE); CprFreeSerialResources(deviceExtension); } // Update our PnP state deviceExtension->PnpState = PnpStateRemoved; CprReleaseRemoveLock(deviceExtension); CprWaitForSafeRemove(deviceExtension); if (deviceExtension->LowerDeviceObject != NULL) { // Send the remove IRP down the stack. Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver (deviceExtension->LowerDeviceObject, Irp); // Detach our device object from the device stack IoDetachDevice(deviceExtension->LowerDeviceObject); } // free device name string if (deviceExtension->DeviceName.Buffer) ExFreePool(deviceExtension->DeviceName.Buffer); // free power work item if (deviceExtension->PowerWorkItem) IoFreeWorkItem(deviceExtension->PowerWorkItem); // attempt to delete our device object if (deviceExtension->DeviceObject) IoDeleteDevice(deviceExtension->DeviceObject); // decrement device instance count --g_Data.InstanceCount; if ((deviceExtension->portNumber > 0) && (deviceExtension->portNumber <= MAX_PORTS)) { if ((g_Data.BackdoorDevice) && (g_Data.BackdoorDevice->DeviceExtension)) { PBACKDOOR_DEVICE_EXTENSION bdExt = (PBACKDOOR_DEVICE_EXTENSION)g_Data.BackdoorDevice->DeviceExtension; bdExt->serialDevice[deviceExtension->portNumber] = NULL; } } if (g_Data.InstanceCount == 0) { // If we don't release it here then the CprUnload routine will not get called. // This is because the PnP Manager would think there were still references // to this driver and will not call CprUnload. // CprDeleteBackdoorDevice(); } CprDebugPrint3(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x InstanceCount %d", Irp, status, g_Data.InstanceCount); return status; case IRP_MN_QUERY_CAPABILITIES: // Check the device capabilities struct deviceCapabilities = irpStack->Parameters.DeviceCapabilities.Capabilities; if (deviceCapabilities->Version != 1 || deviceCapabilities->Size < sizeof(DEVICE_CAPABILITIES)) { // We don't support this version. Fail the request // Pass down. Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; } // Pass the IRP down status = CprSubmitIrpSync(deviceExtension->LowerDeviceObject, Irp); // Lower drivers have finished their operation, so now // we can finish ours. if (NT_SUCCESS(status)) { //***************************************************************** //***************************************************************** // TODO: Override the device capabilities // set by the underlying drivers here. // DAG: No capabilities to override for CPR. //***************************************************************** //***************************************************************** } break; case IRP_MN_QUERY_PNP_DEVICE_STATE: { PNP_DEVICE_STATE pds = 0; // Pass down. Irp->IoStatus.Information = pds; Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; } break; default: if (irpStack->MinorFunction == IRP_MN_QUERY_DEVICE_RELATIONS) { CprDebugPrint1(deviceExtension, DBG_PNP, DBG_INFO, " TYPE %d", irpStack->Parameters.QueryDeviceRelations.Type); if (irpStack->Parameters.QueryDeviceRelations.Type == RemovalRelations) { CprHandleRemovalRelations(DeviceObject, Irp, deviceExtension); Irp->IoStatus.Status = STATUS_SUCCESS; } } //else if (irpStack->MinorFunction == IRP_MN_QUERY_ID) //{ // CprDebugPrint1(deviceExtension, DBG_PNP, DBG_INFO, " IDTYPE %d", irpStack->Parameters.QueryId.IdType); //} // Pass down. IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->LowerDeviceObject, Irp); // Adjust our active I/O count CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_INFO, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); return status; } Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); // Adjust the active I/O count CprReleaseRemoveLock(deviceExtension); CprDebugPrint2(deviceExtension, DBG_PNP, DBG_TRACE, __FUNCTION__"--. IRP %p STATUS %x", Irp, status); } __except( EXCEPTION_EXECUTE_HANDLER ) { status = STATUS_INTERNAL_ERROR; CprDebugPrint2(deviceExtension, DBG_PNP, DBG_ERR, __FUNCTION__"--. EXCEPTION IRP %p STATUS %x", Irp, status); } return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprGetDeviceCapabilities // Gets device capabilities structure from a bus driver // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // NT status code // NTSTATUS CprGetDeviceCapabilities( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { NTSTATUS status; PDEVICE_OBJECT targetDeviceObject; PIRP irp; KEVENT event; IO_STATUS_BLOCK ioStatus; PIO_STACK_LOCATION irpStack; CprDebugPrint(DeviceExtension, DBG_PNP, DBG_TRACE, __FUNCTION__"++"); // initialize device capabilities structure RtlZeroMemory(&DeviceExtension->DeviceCaps, sizeof(DEVICE_CAPABILITIES)); DeviceExtension->DeviceCaps.Size = sizeof(DEVICE_CAPABILITIES); DeviceExtension->DeviceCaps.Version = 1; DeviceExtension->DeviceCaps.UINumber = -1; DeviceExtension->DeviceCaps.Address = -1; targetDeviceObject = IoGetAttachedDeviceReference(DeviceExtension->DeviceObject); KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, targetDeviceObject, NULL, 0, NULL, &event, &ioStatus ); if (irp != NULL) { irpStack = IoGetNextIrpStackLocation(irp); irpStack->MajorFunction = IRP_MJ_PNP; irpStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES; irpStack->Parameters.DeviceCapabilities.Capabilities = &DeviceExtension->DeviceCaps; irp->IoStatus.Status = STATUS_NOT_SUPPORTED; status = IoCallDriver(targetDeviceObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } } else { status = STATUS_INSUFFICIENT_RESOURCES; } ObDereferenceObject(targetDeviceObject); CprDebugPrint1(DeviceExtension, DBG_PNP, DBG_TRACE, __FUNCTION__"--. STATUS %x", status); return status; } NTSTATUS CprHandleRemovalRelations( PDEVICE_OBJECT DeviceObject, PIRP Irp, PCPR_DEVICE_EXTENSION devExt ) { //PAGED_CODE (); PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS Status = STATUS_SUCCESS; CprDebugPrint(devExt, DBG_PNP, DBG_TRACE, __FUNCTION__"++"); // // You might need some locking here if you are testing the state of // some other device that your driver controls // // // removeOk decides if it is 'safe' to remove // this path. A simple rule would be if there exists one // other operational path then it is safe to remove // this path. // if ( CprIsRemovable(devExt) == FALSE ) { int relationsSize = sizeof(DEVICE_RELATIONS); // minimum size; PBACKDOOR_DEVICE_EXTENSION BackdoorDevExt = (PBACKDOOR_DEVICE_EXTENSION)g_Data.BackdoorDevice->DeviceExtension; // // if it is not safe to remove this path then // let pnp know that removal relations are important, // in which case pnp will query the remaining virtual // port devices to see if it is ok to remove them. // CprIsStoppable checks the updateDevice flag in the // backdoor device. If this flag is set (by the // CPR_IOCTL_UPDATE_DRIVER IOCTL), then FALSE is returned. // Then we send back a list of all the other ports. // Then we clear the updateDevice flag so the other requests // to DELETE will succeed. // g_Data.updateDriver = FALSE; // Remove this port from the calculation relationsSize += (g_Data.InstanceCount - 1) * sizeof(PDEVICE_OBJECT); if (relationsSize > 0) { // PnP Manager will release the memory. // PDEVICE_RELATIONS deviceRelations = (PDEVICE_RELATIONS) ExAllocatePoolWithTag( PagedPool, relationsSize, CPR_POOL_TAG_REMOVAL ); if (deviceRelations) { int i; PDEVICE_OBJECT *pSerialDevices; // // point the irp at our new relations structure. // // Where will this be deallocated??? // RtlZeroMemory(deviceRelations, relationsSize); Irp->IoStatus.Information = (ULONG_PTR)(deviceRelations); // // add the virtual devices to the removal relations // pSerialDevices = BackdoorDevExt->serialDevice; for (i = 1; i <= MAX_PORTS; i++) { // Find current serial devices. // Don't include this Device Object if ((pSerialDevices[i] != NULL) && (pSerialDevices[i] != DeviceObject)) { PCPR_DEVICE_EXTENSION deviceExtension; deviceExtension = (PCPR_DEVICE_EXTENSION)pSerialDevices[i]->DeviceExtension; //DbgPrint("Please remove port %d first\n", deviceExtension->portNumber); deviceRelations->Objects[deviceRelations->Count] = pSerialDevices[i]; ObReferenceObject(pSerialDevices[i]); deviceRelations->Count++; } } //DbgPrint("%d ports need removing\n", deviceRelations->Count); Status = STATUS_SUCCESS; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } } // // if you acquired a lock, now would be an excellent time // to release it. // // // make sure we have updated the status // Irp->IoStatus.Status = Status; CprDebugPrint1(devExt, DBG_PNP, DBG_TRACE, __FUNCTION__"--. Status %x", Status); return Status; }