1350 lines
36 KiB
C
1350 lines
36 KiB
C
/*++
|
|
|
|
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 <string.h>
|
|
#include <profiles.h>
|
|
#include <wdmguid.h>
|
|
|
|
#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
|
|
|
|
|