/*++ Copyright (c) 1991-92 Microsoft Corporation Module Name: winproc.c Abstract: Spooler window processing code Author: Muhunthan Sivapragasam (MuhuntS) 5-Nov-96 port of win95 code Environment: User Mode - Win32 Notes: Revision History: BabakJ: Jan 1999, Added thread sync code to allow only one thread doing enumeration, and only one thread waiting. This helps performance specially when Dynamon has many Hydra ports. --*/ #include "precomp.h" #include "local.h" #pragma hdrstop #include static const GUID USB_PRINTER_GUID = { 0x28d78fad, 0x5a12, 0x11d1, { 0xae, 0x5b, 0x0, 0x0, 0xf8, 0x3, 0xa8, 0xc2 } }; static const GUID GUID_DEVCLASS_INFRARED = { 0x6bdd1fc5L, 0x810f, 0x11d0, { 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f } }; typedef struct _DEVICE_REGISTER_INFO { struct _DEVICE_REGISTER_INFO *pNext; HANDLE hDevice; LPVOID pData; PFN_QUERYREMOVE_CALLBACK pfnQueryRemove; HDEVNOTIFY hNotify; } DEVICE_REGISTER_INFO, *PDEVICE_REGISTER_INFO; PDEVICE_REGISTER_INFO gpDevRegnInfo = NULL; HDEVNOTIFY ghPNPNotify_USB = NULL; HDEVNOTIFY ghPNPNotify_IR = NULL; VOID ConfigChangeThread( ) { HINSTANCE hLib; VOID (*pfnSplConfigChange)(); WaitForSpoolerInitialization(); if ( hLib = LoadLibrary(L"localspl.dll") ) { if ( pfnSplConfigChange = GetProcAddress(hLib, "SplConfigChange") ) { pfnSplConfigChange(); } FreeLibrary(hLib); } } VOID ReenumeratePortsThreadWorker( ) { HINSTANCE hLib; VOID (*pfnSplReenumeratePorts)(); WaitForSpoolerInitialization(); if ( hLib = LoadLibrary(L"localspl.dll") ) { if ( pfnSplReenumeratePorts = GetProcAddress(hLib, "SplReenumeratePorts") ) { pfnSplReenumeratePorts(); } FreeLibrary(hLib); } } //////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////// ///// ///// To improve performance, and prevent too many unnecessary port enumerations, specially for Hydra/Dynamon: ///// ///// - We want to allow only one Device Arrival thread to be doing port enumeration. ///// - If above is happneing, we allow only one more Device Arrival thread be waiting to go in. ///// - All other threads will be turned away, as there is no need for them to do port enumeration. ///// //////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////// CRITICAL_SECTION DeviceArrivalCS; // Used to synchronize threads bringing device arrival messages. HANDLE ThdOutEvent; // Signalled after a thread is done doing the EnumPort work; not signaled when created. VOID ReenumeratePortsThread( ) { static BOOL fThdIn; // TRUE if a thread is doing enum work at the moment static BOOL fThdWaiting; // TRUE if a 2nd thread is waiting behind the thread that is inside. EnterCriticalSection( &DeviceArrivalCS ); // Enter the crit section initialized for this at localspl init code if( fThdWaiting ) { LeaveCriticalSection( &DeviceArrivalCS ); return; // A 2nd thread is already waiting to go in. No need for holding more threads. } else { if( fThdIn ) { fThdWaiting = TRUE; // There is a thread inside doing Enum work. Have the current thread wait for it to finish. LeaveCriticalSection( &DeviceArrivalCS ); WaitForSingleObject( ThdOutEvent, INFINITE ); EnterCriticalSection( &DeviceArrivalCS ); fThdWaiting = FALSE; } fThdIn = TRUE; // The current thread is now going in to do Enum work. LeaveCriticalSection( &DeviceArrivalCS ); ReenumeratePortsThreadWorker(); EnterCriticalSection( &DeviceArrivalCS ); fThdIn = FALSE; if( fThdWaiting ) SetEvent( ThdOutEvent ); LeaveCriticalSection( &DeviceArrivalCS ); return; } } DWORD QueryRemove( HANDLE hDevice ) { LPVOID pData = NULL; PFN_QUERYREMOVE_CALLBACK pfnQueryRemove = NULL; PDEVICE_REGISTER_INFO pDevRegnInfo; EnterRouterSem(); for ( pDevRegnInfo = gpDevRegnInfo ; pDevRegnInfo ; pDevRegnInfo = pDevRegnInfo->pNext ) { if ( pDevRegnInfo->hDevice == hDevice ) { pfnQueryRemove = pDevRegnInfo->pfnQueryRemove; pData = pDevRegnInfo->pData; break; } } LeaveRouterSem(); return pfnQueryRemove ? pfnQueryRemove(pData) : NO_ERROR; } DWORD SplProcessPnPEvent( DWORD dwEventType, LPVOID lpEventData, PVOID pVoid ) { HANDLE hThread; DWORD dwThread, dwReturn = NO_ERROR; PDEV_BROADCAST_HANDLE pBroadcast; DBGMSG(DBG_INFO, ("SplProcessPnPEvent: dwEventType: %d\n", dwEventType)); switch (dwEventType) { case DBT_CONFIGCHANGED: hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ConfigChangeThread, NULL, 0, &dwThread); if ( hThread ) CloseHandle(hThread); break; case DBT_DEVICEARRIVAL: case DBT_DEVICEREMOVECOMPLETE: // // In case of device arrival we need to see if there are new ports // and in case of device removal monitors might want to mark ports // as removed so next reboot they do not have to enumerate them // ex. USB does this. // // We use the default process stack size for this thread. Currently 16KB. // hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReenumeratePortsThread, NULL, 0, &dwThread); if ( hThread ) CloseHandle(hThread); break; case DBT_DEVICEQUERYREMOVE: pBroadcast = (PDEV_BROADCAST_HANDLE)lpEventData; // // These checks are to see if we really care about this // if ( !pBroadcast || pBroadcast->dbch_devicetype != DBT_DEVTYP_HANDLE ) break; dwReturn = QueryRemove(pBroadcast->dbch_handle); break; case DBT_SHELLLOGGEDON: default: break; } return dwReturn; } VOID RegisterForPnPEvents( VOID ) { DEV_BROADCAST_DEVICEINTERFACE Filter; // Init the sync objects needed for device arrival thread management ThdOutEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // Manual reset, non-signaled state ZeroMemory(&Filter, sizeof(Filter)); Filter.dbcc_size = sizeof(Filter); Filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; CopyMemory(&Filter.dbcc_classguid, (LPGUID)&USB_PRINTER_GUID, sizeof(Filter.dbcc_classguid)); if ( !(ghPNPNotify_USB = RegisterDeviceNotification(ghSplHandle, &Filter, DEVICE_NOTIFY_SERVICE_HANDLE) )) { DBGMSG(DBG_INFO, ("RegisterForPnPEvents: RegisterDeviceNotification failed for USB. Error %d\n", GetLastError())); } else { DBGMSG(DBG_WARNING, ("RegisterForPnPEvents: RegisterDeviceNotification succesful for USB\n")); } CopyMemory(&Filter.dbcc_classguid, (LPGUID)&GUID_DEVCLASS_INFRARED, sizeof(Filter.dbcc_classguid)); if ( !(ghPNPNotify_IR = RegisterDeviceNotification(ghSplHandle, &Filter, DEVICE_NOTIFY_SERVICE_HANDLE) )) { DBGMSG(DBG_INFO, ("RegisterForPnPEvents: RegisterDeviceNotification failed for IRDA. Error %d\n", GetLastError())); } else { DBGMSG(DBG_WARNING, ("RegisterForPnPEvents: RegisterDeviceNotification succesful for IRDA\n")); } } BOOL SplUnregisterForDeviceEvents( HANDLE hNotify ) { PDEVICE_REGISTER_INFO pDevRegnInfo, pPrev; EnterRouterSem(); // // Find the registration in our list, remove it and then leave CS to // call unregister on it // for ( pDevRegnInfo = gpDevRegnInfo, pPrev = NULL ; pDevRegnInfo ; pPrev = pDevRegnInfo, pDevRegnInfo = pDevRegnInfo->pNext ) { if ( pDevRegnInfo->hNotify == hNotify ) { if ( pPrev ) pPrev->pNext = pDevRegnInfo->pNext; else gpDevRegnInfo = pDevRegnInfo->pNext; break; } } LeaveRouterSem(); if ( pDevRegnInfo ) { UnregisterDeviceNotification(pDevRegnInfo->hNotify); FreeSplMem(pDevRegnInfo); return TRUE; } return FALSE; } HANDLE SplRegisterForDeviceEvents( HANDLE hDevice, LPVOID pData, PFN_QUERYREMOVE_CALLBACK pfnQueryRemove ) { DEV_BROADCAST_HANDLE Filter; PDEVICE_REGISTER_INFO pDevRegnInfo; ZeroMemory(&Filter, sizeof(Filter)); Filter.dbch_size = sizeof(Filter); Filter.dbch_devicetype = DBT_DEVTYP_HANDLE; Filter.dbch_handle = hDevice; pDevRegnInfo = (PDEVICE_REGISTER_INFO) AllocSplMem(sizeof(DEVICE_REGISTER_INFO)); if ( !pDevRegnInfo ) goto Fail; pDevRegnInfo->hDevice = hDevice; pDevRegnInfo->pData = pData; pDevRegnInfo->pfnQueryRemove = pfnQueryRemove; pDevRegnInfo->hNotify = RegisterDeviceNotification( ghSplHandle, &Filter, DEVICE_NOTIFY_SERVICE_HANDLE); if ( pDevRegnInfo->hNotify ) { EnterRouterSem(); pDevRegnInfo->pNext = gpDevRegnInfo; gpDevRegnInfo = pDevRegnInfo; LeaveRouterSem(); return pDevRegnInfo->hNotify; } FreeSplMem(pDevRegnInfo); Fail: return NULL; }