mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
895 lines
29 KiB
895 lines
29 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
device.c
|
|
|
|
Abstract
|
|
|
|
Resource management routines for devices and collections
|
|
|
|
Author:
|
|
|
|
ervinp
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, HidpStartDevice)
|
|
#pragma alloc_text(PAGE, HidpStartCollectionPDO)
|
|
#pragma alloc_text(PAGE, AllocDeviceResources)
|
|
#pragma alloc_text(PAGE, FreeDeviceResources)
|
|
#pragma alloc_text(PAGE, AllocCollectionResources)
|
|
#pragma alloc_text(PAGE, FreeCollectionResources)
|
|
#pragma alloc_text(PAGE, InitializeCollection)
|
|
#pragma alloc_text(PAGE, HidpCleanUpFdo)
|
|
#pragma alloc_text(PAGE, HidpRemoveDevice)
|
|
#pragma alloc_text(PAGE, HidpRemoveCollection)
|
|
#endif
|
|
|
|
/*
|
|
********************************************************************************
|
|
* AllocDeviceResources
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS AllocDeviceResources(FDO_EXTENSION *fdoExt)
|
|
{
|
|
ULONG numCollections;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
/*
|
|
* This will allocate fdoExt->rawReportDescription
|
|
*/
|
|
status = HidpGetDeviceDescriptor(fdoExt);
|
|
if (NT_SUCCESS(status)){
|
|
|
|
/*
|
|
* Ask HIDPARSE to fill in the HIDP_DEVICE_DESC for this device.
|
|
*/
|
|
status = HidP_GetCollectionDescription(
|
|
fdoExt->rawReportDescription,
|
|
fdoExt->rawReportDescriptionLength,
|
|
NonPagedPool,
|
|
&fdoExt->deviceDesc);
|
|
|
|
if (NT_SUCCESS(status)){
|
|
fdoExt->devDescInitialized = TRUE;
|
|
|
|
numCollections = fdoExt->deviceDesc.CollectionDescLength;
|
|
ASSERT(numCollections);
|
|
|
|
fdoExt->classCollectionArray = ALLOCATEPOOL(NonPagedPool, numCollections*sizeof(HIDCLASS_COLLECTION));
|
|
if (!fdoExt->classCollectionArray){
|
|
fdoExt->classCollectionArray = BAD_POINTER;
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
RtlZeroMemory(fdoExt->classCollectionArray, numCollections*sizeof(HIDCLASS_COLLECTION));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fdoExt->rawReportDescription = BAD_POINTER;
|
|
}
|
|
|
|
DBGSUCCESS(status, FALSE)
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* FreeDeviceResources
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID FreeDeviceResources(FDO_EXTENSION *fdoExt)
|
|
{
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++) {
|
|
FreeCollectionResources(fdoExt, fdoExt->classCollectionArray[i].CollectionNumber);
|
|
}
|
|
|
|
/*
|
|
* Free the stuff returned by HIDPARSE's HidP_GetCollectionDescription.
|
|
*/
|
|
if (fdoExt->devDescInitialized){
|
|
HidP_FreeCollectionDescription(&fdoExt->deviceDesc);
|
|
#if DBG
|
|
fdoExt->deviceDesc.CollectionDesc = BAD_POINTER;
|
|
fdoExt->deviceDesc.ReportIDs = BAD_POINTER;
|
|
#endif
|
|
}
|
|
fdoExt->deviceDesc.CollectionDescLength = 0;
|
|
|
|
/*
|
|
* Free the raw report descriptor allocated during START_DEVICE by HidpGetDeviceDescriptor().
|
|
*/
|
|
if (ISPTR(fdoExt->rawReportDescription)){
|
|
ExFreePool(fdoExt->rawReportDescription);
|
|
}
|
|
fdoExt->rawReportDescription = BAD_POINTER;
|
|
|
|
if (ISPTR(fdoExt->classCollectionArray)){
|
|
ExFreePool(fdoExt->classCollectionArray);
|
|
}
|
|
fdoExt->classCollectionArray = BAD_POINTER;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* AllocCollectionResources
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS AllocCollectionResources(FDO_EXTENSION *fdoExt, ULONG collectionNum)
|
|
{
|
|
PHIDCLASS_COLLECTION collection;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
collection = GetHidclassCollection(fdoExt, collectionNum);
|
|
if (collection){
|
|
ULONG descriptorLen;
|
|
|
|
descriptorLen = collection->hidCollectionInfo.DescriptorSize;
|
|
if (descriptorLen){
|
|
collection->phidDescriptor = ALLOCATEPOOL(NonPagedPool, descriptorLen);
|
|
if (collection->phidDescriptor){
|
|
status = HidpGetCollectionDescriptor(
|
|
fdoExt,
|
|
collection->CollectionNumber,
|
|
collection->phidDescriptor,
|
|
&descriptorLen);
|
|
}
|
|
else {
|
|
collection->phidDescriptor = BAD_POINTER;
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)){
|
|
ULONG i = collection->CollectionIndex;
|
|
ULONG inputLength;
|
|
|
|
ASSERT(fdoExt->devDescInitialized);
|
|
inputLength = fdoExt->deviceDesc.CollectionDesc[i].InputLength;
|
|
if (inputLength){
|
|
if (collection->hidCollectionInfo.Polled){
|
|
collection->cookedInterruptReportBuf = BAD_POINTER;
|
|
}
|
|
else {
|
|
collection->cookedInterruptReportBuf = ALLOCATEPOOL(NonPagedPool, inputLength);
|
|
if (!collection->cookedInterruptReportBuf){
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
fdoExt->isOutputOnlyDevice = FALSE;
|
|
}
|
|
else {
|
|
/*
|
|
* This is an output-only device (e.g. USB monitor)
|
|
*/
|
|
DBGWARN(("Zero input length -> output-only device."))
|
|
collection->cookedInterruptReportBuf = BAD_POINTER;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(descriptorLen > 0);
|
|
status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_DEVICE_DATA_ERROR;
|
|
}
|
|
|
|
DBGSUCCESS(status, TRUE)
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* FreeCollectionResources
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID FreeCollectionResources(FDO_EXTENSION *fdoExt, ULONG collectionNum)
|
|
{
|
|
PHIDCLASS_COLLECTION collection;
|
|
|
|
PAGED_CODE();
|
|
|
|
collection = GetHidclassCollection(fdoExt, collectionNum);
|
|
if (collection){
|
|
if (collection->hidCollectionInfo.Polled){
|
|
if (ISPTR(collection->savedPolledReportBuf)){
|
|
ExFreePool(collection->savedPolledReportBuf);
|
|
}
|
|
collection->savedPolledReportBuf = BAD_POINTER;
|
|
}
|
|
else {
|
|
if (ISPTR(collection->cookedInterruptReportBuf)){
|
|
ExFreePool(collection->cookedInterruptReportBuf);
|
|
}
|
|
else {
|
|
// this is an output-only collection
|
|
}
|
|
}
|
|
collection->cookedInterruptReportBuf = BAD_POINTER;
|
|
|
|
if (ISPTR(collection->phidDescriptor)){
|
|
ExFreePool(collection->phidDescriptor);
|
|
}
|
|
collection->phidDescriptor = BAD_POINTER;
|
|
}
|
|
else {
|
|
TRAP;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* InitializeCollection
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS InitializeCollection(FDO_EXTENSION *fdoExt, ULONG collectionIndex)
|
|
{
|
|
PHIDCLASS_COLLECTION collection;
|
|
ULONG descriptorBufLen;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(ISPTR(fdoExt->classCollectionArray));
|
|
|
|
collection = &fdoExt->classCollectionArray[collectionIndex];
|
|
RtlZeroMemory(collection, sizeof(HIDCLASS_COLLECTION));
|
|
|
|
ASSERT(fdoExt->devDescInitialized);
|
|
collection->CollectionNumber = fdoExt->deviceDesc.CollectionDesc[collectionIndex].CollectionNumber;
|
|
collection->CollectionIndex = collectionIndex;
|
|
InitializeListHead(&collection->FileExtensionList);
|
|
KeInitializeSpinLock(&collection->FileExtensionListSpinLock);
|
|
KeInitializeSpinLock(&collection->powerEventSpinLock);
|
|
KeInitializeSpinLock(&collection->secureReadLock);
|
|
collection->secureReadMode = 0;
|
|
|
|
descriptorBufLen = sizeof(HID_COLLECTION_INFORMATION);
|
|
status = HidpGetCollectionInformation( fdoExt,
|
|
collection->CollectionNumber,
|
|
&collection->hidCollectionInfo,
|
|
&descriptorBufLen);
|
|
|
|
DBGSUCCESS(status, TRUE)
|
|
return status;
|
|
}
|
|
|
|
|
|
void
|
|
HidpGetRemoteWakeEnableState(
|
|
PDO_EXTENSION *pdoExt
|
|
)
|
|
{
|
|
HANDLE hKey;
|
|
NTSTATUS status;
|
|
ULONG tmp;
|
|
BOOLEAN wwEnableFound;
|
|
|
|
hKey = NULL;
|
|
wwEnableFound = FALSE;
|
|
|
|
status = IoOpenDeviceRegistryKey (pdoExt->pdo,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_ALL,
|
|
&hKey);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
UNICODE_STRING valueName;
|
|
ULONG length;
|
|
ULONG value;
|
|
PKEY_VALUE_FULL_INFORMATION fullInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlInitUnicodeString (&valueName, HIDCLASS_REMOTE_WAKE_ENABLE);
|
|
|
|
length = sizeof (KEY_VALUE_FULL_INFORMATION)
|
|
+ valueName.MaximumLength
|
|
+ sizeof(value);
|
|
|
|
fullInfo = ExAllocatePool (PagedPool, length);
|
|
|
|
if (fullInfo) {
|
|
status = ZwQueryValueKey (hKey,
|
|
&valueName,
|
|
KeyValueFullInformation,
|
|
fullInfo,
|
|
length,
|
|
&length);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
DBGASSERT (sizeof(value) == fullInfo->DataLength,
|
|
("Value data wrong length for REmote wake reg value."),
|
|
TRUE);
|
|
RtlCopyMemory (&value,
|
|
((PUCHAR) fullInfo) + fullInfo->DataOffset,
|
|
fullInfo->DataLength);
|
|
pdoExt->remoteWakeEnabled = (value ? TRUE : FALSE);
|
|
}
|
|
|
|
ExFreePool (fullInfo);
|
|
}
|
|
|
|
ZwClose (hKey);
|
|
hKey = NULL;
|
|
}
|
|
}
|
|
|
|
WMIGUIDREGINFO HidClassWmiGuidList =
|
|
{
|
|
&GUID_POWER_DEVICE_WAKE_ENABLE,
|
|
1,
|
|
0 // wait wake
|
|
};
|
|
|
|
WMIGUIDREGINFO HidClassFdoWmiGuidList =
|
|
{
|
|
&GUID_POWER_DEVICE_ENABLE,
|
|
1,
|
|
0
|
|
};
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HidpStartCollectionPDO
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HidpStartCollectionPDO(FDO_EXTENSION *fdoExt, PDO_EXTENSION *pdoExt, PIRP Irp)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
/*
|
|
* Initialize the collection only if it's not already initialized.
|
|
* This is so we don't destroy the FileExtensionList after a STOP/START.
|
|
*/
|
|
if (pdoExt->state == COLLECTION_STATE_UNINITIALIZED){
|
|
pdoExt->state = COLLECTION_STATE_INITIALIZED;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)){
|
|
|
|
PHIDCLASS_COLLECTION collection = GetHidclassCollection(fdoExt, pdoExt->collectionNum);
|
|
if (collection){
|
|
|
|
/*
|
|
* If all collection PDOs for this device FDO are initialized,
|
|
* figure out the maximum report size and finish starting the device.
|
|
*/
|
|
if (AnyClientPDOsInitialized(fdoExt, TRUE)){
|
|
|
|
DBGSTATE(fdoExt->state, DEVICE_STATE_START_SUCCESS, FALSE)
|
|
|
|
/*
|
|
* If this is a polled collection,
|
|
* start the background polling loop FOR EACH COLLECTION.
|
|
* Otherwise, if it's an ordinary interrupt collection,
|
|
* start the ping-pong IRPs for it.
|
|
*/
|
|
if (collection->hidCollectionInfo.Polled){
|
|
|
|
if (HidpSetMaxReportSize(fdoExt)){
|
|
|
|
ULONG i;
|
|
for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++){
|
|
PHIDCLASS_COLLECTION ctn;
|
|
ctn = &fdoExt->classCollectionArray[i];
|
|
|
|
/*
|
|
* If one of the collections is polled, they
|
|
* should ALL be polled.
|
|
*/
|
|
ASSERT(ctn->hidCollectionInfo.Polled);
|
|
|
|
ctn->PollInterval_msec = DEFAULT_POLL_INTERVAL_MSEC;
|
|
|
|
/*
|
|
* Allocate the buffer for saving the polled device's
|
|
* last report. Allocate one more byte than the max
|
|
* report size for the device in case we have to
|
|
* prepend a report id byte.
|
|
*/
|
|
ctn->savedPolledReportBuf = ALLOCATEPOOL(NonPagedPool, fdoExt->maxReportSize+1);
|
|
if (ctn->savedPolledReportBuf){
|
|
ctn->polledDataIsStale = TRUE;
|
|
StartPollingLoop(fdoExt, ctn, TRUE);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
ASSERT(ctn->savedPolledReportBuf);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (fdoExt->isOutputOnlyDevice){
|
|
/*
|
|
* Don't start ping-pong IRPs.
|
|
*/
|
|
}
|
|
else {
|
|
status = HidpStartAllPingPongs(fdoExt);
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
pdoExt->state = COLLECTION_STATE_RUNNING;
|
|
#if DBG
|
|
collection->Signature = HIDCLASS_COLLECTION_SIG;
|
|
#endif
|
|
|
|
/*
|
|
* Create the 'file-name' used by clients to open this device.
|
|
*/
|
|
HidpCreateSymbolicLink(pdoExt, pdoExt->collectionNum, TRUE, pdoExt->pdo);
|
|
|
|
if (!pdoExt->MouseOrKeyboard &&
|
|
WAITWAKE_SUPPORTED(fdoExt)) {
|
|
//
|
|
// register for the wait wake guid as well
|
|
//
|
|
pdoExt->WmiLibInfo.GuidCount = sizeof (HidClassWmiGuidList) /
|
|
sizeof (WMIGUIDREGINFO);
|
|
ASSERT (1 == pdoExt->WmiLibInfo.GuidCount);
|
|
|
|
//
|
|
// See if the user has enabled remote wake for the device
|
|
// PRIOR to registering with WMI.
|
|
//
|
|
HidpGetRemoteWakeEnableState(pdoExt);
|
|
|
|
pdoExt->WmiLibInfo.GuidList = &HidClassWmiGuidList;
|
|
pdoExt->WmiLibInfo.QueryWmiRegInfo = HidpQueryWmiRegInfo;
|
|
pdoExt->WmiLibInfo.QueryWmiDataBlock = HidpQueryWmiDataBlock;
|
|
pdoExt->WmiLibInfo.SetWmiDataBlock = HidpSetWmiDataBlock;
|
|
pdoExt->WmiLibInfo.SetWmiDataItem = HidpSetWmiDataItem;
|
|
pdoExt->WmiLibInfo.ExecuteWmiMethod = NULL;
|
|
pdoExt->WmiLibInfo.WmiFunctionControl = NULL;
|
|
|
|
IoWMIRegistrationControl(pdoExt->pdo, WMIREG_ACTION_REGISTER);
|
|
|
|
if (SHOULD_SEND_WAITWAKE(pdoExt)) {
|
|
HidpCreateRemoteWakeIrp(pdoExt);
|
|
}
|
|
}
|
|
|
|
if (AllClientPDOsInitialized(fdoExt, TRUE)){
|
|
HidpStartIdleTimeout(fdoExt, TRUE);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_DEVICE_DATA_ERROR;
|
|
}
|
|
}
|
|
|
|
DBGSUCCESS(status, FALSE)
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HidpStartDevice
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HidpStartDevice(PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, PIRP Irp)
|
|
{
|
|
FDO_EXTENSION *fdoExt;
|
|
enum deviceState previousState;
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(!HidDeviceExtension->isClientPdo);
|
|
fdoExt = &HidDeviceExtension->fdoExt;
|
|
|
|
previousState = fdoExt->state;
|
|
fdoExt->state = DEVICE_STATE_STARTING;
|
|
|
|
/*
|
|
* Get the power-state conversion table
|
|
*/
|
|
status = HidpQueryDeviceCapabilities(
|
|
HidDeviceExtension->hidExt.PhysicalDeviceObject,
|
|
&fdoExt->deviceCapabilities);
|
|
if (NT_SUCCESS(status)){
|
|
|
|
/*
|
|
* Alert the rest of the driver stack that the device is starting.
|
|
*/
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
status = HidpCallDriverSynchronous(fdoExt->fdo, Irp);
|
|
|
|
if (NT_SUCCESS(status)){
|
|
|
|
/*
|
|
* If we're just resuming from STOP,
|
|
* there's nothing else to do;
|
|
* otherwise, need to call down the USB stack
|
|
* for some info and allocate some resources.
|
|
*/
|
|
if (previousState == DEVICE_STATE_INITIALIZED){
|
|
|
|
status = AllocDeviceResources(fdoExt);
|
|
if (NT_SUCCESS(status)){
|
|
/*
|
|
* Assume this is an output-only device until we start
|
|
* a collection-pdo which handles inputs.
|
|
* Only set fdoExt->isOutputOnlyDevice on the first start
|
|
* not on a subsequent start following a stop.
|
|
*/
|
|
fdoExt->isOutputOnlyDevice = TRUE;
|
|
|
|
/*
|
|
* Initialize WMI stuff
|
|
*/
|
|
|
|
fdoExt->WmiLibInfo.GuidCount = sizeof(HidClassFdoWmiGuidList) /
|
|
sizeof (WMIGUIDREGINFO);
|
|
|
|
fdoExt->WmiLibInfo.GuidList = &HidClassFdoWmiGuidList;
|
|
fdoExt->WmiLibInfo.QueryWmiRegInfo = HidpQueryWmiRegInfo;
|
|
fdoExt->WmiLibInfo.QueryWmiDataBlock = HidpQueryWmiDataBlock;
|
|
fdoExt->WmiLibInfo.SetWmiDataBlock = HidpSetWmiDataBlock;
|
|
fdoExt->WmiLibInfo.SetWmiDataItem = HidpSetWmiDataItem;
|
|
fdoExt->WmiLibInfo.ExecuteWmiMethod = NULL;
|
|
fdoExt->WmiLibInfo.WmiFunctionControl = NULL;
|
|
|
|
|
|
|
|
/*
|
|
* Allocate all the collection resources before allocating
|
|
* the pingpong irps, so that we can set a maximum report
|
|
* size.
|
|
*/
|
|
for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++) {
|
|
|
|
// If one of these fails, we will clean up properly
|
|
// in the remove routine, so there's no need to
|
|
// bother cleaning up here.
|
|
|
|
status = InitializeCollection(fdoExt, i);
|
|
if (!NT_SUCCESS(status)){
|
|
break;
|
|
}
|
|
|
|
status = AllocCollectionResources(fdoExt, fdoExt->deviceDesc.CollectionDesc[i].CollectionNumber);
|
|
if (!NT_SUCCESS(status)){
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We need ot allocate the pingpongs in the fdo start
|
|
* routine due to race conditions introduced by selective
|
|
* suspend.
|
|
*/
|
|
if (!fdoExt->isOutputOnlyDevice &&
|
|
!fdoExt->driverExt->DevicesArePolled) {
|
|
status = HidpReallocPingPongIrps(fdoExt, MIN_PINGPONG_IRPS);
|
|
}
|
|
if (NT_SUCCESS(status)){
|
|
/*
|
|
* We will have to create an array of PDOs, one for each device class.
|
|
* The following call will cause NTKERN to call us back with
|
|
* IRP_MN_QUERY_DEVICE_RELATIONS and initialize its collection-PDOs.
|
|
*/
|
|
IoInvalidateDeviceRelations(HidDeviceExtension->hidExt.PhysicalDeviceObject, BusRelations);
|
|
}
|
|
}
|
|
}
|
|
else if (previousState == DEVICE_STATE_STOPPED){
|
|
//
|
|
// Any request that comes in when we are in low power will be
|
|
// dealt with at that time
|
|
//
|
|
DBGSTATE(fdoExt->prevState, DEVICE_STATE_START_SUCCESS, TRUE)
|
|
}
|
|
else {
|
|
TRAP;
|
|
status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)){
|
|
fdoExt->state = DEVICE_STATE_START_SUCCESS;
|
|
#if WIN95_BUILD
|
|
/*
|
|
* Send down the WaitWake IRP.
|
|
* This allows the device to wake up the system.
|
|
*
|
|
* Note: On Win98 there is no way for a client to
|
|
* send a WaitWake IRP. Therefore we initiate
|
|
* a WaitWake IRP for every device, not just
|
|
* if a client sends us a WaitWake IRP, like on NT.
|
|
*/
|
|
/*
|
|
* We could have been suspended, stopped, then started again. In
|
|
* this case, we need to not send a WW irp because we already have
|
|
* on pending.
|
|
*/
|
|
if (fdoExt->deviceCapabilities.SystemWake > PowerSystemWorking) {
|
|
SubmitWaitWakeIrp(HidDeviceExtension);
|
|
}
|
|
#endif
|
|
|
|
#if DBG
|
|
{
|
|
ULONG i;
|
|
|
|
// Win98 doesn't have good debug extensions
|
|
DBGVERBOSE(("Started fdoExt %ph with %d collections: ", fdoExt, fdoExt->deviceDesc.CollectionDescLength))
|
|
for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++){
|
|
DBGVERBOSE((" - collection #%d: (in=%xh,out=%xh,feature=%xh) usagePage %xh, usage %xh ",
|
|
fdoExt->deviceDesc.CollectionDesc[i].CollectionNumber,
|
|
fdoExt->deviceDesc.CollectionDesc[i].InputLength,
|
|
fdoExt->deviceDesc.CollectionDesc[i].OutputLength,
|
|
fdoExt->deviceDesc.CollectionDesc[i].FeatureLength,
|
|
fdoExt->deviceDesc.CollectionDesc[i].UsagePage,
|
|
fdoExt->deviceDesc.CollectionDesc[i].Usage))
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
else {
|
|
fdoExt->state = DEVICE_STATE_START_FAILURE;
|
|
}
|
|
|
|
DBGSUCCESS(status, FALSE)
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
HidpCleanUpFdo(FDO_EXTENSION *fdoExt)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (fdoExt->openCount == 0){
|
|
/*
|
|
* This is the last CLOSE on an alreay-removed device.
|
|
*
|
|
* Free resources and the FDO name
|
|
* (wPdoName that was allocated in HidpAddDevice);
|
|
*
|
|
*/
|
|
DequeueFdoExt(fdoExt);
|
|
FreeDeviceResources(fdoExt);
|
|
RtlFreeUnicodeString(&fdoExt->name);
|
|
IoWMIRegistrationControl(fdoExt->fdo, WMIREG_ACTION_DEREGISTER);
|
|
|
|
/*
|
|
* Delete the device-FDO and all collection-PDOs
|
|
* Don't touch fdoExt after this.
|
|
*/
|
|
HidpDeleteDeviceObjects(fdoExt);
|
|
}
|
|
}
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HidpRemoveDevice
|
|
********************************************************************************
|
|
*
|
|
*/
|
|
NTSTATUS HidpRemoveDevice(FDO_EXTENSION *fdoExt, IN PIRP Irp)
|
|
{
|
|
BOOLEAN proceedWithRemove;
|
|
NTSTATUS status;
|
|
PIRP IdleIrp;
|
|
|
|
PAGED_CODE();
|
|
|
|
/*
|
|
* All collection-PDOs should have been removed by now,
|
|
* but we want to verify this.
|
|
* Only allow removal of this device-FDO if all the
|
|
* collection-PDOs are removed
|
|
* (or if they never got created in the first place).
|
|
*/
|
|
if (fdoExt->prevState == DEVICE_STATE_START_FAILURE){
|
|
proceedWithRemove = TRUE;
|
|
}
|
|
else if (fdoExt->prevState == DEVICE_STATE_STOPPED){
|
|
/*
|
|
* If a device fails to initialize, it may get
|
|
* STOP_DEVICE before being removed, so we want to
|
|
* go ahead and remove it without calling
|
|
* AllClientPDOsInitialized, which accesses some
|
|
* data which may not have been initialized.
|
|
* In this case we're never checking for the
|
|
* case that the device was initialized successfully,
|
|
* then stopped, and then removed without its
|
|
* collection-PDOs being removed; but this is an
|
|
* illegal case, so we'll just punt on it.
|
|
*/
|
|
proceedWithRemove = TRUE;
|
|
}
|
|
else if (AllClientPDOsInitialized(fdoExt, FALSE)){
|
|
proceedWithRemove = TRUE;
|
|
}
|
|
else {
|
|
/*
|
|
* This shouldn't happen -- all the collection-PDOs
|
|
* should have been removed before the device-FDO.
|
|
*/
|
|
DBGERR(("State of fdo %x state is %d",fdoExt->fdo,fdoExt->state))
|
|
TRAP;
|
|
proceedWithRemove = FALSE;
|
|
}
|
|
|
|
if (proceedWithRemove){
|
|
PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension =
|
|
CONTAINING_RECORD(fdoExt, HIDCLASS_DEVICE_EXTENSION, fdoExt);
|
|
|
|
DBGASSERT((fdoExt->state == DEVICE_STATE_REMOVING ||
|
|
fdoExt->state == DEVICE_STATE_INITIALIZED ||
|
|
fdoExt->state == DEVICE_STATE_START_FAILURE),
|
|
("Device is in incorrect state: %x", fdoExt->state),
|
|
TRUE)
|
|
|
|
if (ISPTR(fdoExt->waitWakeIrp)){
|
|
IoCancelIrp(fdoExt->waitWakeIrp);
|
|
fdoExt->waitWakeIrp = BAD_POINTER;
|
|
}
|
|
|
|
HidpCancelIdleNotification(fdoExt, TRUE);
|
|
|
|
if (ISPTR(fdoExt->idleNotificationRequest)) {
|
|
IoFreeIrp(fdoExt->idleNotificationRequest);
|
|
fdoExt->idleNotificationRequest = BAD_POINTER;
|
|
}
|
|
|
|
while (IdleIrp = DequeuePowerDelayedIrp(fdoExt)) {
|
|
IdleIrp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
|
IoCompleteRequest(IdleIrp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
DestroyPingPongs(fdoExt);
|
|
|
|
/*
|
|
* Note: THE ORDER OF THESE ACTIONS IS VERY CRITICAL
|
|
*/
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoSkipCurrentIrpStackLocation (Irp);
|
|
status = HidpCallDriver(fdoExt->fdo, Irp);
|
|
|
|
fdoExt->state = DEVICE_STATE_REMOVED;
|
|
|
|
DerefDriverExt(fdoExt->driverExt->MinidriverObject);
|
|
fdoExt->driverExt = BAD_POINTER;
|
|
|
|
/*
|
|
* After Detach we can no longer send IRPS to this device
|
|
* object as it will be GONE!
|
|
*/
|
|
IoDetachDevice(HidDeviceExtension->hidExt.NextDeviceObject);
|
|
|
|
/*
|
|
* If all client handles on this device have been closed,
|
|
* destroy the objects and our context for it;
|
|
* otherwise, we'll do this when the last client closes
|
|
* their handle.
|
|
*
|
|
* On NT we can only get here if all our creates have been closed, so
|
|
* this is unnecessary, but on Win9x, a remove can be sent with valid
|
|
* opens against the stack.
|
|
*
|
|
* Don't touch fdoExt after this.
|
|
*/
|
|
HidpCleanUpFdo(fdoExt);
|
|
}
|
|
else {
|
|
status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
|
}
|
|
|
|
DBGSUCCESS(status, FALSE)
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HidpRemoveCollection
|
|
********************************************************************************
|
|
*
|
|
*/
|
|
VOID HidpRemoveCollection(FDO_EXTENSION *fdoExt, PDO_EXTENSION *pdoExt, IN PIRP Irp)
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// This pdo is no longer available as it has been removed.
|
|
// It should still be returned for each Query Device Relations
|
|
// IRPS to the HID bus, but it itself should respond to all
|
|
// IRPS with STATUS_DELETE_PENDING.
|
|
//
|
|
if (pdoExt->prevState == COLLECTION_STATE_UNINITIALIZED || // for started pdos
|
|
pdoExt->state == COLLECTION_STATE_UNINITIALIZED){ // For unstarted pdos
|
|
pdoExt->state = COLLECTION_STATE_UNINITIALIZED;
|
|
DBGVERBOSE(("HidpRemoveCollection: collection uninitialized."))
|
|
}
|
|
else {
|
|
ULONG ctnIndx = pdoExt->collectionIndex;
|
|
PHIDCLASS_COLLECTION collection = &fdoExt->classCollectionArray[ctnIndx];
|
|
ULONG numReportIDs = fdoExt->deviceDesc.ReportIDsLength;
|
|
PIRP remoteWakeIrp;
|
|
|
|
if (!pdoExt->MouseOrKeyboard &&
|
|
WAITWAKE_SUPPORTED(fdoExt)) {
|
|
//
|
|
// Unregister for remote wakeup.
|
|
//
|
|
IoWMIRegistrationControl (pdoExt->pdo, WMIREG_ACTION_DEREGISTER);
|
|
}
|
|
|
|
remoteWakeIrp = (PIRP)
|
|
InterlockedExchangePointer(&pdoExt->remoteWakeIrp, NULL);
|
|
|
|
if (remoteWakeIrp) {
|
|
IoCancelIrp(remoteWakeIrp);
|
|
}
|
|
|
|
pdoExt->state = COLLECTION_STATE_UNINITIALIZED;
|
|
|
|
/*
|
|
* Destroy this collection.
|
|
* This will also abort all pending reads on this collection-PDO.
|
|
*/
|
|
HidpDestroyCollection(fdoExt, collection);
|
|
}
|
|
|
|
DBGVERBOSE(("HidpRemoveCollection: removed pdo %ph (refCount=%xh)", pdoExt->pdo, (ULONG)(*(((PUCHAR)pdoExt->pdo)-0x18))))
|
|
}
|