/*++ 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 //#include //#include #define MAX_DEVICES 16 #define STATIC_DEVICE_NAME L"Incoming IRCOMM" #define STATIC_HARDWARE_ID L"IR_NULL_IN" int sprintf(char *, ...); typedef struct _ENUM_OBJECT { PVOID ThreadObject; KEVENT WaitEvent; KTIMER Timer; PDEVICE_OBJECT Fdo; ULONG DeviceCount; IR_DEVICE Devices[MAX_DEVICES]; PASSIVE_LOCK PassiveLock; } 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 ) { 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->Present=TRUE; IrDevice->Static=TRUE; EnumObject->DeviceCount++; 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--; return Status; } NTSTATUS CreateEnumObject( PDEVICE_OBJECT Fdo, ENUM_HANDLE *Object, BOOLEAN StaticDevice ) { NTSTATUS Status; PENUM_OBJECT EnumObject; HANDLE ThreadHandle; *Object=NULL; EnumObject=ALLOCATE_NONPAGED_POOL(sizeof(*EnumObject)); if (EnumObject==NULL) { return STATUS_NO_MEMORY; } RtlZeroMemory(EnumObject,sizeof(*EnumObject)); EnumObject->Fdo=Fdo; if (StaticDevice) { CreateStaticDevice(EnumObject); } INIT_PASSIVE_LOCK(&EnumObject->PassiveLock); KeInitializeEvent( &EnumObject->WaitEvent, NotificationEvent, FALSE ); KeInitializeTimer( &EnumObject->Timer ); 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); } else { ASSERT(0); } 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); 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; } VOID WorkerThread( PVOID Context ) { NTSTATUS Status; PENUM_OBJECT EnumObject=Context; PVOID ObjectArray[2]; D_PNP(DbgPrint("IRENUM: WorkerThread: started\n");) ObjectArray[0]=&EnumObject->WaitEvent; ObjectArray[1]=&EnumObject->Timer; while (1) { LARGE_INTEGER DueTime; DueTime.QuadPart = -10*1000*10000; KeSetTimer( &EnumObject->Timer, DueTime, NULL ); Status=KeWaitForMultipleObjects( 2, &ObjectArray[0], WaitAny, Executive, KernelMode, FALSE, NULL, NULL ); if (Status == 0) { // // the event was signaled, time to exit // break; } else { if (Status == 1) { // // the time expired, check for devices // // D_PNP(DbgPrint("IRENUM: WorkerThread: Timer expired\n");) ACQUIRE_PASSIVE_LOCK(&EnumObject->PassiveLock); EnumIrda(EnumObject); RELEASE_PASSIVE_LOCK(&EnumObject->PassiveLock); } else { ASSERT(0); } } } KeCancelTimer(&EnumObject->Timer); D_PNP(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; UCHAR DevListBuf[512]; PDEVICELIST pDevList = (PDEVICELIST) DevListBuf; ULONG DevListLen = sizeof(DevListBuf); ULONG i; ULONG j; BOOLEAN InvalidateDeviceRelations=FALSE; Status = IrdaDiscoverDevices(pDevList, &DevListLen); if (!NT_SUCCESS(Status)) { D_ERROR(DbgPrint("IRENUM: DiscoveryFailed %08lx\n",Status);) return Status; } // D_PNP(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].Present=FALSE; } } for (i=0; i < pDevList->numDevice; i++) { PIRDA_DEVICE_INFO DeviceInfo=&pDevList->Device[i]; ULONG DeviceId; RtlCopyMemory(&DeviceId, &DeviceInfo->irdaDeviceID[0],4); 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].Present=TRUE; if (DeviceId != EnumObject->Devices[j].DeviceId) { D_ERROR(DbgPrint("IRENUM: Found Dup device %x devices\n",DeviceId);) RtlCopyMemory(&EnumObject->Devices[j].DeviceId,&DeviceInfo->irdaDeviceID[0],4); } break; } } } if ( j < MAX_DEVICES) { // // We found a match, skip this one // continue; } // // at this point we have a new device // if ((DeviceInfo->irdaDeviceHints2 & 4)) { // // it is an ircomm device // for (j=0; j< MAX_DEVICES; j++) { // // find a slot not in use // if (!EnumObject->Devices[j].InUse) { // // found a slot for it, zero the info // RtlZeroMemory(&EnumObject->Devices[j],sizeof(EnumObject->Devices[j])); // // inuse now // EnumObject->Devices[j].InUse=TRUE; EnumObject->Devices[j].Present=TRUE; RtlCopyMemory(&EnumObject->Devices[j].DeviceId,&DeviceInfo->irdaDeviceID[0],4); EnumObject->Devices[j].Hint1=DeviceInfo->irdaDeviceHints1; EnumObject->Devices[j].Hint2=DeviceInfo->irdaDeviceHints2; DeviceNameFromDeviceInfo( DeviceInfo, EnumObject->Devices[j].DeviceName, sizeof(EnumObject->Devices[j].DeviceName)/sizeof(WCHAR) ); DoIasQueries( &EnumObject->Devices[j] ); if (EnumObject->Devices[j].HardwareId != NULL) { Status=CreatePdo( EnumObject->Fdo, &EnumObject->Devices[j] ); D_PNP(DbgPrint( "IRENUM: Name %ws, device id=%08lx, hint1=%x, hint2=%x\n", EnumObject->Devices[j].DeviceName, EnumObject->Devices[j].DeviceId, EnumObject->Devices[j].Hint1, EnumObject->Devices[j].Hint2 );) // // new device // InvalidateDeviceRelations=TRUE; EnumObject->DeviceCount++; } else { // // the device did not report a pnp hardware id // RtlZeroMemory(&EnumObject->Devices[j],sizeof(EnumObject->Devices[j])); } break; } } } } for (j=0; j< MAX_DEVICES; j++) { // // lets see if any thing disappeared // if (EnumObject->Devices[j].InUse) { // // found a slot that is in use // if (!EnumObject->Devices[j].Present) { // // but it does not have a device present // InvalidateDeviceRelations=TRUE; } } } 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->UnEnumerated=FALSE; PdoExtension->DeviceDescription=IrDevice; IrDevice->Pdo = NewPdo; NewPdo->Flags |= DO_POWER_PAGABLE; NewPdo->Flags &= ~DO_DEVICE_INITIALIZING; } else { D_PNP(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)) { DbgPrint("IRENUM: got pnp manufacturer %ws\n",IrDevice->Manufacturer); } Status=IrdaIASStringQuery( IrDevice->DeviceId, "PnP", "Name", &IrDevice->Name ); if (NT_SUCCESS(Status)) { DbgPrint("IRENUM: got pnp name %ws\n",IrDevice->Name); } Status=IrdaIASStringQuery( IrDevice->DeviceId, "PnP", "DeviceID", &IrDevice->HardwareId ); if (NT_SUCCESS(Status)) { 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)) { 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);) } } } // // 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_PNP(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].Present) { EnumObject->Devices[i].Enumerated=TRUE; D_PNP(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=IrDevice->Pdo->DeviceExtension; ULONG i; for (i=0; i < MAX_DEVICES; i++) { // // find this device in the array // if (EnumObject->Devices[i].Pdo == IrDevice->Pdo) { break; } } if (IrDevice->HardwareId != NULL) { FREE_POOL(IrDevice->HardwareId); } if (IrDevice->Name != NULL) { FREE_POOL(IrDevice->Name); } if (IrDevice->Manufacturer != NULL) { FREE_POOL(IrDevice->Manufacturer); } PdoDeviceExtension->DoType=DO_TYPE_DEL_PDO; IoDeleteDevice(IrDevice->Pdo); RtlZeroMemory(&EnumObject->Devices[i],sizeof(IR_DEVICE)); EnumObject->DeviceCount--; } RELEASE_PASSIVE_LOCK(&EnumObject->PassiveLock); return; }