/*++ Copyright (c) 1995 Microsoft Corporation Module Name: initunlo.c Abstract: This module contains the code that is very specific to initialization and unload operations in the irenum driver Author: Brian Lieuallen, 7-13-2000 Environment: Kernel mode Revision History : --*/ #include "internal.h" #define UINT ULONG //tmp #include #include #define MAX_DEVICES 16 #define STATIC_DEVICE_NAME L"Incoming IRCOMM" #define STATIC_HARDWARE_ID L"IR_NULL_IN" #define GENERIC_MODEM_NAME L"Infrared Modem" #define GENERIC_HARDWARE_ID L"PNPC103" #define DEVICE_PRESENT_START_VALUE (1) int sprintf(char *, ...); typedef struct _ENUM_OBJECT { PVOID ThreadObject; KEVENT WaitEvent; KTIMER Timer; PASSIVE_LOCK PassiveLock; HANDLE IoWaitEventHandle; PKEVENT IoWaitEventObject; IO_STATUS_BLOCK IoStatusBlock; HANDLE ControlChannel; PDEVICE_OBJECT Fdo; ULONG DeviceCount; ULONG EnumeratedDevices; UCHAR DeviceListBuffer[512]; IR_DEVICE Devices[MAX_DEVICES]; } ENUM_OBJECT, *PENUM_OBJECT; VOID WorkerThread( PVOID Context ); NTSTATUS EnumIrda( PENUM_OBJECT EnumObject ); NTSTATUS DoIasQueries( PIR_DEVICE IrDevice ); NTSTATUS CreatePdo( PDEVICE_OBJECT Fdo, PIR_DEVICE IrDevice ); NTSTATUS CreateStaticDevice( PENUM_OBJECT EnumObject ); VOID CloseEnumObject( ENUM_HANDLE Handle ); NTSTATUS DeviceNameFromDeviceInfo( PIRDA_DEVICE_INFO DeviceInfo, PWCHAR DeviceName, ULONG NameLength ); VOID FixupDeviceId( PWSTR HardwareId ); #pragma alloc_text(PAGE,WorkerThread) #pragma alloc_text(PAGE,EnumIrda) #pragma alloc_text(PAGE,DoIasQueries) #pragma alloc_text(PAGE,CreatePdo) #pragma alloc_text(PAGE,CreateStaticDevice) #pragma alloc_text(PAGE,CloseEnumObject) #pragma alloc_text(PAGE,DeviceNameFromDeviceInfo) #pragma alloc_text(PAGE,FixupDeviceId) #pragma alloc_text(PAGE,GetDeviceList) #pragma alloc_text(PAGE,RemoveDevice) NTSTATUS CreateStaticDevice( PENUM_OBJECT EnumObject ) { NTSTATUS Status; ULONG DeviceId=0; PIR_DEVICE IrDevice=&EnumObject->Devices[0]; // // zero the whole thing // RtlZeroMemory(IrDevice,sizeof(*IrDevice)); // // inuse now // IrDevice->InUse=TRUE; IrDevice->PresentCount=DEVICE_PRESENT_START_VALUE; IrDevice->Static=TRUE; EnumObject->DeviceCount++; EnumObject->EnumeratedDevices++; RtlCopyMemory(&IrDevice->DeviceId,&DeviceId,4); RtlCopyMemory( IrDevice->DeviceName, STATIC_DEVICE_NAME, sizeof(STATIC_DEVICE_NAME) ); IrDevice->Name=ALLOCATE_PAGED_POOL(sizeof(STATIC_DEVICE_NAME)); if (IrDevice->Name == NULL) { Status=STATUS_NO_MEMORY; goto CleanUp; } RtlCopyMemory( IrDevice->Name, STATIC_DEVICE_NAME, sizeof(STATIC_DEVICE_NAME) ); IrDevice->HardwareId=ALLOCATE_PAGED_POOL(sizeof(STATIC_HARDWARE_ID)); if (IrDevice->HardwareId == NULL) { Status=STATUS_NO_MEMORY; goto CleanUp; } RtlCopyMemory( IrDevice->HardwareId, STATIC_HARDWARE_ID, sizeof(STATIC_HARDWARE_ID) ); Status=CreatePdo( EnumObject->Fdo, IrDevice ); if (NT_SUCCESS(Status)) { return Status; } CleanUp: if (IrDevice->Name != NULL) { FREE_POOL(IrDevice->Name); } if (IrDevice->HardwareId != NULL) { FREE_POOL(IrDevice->HardwareId); } RtlZeroMemory(IrDevice,sizeof(&IrDevice)); EnumObject->DeviceCount--; EnumObject->EnumeratedDevices--; return Status; } NTSTATUS CreateEnumObject( PDEVICE_OBJECT Fdo, ENUM_HANDLE *Object, BOOLEAN StaticDevice ) { NTSTATUS Status; PENUM_OBJECT EnumObject; HANDLE ThreadHandle; UNICODE_STRING EventName; *Object=NULL; EnumObject=ALLOCATE_NONPAGED_POOL(sizeof(*EnumObject)); if (EnumObject==NULL) { return STATUS_NO_MEMORY; } RtlZeroMemory(EnumObject,sizeof(*EnumObject)); KeInitializeEvent( &EnumObject->WaitEvent, NotificationEvent, FALSE ); KeInitializeTimerEx( &EnumObject->Timer, SynchronizationTimer ); INIT_PASSIVE_LOCK(&EnumObject->PassiveLock); EnumObject->Fdo=Fdo; if (StaticDevice) { CreateStaticDevice(EnumObject); } RtlInitUnicodeString( &EventName, L"\\Device\\IrEnumIoEvent" ); EnumObject->IoWaitEventObject=IoCreateNotificationEvent( &EventName, &EnumObject->IoWaitEventHandle ); if (EnumObject->IoWaitEventObject == NULL) { D_ERROR(DbgPrint("IRENUM: could not create event\n");) goto CleanUp; } Status=PsCreateSystemThread( &ThreadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, WorkerThread, EnumObject ); if (!NT_SUCCESS(Status)) { goto CleanUp; } Status=ObReferenceObjectByHandle( ThreadHandle, 0, NULL, KernelMode, &EnumObject->ThreadObject, NULL ); ZwClose(ThreadHandle); ThreadHandle=NULL; if (!NT_SUCCESS(Status)) { goto CleanUp; } *Object=EnumObject; return Status; CleanUp: KeSetEvent( &EnumObject->WaitEvent, IO_NO_INCREMENT, FALSE ); // // make sure we really got the object // if (EnumObject->ThreadObject != NULL) { KeWaitForSingleObject( EnumObject->ThreadObject, Executive, KernelMode, FALSE, NULL ); ObDereferenceObject(EnumObject->ThreadObject); } if (EnumObject->IoWaitEventHandle != NULL) { ZwClose(EnumObject->IoWaitEventHandle); } FREE_POOL(EnumObject); return Status; } VOID CloseEnumObject( ENUM_HANDLE Handle ) { PENUM_OBJECT EnumObject=Handle; ULONG j; KeSetEvent( &EnumObject->WaitEvent, IO_NO_INCREMENT, FALSE ); KeWaitForSingleObject( EnumObject->ThreadObject, Executive, KernelMode, FALSE, NULL ); ObDereferenceObject(EnumObject->ThreadObject); if (EnumObject->IoWaitEventHandle != NULL) { ZwClose(EnumObject->IoWaitEventHandle); } for (j=0; j< MAX_DEVICES; j++) { // // if remove it // if (EnumObject->Devices[j].InUse) { // // not enumerated any more since tha parent is going away // EnumObject->Devices[j].Enumerated=FALSE; RemoveDevice(EnumObject,&EnumObject->Devices[j]); } } FREE_POOL(EnumObject); return; } #define OBJECT_ARRAY_SIZE (3) VOID WorkerThread( PVOID Context ) { NTSTATUS Status; PENUM_OBJECT EnumObject=Context; BOOLEAN ExitLoop=FALSE; PKEVENT ObjectArray[OBJECT_ARRAY_SIZE]; LARGE_INTEGER DueTime; DueTime.QuadPart = -10*1000*10000; D_ENUM(DbgPrint("IRENUM: WorkerThread: started\n");) KeClearEvent(EnumObject->IoWaitEventObject); ObjectArray[0]=&EnumObject->WaitEvent; ObjectArray[1]=(PKEVENT)&EnumObject->Timer; ObjectArray[2]=EnumObject->IoWaitEventObject; KeSetTimer( &EnumObject->Timer, DueTime, NULL ); while (!ExitLoop) { Status=KeWaitForMultipleObjects( OBJECT_ARRAY_SIZE, &ObjectArray[0], WaitAny, Executive, KernelMode, FALSE, NULL, NULL ); switch (Status) { case 0: // // the event was signaled, time to exit // ExitLoop=TRUE; break; case 1: // // the timer expired, check for devices // if (EnumObject->ControlChannel == NULL) { // // we have not been able to open the control channel yet // Status=IrdaOpenControlChannel(&EnumObject->ControlChannel); if (!NT_SUCCESS(Status)) { EnumObject->ControlChannel=NULL; } } if (EnumObject->ControlChannel != NULL) { // // we have the control handle, start the discover request // IrdaLazyDiscoverDevices( EnumObject->ControlChannel, EnumObject->IoWaitEventHandle, &EnumObject->IoStatusBlock, (PDEVICELIST)&EnumObject->DeviceListBuffer[0], sizeof(EnumObject->DeviceListBuffer) ); } else { KeSetTimer( &EnumObject->Timer, DueTime, NULL ); } break; case 2: // // the discovery completed // KeResetEvent(EnumObject->IoWaitEventObject); if (EnumObject->IoStatusBlock.Status == STATUS_SUCCESS) { ACQUIRE_PASSIVE_LOCK(&EnumObject->PassiveLock); EnumIrda(EnumObject); RELEASE_PASSIVE_LOCK(&EnumObject->PassiveLock); // // start another io // IrdaLazyDiscoverDevices( EnumObject->ControlChannel, EnumObject->IoWaitEventHandle, &EnumObject->IoStatusBlock, (PDEVICELIST)&EnumObject->DeviceListBuffer[0], sizeof(EnumObject->DeviceListBuffer) ); } else { // // the discovery failed, just start the timer // KeSetTimer( &EnumObject->Timer, DueTime, NULL ); } break; default: ASSERT(0); break; } } KeCancelTimer(&EnumObject->Timer); D_ENUM(DbgPrint("IRENUM: WorkerThread: stopping\n");) PsTerminateSystemThread(STATUS_SUCCESS); return; } NTSTATUS DeviceNameFromDeviceInfo( PIRDA_DEVICE_INFO DeviceInfo, PWCHAR DeviceName, ULONG NameLength ) { NTSTATUS Status=STATUS_SUCCESS; WCHAR TempBuffer[23]; UNICODE_STRING UnicodeString; // // zero out the temp buffer, so we can copy the remote device name, // so we can be sure it is null terminated // RtlZeroMemory(TempBuffer,sizeof(TempBuffer)); RtlCopyMemory(TempBuffer,DeviceInfo->irdaDeviceName,sizeof(DeviceInfo->irdaDeviceName)); UnicodeString.Length=0; UnicodeString.MaximumLength=(USHORT)(NameLength-1)*sizeof(WCHAR); UnicodeString.Buffer=DeviceName; RtlZeroMemory(UnicodeString.Buffer,UnicodeString.MaximumLength); if (DeviceInfo->irdaCharSet == LmCharSetUNICODE) { // // the name is unicode // Status=RtlAppendUnicodeToString(&UnicodeString,TempBuffer); } else { // // the name is ansi, need to convert unicode // ANSI_STRING AnsiString; RtlInitAnsiString( &AnsiString, (PCSZ)TempBuffer ); Status=RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, FALSE ); } return Status; } NTSTATUS EnumIrda( PENUM_OBJECT EnumObject ) { NTSTATUS Status; PDEVICELIST pDevList = (PDEVICELIST)&EnumObject->DeviceListBuffer[0] ; ULONG i; ULONG j; BOOLEAN InvalidateDeviceRelations=FALSE; PIR_DEVICE IrDevice; D_ENUM(DbgPrint("IRENUM: Found %d devices\n",pDevList->numDevice);) for (j=0; j< MAX_DEVICES; j++) { // // first mark all the device not present // if (!EnumObject->Devices[j].Static) { // // only non-static device go away // EnumObject->Devices[j].PresentCount--; } } for (i=0; i < pDevList->numDevice; i++) { PIRDA_DEVICE_INFO DeviceInfo=&pDevList->Device[i]; ULONG DeviceId; LONG EmptySlot=-1; RtlCopyMemory(&DeviceId, &DeviceInfo->irdaDeviceID[0],4); // // now go through all of our slots to see if we have seen this device before // based on the name it reports // for (j=0; j< MAX_DEVICES; j++) { WCHAR TempBuffer[24]; if (EnumObject->Devices[j].InUse) { DeviceNameFromDeviceInfo( DeviceInfo, TempBuffer, sizeof(TempBuffer)/sizeof(WCHAR) ); if (0 == wcscmp(TempBuffer, EnumObject->Devices[j].DeviceName)) { // // Already present // EnumObject->Devices[j].PresentCount=DEVICE_PRESENT_START_VALUE; if (DeviceId != EnumObject->Devices[j].DeviceId) { // // the device id seems to have changed since we saw it last, just update it // D_ERROR(DbgPrint("IRENUM: Found Dup device %x devices\n",DeviceId);) RtlCopyMemory(&EnumObject->Devices[j].DeviceId,&DeviceInfo->irdaDeviceID[0],4); } break; } } else { // // this slot is empty, remember this for later // if (EmptySlot == -1) { // // only set it for this first one // EmptySlot=j; } } } if ( j < MAX_DEVICES) { // // We found a match, skip this one // continue; } if (EmptySlot == -1) { // // All of the slots are used up // continue; } // // at this point we have a new device // IrDevice=&EnumObject->Devices[EmptySlot]; // // found a slot for it, zero the info // RtlZeroMemory(IrDevice,sizeof(*IrDevice)); EnumObject->DeviceCount++; // // inuse now // IrDevice->InUse=TRUE; IrDevice->PresentCount=DEVICE_PRESENT_START_VALUE; IrDevice->DeviceId=DeviceId; IrDevice->Hint1=DeviceInfo->irdaDeviceHints1; IrDevice->Hint2=DeviceInfo->irdaDeviceHints2; DeviceNameFromDeviceInfo( DeviceInfo, IrDevice->DeviceName, sizeof(IrDevice->DeviceName)/sizeof(WCHAR) ); D_ENUM(DbgPrint( "IRENUM: Name %ws, device id=%08lx, hint1=%x, hint2=%x\n", IrDevice->DeviceName, IrDevice->DeviceId, IrDevice->Hint1, IrDevice->Hint2 );) if (DeviceInfo->irdaDeviceHints1 & LM_HB1_Printer) { // // the device says it is a printer // IrDevice->Printer=TRUE; } if ((DeviceInfo->irdaDeviceHints1 & LM_HB1_Modem) && (DeviceInfo->irdaDeviceHints2 & 4)) { // // Device reports that it is a modem that supports ircomm // IrDevice->Modem=TRUE; } if (DeviceInfo->irdaDeviceHints1 & LM_HB1_PnP) { // // the device says it is pnp aware // DoIasQueries( IrDevice ); if (IrDevice->HardwareId != NULL) { // // we were able to query it for a hardware id // Status=CreatePdo( EnumObject->Fdo, IrDevice ); if (!NT_SUCCESS(Status)) { // // We could not create a PDO for the new device // if (IrDevice->Name != NULL) { FREE_POOL(IrDevice->Name); } if (IrDevice->HardwareId != NULL) { FREE_POOL(IrDevice->HardwareId); } } else { // // we created a PDO for a new child device // EnumObject->EnumeratedDevices++; // // new device // InvalidateDeviceRelations=TRUE; } } else { // // the device did not report a pnp hardware id // EnumObject->Devices[EmptySlot].Pdo=NULL; } } else { // // the device is not pnp aware, make something up // if ((DeviceInfo->irdaDeviceHints1 & LM_HB1_Modem) && (DeviceInfo->irdaDeviceHints2 & 4)) { // // the hint bits report the device as modem that supports ircomm // IrDevice->HardwareId=ALLOCATE_PAGED_POOL(sizeof(GENERIC_HARDWARE_ID)); if (IrDevice->HardwareId != NULL) { wcscpy(IrDevice->HardwareId,GENERIC_HARDWARE_ID); } IrDevice->Name=ALLOCATE_NONPAGED_POOL((wcslen(IrDevice->DeviceName)+1)*sizeof(WCHAR)); if (IrDevice->Name != NULL) { wcscpy(IrDevice->Name,IrDevice->DeviceName); } if (IrDevice->HardwareId != NULL) { // // we were able to query it for a hardware id // Status=CreatePdo( EnumObject->Fdo, IrDevice ); if (!NT_SUCCESS(Status)) { // // We could not create a PDO for the new device // if (IrDevice->Name != NULL) { FREE_POOL(IrDevice->Name); } if (IrDevice->HardwareId != NULL) { FREE_POOL(IrDevice->HardwareId); } } else { // // we created a PDO for a new child device // EnumObject->EnumeratedDevices++; // // new device // InvalidateDeviceRelations=TRUE; } } } else { // // the device does not support pnp and it is not an ircomm device // } } } for (j=0; j< MAX_DEVICES; j++) { // // lets see if anything disappeared // if (EnumObject->Devices[j].InUse) { // // found a slot that is in use // if (EnumObject->Devices[j].PresentCount == 0) { // // but it does not have a device present // D_ENUM(DbgPrint("IRENUM: Name %ws, no longer present\n",EnumObject->Devices[j].Name);) if (EnumObject->Devices[j].Pdo != NULL) { // // we have enumerated a child for this device // InvalidateDeviceRelations=TRUE; } else { // // This one does not have a child, just zero it out // RtlZeroMemory(&EnumObject->Devices[j],sizeof(EnumObject->Devices[j])); EnumObject->DeviceCount--; } } } } if (InvalidateDeviceRelations) { // // tell the system to check the device relations because a device has appeared or // disappeared // PFDO_DEVICE_EXTENSION FdoExtension=EnumObject->Fdo->DeviceExtension; IoInvalidateDeviceRelations(FdoExtension->Pdo,BusRelations); } return Status; } NTSTATUS CreatePdo( PDEVICE_OBJECT Fdo, PIR_DEVICE IrDevice ) { NTSTATUS Status; PDEVICE_OBJECT NewPdo; Status = IoCreateDevice( Fdo->DriverObject, sizeof(PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_BUS_EXTENDER, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &NewPdo ); if (NT_SUCCESS(Status)) { // // got the device // PPDO_DEVICE_EXTENSION PdoExtension=NewPdo->DeviceExtension; PdoExtension->DoType=DO_TYPE_PDO; PdoExtension->ParentFdo=Fdo; PdoExtension->DeviceDescription=IrDevice; IrDevice->Pdo = NewPdo; NewPdo->Flags |= DO_POWER_PAGABLE; NewPdo->Flags &= ~DO_DEVICE_INITIALIZING; } else { D_ENUM(DbgPrint("MODEM: CreateChildPdo: IoCreateDevice() failed %08lx\n",Status);) } return Status; } VOID FixupDeviceId( PWSTR HardwareId ) { // // munge the hardware id to make sure it is compatable with the os requirements // while (*HardwareId != L'\0') { if ((*HardwareId < L' ') || (*HardwareId > 127) || (*HardwareId == L',')) { *HardwareId = L'?'; } HardwareId++; } return; } NTSTATUS DoIasQueries( PIR_DEVICE IrDevice ) { NTSTATUS Status; LONG CompatCount; Status=IrdaIASStringQuery( IrDevice->DeviceId, "PnP", "Manufacturer", &IrDevice->Manufacturer ); if (NT_SUCCESS(Status)) { D_ENUM(DbgPrint("IRENUM: got pnp manufacturer %ws\n",IrDevice->Manufacturer);) } Status=IrdaIASStringQuery( IrDevice->DeviceId, "PnP", "Name", &IrDevice->Name ); if (NT_SUCCESS(Status)) { D_ENUM(DbgPrint("IRENUM: got pnp name %ws\n",IrDevice->Name);) } Status=IrdaIASStringQuery( IrDevice->DeviceId, "PnP", "DeviceID", &IrDevice->HardwareId ); if (NT_SUCCESS(Status)) { D_ENUM(DbgPrint("IRENUM: got pnp id %ws\n",IrDevice->HardwareId);) FixupDeviceId(IrDevice->HardwareId); } // // check for compat id's // IrDevice->CompatIdCount=0; Status=IrdaIASIntegerQuery( IrDevice->DeviceId, "PnP", "CompCnt", &CompatCount ); if (NT_SUCCESS(Status)) { LONG i; if ( CompatCount > 16) { CompatCount=16; } else { if ( CompatCount < 0) { CompatCount = 0; } } for (i=0; i< CompatCount; i++) { CHAR Attribute[20]; sprintf(Attribute,"Comp#%02d",i+1); Status=IrdaIASStringQuery( IrDevice->DeviceId, "PnP", Attribute, &IrDevice->CompatId[IrDevice->CompatIdCount] ); if (NT_SUCCESS(Status)) { D_ENUM(DbgPrint("IRENUM: got compat pnp id %ws\n",IrDevice->CompatId[IrDevice->CompatIdCount]);) FixupDeviceId(IrDevice->CompatId[IrDevice->CompatIdCount]); IrDevice->CompatIdCount++; } else { D_ERROR(DbgPrint("IRENUM: could not get id for %s\n",Attribute);) } } } if (IrDevice->Modem && !IrDevice->Printer) { // // It the hint bits say this is a modem and it is not a printer then // // Create a standard compat ID for all devices, so we can load a standard driver // IrDevice->CompatId[IrDevice->CompatIdCount]=ALLOCATE_PAGED_POOL(sizeof(IRENUM_COMPAT_ID)); if (IrDevice->CompatId[IrDevice->CompatIdCount] != NULL) { RtlCopyMemory(IrDevice->CompatId[IrDevice->CompatIdCount],IRENUM_COMPAT_ID,sizeof(IRENUM_COMPAT_ID)); IrDevice->CompatIdCount++; } } return STATUS_SUCCESS; } NTSTATUS GetDeviceList( ENUM_HANDLE Handle, PIRP Irp ) { PENUM_OBJECT EnumObject=Handle; NTSTATUS Status=STATUS_SUCCESS; PDEVICE_RELATIONS CurrentRelations=(PDEVICE_RELATIONS)Irp->IoStatus.Information; PDEVICE_RELATIONS NewRelations=NULL; ULONG DeviceCount=EnumObject->DeviceCount; ULONG i; ACQUIRE_PASSIVE_LOCK(&EnumObject->PassiveLock); if (CurrentRelations != NULL) { // // we need to allocate a new relations structure and copy the old one to the new one // DeviceCount+=CurrentRelations->Count; } NewRelations=ALLOCATE_PAGED_POOL(sizeof(DEVICE_RELATIONS)+sizeof(PDEVICE_OBJECT)*DeviceCount); if (NewRelations == NULL) { Status= STATUS_INSUFFICIENT_RESOURCES; } else { NewRelations->Count=0; if (CurrentRelations != NULL) { D_ENUM(DbgPrint("IRENUM: GetDeviceList: %d existing devices\n",CurrentRelations->Count);) for (i=0; i < CurrentRelations->Count; i++) { NewRelations->Objects[i]=CurrentRelations->Objects[i]; NewRelations->Count++; } FREE_POOL(CurrentRelations); } for (i=0; i < MAX_DEVICES; i++) { if ((EnumObject->Devices[i].Pdo != NULL) && (EnumObject->Devices[i].PresentCount > 0)) { EnumObject->Devices[i].Enumerated=TRUE; D_ENUM(DbgPrint("IRENUM: GetDeviceList: reporting DO %p\n",EnumObject->Devices[i].Pdo);) NewRelations->Objects[NewRelations->Count]=EnumObject->Devices[i].Pdo; ObReferenceObject(NewRelations->Objects[NewRelations->Count]); NewRelations->Count++; } else { // // the device is no longer present // EnumObject->Devices[i].Enumerated=FALSE; } } Irp->IoStatus.Information=(ULONG_PTR)NewRelations; } RELEASE_PASSIVE_LOCK(&EnumObject->PassiveLock); return Status; } VOID RemoveDevice( ENUM_HANDLE Handle, PIR_DEVICE IrDevice ) { PENUM_OBJECT EnumObject=Handle; ACQUIRE_PASSIVE_LOCK(&EnumObject->PassiveLock); if (IrDevice->Enumerated) { // // the device is still present // // Just leave it alone // } else { // // the parent is not enumerating the device anymore // PPDO_DEVICE_EXTENSION PdoDeviceExtension; LONG i; // // clean things up // if (IrDevice->HardwareId != NULL) { FREE_POOL(IrDevice->HardwareId); } if (IrDevice->Name != NULL) { FREE_POOL(IrDevice->Name); } if (IrDevice->Manufacturer != NULL) { FREE_POOL(IrDevice->Manufacturer); } for (i=0; i< IrDevice->CompatIdCount; i++) { if (IrDevice->CompatId[i] != NULL) { FREE_POOL(IrDevice->CompatId[i]); } } if (IrDevice->Pdo != NULL) { PdoDeviceExtension=IrDevice->Pdo->DeviceExtension; PdoDeviceExtension->DoType=DO_TYPE_DEL_PDO; IoDeleteDevice(IrDevice->Pdo); EnumObject->EnumeratedDevices--; } RtlZeroMemory(IrDevice,sizeof(*IrDevice)); EnumObject->DeviceCount--; } RELEASE_PASSIVE_LOCK(&EnumObject->PassiveLock); return; }