/*++ Copyright (c) 1998 Microsoft Corporation Module Name: dock.c Abstract: Author: Kenneth D. Ray (kenray) Feb 1998 Revision History: --*/ #include "iop.h" #undef ExAllocatePool #undef ExAllocatePoolWithQuota #include "..\config\cmp.h" #include #include #include #if DBG #define ASSERT_SEMA_NOT_SIGNALLED(SemaphoreObject) \ ASSERT(KeReadStateSemaphore(SemaphoreObject) == 0) ; #else // DBG #define ASSERT_SEMA_NOT_SIGNALLED(SemaphoreObject) #endif // DBG // Internal functions to dockhwp.c NTSTATUS IopExecuteHardwareProfileChange( IN HARDWARE_PROFILE_BUS_TYPE Bus, IN PWCHAR * ProfileSerialNumbers, IN ULONG SerialNumbersCount, OUT PHANDLE NewProfile, OUT PBOOLEAN ProfileChanged ); NTSTATUS IopUpdateHardwareProfile ( OUT PBOOLEAN ProfileChanged ); VOID IopHardwareProfileSendCommit( VOID ); VOID IopHardwareProfileSendCancel( VOID ); // List of current dock devices, and the number of dockdevices. // Must hold IopDockDeviceListLock to change these values. LIST_ENTRY IopDockDeviceListHead; ULONG IopDockDeviceCount; FAST_MUTEX IopDockDeviceListLock; KSEMAPHORE IopProfileChangeSemaphore; BOOLEAN IopProfileChangeCancelRequired; LONG IopDocksInTransition; typedef struct { WORK_QUEUE_ITEM WorkItem; KEVENT NotificationCompleted ; LPGUID NotificationGuid ; NTSTATUS FinalStatus ; PPNP_VETO_TYPE VetoType ; PUNICODE_STRING VetoName ; } PROFILE_WORK_ITEM, *PPROFILE_WORK_ITEM ; #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, IopHardwareProfileBeginTransition) #pragma alloc_text(PAGE, IopHardwareProfileMarkDock) #pragma alloc_text(PAGE, IopHardwareProfileQueryChange) #pragma alloc_text(PAGE, IopHardwareProfileCommitStartedDock) #pragma alloc_text(PAGE, IopHardwareProfileCommitRemovedDock) #pragma alloc_text(PAGE, IopHardwareProfileCancelRemovedDock) #pragma alloc_text(PAGE, IopHardwareProfileCancelTransition) #define alloc_text(PAGE, IopHardwareProfileSendCommit) #pragma alloc_text(PAGE, IopHardwareProfileSendCancel) #endif // ALLOC_PRAGMA VOID IopHardwareProfileBeginTransition( IN BOOLEAN SubsumeExistingDeparture ) /*++ Routine Description: This routine must be called before any dock devnodes can be marked for transition (ie arriving or departing). After calling this function, IopHardwareProfileMarkDock should be called for each dock that is appearing or disappearing. Functionally, this code acquires the profile change semaphore. Future changes in the life of the added dock devnodes cause it to be released. Arguments: SubsumeExistingDeparture - Set if we are ejecting the parent of a device that is still in the process of ejecting... Return Value: None. --*/ { NTSTATUS status ; if (SubsumeExistingDeparture) { // We will already have queried in this case. Also, enumeration is // locked right now, so the appropriate devices found cannot disappear. // Assert everything is consistant. ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; ASSERT(IopDocksInTransition != 0) ; return ; } // Take the profile change semaphore. We do this whenever a dock is // in our list, even if no query is going to occur. status = KeWaitForSingleObject( &IopProfileChangeSemaphore, Executive, KernelMode, FALSE, NULL ); ASSERT(status == STATUS_SUCCESS) ; } VOID IopHardwareProfileMarkDock( PDEVICE_NODE DeviceNode, PROFILE_STATUS ChangeInPresence ) /*++ Routine Description: This routine is called to mark a dock as "in transition", ie it is either disappearing or appearing, the results of which determine our final hardware profile state. After all the docks that are transitioning have been passed into this function, IopHardwareProfileQueryChange is called. Arguments: DeviceNode - The dock devnode that is appearing or disappearing ChangeInPresence - Either DOCK_DEPARTING or DOCK_ARRIVING Return Value: Nope. --*/ { PWCHAR deviceSerialNumber; PDEVICE_OBJECT deviceObject; NTSTATUS status; // Verify we are under semaphore, we aren't marking the dock twice, and // our parameters are sensable. ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; ASSERT(DeviceNode->DockInfo.DockStatus == DOCK_QUIESCENT) ; ASSERT((ChangeInPresence == DOCK_DEPARTING)|| (ChangeInPresence == DOCK_ARRIVING)) ; if (ChangeInPresence == DOCK_ARRIVING) { // First, ensure this dock is a member of the dock list. // ADRIAO BUGBUG 11/12/98 - // We should move this into IopProcessNewDeviceNode. if (IsListEmpty(&DeviceNode->DockInfo.ListEntry)) { // Acquire the lock on the list of dock devices ExAcquireFastMutex(&IopDockDeviceListLock); // Add this element to the head of the list InsertHeadList(&IopDockDeviceListHead, &DeviceNode->DockInfo.ListEntry); IopDockDeviceCount++; // Release the lock on the list of dock devices ExReleaseFastMutex(&IopDockDeviceListLock); } // Retrieve the Serial Number from this dock device. We do this just // to test the BIOS today. Later we will be acquiring the information // to determine the profile we are *about* to enter. deviceObject = DeviceNode->PhysicalDeviceObject; status = IopQueryDeviceSerialNumber(deviceObject, &deviceSerialNumber); if (NT_SUCCESS(status) && (deviceSerialNumber != NULL)) { ExFreePool(deviceSerialNumber) ; } } else { // DOCK_DEPARTING case, we must be a member of the dock list... ASSERT(!IsListEmpty(&DeviceNode->DockInfo.ListEntry)) ; } InterlockedIncrement(&IopDocksInTransition) ; DeviceNode->DockInfo.DockStatus = ChangeInPresence ; } NTSTATUS IopHardwareProfileQueryChange( IN BOOLEAN SubsumingExistingDeparture, IN PROFILE_NOTIFICATION_TIME InPnpEvent, OUT PPNP_VETO_TYPE VetoType, OUT PUNICODE_STRING VetoName OPTIONAL ) /*++ Routine Description: This function queries drivers to see if it is OK to exit the current hardware profile and enter next one (as determined by which docks have been marked). One of three functions should be used subsequently to this call: IopHardwareProfileCommitStartedDock (call when a dock has successfully started) IopHardwareProfileCommitRemovedDock (call when a dock is no longer present in the system) IopHardwareProfileCancelTransition (call to abort a transition, say if a dock failed to start or a query returned failure for eject) Arguments: InPnpEvent - This argument indicates whether an operation is being done within the context of another PnpEvent or not. If not, we will queue such an event and block on it. If so, we cannot queue&block (we'd deadlock), so we do the query manually. VetoType - If this function returns false, this parameter will describe who failed the query profile change. The below optional parameter will contain the name of said vetoer. VetoName - This optional parameter will get the name of the vetoer (ie devinst, service name, application name, etc). If VetoName is supplied, the caller must free the buffer returned. Return Value: NTSTATUS. --*/ { PROFILE_WORK_ITEM profileWorkItem; NTSTATUS status; BOOLEAN arrivingDockFound; PLIST_ENTRY listEntry ; PDEVICE_NODE devNode ; ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; // Acquire the lock on the list of dock devices and determine whether any // dock devnodes are arriving. ExAcquireFastMutex(&IopDockDeviceListLock); ASSERT(IopDocksInTransition) ; arrivingDockFound = FALSE ; for (listEntry = IopDockDeviceListHead.Flink; listEntry != &(IopDockDeviceListHead); listEntry = listEntry->Flink ) { devNode = CONTAINING_RECORD(listEntry, DEVICE_NODE, DockInfo.ListEntry); ASSERT((devNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE)&& (devNode->DockInfo.DockStatus != DOCK_EJECTIRP_COMPLETED)) ; if (devNode->DockInfo.DockStatus == DOCK_ARRIVING) { arrivingDockFound = TRUE ; } } // Release the lock on the list of dock devices ExReleaseFastMutex(&IopDockDeviceListLock); if (SubsumingExistingDeparture) { ASSERT(IopProfileChangeCancelRequired) ; // We're nesting. Work off the last query, and don't requery. return STATUS_SUCCESS ; } if (arrivingDockFound) { // We currently don't actually query for hardware profile change on a // dock event as the user may have the lid closed. If we ever find a // piece of hardware that needs to be updated *prior* to actually // switching over, we will have to remove this bit of code. IopProfileChangeCancelRequired = FALSE ; return STATUS_SUCCESS ; } PIDBGMSG(PIDBG_HWPROFILE, ("NTOSKRNL: Sending HW profile change [query]\n")) ; status = IopRequestHwProfileChangeNotification( (LPGUID) &GUID_HWPROFILE_QUERY_CHANGE, InPnpEvent, VetoType, VetoName ); if (NT_SUCCESS(status)) { IopProfileChangeCancelRequired = TRUE ; } else { IopProfileChangeCancelRequired = FALSE ; } return status ; } VOID IopHardwareProfileCommitStartedDock( IN PDEVICE_NODE DeviceNode ) /*++ Routine Description: This routine adds the specified device from the list of current dock devices and requests a Hardware Profile change. Arguments: DeviceNode - Supplies a pointer to a device node which will be started and enumerated. Return Value: None. --*/ { NTSTATUS status; PDEVICE_OBJECT deviceObject; PWCHAR deviceSerialNumber; BOOLEAN profileChanged = FALSE ; ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; ASSERT(DeviceNode->DockInfo.DockStatus == DOCK_ARRIVING) ; ASSERT(!IsListEmpty(&DeviceNode->DockInfo.ListEntry)) ; DeviceNode->DockInfo.DockStatus = DOCK_QUIESCENT ; InterlockedDecrement(&IopDocksInTransition) ; // We only add one dock at a time. So this should have been the last! ASSERT(!IopDocksInTransition) ; // Retrieve the Serial Number from this dock device if (DeviceNode->DockInfo.SerialNumber == NULL) { deviceObject = DeviceNode->PhysicalDeviceObject; status = IopQueryDeviceSerialNumber(deviceObject, &deviceSerialNumber); DeviceNode->DockInfo.SerialNumber = deviceSerialNumber; // Update the current Hardware Profile after successfully starting this // device. This routine does two things for us: // 1) It determines whether the profile actually changed and updates // the global flag IopProfileChangeOccured appropriately. // 2) If the profile changed, this routine updates the registry, but // does *not* broadcast the profile change around. status = IopUpdateHardwareProfile(&profileChanged); if (!NT_SUCCESS(status)) { PIDBGMSG( PIDBG_HWPROFILE, ("IopUpdateHardwareProfile failed with status == %lx\n", status) ) ; } } else { // Couldn't get Serial Number for this dock device, or serial number was NULL status = STATUS_UNSUCCESSFUL; } if (NT_SUCCESS(status) && profileChanged) { IopHardwareProfileSendCommit() ; IopProcessNewProfile(); } else if (IopProfileChangeCancelRequired) { IopHardwareProfileSendCancel() ; } KeReleaseSemaphore(&IopProfileChangeSemaphore, IO_NO_INCREMENT, 1, FALSE); } VOID IopHardwareProfileCommitRemovedDock( IN PDEVICE_NODE DeviceNode ) /*++ Routine Description: This routine removes the specified device from the list of current dock devices and requests a Hardware Profile change. Arguments: DeviceNode - Supplies a pointer to a device node which has been listed as missing in a previous enumeration, has had the final remove IRP sent to it, and is about to be deleted. Return Value: None. --*/ { NTSTATUS status; BOOLEAN profileChanged ; LONG remainingDockCount ; // Acquire the lock on the list of dock devices ExAcquireFastMutex(&IopDockDeviceListLock); // Since we are about to remove this dock device from the list of // all dock devices present, the list should not be empty. ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; ASSERT((DeviceNode->DockInfo.DockStatus == DOCK_DEPARTING)|| (DeviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED)) ; ASSERT(!IsListEmpty(&DeviceNode->DockInfo.ListEntry)) ; // Remove the current devnode from the list of docks RemoveEntryList(&DeviceNode->DockInfo.ListEntry); InitializeListHead(&DeviceNode->DockInfo.ListEntry); if (DeviceNode->DockInfo.SerialNumber) { ExFreePool(DeviceNode->DockInfo.SerialNumber); DeviceNode->DockInfo.SerialNumber = NULL; } IopDockDeviceCount--; DeviceNode->DockInfo.DockStatus = DOCK_QUIESCENT ; remainingDockCount = InterlockedDecrement(&IopDocksInTransition) ; ASSERT(remainingDockCount >= 0) ; // Release the lock on the list of dock devices ExReleaseFastMutex(&IopDockDeviceListLock); if (remainingDockCount) { return ; } // Update the current Hardware Profile after removing this device. status = IopUpdateHardwareProfile(&profileChanged); if (!NT_SUCCESS(status)) { // So we're there physically, but not mentally? Too bad, where broadcasting // change either way. PIDBGMSG( PIDBG_HWPROFILE, ("IopUpdateHardwareProfile failed with status == %lx\n", status) ) ; ASSERT(NT_SUCCESS(status)) ; } if (NT_SUCCESS(status) && profileChanged) { IopHardwareProfileSendCommit() ; IopProcessNewProfile(); } else { ASSERT(IopProfileChangeCancelRequired) ; IopHardwareProfileSendCancel() ; } KeReleaseSemaphore(&IopProfileChangeSemaphore, IO_NO_INCREMENT, 1, FALSE); } VOID IopHardwareProfileCancelRemovedDock( IN PDEVICE_NODE DeviceNode ) /*++ Routine Description: This routine is called when a dock that was marked to disappear didn't (ie, after the eject, the dock device still enumerated). We remove it from the transition list and complete/cancel the HW profile change as appropriate. See IopHardwareProfileSetMarkedDocksEjected. Arguments: DeviceNode - Supplies a pointer to a device node which will be started and enumerated. Return Value: None. --*/ { NTSTATUS status; BOOLEAN profileChanged ; LONG remainingDockCount ; // Acquire the lock on the list of dock devices ExAcquireFastMutex(&IopDockDeviceListLock); // Since we are about to remove this dock device from the list of // all dock devices present, the list should not be empty. ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; ASSERT(DeviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED) ; ASSERT(!IsListEmpty(&DeviceNode->DockInfo.ListEntry)) ; DeviceNode->DockInfo.DockStatus = DOCK_QUIESCENT ; remainingDockCount = InterlockedDecrement(&IopDocksInTransition) ; ASSERT(remainingDockCount >= 0) ; // Release the lock on the list of dock devices ExReleaseFastMutex(&IopDockDeviceListLock); if (remainingDockCount) { return ; } // Update the current Hardware Profile after removing this device. status = IopUpdateHardwareProfile(&profileChanged); if (!NT_SUCCESS(status)) { // So we're there physically, but not mentally? Too bad, where broadcasting // change either way. PIDBGMSG( PIDBG_HWPROFILE, ("IopUpdateHardwareProfile failed with status == %lx\n", status) ) ; ASSERT(NT_SUCCESS(status)) ; } if (NT_SUCCESS(status) && profileChanged) { IopHardwareProfileSendCommit() ; IopProcessNewProfile(); } else { ASSERT(IopProfileChangeCancelRequired) ; IopHardwareProfileSendCancel() ; } KeReleaseSemaphore(&IopProfileChangeSemaphore, IO_NO_INCREMENT, 1, FALSE); } VOID IopHardwareProfileCancelTransition(VOID) /*++ Routine Description: This routine unmarks any marked devnodes (ie, sets them to no change, appearing or disappearing), and sends the CancelQueryProfileChange as appropriate. Once called, other profile changes can occur. Arguments: None. Return Value: Nodda. --*/ { PLIST_ENTRY listEntry; PDEVICE_NODE devNode; ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; // Acquire the lock on the list of dock devices ExAcquireFastMutex(&IopDockDeviceListLock); for (listEntry = IopDockDeviceListHead.Flink; listEntry != &(IopDockDeviceListHead); listEntry = listEntry->Flink ) { devNode = CONTAINING_RECORD(listEntry, DEVICE_NODE, DockInfo.ListEntry); ASSERT((devNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE)&& (devNode->DockInfo.DockStatus != DOCK_EJECTIRP_COMPLETED)) ; if (devNode->DockInfo.DockStatus != DOCK_QUIESCENT) { InterlockedDecrement(&IopDocksInTransition) ; devNode->DockInfo.DockStatus = DOCK_QUIESCENT ; } } ASSERT(!IopDocksInTransition) ; ExReleaseFastMutex(&IopDockDeviceListLock);// Release the lock on the list of dock devices if (IopProfileChangeCancelRequired) { IopHardwareProfileSendCancel() ; } // No need to broadcast the cancels here, as IopQueryHardwareProfileChange // will have taken care of the cancels for us. KeReleaseSemaphore(&IopProfileChangeSemaphore, IO_NO_INCREMENT, 1, FALSE); } VOID IopHardwareProfileSetMarkedDocksEjected(VOID) /*++ Routine Description: This routine moves any departing devnodes to the ejected state. If any subsequent enumeration lists the device as present, we know the eject failed and we appropriately cancel that piece of the profile change. IopHardwareProfileCancelRemovedDock can only be called after this function is called. Arguments: None. Return Value: Nodda. --*/ { PLIST_ENTRY listEntry; PDEVICE_NODE devNode; ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; // Acquire the lock on the list of dock devices ExAcquireFastMutex(&IopDockDeviceListLock); for (listEntry = IopDockDeviceListHead.Flink; listEntry != &(IopDockDeviceListHead); listEntry = listEntry->Flink ) { devNode = CONTAINING_RECORD(listEntry, DEVICE_NODE, DockInfo.ListEntry); ASSERT((devNode->DockInfo.DockStatus == DOCK_QUIESCENT)|| (devNode->DockInfo.DockStatus == DOCK_DEPARTING)) ; if (devNode->DockInfo.DockStatus != DOCK_QUIESCENT) { devNode->DockInfo.DockStatus = DOCK_EJECTIRP_COMPLETED ; } } // Release the lock on the list of dock devices ExReleaseFastMutex(&IopDockDeviceListLock); } VOID IopHardwareProfileSendCommit( VOID ) /*++ Routine Description: This routine (internal to dockhwp.c) simply sends the change complete message. We do not wait for this, as it is asynchronous... Arguments: None. Return Value: Nodda. --*/ { ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; PIDBGMSG(PIDBG_HWPROFILE, ("NTOSKRNL: Sending HW profile change [commit]\n")) ; IopRequestHwProfileChangeNotification( (LPGUID) &GUID_HWPROFILE_CHANGE_COMPLETE, PROFILE_PERHAPS_IN_PNPEVENT, NULL, NULL ); } VOID IopHardwareProfileSendCancel( VOID ) /*++ Routine Description: This routine (internal to dockhwp.c) simply sends the cancel. Arguments: None. Return Value: Nodda. --*/ { PROFILE_WORK_ITEM profileWorkItem; PNP_VETO_TYPE vetoType; NTSTATUS status; ASSERT_SEMA_NOT_SIGNALLED(&IopProfileChangeSemaphore) ; PIDBGMSG(PIDBG_HWPROFILE, ("NTOSKRNL: Sending HW profile change [cancel]\n")) ; IopRequestHwProfileChangeNotification( (LPGUID) &GUID_HWPROFILE_CHANGE_CANCELLED, PROFILE_PERHAPS_IN_PNPEVENT, NULL, NULL ) ; } NTSTATUS IopUpdateHardwareProfile ( OUT PBOOLEAN ProfileChanged ) /*++ Routine Description: This routine scans the list of current dock devices, builds a list of serial numbers from those devices, and calls for the Hardware Profile to be changed, based on that list. Arguments: ProfileChanged - Supplies a variable to receive TRUE if the current hardware profile changes as a result of calling this routine. Return Value: NTSTATUS code. --*/ { NTSTATUS status = STATUS_SUCCESS; PLIST_ENTRY listEntry; PDEVICE_NODE devNode; PWCHAR *profileSerialNumbers, *p; HANDLE hProfileKey=NULL; ULONG len, numProfiles; HANDLE hCurrent, hIDConfigDB; UNICODE_STRING unicodeName; // Acquire the lock on the list of dock devices ExAcquireFastMutex(&IopDockDeviceListLock); // Update the flag for Ejectable Docks RtlInitUnicodeString(&unicodeName, CM_HARDWARE_PROFILE_STR_DATABASE); if(NT_SUCCESS(IopOpenRegistryKey(&hIDConfigDB, NULL, &unicodeName, KEY_READ, FALSE) )) { RtlInitUnicodeString(&unicodeName, CM_HARDWARE_PROFILE_STR_CURRENT_DOCK_INFO); if(NT_SUCCESS(IopOpenRegistryKey(&hCurrent, hIDConfigDB, &unicodeName, KEY_READ | KEY_WRITE, FALSE) )) { RtlInitUnicodeString(&unicodeName, REGSTR_VAL_EJECTABLE_DOCKS); ZwSetValueKey(hCurrent, &unicodeName, 0, REG_DWORD, &IopDockDeviceCount, sizeof(IopDockDeviceCount)); ZwClose(hCurrent); } ZwClose(hIDConfigDB); } if (IopDockDeviceCount == 0) { // if there are no dock devices, the list should // contain a single null entry, in addition to the null // termination. numProfiles = 1; ASSERT(IsListEmpty(&IopDockDeviceListHead)); } else { numProfiles = IopDockDeviceCount; ASSERT(!IsListEmpty(&IopDockDeviceListHead)); } // Allocate space for a null-terminated list of SerialNumber lists. len = (numProfiles+1)*sizeof(PWCHAR); profileSerialNumbers = ExAllocatePool(NonPagedPool, len); if (profileSerialNumbers) { p = profileSerialNumbers; // Create the list of Serial Numbers for (listEntry = IopDockDeviceListHead.Flink; listEntry != &(IopDockDeviceListHead); listEntry = listEntry->Flink ) { devNode = CONTAINING_RECORD(listEntry, DEVICE_NODE, DockInfo.ListEntry); // ADRIAO BUGBUG 11/11/98 - // Is everything in the quiescent state here? ASSERT(devNode->DockInfo.DockStatus != DOCK_NOTDOCKDEVICE) ; if (devNode->DockInfo.SerialNumber) { *p = devNode->DockInfo.SerialNumber; p++; } } ExReleaseFastMutex(&IopDockDeviceListLock); if (p == profileSerialNumbers) { // Set a single list entry to NULL if we look to be in an "undocked" // profile *p = NULL; p++; } // Null-terminate the list *p = NULL; numProfiles = (ULONG)(p - profileSerialNumbers); // Change the current Hardware Profile based on the new Dock State // and perform notification that the Hardware Profile has changed status = IopExecuteHardwareProfileChange(HardwareProfileBusTypeACPI, profileSerialNumbers, numProfiles, &hProfileKey, ProfileChanged); if (hProfileKey) { ZwClose(hProfileKey); } ExFreePool (profileSerialNumbers); } else { ExReleaseFastMutex(&IopDockDeviceListLock); status = STATUS_INSUFFICIENT_RESOURCES; } return status; } NTSTATUS IopExecuteHwpDefaultSelect ( IN PCM_HARDWARE_PROFILE_LIST ProfileList, OUT PULONG ProfileIndexToUse, IN PVOID Context ) { UNREFERENCED_PARAMETER (Context); * ProfileIndexToUse = 0; return STATUS_SUCCESS; } NTSTATUS IopExecuteHardwareProfileChange( IN HARDWARE_PROFILE_BUS_TYPE Bus, IN PWCHAR * ProfileSerialNumbers, IN ULONG SerialNumbersCount, OUT PHANDLE NewProfile, OUT PBOOLEAN ProfileChanged ) /*++ Routine Description: A docking event has occured and now, given a list of Profile Serial Numbers that describe the new docking state: Transition to the given docking state. Set the Current Hardware Profile to based on the new state. (Possibly Prompt the user if there is ambiguity) Send Removes to those devices that are turned off in this profile, Arguments: Bus - This is the bus that is supplying the hardware profile change (currently only HardwareProfileBusTypeAcpi is supported). ProfileSerialNumbers - A list of serial numbers (a list of null terminated UCHAR lists) representing this new docking state. These can be listed in any order, and form a complete representation of the new docking state caused by a docking even on the given bus. A Serial Number string of "\0" represents an "undocked state" and should not be listed with any other strings. This list need not be sorted. SerialNumbersCount - The number of serial numbers listed. NewProfile - a handle to the registry key representing the new hardware profile (IE \CCS\HardwareProfiles\Current".) ProfileChanged - set to TRUE if new current profile (as a result of this docking event, is different that then old current profile. --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG len; ULONG tmplen; ULONG i, j; PWCHAR tmpStr; UNICODE_STRING tmpUStr; PUNICODE_STRING sortedSerials = NULL; PPROFILE_ACPI_DOCKING_STATE dockState = NULL; PIDBGMSG( PIDBG_HWPROFILE, ("Execute Profile (BusType %x), (SerialNumCount %x)\n", Bus, SerialNumbersCount) ) ; // Sort the list of serial numbers len = sizeof (UNICODE_STRING) * SerialNumbersCount; sortedSerials = ExAllocatePool (NonPagedPool, len); if (NULL == sortedSerials) { status = STATUS_INSUFFICIENT_RESOURCES; goto Clean; } for (i = 0; i < SerialNumbersCount; i++) { RtlInitUnicodeString (&sortedSerials[i], ProfileSerialNumbers[i]); } // I do not anticipate getting more than a few serial numbers, and I am // just lasy enough to write this comment and use a buble sort. for (i = 0; i < SerialNumbersCount; i++) { for (j = 0; j < SerialNumbersCount - 1; j++) { if (0 < RtlCompareUnicodeString (&sortedSerials[j], &sortedSerials[j+1], FALSE)) { tmpUStr = sortedSerials[j]; sortedSerials[j] = sortedSerials[j+1]; sortedSerials[j+1] = tmpUStr; } } } // Construct the DockState ID len = 0; for (i = 0; i < SerialNumbersCount; i++) { len += sortedSerials[i].Length; } len += sizeof (WCHAR); // NULL termination; dockState = (PPROFILE_ACPI_DOCKING_STATE)ExAllocatePool (NonPagedPool, len + sizeof (PROFILE_ACPI_DOCKING_STATE)); // BUGBUG wasted WCHAR here. Oh well. if (NULL == dockState) { status = STATUS_INSUFFICIENT_RESOURCES; goto Clean; } for (i = 0, tmpStr = dockState->SerialNumber, tmplen = 0; i < SerialNumbersCount; i++) { tmplen = sortedSerials[i].Length; ASSERT (tmplen <= len - ((PCHAR)tmpStr - (PCHAR)dockState->SerialNumber)); RtlCopyMemory (tmpStr, sortedSerials[i].Buffer, tmplen); (PCHAR) tmpStr += tmplen; } *(tmpStr++) = L'\0'; ASSERT (len == (ULONG) ((PCHAR) tmpStr - (PCHAR) dockState->SerialNumber)); dockState->SerialLength = (USHORT) len; if ((SerialNumbersCount > 1) || (L'\0' != dockState->SerialNumber[0])) { dockState->DockingState = HW_PROFILE_DOCKSTATE_DOCKED; } else { dockState->DockingState = HW_PROFILE_DOCKSTATE_UNDOCKED; } // Set the new Profile switch (Bus) { case HardwareProfileBusTypeACPI: status = CmSetAcpiHwProfile (dockState, IopExecuteHwpDefaultSelect, NULL, NewProfile, ProfileChanged); ASSERT(NT_SUCCESS(status) || (!(*ProfileChanged))) ; break; default: *ProfileChanged = FALSE ; status = STATUS_NOT_SUPPORTED; goto Clean; } Clean: if (NULL != sortedSerials) { ExFreePool (sortedSerials); } if (NULL != dockState) { ExFreePool (dockState); } return status; } // Beyond here lie commented out code. This code is a sketch of what we will // use when we do two-step profile changes (ie, using the serial numbers figure // out what profile we are *about* to enter, and return a set of all existing // profiles that we might transition into. Today this code is not used as not // all BIOS's can tell us what profile we are entering prior to being there. #if 0 struct _IOP_NEW_ACPI_STATE { ULONG Blah; PCM_HARDWARE_PROFILE_LIST ProfileList; } IOP_NEW_ACPI_STATE, *PIOP_NEW_ACPI_STATE; NTSTATUS IopProfileAcquisition ( IN PCM_HARDWARE_PROFILE_LIST ProfileList, OUT PULONG ProfileIndexToUse, IN PIOP_PROFILE_LIST NewAcpiState ) /*++ Routine Description --*/ { PCM_HARDWARE_PROFILE_LIST tmpList; NTSTATUS status = STATUS_SUCCESS; ULONG i; ULONG k; ULONG oldSize; ULONG newSize; BOOLEAN found; // We need to add the entries from ProfileList to our new acpi state // Use the most inefficient manner possible to see if we already have // this profile. Hopefully there will not be that many profiles around. ASSERT (ProfileList->CurrentProfileCount <= ProfileList->MaxProfileCount); for (i = 0; i < ProfileList->CurrentProfileCount; i++) { found = FALSE; for (k = 0; k < NewAcpiState->ProfileList->CurrentProfileCount; k++) { if (ProfileList->Profile[i].Id == NewAcpiState->ProfileList->Profile[k]) { found = TRUE; break; } } if (!found) { // Add it if (NewAcpiState->MaxProfileCount <= NewAcpiState->CurrentProfileCount) { tmpList = NewAcpiState->ProfileList; oldSize = sizeof (CM_HARDWARE_PROFILE_LIST) + (sizeof (CM_HARDWARE_PROFILE) * (tmpList->MaxProfileCount - 1)); newSize = tmpList->MaxProfileCount * 2; newSize = sizeof (CM_HARDWARE_PROFILE_LIST) + (sizeof (CM_HAREWARE_PROFILE) * (newSize - 1)); tmpList = ExAllocatePool (PagedPool, newSize); if (NULL == tmpList) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory (tmpList, NewAcpiState->ProfileList, oldSize); // NB current count and Max count copied over as well tmpList->MaxProfileCount *= 2; ExFreePool (NewAcpiState->ProfileList); NewAcpiState->ProfileList = tmpList; } k = NewAcpiState->ProfileList->CurrentProfileCount++; NewAcpiState->ProfileList->Profile[k] = ProfileList->Profile[i]. } } *ProfileIndexToUse = -1; // Don't select anything yet. return status; } NTSTATUS IopFindPossibleProfiles ( IN PPROFILE_ACPI_DOCKING_STATE * AcpiProfiles, IN ULONG PossibleDockingStates, OUT PCM_HARDWARE_PROFILE_LIST * ProfileList ) /*++ Routine Description: Based on the cononical list of possible acpi docking states, determine the cononical list of Hardware profiles that might result. Arguments: AcpiProfiles - A list of pointers to AcpiProfiles that could be achieved in the next docking state. PossibleDockingStates - The total number of AcpiProfiles. ProfileList - the cononical list of hardware profiles that could result from the given list of acpi docking profiles. --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG i; IOP_PROFILE_CONTEXT context; PROFILE_ACPI_DOCKING_STATE acpiDockState; context->ProfileList = ExAllocatePool (NonPagedPool, sizeof (PCM_HARDWARE_PROFILE_LIST)); if (NULL == context->ProfileList) { status = STATUS_INSUFICIENT_RESOURCES; goto Clean; } context->ProfileList->MaxProfileCount = 1; context->ProfileList->CurrentProfileCount = 0; for (i = 0; i < PossibleDockingStates; i++) { status = CmSetAcpiHwProfile (AcpiProfiles[i], IopProfileAcquisition, &context); if (STATUS_MORE_PROCESSING_REQUIRED == status) { status = STATUS_SUCCESS; } if (!NT_SUCCESS (status)) { goto Clean; } } status = STATUS_SUCCESS *ProfileList = context->ProfileList; Clean: if (NULL != context->ProfileList) { ExFreePool (context->ProfileList); } if (!NT_SUCCESS(status)) { if (possibleProfileList) { ExFreePool (possibleProfileList); } } return status; } typedef struct _IOP_ACPI_DOCKING_PROFILE_INFO { ULONG Blah; } IOP_ACPI_DOCKING_PROFILE_INFO, * PIOP_ACPI_DOCKING_PROFILE_INFO; NTSTATUS IopAcpiQueryDockingProfileEvent ( IN PUCHAR * AcpiDockSerialNumbers, IN ULONG PossibleSerialNumbers, IN PFOOBAR DockRelations, OUT PIOP_ACPI_DOCKING_PROFILE_INFO * AcpiDockInfo, ) /*++ Routine Description: Given a list of ACPI serial numbers that might appear once an ACPI docking event has completed, determine the cononical list of hardware profiles that could result from the docking serial numbers provided. Query remove all devices listed as disabled across all of these profiles. If all queries succeed, then remove all devices. If not cancel the queries. Return success iff all query removes completed successfully. Otherwise fail. Do not actually cause the remove of these devices. Arguments AcpiDockSerialNumbers - a list of serial numbers for the possible docks that may be attached for this acpi docking event. PossibleSerialNumbers - number of elements in that list. BUGBUG DockRelations - the list of (help me out here I don't really know) which may be removed when this docking state is executed. BUGBUG AcpiDockInfo - a context parameter that needs to be passed to IopAcpiExecuteDockingProfile. --*/ { NTSTATUS status = STATUS_SUCCESS; return status; } #endif