// =================================================================== // // testpnp.c // // rev 1.0 beta // // this module handles USB PnP calls. // // USB device driver for USB Device Example // kernel mode driver // // // to be compiled with // - Windows 98 Beta 3 DDK // // =================================================================== #include #include "test98.h" #include "stdio.h" // ================================================================== NTSTATUS Test98_AddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++ to create and initialize our FDO, set things up... DeviceObject - Device object representing the new FDO. DriverObject - This very self referenced driver. --*/ { NTSTATUS status; PDEVICE_OBJECT deviceObject; PFDO_DEVICE_DATA deviceData; UNICODE_STRING deviceNameUni; PWCHAR deviceName; ULONG nameLength; UNICODE_STRING pdoUniName; WCHAR pdoName[] = L"\\Device\\Test-0" ; // DeviceNameBuffer WCHAR DeviceLinkBuffer[] = L"\\DosDevices\\Test-0"; UNICODE_STRING DeviceLinkUnicodeString; RtlInitUnicodeString (&pdoUniName, pdoName); status = IoCreateDevice ( DriverObject, // our driver object sizeof (FDO_DEVICE_DATA), // device object extension size &pdoUniName, FILE_DEVICE_UNKNOWN, 0, // No special characteristics FALSE, &deviceObject); // The device object created if (NT_SUCCESS (status)) { RtlInitUnicodeString (&DeviceLinkUnicodeString, DeviceLinkBuffer); status = IoCreateSymbolicLink(&DeviceLinkUnicodeString, &pdoUniName); deviceData = (PFDO_DEVICE_DATA) deviceObject->DeviceExtension; RtlFillMemory (deviceData, sizeof (FDO_DEVICE_DATA), 0); deviceData->IsFDO = TRUE; deviceData->DebugLevel = TEST_DEFAULT_DEBUG_OUTPUT_LEVEL; deviceData->Self = deviceObject; KeInitializeSpinLock (&deviceData->Spin); deviceData->Removed = FALSE; InitializeListHead (&deviceData->PDOs); // Set the PDO for use with PlugPlay functions deviceData->UnderlyingPDO = PhysicalDeviceObject; // Attach our driver to the device stack. deviceData->TopOfStack = IoAttachDeviceToDeviceStack ( deviceObject, PhysicalDeviceObject); // Bias outstanding request to 1 so that we can look for a // transition to zero when processing the remove device PlugPlay IRP. deviceData->OutstandingIO = 1; KeInitializeEvent(&deviceData->RemoveEvent, SynchronizationEvent, FALSE); // initialized to not signalled deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; deviceObject->Flags |= DO_POWER_PAGABLE; // register device functionality status = IoRegisterDeviceInterface ( PhysicalDeviceObject, (LPGUID) &GUID_TEST_DEVICE, NULL, // No ref string &deviceData->DevClassAssocName); status = IoSetDeviceInterfaceState ( &deviceData->DevClassAssocName, TRUE); } return status; } // ================================================================== NTSTATUS Test98_FDO_PnPComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Pirp, IN PVOID Context ); // ================================================================== NTSTATUS Test98_PnP ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Handles Major PnP IRPS... --*/ { PIO_STACK_LOCATION irpStack; NTSTATUS status; PCOMMON_DEVICE_DATA commonData; KIRQL oldIrq; PAGED_CODE (); status = STATUS_SUCCESS; irpStack = IoGetCurrentIrpStackLocation (Irp); ASSERT (IRP_MJ_PNP == irpStack->MajorFunction); commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension; status = Test98_FDO_PnP ( DeviceObject, Irp, irpStack, (PFDO_DEVICE_DATA) commonData); return status; } // ================================================================== NTSTATUS Test98_FDO_PnP ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpStack, IN PFDO_DEVICE_DATA DeviceData ) /*++ Handles requests from the PlugPlay system for the FDO --*/ { NTSTATUS status; KIRQL oldIrq; KEVENT event; ULONG length; ULONG i; PLIST_ENTRY entry; PPDO_DEVICE_DATA pdoData; PDEVICE_RELATIONS relations; PIO_STACK_LOCATION stack; PAGED_CODE (); status = Test98_IncIoCount (DeviceData); if (!NT_SUCCESS (status)) { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } stack = IoGetCurrentIrpStackLocation (Irp); switch (IrpStack->MinorFunction) { case IRP_MN_START_DEVICE: // PnP manager send this IPR after assigning resoureces to the device; // the device may have been recently numerate and being started // for the first time if (DeviceData->Started) { status = STATUS_SUCCESS; break; } Test98_KdPrint (DeviceData, TEST_DBG_PNP_TRACE, ("Start Device\n")); KeInitializeEvent (&event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext (Irp); IoSetCompletionRoutine (Irp, Test98_FDO_PnPComplete, &event, TRUE, TRUE, TRUE); status = IoCallDriver (DeviceData->TopOfStack, Irp); if (STATUS_PENDING == status) { // wait for it... status = KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, // Not allertable NULL); // No timeout structure ASSERT (STATUS_SUCCESS == status); status = Irp->IoStatus.Status; } if (NT_SUCCESS(status)) { // // Do what ever after device started // if ((NULL == stack->Parameters.StartDevice.AllocatedResources) || (NULL == stack->Parameters.StartDevice.AllocatedResourcesTranslated)) { status = STATUS_INSUFFICIENT_RESOURCES; break; } status = Test98_StartFdo (DeviceData, &stack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList, &stack->Parameters.StartDevice.AllocatedResourcesTranslated->List[0].PartialResourceList); if (NT_SUCCESS (status)) { DeviceData->Started = TRUE; } } // // We must now complete the IRP, since we stopped it in the // completetion routine with MORE_PROCESSING_REQUIRED. // Irp->IoStatus.Information = 0; break; case IRP_MN_QUERY_STOP_DEVICE: Test98_KdPrint (DeviceData, TEST_DBG_PNP_TRACE, ("Query Stop Device\n")); // This occurs during shutdown when user explicitly requesting // the service to be stopped // if (DeviceData->NumPDOs) { status = STATUS_UNSUCCESSFUL; } else { status = STATUS_SUCCESS; } Irp->IoStatus.Status = status; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver (DeviceData->TopOfStack, Irp); Test98_DecIoCount (DeviceData); return status; case IRP_MN_STOP_DEVICE: Test98_KdPrint (DeviceData, TEST_DBG_PNP_TRACE, ("Stop Device\n")); // PnP manager sends this IPR to stop a device so that it can reconfigure // its resources. sends this only after IRP_MN_QUERY_STOP_DEVICE // completed successfully // if (DeviceData->Started) { // // Free resources given by start device. // if (DeviceData->MappedPorts) { MmUnmapIoSpace (DeviceData->TestPortAddress, 1); } } DeviceData->Started = FALSE; // // don't need a completion routine so fire and forget. // Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver (DeviceData->TopOfStack, Irp); Test98_DecIoCount (DeviceData); return status; case IRP_MN_REMOVE_DEVICE: Test98_KdPrint (DeviceData, TEST_DBG_PNP_TRACE, ("Remove Device\n")); // // PnP manager uses this IRP to dirct drivers to remove a device // DeviceData->Removed = TRUE; // // Complete any outstanding IRPs queued by the driver here. // IoSetDeviceInterfaceState (&DeviceData->DevClassAssocName, FALSE); // // complete any outstanding reqeusts now. // Fire and forget // IoSkipCurrentIrpStackLocation (Irp); IoCallDriver (DeviceData->TopOfStack, Irp); // // Wait for all outstanding requests to complete // i = InterlockedDecrement (&DeviceData->OutstandingIO); ASSERT (0 < i); if (0 != InterlockedDecrement (&DeviceData->OutstandingIO)) { Test98_KdPrint (DeviceData, TEST_DBG_PNP_INFO, ("Remove Device waiting for request to complete\n")); KeWaitForSingleObject (&DeviceData->RemoveEvent, Suspended, KernelMode, FALSE, // Not Alertable NULL); // No timeout } // // Free the associated resources // Detatch from the undelying devices. // Test98_KdPrint(DeviceData, TEST_DBG_PNP_INFO, ("IoDetachDevice: 0x%x\n", DeviceData->TopOfStack)); IoDetachDevice (DeviceData->TopOfStack); // // Clean up any resources here // if (DeviceData->Started) { // // Free resources given by start device. // if (DeviceData->MappedPorts) { MmUnmapIoSpace (DeviceData->TestPortAddress, 1); } } ExFreePool (DeviceData->DevClassAssocName.Buffer); Test98_KdPrint(DeviceData, TEST_DBG_PNP_INFO, ("IoDeleteDevice1: 0x%x\n", DeviceObject)); IoDeleteDevice (DeviceObject); return STATUS_SUCCESS; case IRP_MN_QUERY_REMOVE_DEVICE: // // the driver indicates to system whether it can be removed // in response to this IPR... // Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver (DeviceData->TopOfStack, Irp); Test98_DecIoCount (DeviceData); return status; default: // // In the default case we merely call the next driver since // we don't know what to do. // Fire and Forget // IoSkipCurrentIrpStackLocation (Irp); // // Done, do NOT complete the IRP, it will be processed by the lower // device object, which will complete the IRP // status = IoCallDriver (DeviceData->TopOfStack, Irp); Test98_DecIoCount (DeviceData); return status; } Irp->IoStatus.Status = status; DbgRaiseIrql (DISPATCH_LEVEL, &oldIrq); IoCompleteRequest (Irp, IO_NO_INCREMENT); DbgLowerIrql (oldIrq); Test98_DecIoCount (DeviceData); return status; } // ================================================================== NTSTATUS Test98_FDO_PnPComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ A completion routine for use when calling the lower device objects to which our FDO is attached. --*/ { UNREFERENCED_PARAMETER (DeviceObject); if (Irp->PendingReturned) { IoMarkIrpPending( Irp ); } KeSetEvent ((PKEVENT) Context, 1, FALSE); // No special priority // No Wait return STATUS_MORE_PROCESSING_REQUIRED; // Keep this IRP } // ================================================================== NTSTATUS Test98_PnPRemove ( PDEVICE_OBJECT Device, PPDO_DEVICE_DATA PdoData ) /*++ The PnP manager has instructed that this driver should be removed. We should therefore complete any requests queued in the driver and IoDeleteDevice. and Return from the dispatch routine. --*/ { PdoData->Removed = TRUE; // // Complete any outsanding requests with STATUS_DELETE_PENDING. // if (PdoData->Attached) { return STATUS_SUCCESS; } // // Free any resources. // ExFreePool (PdoData->HardwareIDs); Test98_KdPrint(PdoData, TEST_DBG_PNP_INFO, ("IoDeleteDevice2: 0x%x\n", Device)); IoDeleteDevice (Device); return STATUS_SUCCESS; } // ================================================================== NTSTATUS Test98_StartFdo ( IN PFDO_DEVICE_DATA FdoData, IN PCM_PARTIAL_RESOURCE_LIST PartialResourceList, IN PCM_PARTIAL_RESOURCE_LIST PartialResourceListTranslated ) /* starting our FDO... */ { ULONG i; NTSTATUS status = STATUS_SUCCESS; PCM_PARTIAL_RESOURCE_DESCRIPTOR resource; PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceTrans; Test98_KdPrint (FdoData, TEST_DBG_PNP_TRACE, ("StartFdo\n")); for (i = 0, resource = &PartialResourceList->PartialDescriptors[0], resourceTrans = &PartialResourceListTranslated->PartialDescriptors[0]; i < PartialResourceList->Count && NT_SUCCESS(status); i++, resource++, resourceTrans++) { switch (resource->Type) { case CmResourceTypePort: FdoData->PhysicalAddress = resource->u.Port.Start; Test98_KdPrint (FdoData, TEST_DBG_PNP_INFO, ("HardwareResource: Port (%x) ->", FdoData->PhysicalAddress.LowPart)); switch (resourceTrans->Type) { case CmResourceTypePort: ASSERT (1 == resourceTrans->u.Port.Length); // Nothing to do here but note the address; FdoData->TestPortAddress = (PVOID) resourceTrans->u.Port.Start.LowPart; break; case CmResourceTypeMemory: // // We need to map the memory // ASSERT (1 == resourceTrans->u.Memory.Length); FdoData->TestPortAddress = MmMapIoSpace (resourceTrans->u.Memory.Start, resourceTrans->u.Memory.Length, MmNonCached); FdoData->MappedPorts = TRUE; Test98_KdPrint_Cont (FdoData, TEST_DBG_PNP_INFO, ("Mem: (%x)\n", FdoData->TestPortAddress)); break; default: Test98_KdPrint_Cont (FdoData, TEST_DBG_PNP_INFO, ("Unknown \n", FdoData->TestPortAddress)); } break; case CmResourceTypeMemory: ASSERT (CmResourceTypeMemory == resourceTrans->Type); FdoData->PhysicalAddress = resource->u.Memory.Start; FdoData->TestPortAddress = MmMapIoSpace (resourceTrans->u.Memory.Start, resourceTrans->u.Memory.Length, MmNonCached); FdoData->MappedPorts = TRUE; ASSERT (1 == resource->u.Memory.Length); Test98_KdPrint (FdoData, TEST_DBG_PNP_INFO, ("HardwareResource: Memory (%x) -> Mem (%x)", FdoData->PhysicalAddress.LowPart, FdoData->TestPortAddress)); break; case CmResourceTypeInterrupt: default: Test98_KdPrint (FdoData, TEST_DBG_PNP_ERROR, ("Unhandled resource type (0x%x)\n", resource->Type)); status = STATUS_UNSUCCESSFUL; } } return status; } // ================================================================== NTSTATUS Test98_Remove ( PTEST98_REMOVE_HARDWARE Remove, PFDO_DEVICE_DATA FdoData ) /* to remove the FDO and then tell Plug and Play about it. */ { KIRQL irql; PLIST_ENTRY entry; PPDO_DEVICE_DATA pdoData; BOOLEAN found = FALSE; PVOID handle = NULL; KeAcquireSpinLock (&FdoData->Spin, &irql); for (entry = FdoData->PDOs.Flink; entry != &FdoData->PDOs; entry = entry->Flink) { pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link); if (!handle) { handle = pdoData->Self; } if (handle == Remove->HardwareHandle) { pdoData->Attached = FALSE; RemoveEntryList (&pdoData->Link); FdoData->NumPDOs--; found = TRUE; } } KeReleaseSpinLock (&FdoData->Spin, irql); if (found) { IoInvalidateDeviceRelations (FdoData->UnderlyingPDO, BusRelations); return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } // ================================================================== NTSTATUS Test98_Power ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /* We do nothing special for power; */ { PIO_STACK_LOCATION irpStack; NTSTATUS status; PCOMMON_DEVICE_DATA commonData; PAGED_CODE (); status = STATUS_SUCCESS; irpStack = IoGetCurrentIrpStackLocation (Irp); ASSERT (IRP_MJ_POWER == irpStack->MajorFunction); commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension; status = Test98_FDO_Power ((PFDO_DEVICE_DATA) DeviceObject->DeviceExtension, Irp); return status; } // ================================================================== NTSTATUS Test98_PowerComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); // ================================================================== NTSTATUS Test98_FDO_Power ( PFDO_DEVICE_DATA Data, PIRP Irp ) { NTSTATUS status; BOOLEAN hookit = FALSE; POWER_STATE powerState; POWER_STATE_TYPE powerType; PIO_STACK_LOCATION stack; stack = IoGetCurrentIrpStackLocation (Irp); powerType = stack->Parameters.Power.Type; powerState = stack->Parameters.Power.State; PAGED_CODE (); status = Test98_IncIoCount (Data); if (!NT_SUCCESS (status)) { PoStartNextPowerIrp (Irp); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } switch (stack->MinorFunction) { case IRP_MN_SET_POWER: Test98_KdPrint(Data, TEST_DBG_PNP_TRACE, ("Test-PnP Setting %s state to %d\n", ((powerType == SystemPowerState) ? "System" : "Device"), powerState.SystemState)); switch (powerType) { case DevicePowerState: status = Irp->IoStatus.Status = STATUS_SUCCESS; if (Data->DeviceState == powerState.DeviceState) { break; } else if (Data->DeviceState < powerState.DeviceState) { // // Powering down // PoSetPowerState (Data->Self, powerType, powerState); Data->DeviceState = powerState.DeviceState; } else { // // Powering Up // hookit = TRUE; } break; case SystemPowerState: if (Data->PowerQueryLock) { // // The reception of a power irp resolves the query lock. // Data->PowerQueryLock = FALSE; } else { ASSERT (Data->SystemState != powerState.SystemState); } status = Irp->IoStatus.Status = STATUS_SUCCESS; if (Data->SystemState == powerState.SystemState) { break; } else if (Data->SystemState < powerState.SystemState) { // // Powering down // Data->SystemState = powerState.SystemState; powerState.DeviceState = PowerDeviceD3; PoRequestPowerIrp (Data->Self, IRP_MN_SET_POWER, powerState, NULL, // no completion function NULL, // and no context NULL); } else { // // Powering Up // hookit = TRUE; } break; } break; case IRP_MN_QUERY_POWER: // Data->PowerQueryLock = TRUE; status = Irp->IoStatus.Status = STATUS_SUCCESS; break; default: break; } IoCopyCurrentIrpStackLocationToNext (Irp); if (hookit) { status = Test98_IncIoCount (Data); ASSERT (STATUS_SUCCESS == status); IoSetCompletionRoutine (Irp, Test98_PowerComplete, NULL, TRUE, TRUE, TRUE); status = PoCallDriver (Data->TopOfStack, Irp); } else { // // Power IRPS come synchronously; drivers must call // PoStartNextPowerIrp, when they are ready for the next power // irp. This can be called here, or in the completion routine // PoStartNextPowerIrp (Irp); status = PoCallDriver (Data->TopOfStack, Irp); } Test98_DecIoCount (Data); return status; } // ================================================================== NTSTATUS Test98_PowerComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { POWER_STATE powerState; POWER_STATE_TYPE powerType; PIO_STACK_LOCATION stack; PFDO_DEVICE_DATA data; UNREFERENCED_PARAMETER (Context); data = (PFDO_DEVICE_DATA) DeviceObject->DeviceExtension; stack = IoGetCurrentIrpStackLocation (Irp); powerType = stack->Parameters.Power.Type; powerState = stack->Parameters.Power.State; switch (stack->MinorFunction) { case IRP_MN_SET_POWER: switch (powerType) { case DevicePowerState: ASSERT (powerState.DeviceState < data->DeviceState); data->DeviceState = powerState.DeviceState; PoSetPowerState (data->Self, powerType, powerState); break; case SystemPowerState: // // the work of requesting the Device Power IRP on // behalf of the SystemPower Irp is work done by the FDO // ASSERT (powerState.SystemState < data->SystemState); data->SystemState = powerState.SystemState; powerState.DeviceState = PowerDeviceD0; PoRequestPowerIrp (data->Self, IRP_MN_SET_POWER, powerState, NULL, // no completion function NULL, // and no context NULL); break; } break; case IRP_MN_QUERY_POWER: ASSERT (IRP_MN_QUERY_POWER != stack->MinorFunction); break; default: ASSERT (0xBADBAD == IRP_MN_QUERY_POWER); break; } PoStartNextPowerIrp (Irp); Test98_DecIoCount (data); return STATUS_SUCCESS; }