/*++ 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_GetCachedRegistryKeyValueForPdo( 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); #if DBG USBPORT_LogAlloc(&devExt->Log, 16); #else USBPORT_LogAlloc(&devExt->Log, 8); #endif // 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); InitializeListHead(&devExt->Fdo.RegistryCache); 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); devExt->Fdo.PendingRhCallback = 1; // // 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; } // see if we have an interrupt // if so disconnect it // ** // DDK implies that that interrupt resources bust be // freed on surprise remove and the PCI driver depends // on this. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IRQ_CONNECTED)) { // fortunately this cannot fail IoDisconnectInterrupt(devExt->Fdo.InterruptObject); LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'IOCd', 0, 0, 0); CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_IRQ_CONNECTED); } 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); // for some reason we only do this fot XPSP1 // this is really only for the WU install // 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)) { if (deviceObject != NULL) { 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; } else { TEST_TRAP(); // sucess but no devobj? // we will return an error ntStatus = STATUS_UNSUCCESSFUL; } } 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); } /* Registry Key cache for miniports. Since the miniports cannot read the registry from the another thread other than the PNP thread we cache the reg values read from PNP start. Miniports re-read the registry on a re-start. */ PUSBPORT_REG_CACHE_ENTRY USBPORT_GetCahceEntry( PDEVICE_OBJECT FdoDeviceObject, BOOLEAN SoftwareBranch, PWCHAR KeyNameString, ULONG KeyNameStringLength ) /*++ Routine Description: Fetches a registry key value from the cache if there Arguments: Return Value: returns cached entry or NULL if not found --*/ { PLIST_ENTRY listEntry; PDEVICE_EXTENSION devExt; PUSBPORT_REG_CACHE_ENTRY regEntry; GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); // walk the list GET_HEAD_LIST(devExt->Fdo.RegistryCache, listEntry); while (listEntry && listEntry != &devExt->Fdo.RegistryCache) { regEntry = (PUSBPORT_REG_CACHE_ENTRY) CONTAINING_RECORD( listEntry, struct _USBPORT_REG_CACHE_ENTRY, RegLink); ASSERT_REG_CACHE(regEntry); if (KeyNameStringLength == regEntry->KeyNameStringLength && SoftwareBranch == regEntry->SoftwareBranch && RtlCompareMemory(regEntry->KeyNameString, KeyNameString, KeyNameStringLength)) { USBPORT_KdPrint((1, " reg entry found in cache\n")); return regEntry; } listEntry = regEntry->RegLink.Flink; } USBPORT_KdPrint((1, " reg entry not in cache\n")); return NULL; } NTSTATUS USBPORT_AddCahcedRegistryKey( PDEVICE_OBJECT FdoDeviceObject, BOOLEAN SoftwareBranch, PWCHAR KeyNameString, ULONG KeyNameStringLength, PVOID Data, ULONG DataLength ) /*++ Routine Description: Adds a reg key value to the cache Arguments: Return Value: returns STATUS_SUCCESS if the value was added to our cache --*/ { PDEVICE_EXTENSION devExt; PUSBPORT_REG_CACHE_ENTRY regEntry; GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); ALLOC_POOL_Z(regEntry, PagedPool, sizeof(*regEntry)+KeyNameStringLength); if (regEntry != NULL) { ALLOC_POOL_Z(regEntry->Data, PagedPool, DataLength); if (regEntry->Data != NULL) { regEntry->Sig = SIG_REG_CACHE; regEntry->SoftwareBranch = SoftwareBranch; regEntry->KeyNameStringLength; regEntry->DataLength = DataLength; RtlCopyMemory(regEntry->Data, (PUCHAR)Data, DataLength); RtlCopyMemory(®Entry->KeyNameString[0], KeyNameString, KeyNameStringLength); InsertTailList(&devExt->Fdo.RegistryCache, ®Entry->RegLink); USBPORT_KdPrint((1, " adding cache reg entry %x\n", regEntry)); return STATUS_SUCCESS; } else { FREE_POOL(FdoDeviceObject, regEntry); } } return STATUS_INSUFFICIENT_RESOURCES; } NTSTATUS USBPORT_GetCachedRegistryKeyValueForPdo( PDEVICE_OBJECT FdoDeviceObject, PDEVICE_OBJECT PhysicalDeviceObject, BOOLEAN SoftwareBranch, PWCHAR KeyNameString, ULONG KeyNameStringLength, PVOID Data, ULONG DataLength ) /*++ Routine Description: Fetches a registry key value from the cache since we cannot read the registry on a thread other than the PNP,POWER thread We cache entries between PNP start and STOP Arguments: Return Value: returns STATUS_SUCCESS if the value is found in the cache --*/ { PDEVICE_EXTENSION devExt; PUSBPORT_REG_CACHE_ENTRY regEntry; NTSTATUS ntStatus; GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); USBPORT_KdPrint((1, " USBPORT_GetCahcedRegistryKeyValueForPDO\n")); // read from the registry if we can if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_ON_PNP_THREAD)) { ntStatus = USBPORT_GetRegistryKeyValueForPdo(FdoDeviceObject, PhysicalDeviceObject, SoftwareBranch, KeyNameString, KeyNameStringLength, Data, DataLength); if (NT_SUCCESS(ntStatus)) { // cache it, if this fails we just return the result // of the read USBPORT_AddCahcedRegistryKey( FdoDeviceObject, SoftwareBranch, KeyNameString, KeyNameStringLength, Data, DataLength); } return ntStatus; } // just read from the cache regEntry = USBPORT_GetCahceEntry(FdoDeviceObject, SoftwareBranch, KeyNameString, KeyNameStringLength); if (regEntry != NULL) { if (regEntry->DataLength <= DataLength) { RtlCopyMemory(Data, regEntry->Data, regEntry->DataLength); ntStatus = STATUS_SUCCESS; TEST_TRAP(); } else { ntStatus = STATUS_BUFFER_TOO_SMALL; } } else { ntStatus = STATUS_OBJECT_NAME_NOT_FOUND; } return ntStatus; } VOID USBPORT_FlushCahcedRegistryKeys( PDEVICE_OBJECT FdoDeviceObject ) /*++ Routine Description: Flushes cache. Removes all cached registry keys. Arguments: Return Value: none. --*/ { PDEVICE_EXTENSION devExt; PUSBPORT_REG_CACHE_ENTRY regEntry; PLIST_ENTRY listEntry; GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt); while (!IsListEmpty(&devExt->Fdo.RegistryCache)) { listEntry = RemoveHeadList(&devExt->Fdo.RegistryCache); regEntry = (PUSBPORT_REG_CACHE_ENTRY) CONTAINING_RECORD( listEntry, struct _USBPORT_REG_CACHE_ENTRY, RegLink); ASSERT_REG_CACHE(regEntry); FREE_POOL(FdoDeviceObject, regEntry->Data); FREE_POOL(FdoDeviceObject, regEntry); } }