windows-nt/Source/XPSP1/NT/drivers/wdm/usb/usbccgp/pnp.c

485 lines
16 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*
*************************************************************************
* File: PNP.C
*
* Module: USBCCGP.SYS
* USB Common Class Generic Parent driver.
*
* Copyright (c) 1998 Microsoft Corporation
*
*
* Author: ervinp
*
*************************************************************************
*/
#include <wdm.h>
#include <stdio.h>
#include <usbdi.h>
#include <usbdlib.h>
#include <usbioctl.h>
#include <wdmguid.h>
#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;
}