/*++ Copyright (c) 1999 Microsoft Corporation Module Name: pnp.c Abstract: Port driver for USB host controllers Environment: kernel mode only Notes: Revision History: 6-20-99 : created --*/ #include "common.h" // paged functions #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, USBPORT_CreateDeviceObject) #pragma alloc_text(PAGE, USBPORT_DeferredStartDevice) #pragma alloc_text(PAGE, USBPORT_SymbolicLink) #pragma alloc_text(PAGE, USBPORT_GetRegistryKeyValueForPdo) #pragma alloc_text(PAGE, USBPORT_SetRegistryKeyValueForPdo) #pragma alloc_text(PAGE, USBPORT_MakeRootHubPdoName) #pragma alloc_text(PAGE, USBPORT_MakeHcdDeviceName) #pragma alloc_text(PAGE, USBPORT_CreateRootHubPdo) #pragma alloc_text(PAGE, USBPORT_GetIdString) #pragma alloc_text(PAGE, USBPORTSVC_GetMiniportRegistryKeyValue) #pragma alloc_text(PAGE, USBPORT_CreatePortFdoSymbolicLink) #endif // non paged functions //USBPORT_FindMiniport //USBPORT_Unload //USBPORT_PnPAddDevice //USBPORT_GetResources //USBPORT_FdoStart_Complete //USBPORT_FdoPnPIrp //USBPORT_PdoPnPIrp // globals LIST_ENTRY USBPORT_MiniportDriverList; USBPORT_SPIN_LOCK USBPORT_GlobalsSpinLock; BOOLEAN USBPORT_GlobalInitialized = FALSE; LIST_ENTRY USBPORT_USB2fdoList; LIST_ENTRY USBPORT_USB1fdoList; ULONG USB2LIB_HcContextSize; ULONG USB2LIB_EndpointContextSize; ULONG USB2LIB_TtContextSize; /* */ #define USBPORT_DUMMY_USBD_EXT_SIZE 512 PUCHAR USBPORT_DummyUsbdExtension = NULL; #if DBG ULONG USBPORT_GlobalAllocedPagedPool; ULONG USBPORT_GlobalAllocedNonPagedPool; #endif USB_MINIPORT_STATUS USBPORTSVC_GetMiniportRegistryKeyValue( PDEVICE_DATA DeviceData, BOOLEAN SoftwareBranch, PWCHAR KeyNameString, ULONG KeyNameStringLength, PVOID Data, ULONG DataLength ) /*++ Routine Description: Get a registry parameter from either the hardware or software branch of the registry given the PDO Arguments: Return Value: --*/ { PDEVICE_EXTENSION devExt; NTSTATUS ntStatus; PAGED_CODE(); DEVEXT_FROM_DEVDATA(devExt, DeviceData); ASSERT_FDOEXT(devExt); ntStatus = USBPORT_GetRegistryKeyValueForPdo(devExt->HcFdoDeviceObject, devExt->Fdo.PhysicalDeviceObject, SoftwareBranch, KeyNameString, KeyNameStringLength, Data, DataLength); return USBPORT_NtStatus_TO_MiniportStatus(ntStatus); } NTSTATUS USBPORT_GetRegistryKeyValueForPdo( PDEVICE_OBJECT FdoDeviceObject, PDEVICE_OBJECT PhysicalDeviceObject, BOOLEAN SoftwareBranch, PWCHAR KeyNameString, ULONG KeyNameStringLength, PVOID Data, ULONG DataLength ) /*++ Routine Description: Get a registry parameter from either the hardware or software branch of the registry given the PDO Arguments: Return Value: --*/ { NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES; UNICODE_STRING keyNameUnicodeString; ULONG length; PKEY_VALUE_FULL_INFORMATION fullInfo; HANDLE handle; PAGED_CODE(); if (SoftwareBranch) { ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject, PLUGPLAY_REGKEY_DRIVER, STANDARD_RIGHTS_ALL, &handle); } else { ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject, PLUGPLAY_REGKEY_DEVICE, STANDARD_RIGHTS_ALL, &handle); } if (NT_SUCCESS(ntStatus)) { RtlInitUnicodeString(&keyNameUnicodeString, KeyNameString); length = sizeof(KEY_VALUE_FULL_INFORMATION) + KeyNameStringLength + DataLength; ALLOC_POOL_Z(fullInfo, PagedPool, length); USBPORT_KdPrint((2,"' GetRegistryKeyValueForPdo buffer = 0x%x\n", fullInfo)); if (fullInfo) { ntStatus = ZwQueryValueKey(handle, &keyNameUnicodeString, KeyValueFullInformation, fullInfo, length, &length); if (NT_SUCCESS(ntStatus)){ USBPORT_ASSERT(DataLength == fullInfo->DataLength); RtlCopyMemory(Data, ((PUCHAR) fullInfo) + fullInfo->DataOffset, DataLength); } FREE_POOL(FdoDeviceObject, fullInfo); } } return ntStatus; } NTSTATUS USBPORT_SetRegistryKeyValueForPdo( PDEVICE_OBJECT PhysicalDeviceObject, BOOLEAN SoftwareBranch, ULONG Type, PWCHAR KeyNameString, ULONG KeyNameStringLength, PVOID Data, ULONG DataLength ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES; UNICODE_STRING keyNameUnicodeString; HANDLE handle; PAGED_CODE(); if (SoftwareBranch) { ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject, PLUGPLAY_REGKEY_DRIVER, STANDARD_RIGHTS_ALL, &handle); } else { ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject, PLUGPLAY_REGKEY_DEVICE, STANDARD_RIGHTS_ALL, &handle); } if (NT_SUCCESS(ntStatus)) { RtlInitUnicodeString(&keyNameUnicodeString, KeyNameString); ntStatus = ZwSetValueKey(handle, &keyNameUnicodeString, 0, Type, Data, DataLength); } return ntStatus; } NTSTATUS USBPORT_SymbolicLink( BOOLEAN CreateFlag, PDEVICE_EXTENSION DevExt, PDEVICE_OBJECT PhysicalDeviceObject, LPGUID Guid ) /*++ Routine Description: create a symbolic link for a given GUID class and PhysicalDeviceObject We also write the name to the hw branch of the registry to make it easy to find for a particular instance of controller. Arguments: DeviceObject - DeviceObject of the controller to stop Return Value: NT status code. --*/ { NTSTATUS ntStatus; PAGED_CODE(); if (CreateFlag) { /* * Create the symbolic link */ USBPORT_ASSERT(!TEST_FLAG(DevExt->Flags, USBPORT_FLAG_SYM_LINK)); ntStatus = IoRegisterDeviceInterface( PhysicalDeviceObject, Guid, NULL, &DevExt->SymbolicLinkName); if (NT_SUCCESS(ntStatus)) { /* * Now set the symbolic link for the association and * store it.. */ // successfully alloced a link // set the flag so we will free it SET_FLAG(DevExt->Flags, USBPORT_FLAG_SYM_LINK); // write it to the registry -- this is for comaptibilty // with older OS versions ntStatus = USBPORT_SetRegistryKeyValueForPdo( PhysicalDeviceObject, USBPORT_HW_BRANCH, REG_SZ, SYM_LINK_KEY, sizeof(SYM_LINK_KEY), &DevExt->SymbolicLinkName.Buffer[0], DevExt->SymbolicLinkName.Length); if (NT_SUCCESS(ntStatus)) { ntStatus = IoSetDeviceInterfaceState(&DevExt->SymbolicLinkName, TRUE); } } } else { USBPORT_ASSERT(TEST_FLAG(DevExt->Flags, USBPORT_FLAG_SYM_LINK)); /* * Disable the symbolic link */ ntStatus = IoSetDeviceInterfaceState( &DevExt->SymbolicLinkName, FALSE); if (NT_SUCCESS(ntStatus)) { RtlFreeUnicodeString(&DevExt->SymbolicLinkName); CLEAR_FLAG(DevExt->Flags, USBPORT_FLAG_SYM_LINK); } else { DEBUG_BREAK(); } } return ntStatus; } PUSBPORT_MINIPORT_DRIVER USBPORT_FindMiniport( PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Find a miniport given a DriverObject Arguments: DriverObject - pointer to a driver object Return Value: pointer to miniport or NULL --*/ { KIRQL irql; PUSBPORT_MINIPORT_DRIVER found = NULL; PUSBPORT_MINIPORT_DRIVER miniportDriver; PLIST_ENTRY listEntry; KeAcquireSpinLock(&USBPORT_GlobalsSpinLock.sl, &irql); listEntry = &USBPORT_MiniportDriverList; if (!IsListEmpty(listEntry)) { listEntry = USBPORT_MiniportDriverList.Flink; } // LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'FIl+', listEntry, // &USBPORT_MiniportDriverList, 0); while (listEntry != &USBPORT_MiniportDriverList) { miniportDriver = (PUSBPORT_MINIPORT_DRIVER) CONTAINING_RECORD(listEntry, struct _USBPORT_MINIPORT_DRIVER, ListEntry); if (miniportDriver->DriverObject == DriverObject) { found = miniportDriver; break; } // next entry listEntry = miniportDriver->ListEntry.Flink; } KeReleaseSpinLock(&USBPORT_GlobalsSpinLock.sl, irql); // LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Fmpd', found, 0, 0); return found; } VOID USBPORT_Unload( PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Free globally allocated miniport structure used to track this particular miniport driver. note: OS won't unload unless this is the last instance of the miniport Arguments: DriverObject - pointer to a driver object Return Value: None --*/ { KIRQL irql; PUSBPORT_MINIPORT_DRIVER miniportDriver; // find the miniport driver data miniportDriver = USBPORT_FindMiniport(DriverObject); // we had better find it! If we don't we screwed up // the system will crash USBPORT_ASSERT(miniportDriver != NULL); if (miniportDriver == NULL) { BUGCHECK(USBBUGCODE_INTERNAL_ERROR, 0, 0, 0); // prefix happy return; } // the miniport should not need to do anything. // But just in case/ we will call them if they // indicated an unload routine in the DriverObject. USBPORT_KdPrint((1, "'unloading USB miniport\n")); if (miniportDriver->MiniportUnload != NULL) { miniportDriver->MiniportUnload(DriverObject); } USBPORT_InterlockedRemoveEntryList(&miniportDriver->ListEntry, &USBPORT_GlobalsSpinLock.sl); FREE_POOL(NULL, miniportDriver); } NTSTATUS USBPORT_MakeHcdDeviceName( PUNICODE_STRING DeviceNameUnicodeString, ULONG Idx ) /*++ Routine Description: This function generates the name used for the FDO. The name format is USBFDO-n where nnn is 0 - 65535. Arguments: Return Value: None --*/ { ULONG bit, i; PWCHAR deviceNameBuffer; WCHAR nameBuffer[] = L"\\Device\\USBFDO-"; NTSTATUS ntStatus; UNICODE_STRING tmpUnicodeString; WCHAR tmpBuffer[16]; PAGED_CODE(); // enough for 3 digits and NULL tmpUnicodeString.Buffer = tmpBuffer; tmpUnicodeString.MaximumLength = sizeof(tmpBuffer); tmpUnicodeString.Length = 0; ntStatus = RtlIntegerToUnicodeString(Idx, 10, &tmpUnicodeString); if (NT_SUCCESS(ntStatus)) { USHORT siz; siz = sizeof(nameBuffer)+tmpUnicodeString.Length; // we can't log this alloc because the device object // has not been created yet ALLOC_POOL_Z(deviceNameBuffer, PagedPool, siz); if (deviceNameBuffer != NULL) { RtlCopyMemory(deviceNameBuffer, nameBuffer, sizeof(nameBuffer)); RtlInitUnicodeString(DeviceNameUnicodeString, deviceNameBuffer); DeviceNameUnicodeString->MaximumLength = siz; ntStatus = RtlAppendUnicodeStringToString( DeviceNameUnicodeString, &tmpUnicodeString); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } return ntStatus; } NTSTATUS USBPORT_MakeRootHubPdoName( PDEVICE_OBJECT FdoDeviceObject, PUNICODE_STRING PdoNameUnicodeString, ULONG Index ) /*++ Routine Description: This service Creates a name for a PDO created by the HUB Arguments: Return Value: --*/ { PWCHAR nameBuffer = NULL; WCHAR rootName[] = L"\\Device\\USBPDO-"; UNICODE_STRING idUnicodeString; WCHAR buffer[32]; NTSTATUS ntStatus = STATUS_SUCCESS; USHORT length; BOOLEAN haveString = FALSE; PAGED_CODE(); length = sizeof(buffer)+sizeof(rootName); // os frees this when the unicode string is 'freed' ALLOC_POOL_OSOWNED(nameBuffer, PagedPool, length); if (nameBuffer) { RtlCopyMemory(nameBuffer, rootName, sizeof(rootName)); RtlInitUnicodeString(PdoNameUnicodeString, nameBuffer); PdoNameUnicodeString->MaximumLength = length; haveString = TRUE; // we have a string now RtlInitUnicodeString(&idUnicodeString, &buffer[0]); idUnicodeString.MaximumLength = sizeof(buffer); ntStatus = RtlIntegerToUnicodeString( Index, 10, &idUnicodeString); if (NT_SUCCESS(ntStatus)) { ntStatus = RtlAppendUnicodeStringToString(PdoNameUnicodeString, &idUnicodeString); } USBPORT_KdPrint((3, "'USBPORT_MakeNodeName string = %x\n", PdoNameUnicodeString)); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } if (!NT_SUCCESS(ntStatus) && haveString) { RtlFreeUnicodeString(PdoNameUnicodeString); } return ntStatus; } NTSTATUS USBPORT_PnPAddDevice( PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: This routine is called to create a new instance of a USB host controller. This is where we create our deviceObject. Arguments: DriverObject - pointer to the driver object for this instance of HCD PhysicalDeviceObject - pointer to a device object created by the bus Return Value: NT STATUS CODE --*/ { NTSTATUS ntStatus; PDEVICE_OBJECT deviceObject = NULL; PDEVICE_EXTENSION devExt; UNICODE_STRING deviceNameUnicodeString; ULONG deviceNameIdx; PUSBPORT_MINIPORT_DRIVER miniportDriver; // since we raise IRQL in this function it cannot be pagable // find the driver miniportDriver = USBPORT_FindMiniport(DriverObject); USBPORT_ASSERT(miniportDriver != NULL); // // generate a device name // deviceNameIdx = 0; do { ntStatus = USBPORT_MakeHcdDeviceName(&deviceNameUnicodeString, deviceNameIdx); if (NT_SUCCESS(ntStatus)) { ntStatus = USBPORT_CreateDeviceObject(DriverObject, miniportDriver, &deviceObject, &deviceNameUnicodeString); RtlFreeUnicodeString(&deviceNameUnicodeString); if (NT_SUCCESS(ntStatus)) { //preserve idx break; } } deviceNameIdx++; } while (ntStatus == STATUS_OBJECT_NAME_COLLISION); if (NT_SUCCESS(ntStatus)) { GET_DEVICE_EXT(devExt, deviceObject); // BUGBUG OS should zero this RtlZeroMemory(devExt, sizeof(DEVICE_EXTENSION)); devExt->DummyUsbdExtension = USBPORT_DummyUsbdExtension; devExt->Sig = USBPORT_DEVICE_EXT_SIG; devExt->HcFdoDeviceObject = deviceObject; devExt->Fdo.PhysicalDeviceObject = PhysicalDeviceObject; devExt->Fdo.DeviceNameIdx = deviceNameIdx; devExt->Fdo.MiniportDriver = miniportDriver; devExt->Fdo.MiniportDeviceData = &devExt->Fdo.MiniportExtension[0]; if (USBPORT_IS_USB20(devExt)) { PUCHAR pch; pch = (PUCHAR) &devExt->Fdo.MiniportExtension[0]; devExt->Fdo.Usb2LibHcContext = (PVOID) (pch + devExt->Fdo.MiniportDriver->RegistrationPacket.DeviceDataSize); USB2LIB_InitController(devExt->Fdo.Usb2LibHcContext); } else { devExt->Fdo.Usb2LibHcContext = USB_BAD_PTR; } INITIALIZE_PENDING_REQUEST_COUNTER(devExt); // inc once for the add // transition to -1 means we have no pending requests INCREMENT_PENDING_REQUEST_COUNT(deviceObject, NULL); USBPORT_LogAlloc(&devExt->Log, 8); // init the log spinlock here KeInitializeSpinLock(&devExt->Fdo.LogSpinLock.sl); #if DBG USBPORT_LogAlloc(&devExt->TransferLog, 4); USBPORT_LogAlloc(&devExt->EnumLog, 4); #endif USBPORT_KdPrint((1, "'**USBPORT DEVICE OBJECT** (fdo) = %x, ext = %x\n", deviceObject, devExt)); KeInitializeSemaphore(&devExt->Fdo.DeviceLock, 1, 1); KeInitializeSemaphore(&devExt->Fdo.CcLock, 1, 1); InitializeListHead(&devExt->Fdo.DeviceHandleList); InitializeListHead(&devExt->Fdo.MapTransferList); InitializeListHead(&devExt->Fdo.DoneTransferList); InitializeListHead(&devExt->Fdo.GlobalEndpointList); InitializeListHead(&devExt->Fdo.AttendEndpointList); InitializeListHead(&devExt->Fdo.EpStateChangeList); InitializeListHead(&devExt->Fdo.EpClosedList); InitializeListHead(&devExt->Fdo.BadRequestList); devExt->Fdo.BadRequestFlush = 0; // // we need to handle a seemingly random set of requests // to start/stop/remove power up, down etc in order to // handle this we keep a set of PNP state flags // not removed, not started, not stopped devExt->PnpStateFlags = 0; // until we get a start we will consider ourselves OFF devExt->CurrentDevicePowerState = PowerDeviceD3; devExt->Fdo.MpStateFlags = 0; // attach to top of PnP stack devExt->Fdo.TopOfStackDeviceObject = IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject); // // Indicate that the device object is ready for requests. // if (!USBPORT_IS_USB20(devExt)) { deviceObject->Flags |= DO_POWER_PAGABLE; } deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; } USBPORT_KdPrint((2, "'exit USBPORT_PnPAddDevice (%x)\n", ntStatus)); return ntStatus; } NTSTATUS USBPORT_CreateDeviceObject( PDRIVER_OBJECT DriverObject, PUSBPORT_MINIPORT_DRIVER MiniportDriver, PDEVICE_OBJECT *DeviceObject, PUNICODE_STRING DeviceNameUnicodeString ) /*++ Routine Description: This routine is called to create a new instance of a USB host controller. Arguments: DriverObject - pointer to the driver object for USBD. *DeviceObject - ptr to DeviceObject ptr to be filled in with the device object we create. DeviceNameUnicodeString - optional pointer to a device name for this FDO, can be NULL Return Value: NT status code --*/ { NTSTATUS ntStatus; PDEVICE_EXTENSION devExt; ULONG extensionSize; PAGED_CODE(); USBPORT_KdPrint((2, "'enter USBPORT_CreateDeviceObject\n")); extensionSize = sizeof(DEVICE_EXTENSION)+ MiniportDriver->RegistrationPacket.DeviceDataSize + USB2LIB_HcContextSize; ntStatus = IoCreateDevice(DriverObject, extensionSize, DeviceNameUnicodeString, // Name FILE_DEVICE_CONTROLLER, 0, FALSE, //NOT Exclusive DeviceObject); if (NT_SUCCESS(ntStatus)) { devExt = (PDEVICE_EXTENSION) ((*DeviceObject)->DeviceExtension); USBPORT_KdPrint((2, "'USBPORT_CreateDeviceObject: device object %x device extension = %x\n", *DeviceObject, devExt)); } else if (*DeviceObject) { IoDeleteDevice(*DeviceObject); } USBPORT_KdPrint((2, "'exit USBPORT_CreateDeviceObject (%x)\n", ntStatus)); return ntStatus; } NTSTATUS USBPORT_GetResources( PDEVICE_OBJECT FdoDeviceObject, PCM_RESOURCE_LIST ResourceList, PHC_RESOURCES HcResources ) /*++ Routine Description: Arguments: DeviceObject - DeviceObject for this USB controller. ResourceList - Resources for this controller. Return Value: NT status code. --*/ { ULONG i; NTSTATUS ntStatus; PCM_PARTIAL_RESOURCE_DESCRIPTOR interrupt; PCM_PARTIAL_RESOURCE_DESCRIPTOR memory; PCM_PARTIAL_RESOURCE_DESCRIPTOR ioport; PHYSICAL_ADDRESS cardAddress; ULONG addressSpace; PCM_PARTIAL_RESOURCE_LIST PartialResourceList; PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDescriptor; ULONG mpOptionFlags; PDEVICE_EXTENSION devExt; USBPORT_KdPrint((2, "'enter USBPORT_GetResources\n")); GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); mpOptionFlags = REGISTRATION_PACKET(devExt).OptionFlags; // assume success ntStatus = STATUS_SUCCESS; // init the resource list RtlZeroMemory(HcResources, sizeof(*HcResources)); LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'GRES', 0, 0, ResourceList); if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NO_PNP_RESOURCES)) { TEST_TRAP(); // no resources, bail with success return ntStatus; } if (ResourceList == NULL) { USBPORT_KdPrint((1, "'no resources, failing start.\n")); ntStatus = STATUS_NONE_MAPPED; goto USBPORT_GetResources_Done; } fullResourceDescriptor = &ResourceList->List[0]; PartialResourceList = &fullResourceDescriptor->PartialResourceList; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'gres', PartialResourceList->Count, 0, PartialResourceList); interrupt = NULL; memory = NULL; ioport = NULL; for (i = 0; i < PartialResourceList->Count; i++) { LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'resT', i, PartialResourceList->PartialDescriptors[i].Type, 0); switch (PartialResourceList->PartialDescriptors[i].Type) { case CmResourceTypeInterrupt: if (interrupt == NULL) { interrupt = &PartialResourceList->PartialDescriptors[i]; } break; case CmResourceTypeMemory: if (memory == NULL) { memory = &PartialResourceList->PartialDescriptors[i]; } break; case CmResourceTypePort: if (ioport == NULL) { ioport = &PartialResourceList->PartialDescriptors[i]; } break; } } // only map resources this miniport actually needs if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NEED_IOPORT) && ioport != NULL && NT_SUCCESS(ntStatus)) { // // Set up AddressSpace to be of type Port I/O // USBPORT_KdPrint((1, "'Port Resources Found @ %x'%x, %d Ports Available \n", ioport->u.Port.Start.HighPart, ioport->u.Port.Start.LowPart, ioport->u.Port.Length)); addressSpace = (ioport->Flags & CM_RESOURCE_PORT_IO) == CM_RESOURCE_PORT_IO? 1:0; cardAddress=ioport->u.Port.Start; if (!addressSpace) { // HcResources->Flags |= MAP_REGISTERS; HcResources->DeviceRegisters = MmMapIoSpace( cardAddress, ioport->u.Port.Length, FALSE); HcResources->DeviceRegistersLength = ioport->u.Port.Length; } else { // HcResources->Flags &= MAP_REGISTERS; HcResources->DeviceRegisters = (PVOID)(ULONG_PTR)cardAddress.QuadPart; HcResources->DeviceRegistersLength = ioport->u.Port.Length; } // // see if we successfully mapped the IO regs // if (HcResources->DeviceRegisters == NULL) { USBPORT_KdPrint((1, "'Couldn't map the device(port) registers. \n")); ntStatus = STATUS_NONE_MAPPED; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Fmio', 0, 0, ntStatus); } else { USBPORT_KdPrint((2, "'Mapped device(port) registers to 0x%x.\n", HcResources->DeviceRegisters)); HcResources->Flags |= HCR_IO_REGS; } } if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NEED_MEMORY) && memory != NULL && NT_SUCCESS(ntStatus)) { // // Set up AddressSpace to be of type Memory mapped I/O // USBPORT_KdPrint((1, "'Memory Resources Found @ %x'%x, Length = %x\n", memory->u.Memory.Start.HighPart, memory->u.Memory.Start.LowPart, memory->u.Memory.Length)); addressSpace = 0; HcResources->DeviceRegistersLength = memory->u.Memory.Length; cardAddress = memory->u.Memory.Start; HcResources->DeviceRegisters = MmMapIoSpace(cardAddress, HcResources->DeviceRegistersLength, FALSE); if (HcResources->DeviceRegisters == NULL) { USBPORT_KdPrint((1, "'Couldn't map the device(memory) registers. \n")); ntStatus = STATUS_NONE_MAPPED; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Fmmr', 0, 0, ntStatus); } else { USBPORT_KdPrint((2, "'Mapped device(memory) registers to 0x%x.\n", HcResources->DeviceRegisters)); HcResources->Flags |= HCR_MEM_REGS; } } if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NEED_IRQ) && interrupt != NULL && NT_SUCCESS(ntStatus)) { // // Get Vector, level, and affinity information for this interrupt. // USBPORT_KdPrint((1, "'Interrupt Resources Found! Level = %x Vector = %x\n", interrupt->u.Interrupt.Level, interrupt->u.Interrupt.Vector )); HcResources->Flags |= HCR_IRQ; // // Set up our interrupt. // USBPORT_KdPrint((2, "'requesting interrupt vector %x level %x\n", interrupt->u.Interrupt.Level, interrupt->u.Interrupt.Vector)); HcResources->InterruptLevel=(KIRQL)interrupt->u.Interrupt.Level; HcResources->InterruptVector=interrupt->u.Interrupt.Vector; HcResources->Affinity=interrupt->u.Interrupt.Affinity; // // Initialize the interrupt object for the controller. // HcResources->InterruptObject = NULL; HcResources->ShareIRQ = interrupt->ShareDisposition == CmResourceShareShared ? TRUE : FALSE; HcResources->InterruptMode = interrupt->Flags == CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive; #ifdef DEBUG USBPORT_KdPrint((2, "'interrupt->ShareDisposition %x\n", interrupt->ShareDisposition)); if (!HcResources->ShareIRQ) { TEST_TRAP(); } #endif } USBPORT_GetResources_Done: TEST_PATH(ntStatus, FAILED_GETRESOURCES); USBPORT_KdPrint((2, "'exit USBPORT_GetResources (%x)\n", ntStatus)); LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'GRSd', 0, 0, ntStatus); return ntStatus; } NTSTATUS USBPORT_FdoStart_Complete( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context ) /*++ Routine Description: This routine is called when the port driver completes an IRP. Arguments: DeviceObject - Pointer to the device object for the class device. Irp - Irp completed. Context - Driver defined context. Return Value: The function value is the final status from the operation. --*/ { PIO_STACK_LOCATION irpStack; PKEVENT event = Context; irpStack = IoGetCurrentIrpStackLocation (Irp); USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP); USBPORT_ASSERT(irpStack->MinorFunction == IRP_MN_START_DEVICE); // signal the start device dispatch to finsh KeSetEvent(event, 1, FALSE); // defer completion return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS USBPORT_FdoPnPIrp( PDEVICE_OBJECT FdoDeviceObject, PIRP Irp ) /*++ Routine Description: Process the PNP IRPs sent to the FDO for the host controller. Arguments: DeviceObject - pointer to a hcd device object (FDO) Irp - pointer to an I/O Request Packet Return Value: NT status code --*/ { PIO_STACK_LOCATION irpStack; NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_EXTENSION devExt; BOOLEAN hardwarePresent = TRUE; USBPORT_KdPrint((2, "'IRP_MJ_PNP %x\n", FdoDeviceObject)); irpStack = IoGetCurrentIrpStackLocation(Irp); GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP); LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'fPnP', irpStack->MinorFunction, 0, 0); switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: { KEVENT pnpStartEvent; KeInitializeEvent(&pnpStartEvent, NotificationEvent, FALSE); // pass on to host controllers PDO ntStatus = USBPORT_PassIrp(FdoDeviceObject, USBPORT_FdoStart_Complete, &pnpStartEvent, TRUE, TRUE, TRUE, Irp); if (ntStatus == STATUS_PENDING) { KeWaitForSingleObject( &pnpStartEvent, Suspended, KernelMode, FALSE, NULL); ntStatus = Irp->IoStatus.Status; } TEST_PATH(ntStatus, FAILED_LOWER_START); if (NT_SUCCESS(ntStatus)) { // // irp completed succesfully by lower // drivers, start usbport and miniport // ntStatus = USBPORT_DeferredStartDevice( FdoDeviceObject, Irp); #if DBG if (!NT_SUCCESS(ntStatus)) { USBPORT_KdPrint((1, "'miniport failed start %x\n", ntStatus)); DEBUG_BREAK(); } #endif } #if DBG else { USBPORT_KdPrint((1, "'lower drivers failed start %x\n", ntStatus)); DEBUG_BREAK(); } #endif // // we must complete this irp since we defrerred completion // with the completion routine. // USBPORT_CompleteIrp(FdoDeviceObject, Irp, ntStatus, 0); goto USBPORT_ProcessPnPIrp_Done; } break; // // STOP & REMOVE messages unload the driver // when we get a STOP message it is still possible // touch the hardware, when we get a REMOVE message // we have to assume that the hardware is gone. // case IRP_MN_STOP_DEVICE: // check our state and take appropriate action if (TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED)) { // device is started, stop it now ntStatus = USBPORT_StopDevice(FdoDeviceObject, hardwarePresent); // not started flag, note: not started is not the // same as stopped CLEAR_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED); SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STOPPED); } if (!NT_SUCCESS(ntStatus)) { // bugbug what is our state if stop fails? TEST_TRAP(); } // PnP commandment: Thou shalt not fail stop. Irp->IoStatus.Status = ntStatus = STATUS_SUCCESS; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'STOP', 0, devExt->PnpStateFlags, ntStatus); // Pass on to PDO break; case IRP_MN_QUERY_DEVICE_RELATIONS: { PDEVICE_RELATIONS deviceRelations; USBPORT_KdPrint((1, "'IRP_MN_QUERY_DEVICE_RELATIONS %x %x\n", FdoDeviceObject, irpStack->Parameters.QueryDeviceRelations.Type)); ntStatus = STATUS_SUCCESS; switch(irpStack->Parameters.QueryDeviceRelations.Type) { case BusRelations: // query relations. // we report only one child, the root hub // assume success ntStatus = STATUS_SUCCESS; ALLOC_POOL_OSOWNED(deviceRelations, PagedPool, sizeof(*deviceRelations)); if (!deviceRelations) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; // Complete the Irp now with failure, don't pass it down. // USBPORT_CompleteIrp(FdoDeviceObject, Irp, ntStatus, 0); goto USBPORT_ProcessPnPIrp_Done; } if (devExt->Fdo.RootHubPdo == NULL) { // we either have not created it or the current one // has been removed by the OS. // create a new root hub ntStatus = USBPORT_CreateRootHubPdo(FdoDeviceObject, &devExt->Fdo.RootHubPdo); } if (NT_SUCCESS(ntStatus)) { PDEVICE_EXTENSION rhDevExt; KIRQL irql; GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo); ASSERT_PDOEXT(rhDevExt); deviceRelations->Count=1; deviceRelations->Objects[0] = devExt->Fdo.RootHubPdo; ObReferenceObject(devExt->Fdo.RootHubPdo); Irp->IoStatus.Information=(ULONG_PTR)deviceRelations; // report the same PDO every time ie the PDO is never // truely remove until the controller is removed } else { FREE_POOL(FdoDeviceObject, deviceRelations); deviceRelations = NULL; // free the device object if we // created one TEST_TRAP(); } Irp->IoStatus.Status = ntStatus; USBPORT_KdPrint((1, "'IRP_MN_QUERY_DEVICE_RELATIONS %x BusRelations\n", FdoDeviceObject)); break; case TargetDeviceRelation: // // this one gets passed on // USBPORT_KdPrint((1, " IRP_MN_QUERY_DEVICE_RELATIONS %x, TargetDeviceRelation\n", FdoDeviceObject)); break; case RemovalRelations: // assume success ntStatus = STATUS_SUCCESS; deviceRelations = NULL; if (USBPORT_IS_USB20(devExt)) { deviceRelations = USBPORT_FindCompanionControllers(FdoDeviceObject, TRUE, FALSE); if (!deviceRelations) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; // Complete the Irp now with failure, don't pass it down. // USBPORT_CompleteIrp(FdoDeviceObject, Irp, ntStatus, 0); goto USBPORT_ProcessPnPIrp_Done; } } Irp->IoStatus.Information=(ULONG_PTR)deviceRelations; Irp->IoStatus.Status = ntStatus; USBPORT_KdPrint((1, "'IRP_MN_QUERY_DEVICE_RELATIONS %x RemovalRelations\n", FdoDeviceObject)); break; default: // // some other kind of relations // pass this on // USBPORT_KdPrint((1, "'IRP_MN_QUERY_DEVICE_RELATIONS %x, other relations\n", FdoDeviceObject)); } /* case irpStack->Parameters.QueryDeviceRelations.Type */ } break; /* IRP_MN_QUERY_DEVICE_RELATIONS */ case IRP_MN_SURPRISE_REMOVAL: // hardware is gone LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'hcSR', 0, ntStatus, 0); USBPORT_KdPrint((1, " HC FDO (%x) surprise removed\n", FdoDeviceObject)); DEBUG_BREAK(); if (TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_REMOVED)) { // it would be odd to get a surprise remove when // we are already removed but it would not 'surprise' // me if Win2k did this under some cirumstance TEST_TRAP(); ntStatus = USBPORT_PassIrp(FdoDeviceObject, NULL, NULL, TRUE, TRUE, TRUE, Irp); goto USBPORT_ProcessPnPIrp_Done; } USBPORT_InvalidateController(FdoDeviceObject, UsbMpControllerRemoved); break; case IRP_MN_REMOVE_DEVICE: { PDEVICE_OBJECT rootHubPdo; KIRQL irql; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'hcRM', 0, ntStatus, 0); USBPORT_KdPrint((1, " HC FDO (%x) is being removed\n", FdoDeviceObject)); // this device is now 'REMOVED' KeAcquireSpinLock(&devExt->PendingRequestSpin.sl, &irql); USBPORT_ASSERT(!TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_REMOVED)); SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_REMOVED); KeReleaseSpinLock(&devExt->PendingRequestSpin.sl, irql); // if we are started AND // we haven't been stopped yet then stop now. if (TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED) && !TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STOPPED)) { NTSTATUS status; status = USBPORT_StopDevice(FdoDeviceObject, hardwarePresent); SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STOPPED); } // // pass on to our PDO // Irp->IoStatus.Status = STATUS_SUCCESS; ntStatus = USBPORT_PassIrp(FdoDeviceObject, NULL, NULL, TRUE, TRUE, TRUE, Irp); // bugbug // Flush any requests that are still queued in our driver // This DEC matches the INC in our add device, // this is our last reference and this will cause the // transition 0 -> -1 when all irps pending complete // // after this wait we consider it safe to 'unload' DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL); LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'watP', 0, 0, FdoDeviceObject); KeWaitForSingleObject(&devExt->PendingRequestEvent, Suspended, KernelMode, FALSE, NULL); LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'waPD', 0, 0, FdoDeviceObject); // last chance to debug with the log DEBUG_BREAK(); USBPORT_LogFree(FdoDeviceObject, &devExt->Log); USBPORT_LogFree(FdoDeviceObject, &devExt->TransferLog); USBPORT_LogFree(FdoDeviceObject, &devExt->EnumLog); // // important to detach FDO from PDO after we pass the irp on // IoDetachDevice(devExt->Fdo.TopOfStackDeviceObject); // // Delete the device object we created for this controller // rootHubPdo = devExt->Fdo.RootHubPdo; SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_DELETED); USBPORT_KdPrint((1, "'Deleting HC FDO (%x) now.\n", FdoDeviceObject)); IoDeleteDevice(FdoDeviceObject); // HC is FDO gone so root hub is gone. // // note: in some cases we may not have a root hub // PDO since we create it in response to a QBR. if (rootHubPdo != NULL) { PDEVICE_EXTENSION rhDevExt; GET_DEVICE_EXT(rhDevExt, rootHubPdo); ASSERT_PDOEXT(rhDevExt); SET_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_DELETED); USBPORT_KdPrint((1, "'Deleting root hub PDO (%x) now.\n", rootHubPdo)); IoDeleteDevice(rootHubPdo); } goto USBPORT_ProcessPnPIrp_Done; } break; // Quoting from the book of PNP // // 'The FDO must either fail the IRP or set the // IRP's status if it is not going change the IRP's status // using a completion routine.' case IRP_MN_CANCEL_STOP_DEVICE: Irp->IoStatus.Status = ntStatus = STATUS_SUCCESS; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'cstp', 0, devExt->PnpStateFlags, ntStatus); break; case IRP_MN_QUERY_STOP_DEVICE: Irp->IoStatus.Status = ntStatus = STATUS_SUCCESS; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'qstp', 0, devExt->PnpStateFlags, ntStatus); break; case IRP_MN_CANCEL_REMOVE_DEVICE: Irp->IoStatus.Status = ntStatus = STATUS_SUCCESS; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'crmv', 0, devExt->PnpStateFlags, ntStatus); break; case IRP_MN_QUERY_REMOVE_DEVICE: // BUGBUG reverse this in cance query remove? if (USBPORT_IS_USB20(devExt)) { // make a note on the CCs for this USB 2 // master controller USBPORT_WriteHaction(FdoDeviceObject, 2); } Irp->IoStatus.Status = ntStatus = STATUS_SUCCESS; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'qrmv', 0, devExt->PnpStateFlags, ntStatus); break; // // All other PnP messages passed on to our PDO // default: USBPORT_ASSERT(devExt->Fdo.TopOfStackDeviceObject != NULL); USBPORT_KdPrint((2, "'UNKNOWN PNP MESSAGE (%x)\n", irpStack->MinorFunction)); // // All unahndled PnP messages are passed on to the PDO // } /* case PNP minor function */ // // pass on to our PDO // ntStatus = USBPORT_PassIrp(FdoDeviceObject, NULL, NULL, TRUE, TRUE, TRUE, Irp); USBPORT_ProcessPnPIrp_Done: // DO NOT touch the Irp from this point on return ntStatus; } NTSTATUS USBPORT_DeferredStartDevice( PDEVICE_OBJECT FdoDeviceObject, PIRP Irp ) /*++ Routine Description: This function is called as a result of MN_START_DEVICE, it is called after successful completion of the START irp by the lower drivers. Arguments: DeviceObject - DeviceObject for this USB controller. Return Value: NT Status code. --*/ { NTSTATUS ntStatus; PIO_STACK_LOCATION irpStack; PDEVICE_EXTENSION devExt; PAGED_CODE(); GET_DEVICE_EXT(devExt, FdoDeviceObject); irpStack = IoGetCurrentIrpStackLocation (Irp); ntStatus = USBPORT_GetResources(FdoDeviceObject, irpStack->Parameters.StartDevice.AllocatedResourcesTranslated, &devExt->Fdo.HcResources); if (NT_SUCCESS(ntStatus)) { // got resources, start the port driver, // connect the interrupt and start miniport. ntStatus = USBPORT_StartDevice(FdoDeviceObject, &devExt->Fdo.HcResources); } if (NT_SUCCESS(ntStatus)) { CLEAR_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STOPPED); SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED); // consider ourselves powered // // Are we powered if we fail start? // PnP sure thinks we are becuse the OS sends power // irps. Since we handle this bogus case (ie have hit it) // for the OS we just set ourselves to D0 here. devExt->CurrentDevicePowerState = PowerDeviceD0; if (USBPORT_IS_USB20(devExt)) { USBPORT_RegisterUSB2fdo(FdoDeviceObject); if (USBPORT_IS_USB20(devExt)) { // set the default haction to wait (1) on // successful start USBPORT_WriteHaction(FdoDeviceObject, 1); } } else { USBPORT_RegisterUSB1fdo(FdoDeviceObject); } } else { SET_FLAG(devExt->PnpStateFlags, USBPORT_PNP_START_FAILED); } LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'dfST', 0, 0, ntStatus); return ntStatus; } PWCHAR USB_MakeId( PDEVICE_OBJECT FdoDeviceObject, PWCHAR IdString, PWCHAR Buffer, PULONG Length, USHORT NullCount, USHORT Digits, USHORT HexId ) /* given a wide Id string like "FOOnnnn\0" add the HexId value to nnnn as hex this string is appended to the buffer passed in eg in : FOOnnnn\0 , 0x123A out : FOO123A\0 */ { #define NIBBLE_TO_HEX( byte ) ((WCHAR)Nibble[byte]) CONST UCHAR Nibble[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; PWCHAR tmp, id; PUCHAR p; SIZE_T siz, idLen; idLen = wcslen(IdString)*sizeof(WCHAR); siz = idLen+(USHORT)*Length+(NullCount*sizeof(WCHAR)); ALLOC_POOL_OSOWNED(tmp, PagedPool, siz); if (tmp == NULL) { *Length = 0; } else { // this takes care of the nulls RtlCopyMemory(tmp, Buffer, *Length); p = (PUCHAR) tmp; p += *Length; RtlCopyMemory(p, IdString, idLen); id = (PWCHAR) p; *Length = siz; // now convert the vaules while (*id != (WCHAR)'n' && Digits) { id++; } switch(Digits) { case 2: *(id) = NIBBLE_TO_HEX((HexId >> 4) & 0x000f); *(id+1) = NIBBLE_TO_HEX(HexId & 0x000f); break; case 4: *(id) = NIBBLE_TO_HEX(HexId >> 12); *(id+1) = NIBBLE_TO_HEX((HexId >> 8) & 0x000f); *(id+2) = NIBBLE_TO_HEX((HexId >> 4) & 0x000f); *(id+3) = NIBBLE_TO_HEX(HexId & 0x000f); break; } } if (Buffer != NULL) { FREE_POOL(FdoDeviceObject, Buffer); } return tmp; #undef NIBBLE_TO_HEX } PWCHAR USBPORT_GetIdString( PDEVICE_OBJECT FdoDeviceObject, USHORT Vid, USHORT Pid, USHORT Rev ) /*++ Routine Description: Make an id string for PnP Arguments: Return Value: NT Status code. --*/ { PWCHAR id; ULONG length; PDEVICE_EXTENSION devExt; GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); // we need to generate the following series of strings // USB\\ROOT_HUB&VIDnnnn&PIDnnnn&REVnnnn\0 // USB\\ROOT_HUB&VIDnnnn&PIDnnnn\0 // USB\\ROOT_HUB\0\0 // allocate space for the three id plus a NULL id = NULL; length = 0; // USB\\ROOT_HUB&VIDnnnn&PIDnnnn&REVnnnn\0 if (USBPORT_IS_USB20(devExt)) { id = USB_MakeId(FdoDeviceObject, L"USB\\ROOT_HUB20&VIDnnnn\0", id, &length, 0, 4, // four digits Vid); } else { id = USB_MakeId(FdoDeviceObject, L"USB\\ROOT_HUB&VIDnnnn\0", id, &length, 0, 4, // four digits Vid); } id = USB_MakeId(FdoDeviceObject, L"&PIDnnnn\0", id, &length, 0, 4, // four digits Pid); id = USB_MakeId(FdoDeviceObject, L"&REVnnnn\0", id, &length, 1, // add a NULL 4, // four digits Rev); // USB\\ROOT_HUB&VIDnnnn&PIDnnnn\0 if (USBPORT_IS_USB20(devExt)) { id = USB_MakeId(FdoDeviceObject, L"USB\\ROOT_HUB20&VIDnnnn\0", id, &length, 0, 4, // four digits Vid); } else { id = USB_MakeId(FdoDeviceObject, L"USB\\ROOT_HUB&VIDnnnn\0", id, &length, 0, 4, // four digits Vid); } id = USB_MakeId(FdoDeviceObject, L"&PIDnnnn\0", id, &length, 1, 4, // four digits Pid); // USB\\ROOT_HUB\0\0 if (USBPORT_IS_USB20(devExt)) { id = USB_MakeId(FdoDeviceObject, L"USB\\ROOT_HUB20\0", id, &length, 2, // double null 0, // no digits 0); } else { id = USB_MakeId(FdoDeviceObject, L"USB\\ROOT_HUB\0", id, &length, 2, // double null 0, // no digits 0); } return(id); } NTSTATUS USBPORT_PdoPnPIrp( PDEVICE_OBJECT PdoDeviceObject, PIRP Irp ) /*++ Routine Description: Disptach routine for PnP Irps sent to the PDO for the root hub. NOTE: irps sent to the PDO are always completed by the bus driver Arguments: DeviceObject - Pdo for the root hub Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpStack; PDEVICE_CAPABILITIES DeviceCapabilities; NTSTATUS ntStatus; PDEVICE_EXTENSION rhDevExt; PDEVICE_OBJECT fdoDeviceObject; // return no infornation by default ULONG_PTR information; GET_DEVICE_EXT(rhDevExt, PdoDeviceObject); ASSERT_PDOEXT(rhDevExt); fdoDeviceObject = rhDevExt->HcFdoDeviceObject; //GET_DEVICE_EXT(devExt, fdoDeviceObject); //ASSERT_FDOEXT(devExt); irpStack = IoGetCurrentIrpStackLocation (Irp); // don't stomp the current value unless we // have to. information = Irp->IoStatus.Information; USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP); // PNP messages for the PDO created for the root hub switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: { KIRQL irql; USBPORT_KdPrint((1, " Starting Root hub PDO %x\n", PdoDeviceObject)); DEBUG_BREAK(); INCREMENT_PENDING_REQUEST_COUNT(PdoDeviceObject, NULL); // first create the 'Device' ntStatus = USBPORT_RootHub_CreateDevice(fdoDeviceObject, PdoDeviceObject); // // create a symbolic link for the root hub PDO // USBUI uses this link to talk to the hub // if (NT_SUCCESS(ntStatus)) { ntStatus = USBPORT_SymbolicLink(TRUE, rhDevExt, PdoDeviceObject, (LPGUID)&GUID_CLASS_USBHUB); } if (NT_SUCCESS(ntStatus)) { // erases remove and stop flags rhDevExt->PnpStateFlags = USBPORT_PNP_STARTED; // consider ourselves powered when started rhDevExt->CurrentDevicePowerState = PowerDeviceD0; } } break; case IRP_MN_REMOVE_DEVICE: { PDEVICE_EXTENSION devExt; KIRQL irql; USBPORT_KdPrint((1, " Root Hub PDO (%x) is being removed\n", PdoDeviceObject)); LOGENTRY(NULL, fdoDeviceObject, LOG_PNP, 'rhRM', 0, 0, 0); GET_DEVICE_EXT(devExt, rhDevExt->HcFdoDeviceObject); ASSERT_FDOEXT(devExt); // stop if necessary USBPORT_StopRootHubPdo(fdoDeviceObject, PdoDeviceObject); // when is a remove not a remove? when PnP sends it. // this flag will be reset when the root hub pdo is // started SET_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_REMOVED); // since the PnP convention is for the PDO to exist // as long as the physical device exists we do not // delete the root hub PDO until the controller is // removed. // we will call this off just to gixe us a defined state rhDevExt->CurrentDevicePowerState = PowerDeviceD3; ntStatus = STATUS_SUCCESS; } break; case IRP_MN_STOP_DEVICE: // note: since OS PnP will STOP things that are not STARTED // we maintain two separate flags for this. // // the state machine looks like this: // // // / Started \ // stop = = stopped // \ Not Started / USBPORT_KdPrint((1, " Root Hub PDO %x is being stopped\n", PdoDeviceObject)); USBPORT_StopRootHubPdo(fdoDeviceObject, PdoDeviceObject); ntStatus = STATUS_SUCCESS; break; case IRP_MN_QUERY_PNP_DEVICE_STATE: ntStatus = STATUS_SUCCESS; break; case IRP_MN_QUERY_CAPABILITIES: // // Handle query caps for the root hub PDO // USBPORT_KdPrint((1, "'IRP_MN_QUERY_CAPABILITIES (rh PDO)\n")); // // Get the packet. // DeviceCapabilities = irpStack->Parameters.DeviceCapabilities.Capabilities; // // The power state capabilities for the root // hub are based on those of the host controller. // // We then modify them based on the power rules of // USB // RtlCopyMemory(DeviceCapabilities, &rhDevExt->DeviceCapabilities, sizeof(*DeviceCapabilities)); ntStatus = STATUS_SUCCESS; break; case IRP_MN_QUERY_ID: USBPORT_KdPrint((3, "'IOCTL_BUS_QUERY_ID\n")); ntStatus = STATUS_SUCCESS; switch (irpStack->Parameters.QueryId.IdType) { case BusQueryDeviceID: // return the 'generic' root hub ID { PWCHAR deviceId; WCHAR rootHubDeviceId[] = L"USB\\ROOT_HUB\0"; WCHAR rootHubDeviceId_20[] = L"USB\\ROOT_HUB20\0"; PWCHAR id; ULONG siz; PDEVICE_EXTENSION devExt; GET_DEVICE_EXT(devExt, fdoDeviceObject); ASSERT_FDOEXT(devExt); id = &rootHubDeviceId[0]; siz = sizeof(rootHubDeviceId); if (USBPORT_IS_USB20(devExt)) { id = &rootHubDeviceId_20[0]; siz = sizeof(rootHubDeviceId_20); } ALLOC_POOL_OSOWNED(deviceId, PagedPool, siz); if (deviceId) { RtlCopyMemory(deviceId, id, siz); } // device id for root hub is USB\ROOT_HUB information = (ULONG_PTR) deviceId; } LOGENTRY(NULL, fdoDeviceObject, LOG_PNP, 'DVid', information, 0, 0); break; case BusQueryHardwareIDs: { PDEVICE_EXTENSION devExt; // // generate hardware id for root hub // // A host controllers root hub VID,PID,REV is derived // from the controllers PCI VID,DEV,REV that is: // root hub VID = hc VID (vendor id) // root hub PID = hc DEV (device id) // root hub REV = hc REV (revision id) // // this allows filter drivers to be loaded on // specific root hub instances. // for HW IDs we generate: // USB\PORT_ROOT_HUB&VIDnnnn&PIDnnnn&REVnnnn // USB\PORT_ROOT_HUB&VIDnnnn&PIDnnnn // USB\PORT_ROOT_HUB // GET_DEVICE_EXT(devExt, fdoDeviceObject); ASSERT_FDOEXT(devExt); information = (ULONG_PTR) USBPORT_GetIdString( fdoDeviceObject, devExt->Fdo.PciVendorId, devExt->Fdo.PciDeviceId, (USHORT) devExt->Fdo.PciRevisionId); LOGENTRY(NULL, fdoDeviceObject, LOG_PNP, 'HWid', information, 0, 0); } break; case BusQueryCompatibleIDs: information = 0; break; case BusQueryInstanceID: // // The root HUB is instanced solely by the controller's id. // Hence the UniqueDeviceId above. // information = 0; break; default: ntStatus = Irp->IoStatus.Status; break; } break; case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_QUERY_STOP_DEVICE: case IRP_MN_CANCEL_STOP_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: ntStatus = STATUS_SUCCESS; break; case IRP_MN_QUERY_BUS_INFORMATION: { // return the standard USB GUID PPNP_BUS_INFORMATION busInfo; ALLOC_POOL_OSOWNED(busInfo, PagedPool, sizeof(PNP_BUS_INFORMATION)); if (busInfo == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } else { busInfo->BusTypeGuid = GUID_BUS_TYPE_USB; busInfo->LegacyBusType = PNPBus; busInfo->BusNumber = 0; ntStatus = STATUS_SUCCESS; information = (ULONG_PTR) busInfo; } } break; case IRP_MN_QUERY_DEVICE_RELATIONS: USBPORT_KdPrint((1," IRP_MN_QUERY_DEVICE_RELATIONS (PDO) %x %x\n", PdoDeviceObject, irpStack->Parameters.QueryDeviceRelations.Type)); if (irpStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) { PDEVICE_RELATIONS deviceRelations = NULL; ALLOC_POOL_OSOWNED(deviceRelations, PagedPool, sizeof(*deviceRelations)); if (deviceRelations == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } else { // return a reference to ourselves deviceRelations->Count = 1; ObReferenceObject(PdoDeviceObject); deviceRelations->Objects[0] = PdoDeviceObject; ntStatus = STATUS_SUCCESS; } USBPORT_KdPrint((1, " TargetDeviceRelation to Root Hub PDO - complt\n")); information = (ULONG_PTR) deviceRelations; } else { ntStatus = Irp->IoStatus.Status; information = Irp->IoStatus.Information; } break; case IRP_MN_QUERY_INTERFACE: USBPORT_KdPrint((1," IRP_MN_QUERY_INTERFACE (PDO) %x\n", PdoDeviceObject)); ntStatus = USBPORT_GetBusInterface(fdoDeviceObject, PdoDeviceObject, Irp); break; case IRP_MN_SURPRISE_REMOVAL: USBPORT_KdPrint((1," IRP_MN_SURPRISE_REMOVAL (PDO) %x\n", PdoDeviceObject)); ntStatus = STATUS_SUCCESS; break; default: // // default behavior for an unhandled PnP irp is to return the // status currently in the irp USBPORT_KdPrint((1, " PnP IOCTL(%d) to root hub PDO not handled\n", irpStack->MinorFunction)); ntStatus = Irp->IoStatus.Status; } /* switch, PNP minor function */ USBPORT_CompleteIrp(PdoDeviceObject, Irp, ntStatus, information); return ntStatus; } NTSTATUS USBPORT_CreateRootHubPdo( PDEVICE_OBJECT FdoDeviceObject, PDEVICE_OBJECT *RootHubPdo ) /*++ Routine Description: Attempt to create the root hub Arguments: *RootHubPdo set to NULL if unsuccessful Return Value: NTSTATUS --*/ { ULONG index = 0; UNICODE_STRING rootHubPdoUnicodeString; PDEVICE_EXTENSION rhDevExt, devExt; PDEVICE_OBJECT deviceObject = NULL; NTSTATUS ntStatus; PAGED_CODE(); GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); // those who wear priestly robes say we must do this do { ntStatus = USBPORT_MakeRootHubPdoName(FdoDeviceObject, &rootHubPdoUnicodeString, index); if (NT_SUCCESS(ntStatus)) { ntStatus = IoCreateDevice(devExt->Fdo.MiniportDriver->DriverObject, sizeof(DEVICE_EXTENSION), &rootHubPdoUnicodeString, FILE_DEVICE_BUS_EXTENDER, 0, FALSE, &deviceObject); index++; // delete the usbicode string we used for the // device name -- we don't need it anymore RtlFreeUnicodeString(&rootHubPdoUnicodeString); } } while (ntStatus == STATUS_OBJECT_NAME_COLLISION); if (NT_SUCCESS(ntStatus)) { rhDevExt = deviceObject->DeviceExtension; LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'rPDO', deviceObject, rhDevExt, 0); rhDevExt->DummyUsbdExtension = USBPORT_DummyUsbdExtension; rhDevExt->Sig = ROOTHUB_DEVICE_EXT_SIG; INITIALIZE_PENDING_REQUEST_COUNTER(rhDevExt); // transition to -1 means we have no pending requests INCREMENT_PENDING_REQUEST_COUNT(deviceObject, NULL); // point to our creator rhDevExt->HcFdoDeviceObject = FdoDeviceObject; // initialize root hub extension USBPORT_ComputeRootHubDeviceCaps(FdoDeviceObject, deviceObject); // initialize object deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; deviceObject->Flags |= DO_POWER_PAGABLE; deviceObject->StackSize = FdoDeviceObject->StackSize; } if (NT_SUCCESS(ntStatus)) { *RootHubPdo = deviceObject; } else { *RootHubPdo = NULL; } return ntStatus; } NTSTATUS USBPORT_CreatePortFdoSymbolicLink( PDEVICE_OBJECT FdoDeviceObject ) /*++ Routine Description: Attempt to create a symbolic link for the HC. We use the PnP APIs to generate a name based on the USBPORT Host Controller Class GUID defined in USB.H Arguments: *RootHubPdo set to NULL if unsuccessful Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION devExt; NTSTATUS ntStatus; PAGED_CODE(); GET_DEVICE_EXT(devExt, FdoDeviceObject); ntStatus = USBPORT_SymbolicLink(TRUE, devExt, devExt->Fdo.PhysicalDeviceObject, (LPGUID)&GUID_CLASS_USB_HOST_CONTROLLER); return ntStatus; } VOID USBPORT_StopRootHubPdo( PDEVICE_OBJECT FdoDeviceObject, PDEVICE_OBJECT PdoDeviceObject ) /*++ Routine Description: Attempt to STOP the root hub Arguments: Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION rhDevExt, devExt; GET_DEVICE_EXT(rhDevExt, PdoDeviceObject); ASSERT_PDOEXT(rhDevExt); GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); // disable the root hub notification interrupt // we won't need it while we are stopped MPRH_DisableIrq(devExt); // at this point no new notifications can come in for // the root hub // remove any start callback notifications rhDevExt->Pdo.HubInitCallback = NULL; rhDevExt->Pdo.HubInitContext = NULL; // remove the root hub 'device' the root hub PDO // will remain if (TEST_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_STARTED)) { USBPORT_RootHub_RemoveDevice(FdoDeviceObject, PdoDeviceObject); // stopped = NOT started CLEAR_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_STARTED); } if (TEST_FLAG(rhDevExt->Flags, USBPORT_FLAG_SYM_LINK)) { USBPORT_SymbolicLink(FALSE, rhDevExt, PdoDeviceObject, (LPGUID)&GUID_CLASS_USBHUB); } SET_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_STOPPED); // resume the controller if it is 'suspended' USBPORT_ResumeController(FdoDeviceObject); }