/* ************************************************************************* * File: PNP.C * * Module: USBCCGP.SYS * USB Common Class Generic Parent driver. * * Copyright (c) 1998 Microsoft Corporation * * * Author: ervinp * ************************************************************************* */ #include #include #include #include #include #include #include "usbccgp.h" #include "debug.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, USBC_PnP) #pragma alloc_text(PAGE, GetDeviceText) #endif NTSTATUS USBC_PnP(PDEVEXT devExt, PIRP irp) { PIO_STACK_LOCATION irpSp; NTSTATUS status; UCHAR minorFunction; BOOLEAN isParentFdo; enum deviceState oldState; PAGED_CODE(); irpSp = IoGetCurrentIrpStackLocation(irp); /* * Keep these privately so we still have it after the IRP completes * or after the device extension is freed on a REMOVE_DEVICE */ minorFunction = irpSp->MinorFunction; isParentFdo = devExt->isParentFdo; DBG_LOG_PNP_IRP(irp, minorFunction, isParentFdo, FALSE, 0); if (isParentFdo){ PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt; BOOLEAN irpAlreadyCompleted = FALSE; status = NO_STATUS; if (parentFdoExt->state == STATE_SUSPENDED || parentFdoExt->pendingIdleIrp) { ParentSetD0(parentFdoExt); } switch (minorFunction){ case IRP_MN_START_DEVICE: status = StartParentFdo(parentFdoExt, irp); break; case IRP_MN_QUERY_STOP_DEVICE: if (parentFdoExt->state == STATE_SUSPENDED){ status = STATUS_DEVICE_POWER_FAILURE; } else { /* * We will pass this IRP down the driver stack. * However, we need to change the default status * from STATUS_NOT_SUPPORTED to STATUS_SUCCESS. */ irp->IoStatus.Status = STATUS_SUCCESS; } break; case IRP_MN_STOP_DEVICE: if (parentFdoExt->state == STATE_SUSPENDED){ DBGERR(("Got STOP while device suspended")); status = STATUS_DEVICE_POWER_FAILURE; } else { /* * Only set state to STOPPED if the device was * previously started successfully. */ if (parentFdoExt->state == STATE_STARTED){ parentFdoExt->state = STATE_STOPPING; ParentCloseConfiguration(parentFdoExt); } else { DBGWARN(("Got STOP while in state is %xh.", parentFdoExt->state)); } } break; case IRP_MN_QUERY_REMOVE_DEVICE: /* * We will pass this IRP down the driver stack. * However, we need to change the default status * from STATUS_NOT_SUPPORTED to STATUS_SUCCESS. */ irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: /* * Cancel downward IO * Set default status to SUCCESS * Send the IRP down * Detach * Cleanup */ PrepareParentFDOForRemove(parentFdoExt); irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(irp); status = IoCallDriver(parentFdoExt->topDevObj, irp); irpAlreadyCompleted = TRUE; IoDetachDevice(parentFdoExt->topDevObj); FreeParentFDOResources(parentFdoExt); break; case IRP_MN_QUERY_DEVICE_RELATIONS: status = QueryParentDeviceRelations(parentFdoExt, irp); break; case IRP_MN_QUERY_CAPABILITIES: /* * Return the USB PDO's capabilities, but add the SurpriseRemovalOK bit. */ ASSERT(irpSp->Parameters.DeviceCapabilities.Capabilities); IoCopyCurrentIrpStackLocationToNext(irp); status = CallDriverSync(parentFdoExt->topDevObj, irp); if (NT_SUCCESS(status)){ irpSp->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE; } break; } if (status == NO_STATUS){ IoCopyCurrentIrpStackLocationToNext(irp); IoSetCompletionRoutine(irp, USBC_PnpComplete, (PVOID)devExt, TRUE, TRUE, TRUE); status = IoCallDriver(parentFdoExt->topDevObj, irp); } else if (irpAlreadyCompleted){ /* * Don't touch the irp. */ } else if (status != STATUS_PENDING){ /* * Complete the IRP here */ irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); } } else { /* * This is a child function PDO. */ PFUNCTION_PDO_EXT functionPdoExt = &devExt->functionPdoExt; switch (minorFunction){ case IRP_MN_START_DEVICE: functionPdoExt->state = STATE_STARTED; status = STATUS_SUCCESS; break; case IRP_MN_QUERY_STOP_DEVICE: case IRP_MN_CANCEL_STOP_DEVICE: status = STATUS_SUCCESS; break; case IRP_MN_STOP_DEVICE: /* * Only set state to STOPPED if the device was * previously started successfully. */ if (functionPdoExt->state == STATE_STARTED){ functionPdoExt->state = STATE_STOPPED; } status = STATUS_SUCCESS; break; case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: status = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: /* * Since we are the bus driver for the function-PDOs, we cannot * delete the function pdo on a remove-device. We must wait * for the parent to get the remove before deleting the function pdo. */ oldState = functionPdoExt->state; functionPdoExt->state = STATE_REMOVED; status = STATUS_SUCCESS; break; case IRP_MN_QUERY_ID: status = QueryFunctionPdoID(functionPdoExt, irp); break; case IRP_MN_QUERY_DEVICE_RELATIONS: status = QueryFunctionDeviceRelations(functionPdoExt, irp); break; case IRP_MN_QUERY_CAPABILITIES: status = QueryFunctionCapabilities(functionPdoExt, irp); break; case IRP_MN_QUERY_PNP_DEVICE_STATE: irp->IoStatus.Information = 0; switch (functionPdoExt->state){ case STATE_START_FAILED: irp->IoStatus.Information |= PNP_DEVICE_FAILED; break; case STATE_STOPPED: irp->IoStatus.Information |= PNP_DEVICE_DISABLED; break; case STATE_REMOVING: case STATE_REMOVED: irp->IoStatus.Information |= PNP_DEVICE_REMOVED; break; } status = STATUS_SUCCESS; break; case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: // Adrian says that once PnP sends this IRP, the PDO is valid for // PnP functions like IoGetDeviceProperty, etc. // // And since we know that the PDO is valid and the DevNode now exists, // this would also be a good time to handle the MS ExtPropDesc. // InstallExtPropDesc(functionPdoExt); status = STATUS_SUCCESS; break; case IRP_MN_QUERY_BUS_INFORMATION: { PPNP_BUS_INFORMATION busInfo = ALLOCPOOL(PagedPool, sizeof(PNP_BUS_INFORMATION)); if (busInfo) { busInfo->BusTypeGuid = GUID_BUS_TYPE_USB; busInfo->LegacyBusType = PNPBus; busInfo->BusNumber = 0; irp->IoStatus.Information = (ULONG_PTR)busInfo; status = STATUS_SUCCESS; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } break; case IRP_MN_QUERY_DEVICE_TEXT: status = GetDeviceText(functionPdoExt, irp); break; case IRP_MN_QUERY_INTERFACE: /* * Send this down to the parent. */ IoCopyCurrentIrpStackLocationToNext(irp); status = CallDriverSync(functionPdoExt->parentFdoExt->fdo, irp); break; default: /* * Complete the IRP with the default status. */ status = irp->IoStatus.Status; break; } /* * Complete the IRP here */ irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); } DBG_LOG_PNP_IRP(irp, minorFunction, isParentFdo, TRUE, status); return status; } NTSTATUS USBC_PnpComplete(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context) { PDEVEXT devExt = (PDEVEXT)context; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp); NTSTATUS status = irp->IoStatus.Status; ASSERT(devExt->signature == USBCCGP_TAG); if (devExt->isParentFdo){ PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt; switch (irpSp->MinorFunction){ case IRP_MN_START_DEVICE: ASSERT(0); break; case IRP_MN_STOP_DEVICE: /* * Only set the state to STOPPED if the device * was previously successfully started; * otherwise, leave the state alone. */ if (parentFdoExt->state == STATE_STOPPING){ /* * Free the interface list's .Interface pointers, * which we have to re-allocate on a start-after-stop. */ FreeInterfaceList(parentFdoExt, FALSE); parentFdoExt->state = STATE_STOPPED; } break; } } /* * Must propagate the pending bit if a lower driver returned pending. */ if (irp->PendingReturned){ IoMarkIrpPending(irp); } return STATUS_SUCCESS; } /* ******************************************************************************** * GetDeviceText ******************************************************************************** * * If the interface descriptor for this function has a non-zero iInterface * string, return that string. Otherwise, pass this request off to the * parent, which will return the iProduct string from the device descriptor. * */ NTSTATUS GetDeviceText(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp) { NTSTATUS status; UCHAR ifaceStringIndex; ULONG ulBytes = 0; PAGED_CODE(); ifaceStringIndex = functionPdoExt->functionInterfaceList[0].InterfaceDescriptor->iInterface; if (ifaceStringIndex){ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp); PUSB_STRING_DESCRIPTOR ifaceStringDesc; PWCHAR deviceText; switch (irpSp->Parameters.QueryDeviceText.DeviceTextType){ case DeviceTextDescription: ifaceStringDesc = ALLOCPOOL(NonPagedPool, MAXIMUM_USB_STRING_LENGTH); if (ifaceStringDesc){ LANGID langId = (LANGID)(irpSp->Parameters.QueryDeviceText.LocaleId >> 16); if (langId == 0){ /* * Force to English. */ langId = 0x0409; } QDT_Retry: status = GetStringDescriptor( functionPdoExt->parentFdoExt, ifaceStringIndex, langId, ifaceStringDesc, MAXIMUM_USB_STRING_LENGTH); if (NT_SUCCESS(status) && ifaceStringDesc->bLength == 0) { status = STATUS_UNSUCCESSFUL; } if (NT_SUCCESS(status)){ ULONG numWchars = (ifaceStringDesc->bLength - 2*sizeof(UCHAR))/sizeof(WCHAR); deviceText = ALLOCPOOL(PagedPool, (numWchars+1)*sizeof(WCHAR)); if (deviceText){ RtlZeroMemory(deviceText, (numWchars+1)*sizeof(WCHAR)); RtlCopyMemory(deviceText, ifaceStringDesc->bString, numWchars*sizeof(WCHAR)); irp->IoStatus.Information = (ULONG_PTR)deviceText; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else if (langId != 0x409) { // We are running a non-English flavor of the OS, but the // attached USB device does not contain device text in // the requested language. Let's try again for English. langId = 0x0409; goto QDT_Retry; } FREEPOOL(ifaceStringDesc); } else { status = STATUS_INSUFFICIENT_RESOURCES; } if (!NT_SUCCESS(status) && GenericCompositeUSBDeviceString) { // Return generic English string if one could not be // obtained from the device. STRLEN(ulBytes, GenericCompositeUSBDeviceString); ulBytes += sizeof(UNICODE_NULL); deviceText = ALLOCPOOL(PagedPool, ulBytes); if (deviceText) { RtlZeroMemory(deviceText, ulBytes); RtlCopyMemory(deviceText, GenericCompositeUSBDeviceString, ulBytes); irp->IoStatus.Information = (ULONG_PTR) deviceText; status = STATUS_SUCCESS; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } break; case DeviceTextLocationInformation: /* * BUGBUG * Supporting this call to return phys port # is optional. * * We may be able to service it with * a call to IOCTL_INTERNAL_USB_GET_PARENT_HUB_INFO * (Pass a PULONG in Argument2, this will be filled in with the port #.). */ status = irp->IoStatus.Status; break; default: DBGWARN(("GetDeviceText: unhandled DeviceTextType %xh.", (ULONG)irpSp->Parameters.QueryDeviceText.DeviceTextType)); status = irp->IoStatus.Status; break; } } else { IoCopyCurrentIrpStackLocationToNext(irp); status = CallNextDriverSync(functionPdoExt->parentFdoExt, irp); } return status; }