/****************************** Module Header ******************************\ * Module Name: pnp.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * This module tracks device interface changes so we can keep track of know how many mice and * keyboards and mouse * and mouse reports. * * History: * 97-10-16 IanJa Interpreted from a dream that Ken Ray had. \***************************************************************************/ #include "precomp.h" #pragma hdrstop BOOL gbFirstConnectionDone; DEVICE_TEMPLATE aDeviceTemplate[DEVICE_TYPE_MAX + 1] = { // DEVICE_TYPE_MOUSE { sizeof(GENERIC_DEVICE_INFO)+sizeof(MOUSE_DEVICE_INFO), // cbDeviceInfo &GUID_CLASS_MOUSE, // pClassGUID PMAP_MOUCLASS_PARAMS, // uiRegistrySection L"mouclass", // pwszClassName DD_MOUSE_DEVICE_NAME_U L"0", // pwszDefDevName DD_MOUSE_DEVICE_NAME_U L"Legacy0", // pwszLegacyDevName IOCTL_MOUSE_QUERY_ATTRIBUTES, // IOCTL_Attr FIELD_OFFSET(DEVICEINFO, mouse.Attr), // offAttr sizeof((PDEVICEINFO)NULL)->mouse.Attr, // cbAttr FIELD_OFFSET(DEVICEINFO, mouse.Data), // offData sizeof((PDEVICEINFO)NULL)->mouse.Data, // cbData ProcessMouseInput, // Reader routine NULL // pkeHidChange }, // DEVICE_TYPE_KEYBOARD { sizeof(GENERIC_DEVICE_INFO)+sizeof(KEYBOARD_DEVICE_INFO), // cbDeviceInfo &GUID_CLASS_KEYBOARD, // pClassGUID PMAP_KBDCLASS_PARAMS, // uiRegistrySection L"kbdclass", // pwszClassName DD_KEYBOARD_DEVICE_NAME_U L"0", // pwszDefDevName DD_KEYBOARD_DEVICE_NAME_U L"Legacy0", // pwszLegacyDevName IOCTL_KEYBOARD_QUERY_ATTRIBUTES, // IOCTL_Attr FIELD_OFFSET(DEVICEINFO, keyboard.Attr), // offAttr sizeof((PDEVICEINFO)NULL)->keyboard.Attr, // cbAttr FIELD_OFFSET(DEVICEINFO, keyboard.Data), // offData sizeof((PDEVICEINFO)NULL)->keyboard.Data, // cbData ProcessKeyboardInput, // Reader routine NULL // pkeHidChange }, #ifdef GENERIC_INPUT // DEVICE_TYPE_HID { sizeof(GENERIC_DEVICE_INFO)+sizeof(HID_DEVICE_INFO), // cbDeviceInfo &GUID_CLASS_INPUT, // pClassGUID 0, // uiRegistrySection. LATER: add real one L"hid", // pwszClassName L"", // pwszDefDevName L"", // pwszLegacyDevName 0, // IOCTL_ATTR 0, // offAttr 0, // cbAttr 0, // offData 0, // cbData ProcessHidInput, // Reader routine NULL, // pkeHidChange, DT_HID, // dwFlags }, #endif // Add new input device type template here }; // // We need to remember device class notification entries since we need // them to unregister the device class notification when we disconnect // from the console. // PVOID aDeviceClassNotificationEntry[DEVICE_TYPE_MAX + 1]; #ifdef DIAGNOSE_IO NTSTATUS gKbdIoctlLEDSStatus = -1; // last IOCTL_KEYBOARD_QUERY_INDICATORS #endif typedef struct _CDROM_NOTIFY { LIST_ENTRY Entry; ULONG Size; PVOID RegistrationHandle; ULONG Event; // Must be last field MOUNTMGR_DRIVE_LETTER_TARGET DeviceName; } CDROM_NOTIFY, *PCDROM_NOTIFY; PVOID gCDROMClassRegistrationEntry; LIST_ENTRY gCDROMNotifyList; LIST_ENTRY gMediaChangeList; PFAST_MUTEX gMediaChangeMutex; HANDLE gpEventMediaChange; #define EVENT_CDROM_MEDIA_ARRIVAL 1 #define EVENT_CDROM_MEDIA_REMOVAL 2 /***************************************************************************\ * Win32kPnPDriverEntry * * This is the callback function when we call IoCreateDriver to create a * PnP Driver Object. In this function, we need to remember the DriverObject. * * Parameters: * DriverObject - Pointer to the driver object created by the system. * RegistryPath - is NULL. * * Return Value: STATUS_SUCCESS * * History: * 10-20-97 IanJa Taken from ntos\io\pnpinit.c \***************************************************************************/ NTSTATUS Win32kPnPDriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING pustrRegistryPath) { UNREFERENCED_PARAMETER(pustrRegistryPath); TAGMSG1(DBGTAG_PNP, "Win32kPnPDriverEntry(DriverObject 0x%p)", DriverObject); // // Squirrel away the pointer to our driver object. // gpWin32kDriverObject = DriverObject; return STATUS_SUCCESS; } /***************************************************************************\ * Initialize the global event used in notifying CSR that media has changed. * * History: \***************************************************************************/ NTSTATUS InitializeMediaChange( HANDLE hMediaRequestEvent) { NTSTATUS Status; if (!IsRemoteConnection()) { InitializeListHead(&gCDROMNotifyList); InitializeListHead(&gMediaChangeList); Status = ObReferenceObjectByHandle(hMediaRequestEvent, EVENT_ALL_ACCESS, *ExEventObjectType, KernelMode, &gpEventMediaChange, NULL); if (!NT_SUCCESS(Status)) { return Status; } gMediaChangeMutex = UserAllocPoolNonPagedNS(sizeof(FAST_MUTEX), TAG_PNP); if (gMediaChangeMutex) { ExInitializeFastMutex(gMediaChangeMutex); } else { Status = STATUS_NO_MEMORY; } } else { Status = STATUS_SUCCESS; } return Status; } VOID CleanupMediaChange( VOID) { if (gMediaChangeMutex) { UserFreePool(gMediaChangeMutex); gMediaChangeMutex = 0; } } __inline VOID EnterMediaCrit( VOID) { KeEnterCriticalRegion(); ExAcquireFastMutexUnsafe(gMediaChangeMutex); } __inline VOID LeaveMediaCrit( VOID) { ExReleaseFastMutexUnsafe(gMediaChangeMutex); KeLeaveCriticalRegion(); } /***************************************************************************\ * Routines to support CDROM driver letters. * * Execution Context: * * History: \***************************************************************************/ ULONG xxxGetDeviceChangeInfo( VOID) { UNICODE_STRING name; PFILE_OBJECT FileObject; PDEVICE_OBJECT DeviceObject; KEVENT event; PIRP irp; MOUNTMGR_DRIVE_LETTER_INFORMATION output; IO_STATUS_BLOCK ioStatus; NTSTATUS status; PCDROM_NOTIFY pContext; PTHREADINFO ptiCurrent; TL tlContext; TL tlFileObject; ULONG retval = 0; if (!(ISCSRSS())) { return 0; } EnterMediaCrit(); if (!IsListEmpty(&gMediaChangeList)) { pContext = (PCDROM_NOTIFY)RemoveTailList(&gMediaChangeList); } else { pContext = NULL; } LeaveMediaCrit(); if (pContext == NULL) { return 0; } ptiCurrent = PtiCurrent(); ThreadLockPool(ptiCurrent, pContext, &tlContext); RtlInitUnicodeString(&name, MOUNTMGR_DEVICE_NAME); status = IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); if (NT_SUCCESS(status)) { PushW32ThreadLock(FileObject, &tlFileObject, UserDereferenceObject); KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER, DeviceObject, &pContext->DeviceName, sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) + pContext->DeviceName.DeviceNameLength, &output, sizeof(output), FALSE, &event, &ioStatus); if (irp) { /* * This IoCallDriver may block nearly for good --- the device * may be in the D3 state and IoCallDriver could take way * too long, waiting for its powering up. * They may not even return STATUS_PENDING. We'd better * leave the critsec here. */ LeaveCrit(); status = IoCallDriver(DeviceObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } EnterCrit(); if ((status == STATUS_SUCCESS) && (output.CurrentDriveLetter)) { UserAssert((output.CurrentDriveLetter - 'A') < 30); retval = 1 << (output.CurrentDriveLetter - 'A'); if (pContext->Event & EVENT_CDROM_MEDIA_ARRIVAL) { retval |= HMCE_ARRIVAL; } } } PopAndFreeW32ThreadLock(&tlFileObject); } // // Allways free the request // ThreadUnlockAndFreePool(ptiCurrent, &tlContext); return retval; } /***************************************************************************\ * Handle device notifications such as MediaChanged * * Execution Context: * * History: \***************************************************************************/ NTSTATUS DeviceCDROMNotify( IN PTARGET_DEVICE_CUSTOM_NOTIFICATION Notification, IN PCDROM_NOTIFY pContext) { PCDROM_NOTIFY pNew; CheckCritOut(); if (IsRemoteConnection()) { return STATUS_SUCCESS; } UserAssert(pContext); if (IsEqualGUID(&Notification->Event, &GUID_IO_MEDIA_ARRIVAL)) { pContext->Event = EVENT_CDROM_MEDIA_ARRIVAL; } else if (IsEqualGUID(&Notification->Event, &GUID_IO_MEDIA_REMOVAL)) { pContext->Event = EVENT_CDROM_MEDIA_REMOVAL; } else if (IsEqualGUID(&Notification->Event, &GUID_TARGET_DEVICE_REMOVE_COMPLETE)) { EnterMediaCrit(); if (!gCDROMClassRegistrationEntry) { // This is being cleaned up by xxxUnregisterDeviceNotifications LeaveMediaCrit(); return STATUS_SUCCESS; } RemoveEntryList(&pContext->Entry); LeaveMediaCrit(); IoUnregisterPlugPlayNotification(pContext->RegistrationHandle); UserFreePool(pContext); return STATUS_SUCCESS; } #ifdef AUTORUN_CURSOR else if (IsEqualGUID(&Notification->Event, &GUID_IO_DEVICE_BECOMING_READY)) { PDEVICE_EVENT_BECOMING_READY pdebr = (DEVICE_EVENT_BECOMING_READY*)Notification->CustomDataBuffer; ShowAutorunCursor(pdebr->Estimated100msToReady * 10); return STATUS_SUCCESS; } #endif else { return STATUS_SUCCESS; } // // Process the arrival or removal. // // We must queue this otherwise we end up bugchecking on Terminal Server // This is due to opening a handle from within the system process which // requires us to do an attach process. // pNew = UserAllocPoolNonPaged(pContext->Size, TAG_PNP); if (pNew) { RtlCopyMemory(pNew, pContext, pContext->Size); EnterMediaCrit(); InsertHeadList(&gMediaChangeList, &pNew->Entry); LeaveMediaCrit(); KeSetEvent(gpEventMediaChange, EVENT_INCREMENT, FALSE); } return STATUS_SUCCESS; } /***************************************************************************\ * DeviceClassCDROMNotify * * This gets called when CDROM appears or disappears * \***************************************************************************/ NTSTATUS DeviceClassCDROMNotify ( IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION classChange, IN PVOID Unused ) { NTSTATUS Status = STATUS_SUCCESS; PFILE_OBJECT FileObject; PDEVICE_OBJECT DeviceObject; PCDROM_NOTIFY pContext; ULONG Size; UNREFERENCED_PARAMETER(Unused); CheckCritOut(); /* * Sanity check the DeviceType, and that it matches the InterfaceClassGuid */ UserAssert(IsEqualGUID(&classChange->InterfaceClassGuid, &CdRomClassGuid)); if (IsEqualGUID(&classChange->Event, &GUID_DEVICE_INTERFACE_ARRIVAL)) { Status = IoGetDeviceObjectPointer(classChange->SymbolicLinkName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); if (NT_SUCCESS(Status)) { Size = sizeof(CDROM_NOTIFY) + classChange->SymbolicLinkName->Length; pContext = (PCDROM_NOTIFY) UserAllocPool(Size, TAG_PNP); // // Register For MediaChangeNotifications on all the CDROMs. // if (pContext) { pContext->Size = Size; pContext->DeviceName.DeviceNameLength = classChange->SymbolicLinkName->Length; RtlCopyMemory(pContext->DeviceName.DeviceName, classChange->SymbolicLinkName->Buffer, pContext->DeviceName.DeviceNameLength); if (NT_SUCCESS(IoRegisterPlugPlayNotification ( EventCategoryTargetDeviceChange, 0, FileObject, gpWin32kDriverObject, DeviceCDROMNotify, pContext, &(pContext->RegistrationHandle)))) { EnterMediaCrit(); InsertHeadList(&gCDROMNotifyList, &pContext->Entry); LeaveMediaCrit(); } else { RIPMSG2(RIP_WARNING, "Failed to register CDROM Device Notification '%.*ws'.", pContext->DeviceName.DeviceNameLength, pContext->DeviceName.DeviceName); UserFreePool(pContext); } } else { RIPMSG2(RIP_WARNING, "Failed to allocate pool block for CDROM '%.*ws'.", pContext->DeviceName.DeviceNameLength, pContext->DeviceName.DeviceName); } ObDereferenceObject(FileObject); } } else if (IsEqualGUID(&classChange->Event, &GUID_DEVICE_INTERFACE_REMOVAL)) { // // Do nothing - we already remove the registration. // } else { RIPMSG0(RIP_ERROR, "Unrecognized Event GUID"); } return STATUS_SUCCESS; } #ifdef TRACK_PNP_NOTIFICATION PPNP_NOTIFICATION_RECORD gpPnpNotificationRecord; DWORD gdwPnpNotificationRecSize = 256; UINT giPnpSeq; BOOL gfRecordPnpNotification = TRUE; VOID CleanupPnpNotificationRecord( VOID) { CheckDeviceInfoListCritIn(); gfRecordPnpNotification = FALSE; if (gpPnpNotificationRecord) { UserFreePool(gpPnpNotificationRecord); gpPnpNotificationRecord = NULL; } } VOID RecordPnpNotification( PNP_NOTIFICATION_TYPE type, PDEVICEINFO pDeviceInfo, ULONG_PTR NotificationCode) { UINT iIndex; UINT i = 0; PUNICODE_STRING pName = NULL; HANDLE hDeviceInfo = NULL; CheckDeviceInfoListCritIn(); UserAssert(gfRecordPnpNotification); if (gpPnpNotificationRecord == NULL) { gpPnpNotificationRecord = UserAllocPoolZInit(sizeof *gpPnpNotificationRecord * gdwPnpNotificationRecSize, TAG_PNP); } if (gpPnpNotificationRecord == NULL) { return; } iIndex = giPnpSeq % gdwPnpNotificationRecSize; gpPnpNotificationRecord[iIndex].pKThread = PsGetCurrentThread(); gpPnpNotificationRecord[iIndex].iSeq = ++giPnpSeq; // the first record is numbered as 1. gpPnpNotificationRecord[iIndex].type = type; /* * If there is a pathname, copy it here. */ switch (type) { case PNP_NTF_CLASSNOTIFY: /* * pDeviceInfo is actually a pUnicodeString. */ pName = (PUNICODE_STRING)pDeviceInfo; pDeviceInfo = NULL; break; case PNP_NTF_DEVICENOTIFY_UNLISTED: /* * pDeviceInfo is invalid, cannot be looked up. */ UserAssert(pName == NULL); break; default: if (pDeviceInfo) { pName = &pDeviceInfo->ustrName; hDeviceInfo = PtoHq(pDeviceInfo); } break; } UserAssert(i == 0); if (pName) { for ( ; i < ARRAY_SIZE(gpPnpNotificationRecord[iIndex].szPathName) - 1 && i < (UINT)pName->Length / sizeof(WCHAR); ++i) { gpPnpNotificationRecord[iIndex].szPathName[i] = (UCHAR)pName->Buffer[i]; } } gpPnpNotificationRecord[iIndex].szPathName[i] = 0; /* * Store the rest of information */ gpPnpNotificationRecord[iIndex].pDeviceInfo = pDeviceInfo; gpPnpNotificationRecord[iIndex].hDeviceInfo = hDeviceInfo; gpPnpNotificationRecord[iIndex].NotificationCode = NotificationCode; /* * Store the stack trace. */ RtlWalkFrameChain(gpPnpNotificationRecord[iIndex].trace, ARRAY_SIZE(gpPnpNotificationRecord[iIndex].trace), 0); } #endif // TRACK_PNP_NOTIFICATION /***************************************************************************\ * CreateDeviceInfo * * This creates an instance of an input device for USER. To do this it: * - Allocates a DEVICEINFO struct * - Adds it to USER's list of input devices * - Initializes some of the fields * - Signals the input servicing thread to open and read the new device. * * Type - the device type (DEVICE_TYPE_MOUSE, DEVICE_TYPE_KEYBOARD) * Name - the device name. * When trying to open a HYDRA client's mouse, Name is NULL. * bFlags - some initial flags to set (eg: GDIF_NOTPNP) * * THIS FUNCTION IS CALLED IN THE CONTEXT OF THE KERNEL PROCESS * so we mustn't open the mouse here, else the handle we get will not belong * to the Win32k process. * * History: * 11-26-90 DavidPe Created. * 01-07-98 IanJa Plug & Play \***************************************************************************/ PDEVICEINFO CreateDeviceInfo(DWORD DeviceType, PUNICODE_STRING pustrName, BYTE bFlags) { PDEVICEINFO pDeviceInfo = NULL; CheckCritIn(); BEGINATOMICCHECK(); UserAssert(pustrName != NULL); TAGMSGF4(DBGTAG_PNP, "CreateDeviceInfo(%d, %.*ws, %x)", DeviceType, pustrName->Length / sizeof(WCHAR), pustrName->Buffer, bFlags); if (DeviceType > DEVICE_TYPE_MAX) { RIPMSGF1(RIP_ERROR, "Unknown DeviceType %lx", DeviceType); } #if defined(PRERELEASE) && defined(CHECK_DEVICE_DUPLICATE) { PDEVICEINFO pdi; CheckCritIn(); EnterDeviceInfoListCrit(); for (pdi = gpDeviceInfoList; pdi; pdi = pdi->pNext) { if (wcsncmp(pustrName->Buffer, pdi->ustrName.Buffer, pdi->ustrName.Length / sizeof(WCHAR)) == 0) { TAGMSGF1(DBGTAG_PNP, "the new device is already in the list! %p", pdi); break; } } LeaveDeviceInfoListCrit(); } #endif #ifdef GENERIC_INPUT pDeviceInfo = (PDEVICEINFO)HMAllocObject(NULL, NULL, (BYTE)TYPE_DEVICEINFO, (DWORD)aDeviceTemplate[DeviceType].cbDeviceInfo); #else pDeviceInfo = UserAllocPoolZInit(aDeviceTemplate[DeviceType].cbDeviceInfo, TAG_PNP); #endif if (pDeviceInfo == NULL) { RIPMSGF0(RIP_WARNING, "out of memory allocating DEVICEINFO"); EXITATOMICCHECK(); return NULL; } if (pustrName->Buffer != NULL) { pDeviceInfo->ustrName.Buffer = UserAllocPool(pustrName->Length, TAG_PNP); if (pDeviceInfo->ustrName.Buffer == NULL) { RIPMSGF2(RIP_WARNING, "Can't duplicate string %.*ws", pustrName->Length / sizeof(WCHAR), pustrName->Buffer); goto CreateFailed; } pDeviceInfo->ustrName.MaximumLength = pustrName->Length; RtlCopyUnicodeString(&pDeviceInfo->ustrName, pustrName); } pDeviceInfo->type = (BYTE)DeviceType; pDeviceInfo->bFlags |= bFlags; /* * Create this device's HidChangeCompletion event. When the RIT completes * a synchronous ProcessDeviceChanges() it signals the HidChangeCompletion * event to wake the requesting RequestDeviceChange() which is blocking on * the event. * Each device has it's own HidChangeCompletion event, * since multiple PnP notification may arrive for several different * devices simultaneously. (see #331320 IanJa) */ pDeviceInfo->pkeHidChangeCompleted = CreateKernelEvent(SynchronizationEvent, FALSE); if (pDeviceInfo->pkeHidChangeCompleted == NULL) { RIPMSGF0(RIP_WARNING, "failed to create pkeHidChangeCompleted"); goto CreateFailed; } EnterDeviceInfoListCrit(); #ifdef TRACK_PNP_NOTIFICATION /* * Placing tracking code here may miss the failure cases above, * but they're pretty exceptional cases that can be safely ignored. */ if (gfRecordPnpNotification) { RecordPnpNotification(PNP_NTF_CREATEDEVICEINFO, pDeviceInfo, DeviceType); } #endif #ifdef GENERIC_INPUT if (aDeviceTemplate[DeviceType].dwFlags & DT_HID) { /* * Create HID specific information. */ pDeviceInfo->hid.pHidDesc = HidCreateDeviceInfo(pDeviceInfo); if (pDeviceInfo->hid.pHidDesc == NULL) { /* * Something wrong happened and we failed to * create the device information. * Or the device is not our target. * Should bail out anyway. */ TAGMSGF0(DBGTAG_PNP, "HidCreateDeviceInfo bailed out."); LeaveDeviceInfoListCrit(); goto CreateFailed; } } #endif /* * Link it in */ pDeviceInfo->pNext = gpDeviceInfoList; gpDeviceInfoList = pDeviceInfo; /* * Tell the RIT there is a new device so that it can open it and start * reading from it. This is non-blocking (no GDIAF_PNPWAITING bit set) */ RequestDeviceChange(pDeviceInfo, GDIAF_ARRIVED, TRUE); LeaveDeviceInfoListCrit(); EXITATOMICCHECK(); return pDeviceInfo; CreateFailed: if (pDeviceInfo) { if (pDeviceInfo->ustrName.Buffer) { UserFreePool(pDeviceInfo->ustrName.Buffer); } #ifdef GENERIC_INPUT if (pDeviceInfo->hid.pHidDesc) { FreeHidDesc(pDeviceInfo->hid.pHidDesc); #if DBG pDeviceInfo->hid.pHidDesc = NULL; #endif } if (pDeviceInfo->pkeHidChangeCompleted) { FreeKernelEvent(&pDeviceInfo->pkeHidChangeCompleted); } HMFreeObject(pDeviceInfo); #else UserFreePool(pDeviceInfo); #endif } ENDATOMICCHECK(); return NULL; } /***************************************************************************\ * DeviceClassNotify * * This gets called when an input device is attached or detached. * If this happens during initialization (for mice already connected) we * come here by in the context of the RIT. If hot-(un)plugging a mouse, * then we are called on a thread from the Kernel process. * * History: * 10-20-97 IanJa Taken from some old code of KenRay's \***************************************************************************/ NTSTATUS DeviceClassNotify ( IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION classChange, IN PVOID DeviceType // (context) ) { DWORD dwDeviceType; CheckCritOut(); dwDeviceType = PtrToUlong( DeviceType ); TAGMSG2(DBGTAG_PNP, "enter DeviceClassNotify(%lx, %lx)", classChange, dwDeviceType); /* * Sanity check the DeviceType, and that it matches the InterfaceClassGuid */ UserAssert(dwDeviceType <= DEVICE_TYPE_MAX); UserAssert(IsEqualGUID(&classChange->InterfaceClassGuid, aDeviceTemplate[dwDeviceType].pClassGUID)); if (IsRemoteConnection()) { return STATUS_SUCCESS; } TAGMSG3(DBGTAG_PNP | RIP_THERESMORE, " Event GUID %lx, %x, %x", classChange->Event.Data1, classChange->Event.Data2, classChange->Event.Data3); TAGMSG8(DBGTAG_PNP | RIP_THERESMORE, " %2x%2x%2x%2x%2x%2x%2x%2x", classChange->Event.Data4[0], classChange->Event.Data4[1], classChange->Event.Data4[2], classChange->Event.Data4[3], classChange->Event.Data4[4], classChange->Event.Data4[5], classChange->Event.Data4[6], classChange->Event.Data4[7]); TAGMSG4(DBGTAG_PNP | RIP_THERESMORE, " InterfaceClassGuid %lx, %lx, %lx, %lx", ((DWORD *)&(classChange->InterfaceClassGuid))[0], ((DWORD *)&(classChange->InterfaceClassGuid))[1], ((DWORD *)&(classChange->InterfaceClassGuid))[2], ((DWORD *)&(classChange->InterfaceClassGuid))[3]); TAGMSG1(DBGTAG_PNP | RIP_THERESMORE, " SymbolicLinkName %ws", classChange->SymbolicLinkName->Buffer); if (IsEqualGUID(&classChange->Event, &GUID_DEVICE_INTERFACE_ARRIVAL)) { // A new hid device class association has arrived EnterCrit(); TRACE_INIT(("DeviceClassNotify - SymbolicLinkName : %ws \n", classChange->SymbolicLinkName->Buffer)); #ifdef TRACK_PNP_NOTIFICATION if (gfRecordPnpNotification) { CheckDeviceInfoListCritOut(); EnterDeviceInfoListCrit(); RecordPnpNotification(PNP_NTF_CLASSNOTIFY, (PVOID)classChange->SymbolicLinkName, (ULONG_PTR)DeviceType); LeaveDeviceInfoListCrit(); } #endif CreateDeviceInfo(dwDeviceType, classChange->SymbolicLinkName, 0); LeaveCrit(); TAGMSG0(DBGTAG_PNP, "=== CREATED ==="); } return STATUS_SUCCESS; } /****************************************************************************\ * If a device class "all-for-one" setting (ConnectMultiplePorts) is on, * then we just open the device the old (non-PnP) way and return TRUE. (As a * safety feature we also do this if gpWin32kDriverObject is NULL, because this * driver object is needed to register for PnP device class notifications) * Otherwise, return FALSE so we can continue and register for Arrival/Departure * notifications. * * This code was originally intended to be temporary until ConnectMultiplePorts * was finally turned off. * But now I think we have to keep it for backward compatibility with * drivers that filter Pointer/KeyboardClass0 and/or those that replace * Pointer/KeyboardClass0 by putting a different name in the registry under * System\CurrentControlSet\Services\RIT\mouclass (or kbbclass) \****************************************************************************/ BOOL OpenMultiplePortDevice(DWORD DeviceType) { WCHAR awchDeviceName[MAX_PATH]; UNICODE_STRING DeviceName; PDEVICE_TEMPLATE pDevTpl; PDEVICEINFO pDeviceInfo; PWCHAR pwchNameIndex; UINT uiConnectMultiplePorts = 0; CheckCritIn(); if (DeviceType <= DEVICE_TYPE_MAX) { pDevTpl = &aDeviceTemplate[DeviceType]; } else { RIPMSG1(RIP_ERROR, "OpenMultiplePortDevice(%d) - unknown type", DeviceType); return FALSE; } if (IsRemoteConnection()) { return FALSE; } #ifdef GENERIC_INPUT if (pDevTpl->dwFlags & DT_HID) { /* * HID devices don't need multiple port */ return FALSE; } #endif // GENERIC_INPUT /* * Note that we don't need to FastOpenUserProfileMapping() here since * uiRegistrySection (PMAP_MOUCLASS_PARAMS/PMAP_KBDCLASS_PARAMS) is a * machine setiing, not a user setting. */ FastGetProfileDwordW(NULL, pDevTpl->uiRegistrySection, L"ConnectMultiplePorts", 0, &uiConnectMultiplePorts, 0); /* * Open the device for read access. */ if (uiConnectMultiplePorts || (gpWin32kDriverObject == NULL)) { /* * Find out if there is a name substitution in the registry. * Note that we don't need to FastOpenUserProfileMapping() here since * PMAP_INPUT is a machine setting, not a user setting. */ FastGetProfileStringW(NULL, PMAP_INPUT, pDevTpl->pwszClassName, pDevTpl->pwszDefDevName, // if no substitution, use this default awchDeviceName, sizeof(awchDeviceName)/sizeof(WCHAR), 0); RtlInitUnicodeString(&DeviceName, awchDeviceName); pDeviceInfo = CreateDeviceInfo(DeviceType, &DeviceName, GDIF_NOTPNP); if (pDeviceInfo) { return TRUE; } } else { DeviceName.Length = 0; DeviceName.MaximumLength = sizeof(awchDeviceName); DeviceName.Buffer = awchDeviceName; RtlAppendUnicodeToString(&DeviceName, pDevTpl->pwszLegacyDevName); pwchNameIndex = &DeviceName.Buffer[(DeviceName.Length / sizeof(WCHAR)) - 1]; for (*pwchNameIndex = L'0'; *pwchNameIndex <= L'9'; (*pwchNameIndex)++) { CreateDeviceInfo(DeviceType, &DeviceName, GDIF_NOTPNP); } } return FALSE; } /***************************************************************************\ * RegisterCDROMNotify * * History: * 08-21-00 VTan Created \***************************************************************************/ VOID RegisterCDROMNotify( VOID) { UserAssert(!IsRemoteConnection()); UserAssert(gpWin32kDriverObject != NULL); if (gpWin32kDriverObject != NULL) { IoRegisterPlugPlayNotification ( EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, (PVOID) &CdRomClassGuid, gpWin32kDriverObject, (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)DeviceClassCDROMNotify, NULL, &gCDROMClassRegistrationEntry); } } /***************************************************************************\ * RegisterForDeviceClassNotifications * * Get ready to receive notifications that a mouse or keyboard is plugged in * or removed, then request notifications by registering for them. * * History: * 10-20-97 IanJa Taken from ntos\io\pnpinit.c \***************************************************************************/ NTSTATUS xxxRegisterForDeviceClassNotifications( VOID) { IO_NOTIFICATION_EVENT_CATEGORY eventCategory; ULONG eventFlags; NTSTATUS Status; UNICODE_STRING ustrDriverName; DWORD DeviceType; CheckCritIn(); TAGMSG0(DBGTAG_PNP, "enter xxxRegisterForDeviceClassNotifications()"); /* * Remote hydra session indicates CreateDeviceInfo in xxxRemoteReconnect. */ UserAssert(!IsRemoteConnection()); if (!gbFirstConnectionDone) { if (!gbRemoteSession) { // Session 0 /* * This must be done before devices are registered for device * notifications which will occur as a result of CreateDeviceInfo. */ RtlInitUnicodeString(&ustrDriverName, L"\\Driver\\Win32k"); Status = IoCreateDriver(&ustrDriverName, Win32kPnPDriverEntry); TAGMSG1(DBGTAG_PNP | RIP_THERESMORE, "IoCreateDriver returned status = %lx", Status); TAGMSG1(DBGTAG_PNP, "gpWin32kDriverObject = %lx", gpWin32kDriverObject); if (!NT_SUCCESS(Status)) { RIPMSG1(RIP_ERROR, "IoCreateDriver failed, status %lx", Status); Status = STATUS_SUCCESS; } UserAssert(gpWin32kDriverObject); } else { UserAssert(gpWin32kDriverObject == NULL); /* * Non-Zero session attached to the console */ RtlInitUnicodeString(&ustrDriverName, L"\\Driver\\Win32k"); // // Attempt to open the driver object // Status = ObReferenceObjectByName(&ustrDriverName, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, &gpWin32kDriverObject); if (!NT_SUCCESS(Status)) { RIPMSG1(RIP_ERROR, "ObReferenceObjectByName failed, status %lx", Status); Status = STATUS_SUCCESS; } UserAssert(gpWin32kDriverObject); } } // // We are only interested in DeviceClasses changing. // eventCategory = EventCategoryDeviceInterfaceChange; // // We want to be notified for all devices that are in the system. // those that are know now, and those that will arive later. // This allows us to have one code path for adding devices, and eliminates // the nasty race condition. If we were only interested in the devices // that exist at this one moment in time, and not future devices, we // would call IoGetDeviceClassAssociations. // eventFlags = PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES; /* * For all input device types: * If they are Multiple Port Devices (ie: not PnP) just open them * Else Register them for PnP notifications (they will be opened when the * arrival notification arrives. * If devices are already attached, we will received immediate notification * during the call to IoRegisterPlugPlayNotification, so we must LeaveCrit * because the callback routine DeviceClassNotify expects it. */ for (DeviceType = 0; DeviceType <= DEVICE_TYPE_MAX; DeviceType++) { if (!OpenMultiplePortDevice(DeviceType) && (gpWin32kDriverObject != NULL)) { /* * Make the registration. */ TAGMSG1(DBGTAG_PNP, "Registering device type %d", DeviceType); LeaveCrit(); // for DeviceClassNotify Status = IoRegisterPlugPlayNotification ( eventCategory, eventFlags, (PVOID)aDeviceTemplate[DeviceType].pClassGUID, gpWin32kDriverObject, (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)DeviceClassNotify, LongToPtr( DeviceType ), &aDeviceClassNotificationEntry[DeviceType]); EnterCrit(); TAGMSG1(DBGTAG_PNP, "Registration returned status %lx", Status); if (!NT_SUCCESS(Status)) { RIPMSG2(RIP_ERROR, "IoRegisterPlugPlayNotification(%d) failed, status %lx", DeviceType, Status); } } } // Now Register for CD_ROM notifications LeaveCrit(); // for DeviceClassNotify if (!gbFirstConnectionDone && gpWin32kDriverObject != NULL) { if (!IsRemoteConnection()) { RegisterCDROMNotify(); } gbFirstConnectionDone = TRUE; } EnterCrit(); return Status; } /***************************************************************************\ * UnregisterDeviceClassNotifications * * Remove device class notification registrations. * * History: * 02-28-00 Earhart Created \***************************************************************************/ VOID xxxUnregisterDeviceClassNotifications( VOID) { // Our input devices will automatically unregister themselves; we // need to clean up cdrom, though. PLIST_ENTRY pNext; PCDROM_NOTIFY pContext; PVOID RegistrationEntry; EnterMediaCrit(); if (gCDROMClassRegistrationEntry) { RegistrationEntry = gCDROMClassRegistrationEntry; gCDROMClassRegistrationEntry = NULL; LeaveMediaCrit(); IoUnregisterPlugPlayNotification(RegistrationEntry); EnterMediaCrit(); } while (TRUE) { pNext = RemoveHeadList(&gCDROMNotifyList); if (!pNext || pNext == &gCDROMNotifyList) { break; } pContext = CONTAINING_RECORD(pNext, CDROM_NOTIFY, Entry); LeaveMediaCrit(); /* in case there's a notification pending */ IoUnregisterPlugPlayNotification(pContext->RegistrationHandle); UserFreePool(pContext); EnterMediaCrit(); } LeaveMediaCrit(); } /***************************************************************************\ * GetKbdExId * * Get extended keyboard id with WMI * * History: * 01-02-01 Hiroyama Created \***************************************************************************/ NTSTATUS GetKbdExId( HANDLE hDevice, PKEYBOARD_ID_EX pIdEx) { PWNODE_SINGLE_INSTANCE pNode; ULONG size; PVOID p = NULL; NTSTATUS status; UNICODE_STRING str; status = IoWMIOpenBlock((LPGUID)&MSKeyboard_ExtendedID_GUID, WMIGUID_QUERY, &p); if (NT_SUCCESS(status)) { status = IoWMIHandleToInstanceName(p, hDevice, &str); TAGMSG2(DBGTAG_PNP, "GetKbdExId: DevName='%.*ws'", str.Length / sizeof(WCHAR), str.Buffer); if (NT_SUCCESS(status)) { // Get the size size = 0; IoWMIQuerySingleInstance(p, &str, &size, NULL); size += sizeof *pIdEx; pNode = UserAllocPoolNonPaged(size, TAG_KBDEXID); if (pNode) { status = IoWMIQuerySingleInstance(p, &str, &size, pNode); if (NT_SUCCESS(status)) { *pIdEx = *(PKEYBOARD_ID_EX)(((PUCHAR)pNode) + pNode->DataBlockOffset); } UserFreePool(pNode); } RtlFreeUnicodeString(&str); } ObDereferenceObject(p); } return status; } /***************************************************************************\ * QueryDeviceInfo * * Query the device information. This function is an async function, * so be sure any buffers it uses aren't allocated on the stack! * * If this is an asynchronous IOCTL, perhaps we should be waiting on * the file handle or on an event for it to succeed? * * This function must called by the RIT, not directly by PnP notification * (else the handle we issue the IOCTL on will be invalid) * * History: * 01-20-99 IanJa Created. \***************************************************************************/ NTSTATUS QueryDeviceInfo( PDEVICEINFO pDeviceInfo) { NTSTATUS Status; PDEVICE_TEMPLATE pDevTpl = &aDeviceTemplate[pDeviceInfo->type]; KEYBOARD_ID_EX IdEx; #ifdef GENERIC_INPUT UserAssert(pDeviceInfo->type != DEVICE_TYPE_HID); #endif #ifdef DIAGNOSE_IO pDeviceInfo->AttrStatus = #endif Status = ZwDeviceIoControlFile(pDeviceInfo->handle, NULL, NULL, NULL, &pDeviceInfo->iosb, pDevTpl->IOCTL_Attr, NULL, 0, (PVOID)((PBYTE)pDeviceInfo + pDevTpl->offAttr), pDevTpl->cbAttr); if (!NT_SUCCESS(Status)) { RIPMSG2(RIP_WARNING, "QueryDeviceInfo(%p): IOCTL failed - Status %lx", pDeviceInfo, Status); } TAGMSG1(DBGTAG_PNP, "IOCTL_*_QUERY_ATTRIBUTES returns Status %lx", Status); if (pDeviceInfo->type == DEVICE_TYPE_KEYBOARD) { if (NT_SUCCESS(GetKbdExId(pDeviceInfo->handle, &IdEx))) { TAGMSG4(DBGTAG_PNP, "QueryDeviceInfo: kbd (%x,%x) ExId:(%x,%x)", pDeviceInfo->keyboard.Attr.KeyboardIdentifier.Type, pDeviceInfo->keyboard.Attr.KeyboardIdentifier.Subtype, IdEx.Type, IdEx.Subtype); pDeviceInfo->keyboard.IdEx = IdEx; } else { // What can we do? pDeviceInfo->keyboard.IdEx.Type = pDeviceInfo->keyboard.Attr.KeyboardIdentifier.Type; pDeviceInfo->keyboard.IdEx.Subtype = pDeviceInfo->keyboard.Attr.KeyboardIdentifier.Subtype; TAGMSG3(DBGTAG_PNP, "QueryDeviceInfo: failed to get ExId for pDevice=%p, fallback to (%x,%x)", pDeviceInfo, pDeviceInfo->keyboard.IdEx.Type, pDeviceInfo->keyboard.IdEx.Subtype); } } return Status; } /***************************************************************************\ * OpenDevice * * This function opens an input device for USER, mouse or keyboard. * * * Return value * BOOL did the operation succeed? * * When trying to open a HYDRA client's mouse (or kbd?), pDeviceInfo->ustrName * is NULL. * * This function must called by the RIT, not directly by PnP * notification (that way the handle we are about to create will be in the right * our process) * * History: * 11-26-90 DavidPe Created. * 01-07-98 IanJa Plug & Play * 04-17-98 IanJa Only open mice in RIT context. \***************************************************************************/ BOOL OpenDevice( PDEVICEINFO pDeviceInfo) { OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; ULONG ulAccessMode = FILE_READ_DATA | SYNCHRONIZE; ULONG ulShareMode = FILE_SHARE_WRITE; UINT i; CheckCritIn(); UserAssert((PtiCurrentShared() == gptiRit) || (PtiCurrentShared() == gTermIO.ptiDesktop)); TAGMSG4(DBGTAG_PNP, "OpenDevice(): Opening type %d (%lx %.*ws)", pDeviceInfo->type, pDeviceInfo->handle, pDeviceInfo->ustrName.Length / sizeof(WCHAR), pDeviceInfo->ustrName.Buffer); #ifdef DIAGNOSE_IO pDeviceInfo->OpenerProcess = PsGetCurrentProcessId(); #endif if (IsRemoteConnection()) { TRACE_INIT(("OpenDevice - Remote mode\n")); /* * For other than the console, the mouse handle is * set before createwinstation. */ pDeviceInfo->bFlags |= GDIF_NOTPNP; switch (pDeviceInfo->type) { case DEVICE_TYPE_MOUSE: pDeviceInfo->handle = ghRemoteMouseChannel; if (ghRemoteMouseChannel == NULL) { return FALSE; } break; case DEVICE_TYPE_KEYBOARD: pDeviceInfo->handle = ghRemoteKeyboardChannel; if (ghRemoteKeyboardChannel == NULL) { return FALSE; } break; default: RIPMSG2(RIP_ERROR, "Unknown device type %d DeviceInfo %#p", pDeviceInfo->type, pDeviceInfo); return FALSE; } } else { InitializeObjectAttributes(&ObjectAttributes, &(pDeviceInfo->ustrName), 0, NULL, NULL); #ifdef GENERIC_INPUT if (pDeviceInfo->type == DEVICE_TYPE_HID) { ulAccessMode |= FILE_WRITE_DATA; ulShareMode |= FILE_SHARE_READ; } #endif // USB devices are slow, so they may not have been closed before we // open again here so let us delay execution for some time and try // to open them again. We delay 1/10th of a second for a max of 30 // times, making a total wait time of 3 seconds. // // If we fast user switch too fast, the serial port may be in the // process of closing where it stalls execution. This is a rare // case where we may open the serial port while it is stalling // and get back STATUS_ACCESS_DENIED and lose the user's device. // In this case, we should retry the open and it should succeed // once the serial port has closed. for (i = 0; i < MAX_RETRIES_TO_OPEN; i++) { #ifdef DIAGNOSE_IO pDeviceInfo->OpenStatus = #endif Status = ZwCreateFile(&pDeviceInfo->handle, ulAccessMode, &ObjectAttributes, &pDeviceInfo->iosb, NULL, 0, ulShareMode, FILE_OPEN_IF, 0, NULL, 0); if ((STATUS_SHARING_VIOLATION == Status) || (Status == STATUS_ACCESS_DENIED)) { // Sleep for 1/10th of a second UserSleep(100); } else { // Device opened successfully or some other error occured break; } } TAGMSG2(DBGTAG_PNP, "ZwCreateFile returns handle %lx, Status %lx", pDeviceInfo->handle, Status); if (!NT_SUCCESS(Status)) { if ((pDeviceInfo->bFlags & GDIF_NOTPNP) == 0) { /* * Don't warn about PS/2 mice: the PointerClassLegacy0 -9 and * KeyboardClassLegacy0 - 9 will usually fail to be created */ RIPMSG1(RIP_WARNING, "OpenDevice: ZwCreateFile failed with Status %lx", Status); } TRACE_INIT(("OpenDevice: ZwCreateFile failed with Status %lx", Status)); /* * Don't FreeDeviceInfo here because that alters gpDeviceInfoList * which our caller, ProcessDeviceChanges, is traversing. * Instead, let ProcessDeviceChanges do it. */ return FALSE; } } #ifdef GENERIC_INPUT /* * All the HID Information has been already acquired through * HidCreateDeviceInfo. Let's skip HID deviceinfo here. */ if (pDeviceInfo->type != DEVICE_TYPE_HID) { #endif Status = QueryDeviceInfo(pDeviceInfo); #ifdef GENERIC_INPUT } #endif return NT_SUCCESS(Status); } VOID CloseDevice( PDEVICEINFO pDeviceInfo) { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; CheckCritIn(); #ifdef TRACK_PNP_NOTIFICATION if (gfRecordPnpNotification) { CheckDeviceInfoListCritIn(); RecordPnpNotification(PNP_NTF_CLOSEDEVICE, pDeviceInfo, pDeviceInfo->usActions); } #endif // TRACK_PNP_NOTIFICATION TAGMSG5(DBGTAG_PNP, "CloseDevice(%p): closing type %d (%lx %.*ws)", pDeviceInfo, pDeviceInfo->type, pDeviceInfo->handle, pDeviceInfo->ustrName.Length / sizeof(WCHAR), pDeviceInfo->ustrName.Buffer); if (pDeviceInfo->handle) { UserAssert(pDeviceInfo->OpenerProcess == PsGetCurrentProcessId()); ZwCancelIoFile(pDeviceInfo->handle, &IoStatusBlock); UserAssertMsg2(NT_SUCCESS(IoStatusBlock.Status), "NtCancelIoFile handle %x failed status %#x", pDeviceInfo->handle, IoStatusBlock.Status); if (pDeviceInfo->handle == ghRemoteMouseChannel) { UserAssert(pDeviceInfo->type == DEVICE_TYPE_MOUSE); pDeviceInfo->handle = 0; return; } if (pDeviceInfo->handle == ghRemoteKeyboardChannel) { UserAssert(pDeviceInfo->type == DEVICE_TYPE_KEYBOARD); pDeviceInfo->handle = 0; return; } Status = ZwClose(pDeviceInfo->handle); UserAssertMsg2(NT_SUCCESS(Status), "ZwClose handle %x failed status %#x", pDeviceInfo->handle, Status); pDeviceInfo->handle = 0; } else { #ifdef GENERIC_INPUT if (pDeviceInfo->type == DEVICE_TYPE_HID) { /* * HID devices may be closed regardless the error conditions. */ TAGMSG2(DBGTAG_PNP, "CloseDevice: hid: pDeviceInfo->iosb.Status=%x, ReadStatus=%x", pDeviceInfo->iosb.Status, pDeviceInfo->ReadStatus); } else { #endif /* * Assert the IO was cancelled or we tried to read the device * after the first close (which set the handle to 0 - an invalid handle) */ UserAssert((pDeviceInfo->iosb.Status == STATUS_CANCELLED) || (pDeviceInfo->ReadStatus == STATUS_INVALID_HANDLE)); #ifdef GENERIC_INPUT } #endif } } /*****************************************************************************\ * RegisterForDeviceChangeNotifications() * * Device Notifications such as QueryRemove, RemoveCancelled, RemoveComplete * tell us what is going on with the mouse. * To register for device notifications: * (1) Obtain a pointer to the device object (pFileObject) * (2) Register for target device change notifications, saving the * notification handle (which we will need in order to deregister) * * It doesn't matter too much if this fails: we just won't be able to eject the * hardware via the UI very successfully. (We can still just yank it though). * This will also fail if the ConnectMultiplePorts was set for this device. * * 1998-10-05 IanJa Created \*****************************************************************************/ BOOL RegisterForDeviceChangeNotifications( PDEVICEINFO pDeviceInfo) { PFILE_OBJECT pFileObject; NTSTATUS Status; /* * In or Out of User critical section: * In when called from RIT ProcessDeviceChanges(); * Out when called from the DeviceNotify callback */ if (IsRemoteConnection()) { TRACE_INIT(("RegisterForDeviceChangeNotifications called for remote session\n")); return TRUE; } CheckCritIn(); UserAssert((PtiCurrentShared() == gptiRit) || (PtiCurrentShared() == gTermIO.ptiDesktop)); UserAssert(pDeviceInfo->handle); UserAssert(pDeviceInfo->OpenerProcess == PsGetCurrentProcessId()); if (pDeviceInfo->bFlags & GDIF_NOTPNP) { return TRUE; } Status = ObReferenceObjectByHandle(pDeviceInfo->handle, 0, NULL, KernelMode, (PVOID)&pFileObject, NULL); if (NT_SUCCESS(Status)) { Status = IoRegisterPlugPlayNotification ( EventCategoryTargetDeviceChange, // EventCategory 0, // EventCategoryFlags (PVOID)pFileObject, // EventCategoryData gpWin32kDriverObject, // DriverObject // (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) DeviceNotify, (PVOID)pDeviceInfo, // Context &pDeviceInfo->NotificationEntry); ObDereferenceObject(pFileObject); if (!NT_SUCCESS(Status)) { // This is only OK if ConnectMultiplePorts is on (ie: not a PnP device) // For the record, the old RIPMSG referred NTBUG #333453. RIPMSG3(RIP_ERROR, "IoRegisterPlugPlayNotification failed on device %.*ws, status %lx", pDeviceInfo->ustrName.Length / sizeof(WCHAR), pDeviceInfo->ustrName.Buffer, Status); } } else { // non-catastrophic error (won't be able to remove device) RIPMSG2(RIP_ERROR, "Can't get pFileObject from handle %lx, status %lx", pDeviceInfo->handle, Status); } return NT_SUCCESS(Status); } BOOL UnregisterForDeviceChangeNotifications(PDEVICEINFO pDeviceInfo) { NTSTATUS Status; #ifdef TRACK_PNP_NOTIFICATION if (gfRecordPnpNotification) { CheckDeviceInfoListCritIn(); RecordPnpNotification(PNP_NTF_UNREGISTER_NOTIFICATION, pDeviceInfo, pDeviceInfo->usActions); } #endif CheckCritIn(); UserAssert((PtiCurrentShared() == gptiRit) || (PtiCurrentShared() == gTermIO.ptiDesktop)); UserAssert(pDeviceInfo->OpenerProcess == PsGetCurrentProcessId()); if (pDeviceInfo->NotificationEntry == NULL) { /* * This happens for non-PnP devices or if the earlier * IoRegisterPlugPlayNotification() failed. Return now since * IoUnregisterPlugPlayNotification(NULL) will bluescreen. * And other case is also when we detach remote devices (which are * not PnP) when reconnecting locally. */ return TRUE; } // non-PnP devices should not have any NotificationEntry: UserAssert((pDeviceInfo->bFlags & GDIF_NOTPNP) == 0); TAGMSG4(DBGTAG_PNP, "UnregisterForDeviceChangeNotifications(): type %d (%lx %.*ws)", pDeviceInfo->type, pDeviceInfo, pDeviceInfo->ustrName.Length / sizeof(WCHAR), pDeviceInfo->ustrName.Buffer); Status = IoUnregisterPlugPlayNotification(pDeviceInfo->NotificationEntry); if (!NT_SUCCESS(Status)) { RIPMSG2(RIP_ERROR, "IoUnregisterPlugPlayNotification failed Status = %lx, DEVICEINFO %lx", Status, pDeviceInfo); return FALSE; } pDeviceInfo->NotificationEntry = 0; return TRUE; } /***************************************************************************\ * Handle device notifications such as QueryRemove, CancelRemove etc. * * Execution Context: * when yanked: a non-WIN32 thread. * via UI: ??? (won't see this except from laptop being undocked?) * * History: \***************************************************************************/ __inline USHORT GetPnpActionFromGuid( GUID *pEvent) { USHORT usAction = 0; if (IsEqualGUID(pEvent, &GUID_TARGET_DEVICE_QUERY_REMOVE)) { TAGMSG0(DBGTAG_PNP | RIP_NONAME, "QueryRemove"); usAction = GDIAF_QUERYREMOVE; } else if (IsEqualGUID(pEvent, &GUID_TARGET_DEVICE_REMOVE_CANCELLED)) { TAGMSG0(DBGTAG_PNP | RIP_NONAME, "RemoveCancelled"); usAction = GDIAF_REMOVECANCELLED; } else if (IsEqualGUID(pEvent, &GUID_TARGET_DEVICE_REMOVE_COMPLETE)) { TAGMSG1(DBGTAG_PNP | RIP_NONAME, "RemoveComplete (process %#x)", PsGetCurrentProcessId()); usAction = GDIAF_DEPARTED; } else { TAGMSG4(DBGTAG_PNP | RIP_NONAME, "GUID Unknown: %lx:%lx:%lx:%x...", pEvent->Data1, pEvent->Data2, pEvent->Data3, pEvent->Data4[0]); } return usAction; } NTSTATUS DeviceNotify( IN PPLUGPLAY_NOTIFY_HDR pNotification, IN PDEVICEINFO pDeviceInfo) // should the context be a kernel address? { USHORT usAction; PDEVICEINFO pDeviceInfoTmp; CheckCritOut(); CheckDeviceInfoListCritOut(); /* * Check the validity of pDeviceInfo. */ EnterDeviceInfoListCrit(); for (pDeviceInfoTmp = gpDeviceInfoList; pDeviceInfoTmp; pDeviceInfoTmp = pDeviceInfoTmp->pNext) { if (pDeviceInfoTmp == pDeviceInfo) { break; } } if (pDeviceInfoTmp == NULL) { /* * This is an unknown device, most likely the one already freed. */ #ifdef TRACK_PNP_NOTIFICATION if (gfRecordPnpNotification) { RecordPnpNotification(PNP_NTF_DEVICENOTIFY_UNLISTED, pDeviceInfo, GetPnpActionFromGuid(&pNotification->Event)); } #endif RIPMSG1(RIP_ERROR, "win32k!DeviceNotify: Notification for unlisted DEVICEINFO %p, contact ntuserdt!", pDeviceInfo); LeaveDeviceInfoListCrit(); /* * Not to prevent device removal etc., * return success here. */ return STATUS_SUCCESS; } #ifdef TRACK_PNP_NOTIFICATION if (gfRecordPnpNotification) { RecordPnpNotification(PNP_NTF_DEVICENOTIFY, pDeviceInfo, GetPnpActionFromGuid(&pNotification->Event)); } #endif LeaveDeviceInfoListCrit(); if (IsRemoteConnection()) { return STATUS_SUCCESS; } TAGMSG1(DBGTAG_PNP | RIP_THERESMORE, "DeviceNotify >>> %lx", pDeviceInfo); UserAssert(pDeviceInfo->OpenerProcess != PsGetCurrentProcessId()); UserAssert(pDeviceInfo->usActions == 0); usAction = GetPnpActionFromGuid(&pNotification->Event); if (usAction == 0) { return STATUS_UNSUCCESSFUL; } /* * Signal the RIT to ProcessDeviceChanges() * Wait for completion according to the GDIAF_PNPWAITING bit */ CheckCritOut(); CheckDeviceInfoListCritOut(); /* * There is small window where we can get a PnP notification for a device that * we just have unregister unregistered a notification for and that we are deleting * so for PnP notification we need to check the device is valid (still in the list * and not being deleted. */ EnterDeviceInfoListCrit(); pDeviceInfoTmp = gpDeviceInfoList; while (pDeviceInfoTmp) { if (pDeviceInfoTmp == pDeviceInfo ) { if (!(pDeviceInfo->usActions & (GDIAF_FREEME | GDIAF_DEPARTED))) { KeResetEvent(gpEventPnPWainting); gbPnPWaiting = TRUE; RequestDeviceChange(pDeviceInfo, (USHORT)(usAction | GDIAF_PNPWAITING), TRUE); gbPnPWaiting = FALSE; KeSetEvent(gpEventPnPWainting, EVENT_INCREMENT, FALSE); } break; } pDeviceInfoTmp = pDeviceInfoTmp->pNext; } LeaveDeviceInfoListCrit(); return STATUS_SUCCESS; } /***************************************************************************\ * StartDeviceRead * * This function makes an asynchronous read request to the input device driver, * unless the device has been marked for destruction (GDIAF_FREEME) * * Returns: * The next DeviceInfo on the list if this device was freed: If the caller * was not already in the DeviceInfoList critical section, the this must be * ignored as it is not safe. * NULL if the read succeeded. * * History: * 11-26-90 DavidPe Created. * 10-20-98 IanJa Generalized for PnP input devices \***************************************************************************/ PDEVICEINFO StartDeviceRead( PDEVICEINFO pDeviceInfo) { PDEVICE_TEMPLATE pDevTpl; #ifdef GENERIC_INPUT PVOID pBuffer; ULONG ulLengthToRead; #endif #if !defined(GENERIC_INPUT) pDeviceInfo->bFlags |= GDIF_READING; #endif /* * If this device need to be freed, abandon * reading now and request the free. */ if (pDeviceInfo->usActions & GDIAF_FREEME) { #ifdef GENERIC_INPUT BEGIN_REENTERCRIT() { #if DBG if (fAlreadyHadCrit) { CheckDeviceInfoListCritIn(); } #endif #endif BEGIN_REENTER_DEVICEINFOLISTCRIT() { pDeviceInfo->bFlags &= ~GDIF_READING; pDeviceInfo = FreeDeviceInfo(pDeviceInfo); } END_REENTER_DEVICEINFOLISTCRIT(); #ifdef GENERIC_INPUT } END_REENTERCRIT(); #endif return pDeviceInfo; } if (gbExitInProgress || gbStopReadInput) { // Let's not post any more reads when we're trying to exit, eh? pDeviceInfo->bFlags &= ~GDIF_READING; pDeviceInfo->iosb.Status = STATUS_UNSUCCESSFUL; return NULL; } /* * Initialize in case read fails */ pDeviceInfo->iosb.Status = STATUS_UNSUCCESSFUL; // catch concurrent writes? pDeviceInfo->iosb.Information = 0; pDevTpl = &aDeviceTemplate[pDeviceInfo->type]; UserAssert(pDeviceInfo->OpenerProcess == PsGetCurrentProcessId()); #ifdef GENERIC_INPUT if (pDeviceInfo->type == DEVICE_TYPE_HID) { UserAssert(pDeviceInfo->hid.pTLCInfo); if (pDeviceInfo->handle == NULL) { /* * Currently this device is not requested by anyone. */ TAGMSG1(DBGTAG_PNP, "StartDeviceRead: pDevInfo=%p has been closed on demand.", pDeviceInfo); BEGIN_REENTER_DEVICEINFOLISTCRIT() if (pDeviceInfo->handle == NULL) { if (pDeviceInfo->bFlags & GDIF_READING) { pDeviceInfo->bFlags &= ~GDIF_READING; TAGMSG1(DBGTAG_PNP, "StartDeviceRead: pDevInfo=%p, bFlags has been reset.", pDeviceInfo); } } END_REENTER_DEVICEINFOLISTCRIT(); return NULL; } pBuffer = pDeviceInfo->hid.pHidDesc->pInputBuffer; ulLengthToRead = pDeviceInfo->hid.pHidDesc->hidpCaps.InputReportByteLength * MAXIMUM_ITEMS_READ; } else { pBuffer = (PBYTE)pDeviceInfo + pDevTpl->offData; ulLengthToRead = pDevTpl->cbData; } #endif if (pDeviceInfo->handle == NULL) { BEGIN_REENTER_DEVICEINFOLISTCRIT() { /* * Make sure the handle is truely NULL. * If this is the case, perhaps this is called from APC * that happened at bad timing, like in the middle of * device removal query, when ProcessDeviceChanges completed * but RequestDeviceChange is not awaken for the complete event. * The code can olnly simply bail out once in the situation. */ if (pDeviceInfo->handle == NULL) { pDeviceInfo->bFlags &= ~GDIF_READING; pDeviceInfo->ReadStatus = STATUS_INVALID_HANDLE; } } END_REENTER_DEVICEINFOLISTCRIT(); return NULL; } #ifdef GENERIC_INPUT pDeviceInfo->bFlags |= GDIF_READING; #endif LOGTIME(pDeviceInfo->timeStartRead); #ifdef DIAGNOSE_IO pDeviceInfo->nReadsOutstanding++; #endif UserAssert(pDeviceInfo->handle); /* * Avoid to start reading NULL device handle. * This happen when the DeviceNotify receives QUERY_REMOVE * and the RIT finishes processing it, but RequestDeviceChange * has not finished its wait. */ #ifdef GENERIC_INPUT pDeviceInfo->ReadStatus = ZwReadFile( pDeviceInfo->handle, NULL, // hReadEvent InputApc, // InputApc() pDeviceInfo, // ApcContext &pDeviceInfo->iosb, pBuffer, ulLengthToRead, PZERO(LARGE_INTEGER), NULL); #else pDeviceInfo->ReadStatus = ZwReadFile( pDeviceInfo->handle, NULL, // hReadEvent InputApc, // InputApc() pDeviceInfo, // ApcContext &pDeviceInfo->iosb, (PVOID)((PBYTE)pDeviceInfo + pDevTpl->offData), pDevTpl->cbData, PZERO(LARGE_INTEGER), NULL); #endif LOGTIME(pDeviceInfo->timeEndRead); #if DBG if (pDeviceInfo->bFlags & GDIF_DBGREAD) { TAGMSG2(DBGTAG_PNP, "ZwReadFile of Device handle %lx returned status %lx", pDeviceInfo->handle, pDeviceInfo->ReadStatus); } #endif if (!NT_SUCCESS(pDeviceInfo->ReadStatus)) { BEGIN_REENTER_DEVICEINFOLISTCRIT() { /* * If insufficient resources, retry the read the next time the RIT * wakes up for the ID_TIMER event by incrementing gnRetryReadInput * (Cheaper than setting our own timer), * Else just abandon reading. */ if (pDeviceInfo->ReadStatus == STATUS_INSUFFICIENT_RESOURCES) { if (pDeviceInfo->nRetryRead++ < MAXIMUM_READ_RETRIES) { pDeviceInfo->usActions |= GDIAF_RETRYREAD; gnRetryReadInput++; } } else { pDeviceInfo->bFlags &= ~GDIF_READING; } #ifdef DIAGNOSE_IO pDeviceInfo->nReadsOutstanding--; #endif } END_REENTER_DEVICEINFOLISTCRIT(); } else { pDeviceInfo->nRetryRead = 0; } if (!gbRemoteSession && !NT_SUCCESS(pDeviceInfo->ReadStatus)) RIPMSG2(RIP_WARNING, "StartDeviceRead %#p failed Status %#x", pDeviceInfo, pDeviceInfo->ReadStatus); return NULL; } #ifdef GENERIC_INPUT /***************************************************************************\ * StopDeviceRead * * History: * XX-XX-00 Hiroyama created \***************************************************************************/ PDEVICEINFO StopDeviceRead( PDEVICEINFO pDeviceInfo) { IO_STATUS_BLOCK IoStatusBlock; TAGMSG1(DBGTAG_PNP, "StopDeviceRead(%p)", pDeviceInfo); CheckCritIn(); CheckDeviceInfoListCritIn(); UserAssert(pDeviceInfo->type == DEVICE_TYPE_HID); UserAssert(pDeviceInfo->handle); UserAssert(pDeviceInfo->OpenerProcess == PsGetCurrentProcessId()); /* * Stop reading this HID device. */ pDeviceInfo->bFlags &= ~GDIF_READING; ZwCancelIoFile(pDeviceInfo->handle, &IoStatusBlock); UserAssertMsg2(NT_SUCCESS(IoStatusBlock.Status), "NtCancelIoFile handle %x failed status %#x", pDeviceInfo->handle, IoStatusBlock.Status); CloseDevice(pDeviceInfo); return NULL; } #endif /***************************************************************************\ * IsKnownKeyboardType * * Checks if the given type/subtype is the known IDs * History: * XX-XX-00 Hiroyama created \***************************************************************************/ __inline BOOL IsKnownKeyboardType( DWORD dwType, DWORD dwSubType) { switch (dwType) { case 4: // Generic if ((BYTE)dwSubType == 0xff) { /* * Bogus subtype, most likely invalid Hydra device. */ return FALSE; } return TRUE; case 7: // Japanese case 8: // Korean return TRUE; default: break; } return FALSE; } /***************************************************************************\ * IsPS2Keyboard * * return TRUE for the PS/2 device name * XX-XX-00 Hiroyama created \***************************************************************************/ __inline BOOL IsPS2Keyboard( LPWSTR pwszDevice) { static const WCHAR wszPS2Header[] = L"\\??\\Root#*"; static const WCHAR wszPS2HeaderACPI[] = L"\\??\\ACPI#*"; return wcsncmp(pwszDevice, wszPS2Header, ARRAY_SIZE(wszPS2Header) - 1) == 0 || wcsncmp(pwszDevice, wszPS2HeaderACPI, ARRAY_SIZE(wszPS2HeaderACPI) - 1) == 0; } __inline BOOL IsRDPKeyboard( LPWSTR pwszDevice) { static const WCHAR wszRDPHeader[] = L"\\??\\Root#RDP"; return wcsncmp(pwszDevice, wszRDPHeader, ARRAY_SIZE(wszRDPHeader) - 1) == 0; } VOID ProcessDeviceChanges( DWORD DeviceType) { PDEVICEINFO pDeviceInfo; USHORT usOriginalActions; #if DBG volatile int nChanges = 0; ULONG timeStartReadPrev; #endif /* * Reset summary information for all Mice and Keyboards */ DWORD nMice = 0; DWORD nWheels = 0; DWORD nMaxButtons = 0; int nKeyboards = 0; BOOLEAN fKeyboardIdSet = FALSE; #ifdef GENERIC_INPUT int nHid = 0; #endif CheckCritIn(); BEGINATOMICCHECK(); UserAssert((PtiCurrentShared() == gptiRit) || (PtiCurrentShared() == gTermIO.ptiDesktop)); EnterDeviceInfoListCrit(); BEGINATOMICDEVICEINFOLISTCHECK(); #ifdef TRACK_PNP_NOTIFICATION if (gfRecordPnpNotification) { RecordPnpNotification(PNP_NTF_PROCESSDEVICECHANGES, NULL, DeviceType); } #endif if (DeviceType == DEVICE_TYPE_KEYBOARD) { /* * Set the fallback value. */ gKeyboardInfo = gKeyboardDefaultInfo; } /* * Look for devices to Create (those which have newly arrived) * and for devices to Terminate (these which have just departed) * and for device change notifications. * Make sure the actions are processed in the right order in case we * are being asked for more than one action per device: for example, * we sometimes get QueryRemove followed quickly by RemoveCancelled * and both actions arrive here together: we should do them in the * correct order. */ pDeviceInfo = gpDeviceInfoList; while (pDeviceInfo) { if (pDeviceInfo->type != DeviceType) { pDeviceInfo = pDeviceInfo->pNext; continue; } usOriginalActions = pDeviceInfo->usActions; UserAssert((usOriginalActions == 0) || (usOriginalActions & ~GDIAF_PNPWAITING)); /* * Refresh Mouse: * We read a MOUSE_ATTRIBUTES_CHANGED flag when a PS/2 mouse * is plugged back in. Find out the attributes of the device. */ if (pDeviceInfo->usActions & GDIAF_REFRESH_MOUSE) { pDeviceInfo->usActions &= ~GDIAF_REFRESH_MOUSE; UserAssert(pDeviceInfo->type == DEVICE_TYPE_MOUSE); #if DBG nChanges++; #endif TAGMSG1(DBGTAG_PNP, "QueryDeviceInfo: %lx", pDeviceInfo); QueryDeviceInfo(pDeviceInfo); } /* * QueryRemove: * Close the file object, but retain the DEVICEINFO struct and the * registration in case we later get a RemoveCancelled. */ if (pDeviceInfo->usActions & GDIAF_QUERYREMOVE) { pDeviceInfo->usActions &= ~GDIAF_QUERYREMOVE; #if DBG nChanges++; #endif TAGMSG1(DBGTAG_PNP, "QueryRemove: %lx", pDeviceInfo); CloseDevice(pDeviceInfo); } /* * New device arrived or RemoveCancelled: * If new device, Open it, register for notifications and start reading * If RemoveCancelled, unregister the old notfications first */ if (pDeviceInfo->usActions & (GDIAF_ARRIVED | GDIAF_REMOVECANCELLED)) { // Reopen the file object, (this is a new file object, of course), // Unregister for the old file, register with this new one. if (pDeviceInfo->usActions & GDIAF_REMOVECANCELLED) { pDeviceInfo->usActions &= ~GDIAF_REMOVECANCELLED; #if DBG nChanges++; #endif TAGMSG1(DBGTAG_PNP, "RemoveCancelled: %lx", pDeviceInfo); UnregisterForDeviceChangeNotifications(pDeviceInfo); } #if DBG if (pDeviceInfo->usActions & GDIAF_ARRIVED) { nChanges++; } #endif pDeviceInfo->usActions &= ~GDIAF_ARRIVED; if (OpenDevice(pDeviceInfo)) { PDEVICEINFO pDeviceInfoNext; if (!IsRemoteConnection()) { RegisterForDeviceChangeNotifications(pDeviceInfo); } #ifdef GENERIC_INPUT if (pDeviceInfo->type == DEVICE_TYPE_HID) { /* * If this device is not requested, close the device now. */ UserAssert(pDeviceInfo->handle); UserAssert(pDeviceInfo->hid.pTLCInfo); if (pDeviceInfo->handle && !HidTLCActive(pDeviceInfo->hid.pTLCInfo)) { StopDeviceRead(pDeviceInfo); // also closes the handle } } if (!((IsRemoteConnection()) && (pDeviceInfo->usActions & GDIAF_RECONNECT)) && pDeviceInfo->handle) { pDeviceInfoNext = StartDeviceRead(pDeviceInfo); if (pDeviceInfoNext) { /* * pDeviceInfo was freed, move onto the next */ pDeviceInfo = pDeviceInfoNext; continue; } } #else if (!((IsRemoteConnection()) && (pDeviceInfo->usActions & GDIAF_RECONNECT))) { pDeviceInfoNext = StartDeviceRead(pDeviceInfo); if (pDeviceInfoNext) { /* * pDeviceInfo wasa freed, move onto the next */ pDeviceInfo = pDeviceInfoNext; continue; } } #endif pDeviceInfo->usActions &= ~GDIAF_RECONNECT; } else { /* * If the Open failed, we free the device here, and move on to * the next device. * Assert to catch re-open failure upon RemoveCancelled. */ #if DBG if ((usOriginalActions & GDIAF_ARRIVED) == 0) { RIPMSG2(RIP_WARNING, "Re-Open %#p failed status %x during RemoveCancelled", pDeviceInfo, pDeviceInfo->OpenStatus); } #endif #ifdef GENERIC_INPUT if (pDeviceInfo->type == DEVICE_TYPE_HID) { /* * Some other applications may open this device * exclusively. We may succeed to open it later on, so * keep this deviceinfo around until it's physically * detached. */ RIPMSG1(RIP_WARNING, "ProcessDeviceChanges: failed to open the device %p", pDeviceInfo); } else { #endif pDeviceInfo = FreeDeviceInfo(pDeviceInfo); continue; #ifdef GENERIC_INPUT } #endif } } /* * RemoveComplete: * Close the file object, if you have not already done so, Unregister. * FreeDeviceInfo here (which will actually request a free from the * reader or the PnP requestor thread), and move on to the next device. */ if (pDeviceInfo->usActions & GDIAF_DEPARTED) { pDeviceInfo->usActions &= ~GDIAF_DEPARTED; #if DBG nChanges++; #endif TAGMSG1(DBGTAG_PNP, "RemoveComplete: %lx (process %#x)", pDeviceInfo); CloseDevice(pDeviceInfo); UnregisterForDeviceChangeNotifications(pDeviceInfo); pDeviceInfo = FreeDeviceInfo(pDeviceInfo); continue; } if (pDeviceInfo->usActions & GDIAF_IME_STATUS) { pDeviceInfo->usActions &= ~GDIAF_IME_STATUS; #if DBG nChanges++; #endif if ((pDeviceInfo->type == DEVICE_TYPE_KEYBOARD) && (pDeviceInfo->handle)) { if (FUJITSU_KBD_CONSOLE(pDeviceInfo->keyboard.Attr.KeyboardIdentifier) || (gbRemoteSession && FUJITSU_KBD_REMOTE(gRemoteClientKeyboardType)) ) { /* * Fill up the KEYBOARD_IME_STATUS structure. */ ZwDeviceIoControlFile(pDeviceInfo->handle, NULL, NULL, NULL, &giosbKbdControl, IOCTL_KEYBOARD_SET_IME_STATUS, (PVOID)&gKbdImeStatus, sizeof(gKbdImeStatus), NULL, 0); } } } if (pDeviceInfo->usActions & GDIAF_RETRYREAD) { PDEVICEINFO pDeviceInfoNext; pDeviceInfo->usActions &= ~GDIAF_RETRYREAD; UserAssert(pDeviceInfo->ReadStatus == STATUS_INSUFFICIENT_RESOURCES); #if DBG timeStartReadPrev = pDeviceInfo->timeStartRead; #endif TAGMSG2(DBGTAG_PNP, "Retry Read %#p after %lx ticks", pDeviceInfo, pDeviceInfo->timeStartRead - timeStartReadPrev); pDeviceInfoNext = StartDeviceRead(pDeviceInfo); if (pDeviceInfoNext) { /* * pDeviceInfo wasa freed, move onto the next */ pDeviceInfo = pDeviceInfoNext; continue; } } #ifdef GENERIC_INPUT if (pDeviceInfo->usActions & GDIAF_STARTREAD) { pDeviceInfo->usActions &= ~GDIAF_STARTREAD; #if DBG timeStartReadPrev = pDeviceInfo->timeStartRead; #endif TAGMSG1(DBGTAG_PNP, "Start Read %#p", pDeviceInfo); UserAssert(pDeviceInfo->handle == NULL); UserAssert(pDeviceInfo->type == DEVICE_TYPE_HID); UserAssert(HidTLCActive(pDeviceInfo->hid.pTLCInfo)); // a bit over active assert? if (!OpenDevice(pDeviceInfo)) { /* * Failed to open, perhaps some other applications * has opened this device exclusively. * We can't do nothing more than ignoring the failure. * Let's get going. */ RIPMSG1(RIP_WARNING, "ProcessDeviceChanges: STARTREAD failed to reopen the device %p", pDeviceInfo); } else { PDEVICEINFO pDeviceInfoNext; pDeviceInfoNext = StartDeviceRead(pDeviceInfo); if (pDeviceInfoNext) { /* * pDeviceInfo was freed, move onto the next */ pDeviceInfo = pDeviceInfoNext; continue; } } } if (pDeviceInfo->usActions & GDIAF_STOPREAD) { pDeviceInfo->usActions &= ~GDIAF_STOPREAD; UserAssert(pDeviceInfo->type == DEVICE_TYPE_HID); if (pDeviceInfo->handle) { PDEVICEINFO pDeviceInfoNext; /* * StopDeviceRead cancels pending I/O, * and closes the device handle, * but basically the deviceinfo itself keeps * alive. */ pDeviceInfoNext = StopDeviceRead(pDeviceInfo); if (pDeviceInfoNext) { /* * pDeviceInfo was freed, move onto the next */ pDeviceInfo = pDeviceInfoNext; } } else { RIPMSG1(RIP_WARNING, "ProcessDeviceChanges: STOPREAD, but handle is already NULL for %p", pDeviceInfo); } } #endif /* * Gather summary information on open devices */ if (pDeviceInfo->handle) { switch (pDeviceInfo->type) { case DEVICE_TYPE_MOUSE: UserAssert(PtiCurrentShared() == gTermIO.ptiDesktop); if (pDeviceInfo->usActions & GDIAF_REFRESH_MOUSE) { pDeviceInfo->usActions &= ~GDIAF_REFRESH_MOUSE; #if DBG nChanges++; #endif } nMice++; nMaxButtons = max(nMaxButtons, pDeviceInfo->mouse.Attr.NumberOfButtons); switch(pDeviceInfo->mouse.Attr.MouseIdentifier) { case WHEELMOUSE_I8042_HARDWARE: case WHEELMOUSE_SERIAL_HARDWARE: case WHEELMOUSE_HID_HARDWARE: nWheels++; } break; case DEVICE_TYPE_KEYBOARD: UserAssert(PtiCurrentShared() == gptiRit); // LEDStatus held in win32k.sys and later force the new keyboard // to be set accordingly. if (pDeviceInfo->ustrName.Buffer == NULL) { /* * This most likely is a bogus Hydra device. */ RIPMSG1(RIP_WARNING, "ProcessDeviceChanges: KBD pDevInfo=%p has no name!", pDeviceInfo); if (!fKeyboardIdSet) { /* * If keyboard id/attr is not set, try to get it from this device * anyway. If there are legit PS/2 devices after this, we'll get * a chance to re-aquire more meaningful id/attr. */ goto get_attr_anyway; } } else { NTSTATUS Status; if ((!fKeyboardIdSet || IsPS2Keyboard(pDeviceInfo->ustrName.Buffer)) && !IsRDPKeyboard(pDeviceInfo->ustrName.Buffer)) { get_attr_anyway: #if 0 /* * LATER: when other GI stuff in ntinput.c goes in, * move this boot-time LED and type/subtype initialization to * ntinput.c where the RIT is initialized. */ #ifdef DIAGNOSE_IO gKbdIoctlLEDSStatus = #endif Status = ZwDeviceIoControlFile(pDeviceInfo->handle, NULL, NULL, NULL, &giosbKbdControl, IOCTL_KEYBOARD_QUERY_INDICATORS, NULL, 0, (PVOID)&gklpBootTime, sizeof(gklpBootTime)); UserAssertMsg2(NT_SUCCESS(Status), "IOCTL_KEYBOARD_QUERY_INDICATORS failed: DeviceInfo %#x, Status %#x", pDeviceInfo, Status); TAGMSG1(DBGTAG_PNP, "ProcessDeviceChanges: led flag is %x", gklpBootTime.LedFlags); #else UNREFERENCED_PARAMETER(Status); #endif // 0 if (IsKnownKeyboardType(pDeviceInfo->keyboard.Attr.KeyboardIdentifier.Type, pDeviceInfo->keyboard.Attr.KeyboardIdentifier.Subtype)) { USHORT NumberOfFunctionKeysSave = gKeyboardInfo.NumberOfFunctionKeys; gKeyboardInfo = pDeviceInfo->keyboard.Attr; /* * Store the maximum number of function keys into gKeyboardInfo. */ if (NumberOfFunctionKeysSave > gKeyboardInfo.NumberOfFunctionKeys) { gKeyboardInfo.NumberOfFunctionKeys = NumberOfFunctionKeysSave; } } else { RIPMSG3(RIP_WARNING, "ProcessDeviceChanges: kbd pDevInfo %p has bogus type/subtype=%x/%x", pDeviceInfo, pDeviceInfo->keyboard.Attr.KeyboardIdentifier.Type, pDeviceInfo->keyboard.Attr.KeyboardIdentifier.Subtype); } if (pDeviceInfo->ustrName.Buffer) { /* * If this is a legit device, remember it so that we won't * try to get other non PS/2 keyboard id/attr. */ fKeyboardIdSet = TRUE; } } } nKeyboards++; break; #ifdef GENERIC_INPUT case DEVICE_TYPE_HID: ++nHid; break; #endif default: // Add code for a new type of input device here RIPMSG2(RIP_ERROR, "pDeviceInfo %#p has strange type %d", pDeviceInfo, pDeviceInfo->type); break; } } #ifdef GENERIC_INPUT else if (pDeviceInfo->type == DEVICE_TYPE_HID) { ++nHid; TAGMSG1(DBGTAG_PNP, "ProcessDeviceChanges: HID DeviceInfo %p", pDeviceInfo); } #endif /* * Notify the PnP thread that a change has been completed */ if (usOriginalActions & GDIAF_PNPWAITING) { KeSetEvent(pDeviceInfo->pkeHidChangeCompleted, EVENT_INCREMENT, FALSE); } pDeviceInfo = pDeviceInfo->pNext; } ENDATOMICDEVICEINFOLISTCHECK(); LeaveDeviceInfoListCrit(); switch (DeviceType) { case DEVICE_TYPE_MOUSE: /* * Apply summary information for Mice */ if (nMice) { if (gnMice == 0) { /* * We had no mouse before but we have one now: add a cursor */ SET_GTERMF(GTERMF_MOUSE); SYSMET(MOUSEPRESENT) = TRUE; SetGlobalCursorLevel(0); UserAssert(PpiFromProcess(gpepCSRSS)->ptiList->iCursorLevel == 0); UserAssert(PpiFromProcess(gpepCSRSS)->ptiList->pq->iCursorLevel == 0); GreMovePointer(gpDispInfo->hDev, gpsi->ptCursor.x, gpsi->ptCursor.y, MP_PROCEDURAL); } } else { if (gnMice != 0) { /* * We had a mouse before but we don't now: remove the cursor */ CLEAR_GTERMF(GTERMF_MOUSE); SYSMET(MOUSEPRESENT) = FALSE; SetGlobalCursorLevel(-1); /* * Don't leave mouse buttons stuck down, clear the global button * state here, otherwise weird stuff might happen. * Also do this in Alt-Tab processing and zzzCancelJournalling. */ #if DBG if (gwMouseOwnerButton) RIPMSG1(RIP_WARNING, "gwMouseOwnerButton=%x, being cleared forcibly\n", gwMouseOwnerButton); #endif gwMouseOwnerButton = 0; } } /* * Mouse button count represents the number of buttons on the mouse with * the most buttons. */ SYSMET(CMOUSEBUTTONS) = nMaxButtons; SYSMET(MOUSEWHEELPRESENT) = (nWheels > 0); gnMice = nMice; break; case DEVICE_TYPE_KEYBOARD: /* * Apply summary information for Keyboards */ if (nKeyboards > gnKeyboards) { /* * We have more keyboards, let set their LEDs properly */ UpdateKeyLights(FALSE); /* * The new keyboard arrived. Tell the RIT to set * the repeat rate. */ RequestKeyboardRateUpdate(); } if ((nKeyboards != 0) && (gnKeyboards == 0)) { /* * We had no keyboard but we have one now: set the system hotkeys. */ SetDebugHotKeys(); } gnKeyboards = nKeyboards; break; #ifdef GENERIC_INPUT case DEVICE_TYPE_HID: gnHid = nHid; break; #endif default: break; } ENDATOMICCHECK(); } /***************************************************************************\ * RequestDeviceChange() * * Flag the Device for the specified actions, then set its pkeHidChange to * trigger the RIT to perform the actions. * The current thread may not be able to do this if it is a PnP notification * from another process. * * History: * 01-20-99 IanJa Created. \***************************************************************************/ VOID RequestDeviceChange( PDEVICEINFO pDeviceInfo, USHORT usAction, BOOL fInDeviceInfoListCrit) { PDEVICE_TEMPLATE pDevTpl = &aDeviceTemplate[pDeviceInfo->type]; UserAssert(pDevTpl->pkeHidChange != NULL); UserAssert((usAction & GDIAF_FREEME) == 0); UserAssert((pDeviceInfo->usActions & GDIAF_PNPWAITING) == 0); #if DBG if (pDeviceInfo->usActions != 0) { TAGMSG3(DBGTAG_PNP, "RequestDeviceChange(%#p, %x), but action %x pending", pDeviceInfo, usAction, pDeviceInfo->usActions); } /* * We can't ask for synchronized actions to be performed on the Device List * if we are holding the Device List lock or the User Critical Section: * ProcessDeviceChanges() requires both of these itself. */ //if (usAction & GDIAF_PNPWAITING) { // CheckDeviceInfoListCritOut(); // CheckCritOut(); //} #endif TAGMSG2(DBGTAG_PNP, "RequestDeviceChange(%p, %x)", pDeviceInfo, usAction); /* * Grab the DeviceInfoList critical section if we don't already have it */ UserAssert(!fInDeviceInfoListCrit == !ExIsResourceAcquiredExclusiveLite(gpresDeviceInfoList)); #ifdef TRACK_PNP_NOTIFICATION if (gfRecordPnpNotification) { if (!fInDeviceInfoListCrit) { EnterDeviceInfoListCrit(); } RecordPnpNotification(PNP_NTF_REQUESTDEVICECHANGE, pDeviceInfo, usAction); if (!fInDeviceInfoListCrit) { LeaveDeviceInfoListCrit(); } } #endif #ifdef GENERIC_INPUT if (!fInDeviceInfoListCrit) { EnterDeviceInfoListCrit(); } CheckDeviceInfoListCritIn(); pDeviceInfo->usActions |= usAction; if ((pDeviceInfo->usActions & (GDIAF_STARTREAD | GDIAF_STOPREAD)) == (GDIAF_STARTREAD | GDIAF_STOPREAD)) { pDeviceInfo->usActions &= ~(GDIAF_STARTREAD | GDIAF_STOPREAD); } if (!fInDeviceInfoListCrit) { LeaveDeviceInfoListCrit(); } #else if (fInDeviceInfoListCrit) { CheckDeviceInfoListCritIn(); pDeviceInfo->usActions |= usAction; } else { EnterDeviceInfoListCrit(); pDeviceInfo->usActions |= usAction; LeaveDeviceInfoListCrit(); } #endif if (usAction & GDIAF_PNPWAITING) { CheckDeviceInfoListCritIn(); KeSetEvent(pDevTpl->pkeHidChange, EVENT_INCREMENT, FALSE); LeaveDeviceInfoListCrit(); KeWaitForSingleObject(pDeviceInfo->pkeHidChangeCompleted, WrUserRequest, KernelMode, FALSE, NULL); #ifdef GENERIC_INPUT BESURE_IN_USERCRIT(pDeviceInfo->usActions & GDIAF_FREEME); #endif EnterDeviceInfoListCrit(); /* * Assert that nothing else cleared GDIAF_PNPWAITING - only do it here. * Check that the action we were waiting for actually occurred. */ UserAssert(pDeviceInfo->usActions & GDIAF_PNPWAITING); pDeviceInfo->usActions &= ~GDIAF_PNPWAITING; UserAssert((pDeviceInfo->usActions & usAction) == 0); if (pDeviceInfo->usActions & GDIAF_FREEME) { FreeDeviceInfo(pDeviceInfo); } #ifdef GENERIC_INPUT LeaveDeviceInfoListCrit(); END_IN_USERCRIT(); EnterDeviceInfoListCrit(); #endif } else { KeSetEvent(pDevTpl->pkeHidChange, EVENT_INCREMENT, FALSE); } } /***************************************************************************\ * RemoveInputDevices() * * Used to detach input devices from a session. When disconnecting from a * session that owns the local input devices we need to release them so that * the new session that will take ownership of local console can use them * \***************************************************************************/ VOID RemoveInputDevices( VOID) { PDEVICEINFO pDeviceInfo; ULONG DeviceType; NTSTATUS Status; /* * First Thing is to remove device class notification. */ for (DeviceType = 0; DeviceType <= DEVICE_TYPE_MAX; DeviceType++) { if (aDeviceClassNotificationEntry[DeviceType] != NULL) { IoUnregisterPlugPlayNotification(aDeviceClassNotificationEntry[DeviceType]); aDeviceClassNotificationEntry[DeviceType] = NULL; } } /* * Then walk the device liste detaching mice and keyboads. */ EnterDeviceInfoListCrit(); PNP_SAFE_DEVICECRIT_IN(); pDeviceInfo = gpDeviceInfoList; while (pDeviceInfo) { #ifdef GENERIC_INPUT if (pDeviceInfo->usActions & (GDIAF_DEPARTED | GDIAF_FREEME)) { pDeviceInfo = pDeviceInfo->pNext; continue; } #else if ((pDeviceInfo->type != DEVICE_TYPE_KEYBOARD && pDeviceInfo->type != DEVICE_TYPE_MOUSE) || (pDeviceInfo->usActions & GDIAF_DEPARTED) || (pDeviceInfo->usActions & GDIAF_FREEME) ) { pDeviceInfo = pDeviceInfo->pNext; continue; } #endif #ifdef PRERELEASE pDeviceInfo->fForcedDetach = TRUE; #endif RequestDeviceChange(pDeviceInfo, GDIAF_DEPARTED, TRUE); pDeviceInfo = gpDeviceInfoList; } LeaveDeviceInfoListCrit(); } /***************************************************************************\ * AttachInputDevices * * Used to Attach input devices to a session. * \***************************************************************************/ BOOL AttachInputDevices( BOOL bLocalDevices) { UNICODE_STRING ustrName; BOOL fSuccess = TRUE; if (!bLocalDevices) { RtlInitUnicodeString(&ustrName, NULL); fSuccess &= !!CreateDeviceInfo(DEVICE_TYPE_MOUSE, &ustrName, 0); fSuccess &= !!CreateDeviceInfo(DEVICE_TYPE_KEYBOARD, &ustrName, 0); if (!fSuccess) { RIPMSG0(RIP_WARNING, "AttachInputDevices Failed the creation of input devices"); } } else { /* * For local devices just register Device class notification and let * PnP call us back. */ xxxRegisterForDeviceClassNotifications(); } return fSuccess; }