485 lines
16 KiB
C
485 lines
16 KiB
C
/*
|
|
*************************************************************************
|
|
* 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;
|
|
}
|
|
|