summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/acd
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/tdi/acd
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/tdi/acd')
-rw-r--r--private/ntos/tdi/acd/acddefs.h100
-rw-r--r--private/ntos/tdi/acd/api.c1556
-rw-r--r--private/ntos/tdi/acd/debug.h74
-rw-r--r--private/ntos/tdi/acd/makefile6
-rw-r--r--private/ntos/tdi/acd/mem.c289
-rw-r--r--private/ntos/tdi/acd/mem.h41
-rw-r--r--private/ntos/tdi/acd/ntdisp.c411
-rw-r--r--private/ntos/tdi/acd/ntinit.c223
-rw-r--r--private/ntos/tdi/acd/rasacd.rc12
-rw-r--r--private/ntos/tdi/acd/request.c303
-rw-r--r--private/ntos/tdi/acd/request.h31
-rw-r--r--private/ntos/tdi/acd/sources46
-rw-r--r--private/ntos/tdi/acd/table.c286
-rw-r--r--private/ntos/tdi/acd/table.h77
-rw-r--r--private/ntos/tdi/acd/timer.c145
15 files changed, 3600 insertions, 0 deletions
diff --git a/private/ntos/tdi/acd/acddefs.h b/private/ntos/tdi/acd/acddefs.h
new file mode 100644
index 000000000..0563e9926
--- /dev/null
+++ b/private/ntos/tdi/acd/acddefs.h
@@ -0,0 +1,100 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ acddefs.h
+
+Abstract:
+
+ Shared internal structure defintions for the Implicit
+ Connection Driver (acd.sys).
+
+Author:
+
+ Anthony Discolo (adiscolo) 23-Jun-1995
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#ifndef _ACDDEFS_
+#define _ACDDEFS_
+
+//
+// Min macro
+//
+#define MIN(a, b) (a) < (b) ? (a) : (b)
+
+//
+// List entry structure for the AcdCompletionQueue.
+//
+typedef struct _ACD_COMPLETION {
+ LIST_ENTRY ListEntry;
+ ULONG ulDriverId; // transport driver id
+ BOOLEAN fCanceled; // TRUE if request was canceled
+ BOOLEAN fCompleted; // TRUE if request was completed
+ ACD_NOTIFICATION notif;
+ ACD_CONNECT_CALLBACK pProc; // callback proc
+ USHORT nArgs; // argument count
+ PVOID pArgs[1]; // variable length arguments
+} ACD_COMPLETION, *PACD_COMPLETION;
+
+//
+// A connection block.
+//
+// For each pending connection, there is a
+// connection block that describes the current
+// state.
+//
+typedef struct _ACD_CONNECTION {
+ LIST_ENTRY ListEntry; // connection list
+ BOOLEAN fNotif; // TRUE if service has been notified
+ BOOLEAN fProgressPing; // TRUE if service has pinged
+ BOOLEAN fCompleting; // TRUE if in AcdSignalCompletionCommon
+ ULONG ulTimerCalls; // # of total pings
+ ULONG ulMissedPings; // # of missed pings
+ LIST_ENTRY CompletionList; // completion list
+} ACD_CONNECTION, *PACD_CONNECTION;
+
+//
+// Generic hash table entry.
+//
+typedef struct _HASH_ENTRY {
+ LIST_ENTRY ListEntry;
+ ACD_ADDR szKey;
+ ULONG ulData;
+} HASH_ENTRY, *PHASH_ENTRY;
+
+extern KSPIN_LOCK AcdSpinLockG;
+
+extern KEVENT AcdRequestThreadEventG;
+
+extern LIST_ENTRY AcdNotificationQueueG;
+extern LIST_ENTRY AcdCompletionQueueG;
+extern LIST_ENTRY AcdConnectionQueueG;
+extern LIST_ENTRY AcdDriverListG;
+
+extern BOOLEAN fConnectionInProgressG;
+extern BOOLEAN fProgressPingG;
+extern ULONG nTimerCallsG;
+extern ULONG nMissedPingsG;
+
+extern PDEVICE_OBJECT pAcdDeviceObjectG;
+
+extern ACD_ADDR szComputerName;
+
+//
+// Miscellaneous routines.
+//
+VOID
+AcdPrintAddress(
+ IN PACD_ADDR pAddr
+ );
+
+#endif // _ACDDEFS_
diff --git a/private/ntos/tdi/acd/api.c b/private/ntos/tdi/acd/api.c
new file mode 100644
index 000000000..4ad13b54f
--- /dev/null
+++ b/private/ntos/tdi/acd/api.c
@@ -0,0 +1,1556 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ api.c
+
+Abstract:
+
+ Exported routines to transports for automatic connection
+ management.
+
+Author:
+
+ Anthony Discolo (adiscolo) 17-Apr-1995
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#include <ntifs.h>
+#include <cxport.h>
+#include <tdi.h>
+#include <tdikrnl.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <acd.h>
+
+#include "acdapi.h"
+#include "acddefs.h"
+#include "request.h"
+#include "mem.h"
+#include "debug.h"
+
+//
+// Driver enabled mode. The automatic
+// connection system service sets
+// this depending on whether a user
+// has logged in, and whether there's
+// general network connectivity.
+//
+BOOLEAN fAcdEnabledG;
+
+//
+// Spin lock for this module.
+//
+KSPIN_LOCK AcdSpinLockG;
+
+//
+// Event signaled when the AcdNotificationRequestThread
+// thread has a notification to process.
+//
+KEVENT AcdRequestThreadEventG;
+
+//
+// This is a list of one irp representing
+// a user-space process waiting to create a
+// new network connection given an address.
+//
+LIST_ENTRY AcdNotificationQueueG;
+
+//
+// This is a list of ACD_CONNECTION blocks representing
+// requests from transports about unsuccessful connection
+// attempts. There may be multiple ACD_COMPLETION block
+// linked onto the same ACD_CONNECTION, grouped by
+// address.
+//
+LIST_ENTRY AcdConnectionQueueG;
+
+//
+// This is a list of ACD_COMPLETION blocks representing
+// other requests from transports.
+//
+LIST_ENTRY AcdCompletionQueueG;
+
+//
+// The list of drivers that have binded
+// with us.
+//
+LIST_ENTRY AcdDriverListG;
+
+//
+// Statistics
+//
+typedef struct _ACD_STATS {
+ ULONG ulConnects; // connection attempts
+ ULONG ulCancels; // connection cancels
+} ACD_STATS;
+ACD_STATS AcdStatsG[ACD_ADDR_MAX];
+
+//
+// Forward declarations
+//
+VOID
+AcdPrintAddress(
+ IN PACD_ADDR pAddr
+ );
+
+VOID
+ClearRequests(
+ IN KIRQL irql
+ );
+
+//
+// External variables
+//
+extern ULONG ulAcdOpenCountG;
+
+
+
+VOID
+SetDriverMode(
+ IN BOOLEAN fEnable
+ )
+
+/*++
+
+DESCRIPTION
+ Set the global driver mode value, and inform
+ all bound transports of the change.
+
+ Note: this call assumes AcdSpinLockG is already
+ acquired.
+
+ARGUMENTS
+ fEnable: the new driver mode value
+
+RETURN VALUE
+ None.
+
+--*/
+
+{
+ KIRQL dirql;
+ PLIST_ENTRY pEntry;
+ PACD_DRIVER pDriver;
+
+ //
+ // Set the new global driver mode value.
+ //
+ fAcdEnabledG = fEnable;
+ //
+ // Inform all the drivers that have binded
+ // with us of the new enable mode.
+ //
+ for (pEntry = AcdDriverListG.Flink;
+ pEntry != &AcdDriverListG;
+ pEntry = pEntry->Flink)
+ {
+ pDriver = CONTAINING_RECORD(pEntry, ACD_DRIVER, ListEntry);
+
+ KeAcquireSpinLock(&pDriver->SpinLock, &dirql);
+ pDriver->fEnabled = fEnable;
+ KeReleaseSpinLock(&pDriver->SpinLock, dirql);
+ }
+} // SetDriverMode
+
+
+
+NTSTATUS
+AcdEnable(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+
+/*++
+
+DESCRIPTION
+ Set the enable mode for the driver. This determines
+ which notifications it will pass up to the automatic
+ connection system service.
+
+ARGUMENTS
+ pIrp: a pointer to the irp to be enqueued.
+
+ pIrpSp: a pointer to the current irp stack.
+
+RETURN VALUE
+ STATUS_BUFFER_TOO_SMALL: the supplied user buffer is too small to hold
+ an ACD_ENABLE_MODE value.
+
+ STATUS_SUCCESS: if the enabled bit was set successfully.
+
+--*/
+
+{
+ KIRQL irql;
+ BOOLEAN fEnable;
+
+ //
+ // Verify the input buffer is sufficient to hold
+ // a BOOLEAN structure.
+ //
+ if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof (BOOLEAN))
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ fEnable = *(BOOLEAN *)pIrp->AssociatedIrp.SystemBuffer;
+ SetDriverMode(fEnable);
+ //
+ // Clear all pending requests if
+ // we are disabling the driver.
+ //
+ if (!fEnable)
+ ClearRequests(irql);
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+
+ return STATUS_SUCCESS;
+} // AcdEnable
+
+
+
+VOID
+CancelNotification(
+ IN PDEVICE_OBJECT pDeviceObject,
+ IN PIRP pIrp
+ )
+
+/*++
+
+DESCRIPTION
+ Generic cancel routine for irps on the AcdNotificationQueueG.
+
+ARGUMENTS
+ pDeviceObject: unused
+
+ pIrp: pointer to the irp to be cancelled.
+
+RETURN VALUE
+ None.
+
+--*/
+
+{
+ KIRQL irql;
+
+ UNREFERENCED_PARAMETER(pDeviceObject);
+ //
+ // Mark this irp as cancelled.
+ //
+ pIrp->IoStatus.Status = STATUS_CANCELLED;
+ pIrp->IoStatus.Information = 0;
+ //
+ // Release the spin lock the I/O system acquired.
+ //
+ IoReleaseCancelSpinLock(pIrp->CancelIrql);
+ //
+ // Remove it from our list.
+ //
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+ //
+ // Complete the request.
+ //
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+} // CancelNotification
+
+
+
+VOID
+AcdCancelNotifications()
+
+/*++
+
+DESCRIPTION
+ Cancel all irps on the AcdNotification queue. Although
+ technically more than one user address space can be waiting
+ for these notifications, we allow only one at this time.
+
+ARGUMENTS
+ None.
+
+RETURN VALUE
+ None.
+
+--*/
+
+{
+ KIRQL irql;
+ PLIST_ENTRY pHead;
+ PIRP pIrp;
+ PIO_STACK_LOCATION pIrpSp;
+
+ //
+ // Complete all the irps in the list.
+ //
+ while ((pHead = ExInterlockedRemoveHeadList(
+ &AcdNotificationQueueG,
+ &AcdSpinLockG)) != NULL)
+ {
+ pIrp = CONTAINING_RECORD(pHead, IRP, Tail.Overlay.ListEntry);
+ //
+ // Mark this irp as cancelled.
+ //
+ pIrp->IoStatus.Status = STATUS_CANCELLED;
+ pIrp->IoStatus.Information = 0;
+ //
+ // Complete the irp.
+ //
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+} // AcdCancelNotifications
+
+
+
+NTSTATUS
+AcdWaitForNotification(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+
+/*++
+
+DESCRIPTION
+ Enqueue an connection notification irp. This is done
+ done by the automatic connection system service.
+
+ARGUMENTS
+ pIrp: a pointer to the irp to be enqueued.
+
+ pIrpSp: a pointer to the current irp stack.
+
+RETURN VALUE
+ STATUS_BUFFER_TOO_SMALL: the supplied user buffer is too small to hold
+ an ACD_NOTIFICATION structure.
+
+ STATUS_PENDING: if the ioctl was successfully enqueued
+
+ STATUS_SUCCESS: if there is a notification already available
+
+--*/
+
+{
+ KIRQL irql, irql2;
+ PLIST_ENTRY pHead;
+ PACD_COMPLETION pCompletion;
+ PACD_NOTIFICATION pNotification;
+ PEPROCESS pProcess;
+
+ //
+ // Verify the output buffer is sufficient to hold
+ // an ACD_NOTIFICATION structure.
+ //
+ if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof (ACD_NOTIFICATION))
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+ IoAcquireCancelSpinLock(&irql);
+ KeAcquireSpinLock(&AcdSpinLockG, &irql2);
+ //
+ // There is no notification available.
+ // Mark the irp as pending and wait for one.
+ //
+ pIrp->IoStatus.Status = STATUS_PENDING;
+ IoMarkIrpPending(pIrp);
+ //
+ // Set the irp's cancel routine.
+ //
+ IoSetCancelRoutine(pIrp, CancelNotification);
+ //
+ // Append the irp at the end of the
+ // connection notification list.
+ //
+ InsertTailList(&AcdNotificationQueueG, &pIrp->Tail.Overlay.ListEntry);
+ //
+ // Signal the request thread there is
+ // work to do.
+ //
+ KeSetEvent(&AcdRequestThreadEventG, 0, FALSE);
+
+ KeReleaseSpinLock(&AcdSpinLockG, irql2);
+ IoReleaseCancelSpinLock(irql);
+
+ return STATUS_PENDING;
+} // AcdWaitForNotification
+
+
+
+BOOLEAN
+EqualAddress(
+ IN PACD_ADDR p1,
+ IN PACD_ADDR p2
+ )
+{
+ ULONG i;
+
+ if (p1->fType != p2->fType)
+ return FALSE;
+
+ switch (p1->fType) {
+ case ACD_ADDR_IP:
+ return (p1->ulIpaddr == p2->ulIpaddr);
+ case ACD_ADDR_IPX:
+ return (BOOLEAN)RtlEqualMemory(
+ &p1->cNode,
+ &p2->cNode,
+ ACD_ADDR_IPX_LEN);
+ case ACD_ADDR_NB:
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint((
+ "EqualAddress: NB: (%15s,%15s) result=%d\n",
+ p1->cNetbios,
+ p2->cNetbios,
+ RtlEqualMemory(&p1->cNetbios, &p2->cNetbios, ACD_ADDR_NB_LEN - 1)));
+ }
+ return (BOOLEAN)RtlEqualMemory(
+ &p1->cNetbios,
+ &p2->cNetbios,
+ ACD_ADDR_NB_LEN - 1);
+ case ACD_ADDR_INET:
+ for (i = 0; i < ACD_ADDR_INET_LEN; i++) {
+ if (p1->szInet[i] != p2->szInet[i])
+ return FALSE;
+ if (p1->szInet[i] == '\0' || p2->szInet[i] == '\0')
+ break;
+ }
+ return TRUE;
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+
+ return FALSE;
+} // EqualAddress
+
+
+
+PACD_CONNECTION
+FindConnection(
+ IN PACD_ADDR pAddr
+ )
+
+/*++
+
+DESCRIPTION
+ Search for a connection block with the specified
+ address.
+
+ARGUMENTS
+ pAddr: a pointer to the target ACD_ADDR
+
+RETURN VALUE
+ A PACD_CONNECTION with the specified address, if found;
+ otherwise NULL.
+
+--*/
+
+{
+ PLIST_ENTRY pEntry;
+ PACD_CONNECTION pConnection;
+ PACD_COMPLETION pCompletion;
+
+ for (pEntry = AcdConnectionQueueG.Flink;
+ pEntry != &AcdConnectionQueueG;
+ pEntry = pEntry->Flink)
+ {
+ pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry);
+ pCompletion = CONTAINING_RECORD(pConnection->CompletionList.Flink, ACD_COMPLETION, ListEntry);
+
+ if (EqualAddress(pAddr, &pCompletion->notif.addr))
+ return pConnection;
+ }
+
+ return NULL;
+} // FindConnection
+
+
+
+NTSTATUS
+AcdConnectionInProgress(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+
+/*++
+
+DESCRIPTION
+ Refresh the progress indicator for the connection
+ attempt. If the progress indicator is not updated
+ by the user
+
+ARGUMENTS
+ pIrp: a pointer to the irp to be enqueued.
+
+ pIrpSp: a pointer to the current irp stack.
+
+RETURN VALUE
+ STATUS_INVALID_CONNECTION: if there is no connection
+ attempt in progress.
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ KIRQL irql;
+ PACD_STATUS pStatus;
+ PACD_CONNECTION pConnection;
+
+ //
+ // Verify the input buffer is sufficient to hold
+ // a BOOLEAN structure.
+ //
+ if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof (ACD_STATUS))
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Get the success code from the
+ // connection attempt and pass it
+ // to the completion routine.
+ //
+ pStatus = (PACD_STATUS)pIrp->AssociatedIrp.SystemBuffer;
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ pConnection = FindConnection(&pStatus->addr);
+ if (pConnection != NULL)
+ pConnection->fProgressPing = TRUE;
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+
+ return (pConnection != NULL) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
+} // AcdConnectionInProgress
+
+
+
+BOOLEAN
+AddCompletionToConnection(
+ IN PACD_COMPLETION pCompletion
+ )
+{
+ PACD_CONNECTION pConnection;
+
+ pConnection = FindConnection(&pCompletion->notif.addr);
+ //
+ // If the connection already exists, then add
+ // the completion request to its list.
+ //
+ if (pConnection != NULL) {
+ InsertTailList(&pConnection->CompletionList, &pCompletion->ListEntry);
+ return TRUE;
+ }
+ //
+ // This is a connection to a new address.
+ // Create the connection block, enqueue it,
+ // and start the connection timer.
+ //
+ ALLOCATE_MEMORY(ACD_OBJECT_CONNECTION, pConnection);
+ if (pConnection == NULL) {
+ DbgPrint("AddCompletionToConnection: ExAllocatePool failed\n");
+ return FALSE;
+ }
+ pConnection->fNotif = FALSE;
+ pConnection->fProgressPing = FALSE;
+ pConnection->fCompleting = FALSE;
+ pConnection->ulTimerCalls = 0;
+ pConnection->ulMissedPings = 0;
+ InitializeListHead(&pConnection->CompletionList);
+ InsertHeadList(&pConnection->CompletionList, &pCompletion->ListEntry);
+ InsertTailList(&AcdConnectionQueueG, &pConnection->ListEntry);
+ return TRUE;
+} // AddCompletionToConnection
+
+
+
+BOOLEAN
+AddCompletionBlock(
+ IN ULONG ulDriverId,
+ IN PACD_ADDR pAddr,
+ IN ULONG ulFlags,
+ IN PACD_ADAPTER pAdapter,
+ IN ACD_CONNECT_CALLBACK pProc,
+ IN USHORT nArgs,
+ IN PVOID *pArgs
+ )
+
+/*++
+
+DESCRIPTION
+ Create a block that represents an outstanding
+ transport request waiting for an automatic
+ connection. Link this block into the global
+ list of outstanding transport requests.
+
+ARGUMENTS
+ ulDriverId: a unique value for the transport driver
+
+ pAddr: the network address of the connection
+
+ ulFlags: connection flags
+
+ pAdapter: pointer to adapter identifier
+
+ pProc: a completion callback procedure
+
+ nArgs: the number of parameters passed in pArgs
+
+ pArgs: the parameters to pProc
+
+RETURN VALUE
+ TRUE if successful, FALSE otherwise
+
+--*/
+
+{
+ PACD_COMPLETION pCompletion;
+ ULONG i;
+
+ ALLOCATE_MEMORY(
+ sizeof (ACD_COMPLETION) + ((nArgs - 1) * sizeof (PVOID)),
+ pCompletion);
+ if (pCompletion == NULL) {
+ DbgPrint("AcdAddCompletionBlock: ExAllocatePool failed\n");
+ return FALSE;
+ }
+ //
+ // Copy the arguments into the information block.
+ //
+ pCompletion->ulDriverId = ulDriverId;
+ pCompletion->fCanceled = FALSE;
+ pCompletion->fCompleted = FALSE;
+ RtlCopyMemory(&pCompletion->notif.addr, pAddr, sizeof (ACD_ADDR));
+ pCompletion->notif.ulFlags = ulFlags;
+ if (pAdapter != NULL) {
+ RtlCopyMemory(
+ &pCompletion->notif.adapter,
+ pAdapter,
+ sizeof (ACD_ADAPTER));
+ }
+ else
+ RtlZeroMemory(&pCompletion->notif.adapter, sizeof (ACD_ADAPTER));
+ pCompletion->pProc = pProc;
+ pCompletion->nArgs = nArgs;
+ for (i = 0; i < nArgs; i++)
+ pCompletion->pArgs[i] = pArgs[i];
+ //
+ // If this is a unsuccessful connection request,
+ // then insert it onto the connection queue for
+ // that address; Otherwise, insert it into the list
+ // for all other requests.
+ //
+ if (ulFlags & ACD_NOTIFICATION_SUCCESS) {
+ InsertTailList(&AcdCompletionQueueG, &pCompletion->ListEntry);
+ }
+ else {
+ if (!AddCompletionToConnection(pCompletion)) {
+ FREE_MEMORY(pCompletion);
+ return FALSE;
+ }
+ }
+ //
+ // Inform the request thread
+ // there is new work to do.
+ //
+ KeSetEvent(&AcdRequestThreadEventG, 0, FALSE);
+
+ return TRUE;
+} // AddCompletionBlock
+
+
+
+VOID
+AcdNewConnection(
+ IN PACD_ADDR pAddr,
+ IN PACD_ADAPTER pAdapter
+ )
+{
+ KIRQL irql;
+
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint(("AcdNewConnection: "));
+ AcdPrintAddress(pAddr);
+ AcdPrint(("\n"));
+ }
+ //
+ // If the driver is disabled, then fail
+ // all requests.
+ //
+ if (!fAcdEnabledG) {
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint(("AcdNewConnection: driver disabled\n"));
+ }
+ return;
+ }
+ //
+ // Acquire our spin lock.
+ //
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ //
+ // Allocate a new completion block.
+ //
+ AddCompletionBlock(
+ 0,
+ pAddr,
+ ACD_NOTIFICATION_SUCCESS,
+ pAdapter,
+ NULL,
+ 0,
+ NULL);
+ //
+ // Release the spin lock.
+ //
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+} // AcdNewConnection
+
+
+
+BOOLEAN
+AcdStartConnection(
+ IN ULONG ulDriverId,
+ IN PACD_ADDR pAddr,
+ IN ULONG ulFlags,
+ IN ACD_CONNECT_CALLBACK pProc,
+ IN USHORT nArgs,
+ IN PVOID *pArgs
+ )
+
+/*++
+
+DESCRIPTION
+ Create a new connection completion block, and enqueue
+ it on the list of network requests to be completed when
+ a new network connection has been created.
+
+ARGUMENTS
+ ulDriverId: a unique value for the transport driver
+
+ pAddr: the address of the connection attempt
+
+ ulFlags: connection flags
+
+ pProc: the transport callback to be called when a new
+ connection has been created.
+
+ nArgs: the number of arguments to pProc.
+
+ pArgs: a pointer to an array of pProc's parameters
+
+RETURN VALUE
+ TRUE if successful, FALSE otherwise.
+
+--*/
+
+{
+ BOOLEAN fSuccess = FALSE, fFound;
+ KIRQL irql;
+ ULONG ulAttributes = 0;
+ PACD_COMPLETION pCompletion;
+ PCHAR psz, pszOrg;
+ ACD_ADDR szOrgAddr;
+
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint(("AcdStartConnection: "));
+ AcdPrintAddress(pAddr);
+ AcdPrint((", ulFlags=0x%x\n", ulFlags));
+ }
+ //
+ // If the driver is disabled, then fail
+ // all requests.
+ //
+ if (!fAcdEnabledG) {
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint(("AcdStartConnection: driver disabled\n"));
+ }
+ return FALSE;
+ }
+ //
+ // Validate the address type.
+ //
+ if ((ULONG)pAddr->fType >= ACD_ADDR_MAX) {
+ AcdPrint(("AcdStartConnection: bad address type (%d)\n", pAddr->fType));
+ return FALSE;
+ }
+ //
+ // Acquire our spin lock.
+ //
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ //
+ // Update statistics.
+ //
+ AcdStatsG[pAddr->fType].ulConnects++;
+ //
+ // Allocate a new completion block.
+ //
+ fSuccess = AddCompletionBlock(
+ ulDriverId,
+ pAddr,
+ ulFlags,
+ NULL,
+ pProc,
+ nArgs,
+ pArgs);
+ //
+ // Release the spin lock.
+ //
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+
+ return fSuccess;
+} // AcdStartConnection
+
+
+
+BOOLEAN
+AcdCancelConnection(
+ IN ULONG ulDriverId,
+ IN PACD_ADDR pAddr,
+ IN ACD_CANCEL_CALLBACK pProc,
+ IN PVOID pArg
+ )
+
+/*++
+
+DESCRIPTION
+ Remove a previously enqueued connection information
+ block from the list.
+
+ARGUMENTS
+ ulDriverId: a unique value for the transport driver
+
+ pAddr: the address of the connection attempt
+
+ pProc: the enumerator procecdure
+
+ pArg: the enumerator procedure argument
+
+RETURN VALUE
+ None.
+
+--*/
+
+{
+ BOOLEAN fCanceled = FALSE;
+ KIRQL irql;
+ PLIST_ENTRY pEntry;
+ PACD_CONNECTION pConnection;
+ PACD_COMPLETION pCompletion;
+
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint(("AcdCancelConnection: ulDriverId=0x%x, "));
+ AcdPrintAddress(pAddr);
+ AcdPrint(("\n"));
+ }
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ //
+ // Enumerate the list looking for
+ // the information block with the
+ // supplied parameters.
+ //
+ pConnection = FindConnection(pAddr);
+ if (pConnection != NULL) {
+ for (pEntry = pConnection->CompletionList.Flink;
+ pEntry != &pConnection->CompletionList;
+ pEntry = pEntry->Flink)
+ {
+ pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry);
+ //
+ // If we have a match, remove it from
+ // the list and free the information block.
+ //
+ if (pCompletion->ulDriverId == ulDriverId &&
+ !pCompletion->fCanceled &&
+ !pCompletion->fCompleted)
+ {
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint((
+ "AcdCancelConnection: pCompletion=0x%x\n",
+ pCompletion));
+ }
+ if ((*pProc)(
+ pArg,
+ pCompletion->notif.ulFlags,
+ pCompletion->pProc,
+ pCompletion->nArgs,
+ pCompletion->pArgs))
+ {
+ pCompletion->fCanceled = TRUE;
+ fCanceled = TRUE;
+ //
+ // Update statistics.
+ //
+ AcdStatsG[pAddr->fType].ulCancels++;
+ break;
+ }
+ }
+ }
+ }
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+
+ return fCanceled;
+} // AcdCancelConnection
+
+
+
+VOID
+ConnectAddressComplete(
+ BOOLEAN fSuccess,
+ PVOID *pArgs
+ )
+{
+ PIRP pIrp = pArgs[0];
+ PIO_STACK_LOCATION pIrpSp = pArgs[1];
+ KIRQL irql;
+
+ //
+ // Complete the request.
+ //
+ pIrp->IoStatus.Status = fSuccess ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
+ pIrp->IoStatus.Information = 0;
+ IoAcquireCancelSpinLock(&irql);
+ IoSetCancelRoutine(pIrp, NULL);
+ IoReleaseCancelSpinLock(irql);
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+} // ConnectAddressComplete
+
+
+
+BOOLEAN
+CancelConnectAddressCallback(
+ IN PVOID pArg,
+ IN ULONG ulFlags,
+ IN ACD_CONNECT_CALLBACK pProc,
+ IN USHORT nArgs,
+ IN PVOID *pArgs
+ )
+{
+ return (nArgs == 2 && pArgs[0] == pArg);
+} // CancelConnectAddressCallback
+
+
+
+VOID
+CancelConnectAddress(
+ PDEVICE_OBJECT pDevice,
+ PIRP pIrp
+ )
+{
+ KIRQL irql;
+ PACD_NOTIFICATION pNotification;
+
+ ASSERT(pIrp->Cancel);
+ //
+ // Remove our outstanding request.
+ //
+ pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer;
+ //
+ // If we can't find the request on the connection
+ // list, then it has already been completed.
+ //
+ if (!AcdCancelConnection(
+ 0,
+ &pNotification->addr,
+ CancelConnectAddressCallback,
+ pIrp))
+ {
+ IoReleaseCancelSpinLock(pIrp->CancelIrql);
+ return;
+ }
+ //
+ // Mark this irp as cancelled.
+ //
+ pIrp->IoStatus.Status = STATUS_CANCELLED;
+ pIrp->IoStatus.Information = 0;
+ IoSetCancelRoutine(pIrp, NULL);
+ //
+ // Release the spin lock the I/O system acquired.
+ //
+ IoReleaseCancelSpinLock(pIrp->CancelIrql);
+ //
+ // Complete the I/O request.
+ //
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+} // CancelConnectAddress
+
+
+
+NTSTATUS
+AcdConnectAddress(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+
+/*++
+
+DESCRIPTION
+ Manufacture a call to ourselves to simulate a transport
+ requesting an automatic connection. This allows a user
+ address space to initiate an automatic connection.
+
+ARGUMENTS
+ pIrp: a pointer to the irp to be enqueued.
+
+ pIrpSp: a pointer to the current irp stack.
+
+RETURN VALUE
+ STATUS_BUFFER_TOO_SMALL: the supplied user buffer is too small to hold
+ an ACD_NOTIFICATION structure.
+
+ STATUS_UNSUCCESSFUL: an error occurred initiating the
+ automatic connection.
+
+ STATUS_PENDING: success
+
+--*/
+
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ KIRQL irql;
+ PACD_NOTIFICATION pNotification;
+ PVOID pArgs[2];
+
+ //
+ // Verify the input buffer is sufficient to hold
+ // an ACD_NOTIFICATION structure.
+ //
+ if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof (ACD_NOTIFICATION))
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer;
+ pArgs[0] = pIrp;
+ pArgs[1] = pIrpSp;
+ //
+ // Start the connection.
+ //
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint(("AcdConnectAddress: "));
+ AcdPrintAddress(&pNotification->addr);
+ AcdPrint((", ulFlags=0x%x\n", pNotification->ulFlags));
+ }
+ if (pNotification->ulFlags & ACD_NOTIFICATION_SUCCESS) {
+ AcdNewConnection(
+ &pNotification->addr,
+ &pNotification->adapter);
+ status = STATUS_SUCCESS;
+ }
+ else {
+ IoAcquireCancelSpinLock(&irql);
+ if (AcdStartConnection(
+ 0,
+ &pNotification->addr,
+ pNotification->ulFlags,
+ ConnectAddressComplete,
+ 2,
+ pArgs))
+ {
+ //
+ // We enqueued the request successfully.
+ // Mark the irp as pending.
+ //
+ IoSetCancelRoutine(pIrp, CancelConnectAddress);
+ IoMarkIrpPending(pIrp);
+ status = STATUS_PENDING;
+ }
+ IoReleaseCancelSpinLock(irql);
+ }
+
+ return status;
+} // AcdConnectAddress
+
+
+
+VOID
+AcdSignalCompletionCommon(
+ IN PACD_CONNECTION pConnection,
+ IN BOOLEAN fSuccess
+ )
+{
+ KIRQL irql;
+ PLIST_ENTRY pEntry;
+ PACD_COMPLETION pCompletion;
+ BOOLEAN fFound;
+
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint((
+ "AcdSignalCompletionCommon: pConnection=0x%x, fCompleting=%d\n",
+ pConnection,
+ pConnection->fCompleting));
+ }
+again:
+ fFound = FALSE;
+ //
+ // Acquire our lock and look for
+ // the next uncompleted request.
+ //
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ for (pEntry = pConnection->CompletionList.Flink;
+ pEntry != &pConnection->CompletionList;
+ pEntry = pEntry->Flink)
+ {
+ pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry);
+
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint((
+ "AcdSignalCompletionCommon: pCompletion=0x%x, fCanceled=%d, fCompleted=%d\n",
+ pCompletion,
+ pCompletion->fCanceled,
+ pCompletion->fCompleted));
+ }
+ //
+ // Only complete this request if it
+ // hasn't already been completed
+ // or canceled.
+ //
+ if (!pCompletion->fCanceled && !pCompletion->fCompleted) {
+ pCompletion->fCompleted = TRUE;
+ fFound = TRUE;
+ break;
+ }
+ }
+ //
+ // If there are no more requests to
+ // complete then remove this connection
+ // from the connection list and free its
+ // memory.
+ //
+ if (!fFound) {
+ RemoveEntryList(&pConnection->ListEntry);
+ while (!IsListEmpty(&pConnection->CompletionList)) {
+ pEntry = RemoveHeadList(&pConnection->CompletionList);
+ pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry);
+
+ FREE_MEMORY(pCompletion);
+ }
+ FREE_MEMORY(pConnection);
+ //
+ // Signal the request thread that
+ // the connection list has changed.
+ //
+ KeSetEvent(&AcdRequestThreadEventG, 0, FALSE);
+ }
+ //
+ // Release our lock.
+ //
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+ //
+ // If we found a request, then
+ // call its completion proc.
+ //
+ if (fFound) {
+ IF_ACDDBG(ACD_DEBUG_CONNECTION) {
+ AcdPrint(("AcdSignalCompletionCommon: pCompletion=0x%x, ", pCompletion));
+ AcdPrintAddress(&pCompletion->notif.addr);
+ AcdPrint(("\n"));
+ }
+ (*pCompletion->pProc)(fSuccess, pCompletion->pArgs);
+ //
+ // Look for another request.
+ //
+ goto again;
+ }
+} // AcdSignalCompletionCommon
+
+
+
+NTSTATUS
+AcdSignalCompletion(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+
+/*++
+
+DESCRIPTION
+ For each thread waiting on the AcdCompletionQueueG,
+ call the transport-dependent callback to retry the
+ connection attempt and complete the irp.
+
+ARGUMENTS
+ pIrp: unused
+
+ pIrpSp: unused
+
+RETURN VALUE
+ STATUS_SUCCESS
+
+--*/
+
+{
+ KIRQL irql;
+ PACD_STATUS pStatus;
+ PACD_CONNECTION pConnection;
+ BOOLEAN fFound = FALSE;
+
+ //
+ // Verify the input buffer is sufficient to hold
+ // a BOOLEAN structure.
+ //
+ if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof (ACD_STATUS))
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Get the success code from the
+ // connection attempt and pass it
+ // to the completion routine.
+ //
+ pStatus = (PACD_STATUS)pIrp->AssociatedIrp.SystemBuffer;
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ pConnection = FindConnection(&pStatus->addr);
+ if (pConnection != NULL && !pConnection->fCompleting) {
+ //
+ // Set the completion-in-progress flag so
+ // this request cannot be timed-out after
+ // we release the spin lock.
+ //
+ pConnection->fCompleting = TRUE;
+ fFound = TRUE;
+ }
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+ //
+ // If we didn't find the connection block,
+ // or the completion was already in progress,
+ // then return an error.
+ //
+ if (!fFound)
+ return STATUS_UNSUCCESSFUL;
+
+ AcdSignalCompletionCommon(pConnection, pStatus->fSuccess);
+ return STATUS_SUCCESS;
+} // AcdSignalCompletion
+
+
+
+VOID
+ClearRequests(
+ IN KIRQL irql
+ )
+
+/*++
+
+DESCRIPTION
+ Complete all pending requests with failure status.
+ This call assumes the AcdSpinLockG is already held,
+ and it returns with it held.
+
+ARGUMENTS
+ None.
+
+RETURN VALUE
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY pHead, pEntry;
+ PACD_COMPLETION pCompletion;
+ PACD_CONNECTION pConnection;
+
+again:
+ //
+ // Complete all pending connections with
+ // an error.
+ //
+ for (pEntry = AcdConnectionQueueG.Flink;
+ pEntry != &AcdConnectionQueueG;
+ pEntry = pEntry->Flink)
+ {
+ pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry);
+
+ if (!pConnection->fCompleting) {
+ pConnection->fCompleting = TRUE;
+ //
+ // We need to release our lock to
+ // complete the request.
+ //
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+ //
+ // Complete the request.
+ //
+ AcdSignalCompletionCommon(pConnection, FALSE);
+ //
+ // Check for more uncompleted requests.
+ //
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ goto again;
+ }
+ }
+ //
+ // Clear out all other pending requests.
+ //
+ while (!IsListEmpty(&AcdCompletionQueueG)) {
+ pHead = RemoveHeadList(&AcdCompletionQueueG);
+ pCompletion = CONTAINING_RECORD(pHead, ACD_COMPLETION, ListEntry);
+
+ FREE_MEMORY(pCompletion);
+ }
+} // ClearRequests
+
+
+
+VOID
+AcdReset()
+
+/*++
+
+DESCRIPTION
+ Complete all pending requests with failure status.
+ This is called when the reference count on the driver
+ object goes to zero, and prevents stale requests from
+ being presented to the system service if it is restarted
+ when there are pending completion requests.
+
+ARGUMENTS
+ None.
+
+RETURN VALUE
+ None.
+
+--*/
+
+{
+ KIRQL irql;
+ PLIST_ENTRY pHead, pEntry;
+ PACD_COMPLETION pCompletion;
+ PACD_CONNECTION pConnection;
+
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ //
+ // Reset the notification mode to disabled.
+ //
+ SetDriverMode(FALSE);
+ //
+ // Complete all pending connections with
+ // an error.
+ //
+ ClearRequests(irql);
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+} // AcdReset
+
+
+
+NTSTATUS
+AcdBind(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+
+/*++
+
+DESCRIPTION
+ Return the list of entry points to a client
+ transport driver.
+
+ARGUMENTS
+ pIrp: a pointer to the irp to be enqueued.
+
+ pIrpSp: a pointer to the current irp stack.
+
+RETURN VALUE
+ STATUS_BUFFER_TOO_SMALL if the supplied SystemBuffer is too
+ small. STATUS_SUCCESS otherwise.
+
+--*/
+
+{
+ NTSTATUS status;
+ PACD_DRIVER *ppDriver, pDriver;
+ KIRQL irql, dirql;
+
+ //
+ // Verify the input buffer a pointer to
+ // the driver's ACD_DRIVER structure.
+ //
+ if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof (PACD_DRIVER))
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+ ppDriver = (PACD_DRIVER *)pIrp->AssociatedIrp.SystemBuffer;
+ pDriver = *ppDriver;
+#if DBG
+ //
+ // Selectively bind with some transports.
+ //
+ switch (pDriver->ulDriverId) {
+ case 'Nbf ':
+ break;
+ case 'Tcp ':
+#ifdef notdef
+ DbgPrint("AcdBind: ignoring Tcp\n");
+ pDriver->fEnabled = FALSE;
+ pIrp->IoStatus.Information = 0;
+ return STATUS_SUCCESS;
+#endif
+ break;
+ case 'Nbi ':
+#ifdef notdef
+ DbgPrint("AcdBind: ignoring Nbi\n");
+ pDriver->fEnabled = FALSE;
+ pIrp->IoStatus.Information = 0;
+ return STATUS_SUCCESS;
+#endif
+ break;
+ }
+#endif
+ //
+ // Fill in the entry point structure.
+ //
+ pDriver->lpfnNewConnection = AcdNewConnection;
+ pDriver->lpfnStartConnection = AcdStartConnection;
+ pDriver->lpfnCancelConnection = AcdCancelConnection;
+ //
+ // Insert this block into our driver list.
+ //
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ KeAcquireSpinLock(&pDriver->SpinLock, &dirql);
+ pDriver->fEnabled = fAcdEnabledG;
+ KeReleaseSpinLock(&pDriver->SpinLock, dirql);
+ InsertTailList(&AcdDriverListG, &pDriver->ListEntry);
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+ //
+ // No data should be copied back.
+ //
+ pIrp->IoStatus.Information = 0;
+
+ return STATUS_SUCCESS;
+} // AcdBind
+
+
+
+NTSTATUS
+AcdUnbind(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+
+/*++
+
+DESCRIPTION
+ Unbind a client transport driver.
+
+ARGUMENTS
+ pIrp: a pointer to the irp to be enqueued.
+
+ pIrpSp: a pointer to the current irp stack.
+
+RETURN VALUE
+ STATUS_BUFFER_TOO_SMALL if the supplied SystemBuffer is too
+ small. STATUS_SUCCESS otherwise.
+
+--*/
+
+{
+ KIRQL irql, dirql;
+ PLIST_ENTRY pEntry, pEntry2;
+ PACD_DRIVER *ppDriver, pDriver;
+ PACD_CONNECTION pConnection;
+ PACD_COMPLETION pCompletion;
+
+ //
+ // Verify the input buffer a pointer to
+ // the driver's ACD_DRIVER structure.
+ //
+ if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof (PACD_DRIVER))
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+ ppDriver = (PACD_DRIVER *)pIrp->AssociatedIrp.SystemBuffer;
+ pDriver = *ppDriver;
+
+ KeAcquireSpinLock(&AcdSpinLockG, &irql);
+ //
+ // Enumerate the list looking for
+ // any connection request initiated by the
+ // specified driver.
+ //
+ for (pEntry = AcdConnectionQueueG.Flink;
+ pEntry != &AcdConnectionQueueG;
+ pEntry = pEntry->Flink)
+ {
+ pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry);
+
+ for (pEntry2 = pConnection->CompletionList.Flink;
+ pEntry2 != &pConnection->CompletionList;
+ pEntry2 = pEntry2->Flink)
+ {
+ pCompletion = CONTAINING_RECORD(pEntry2, ACD_COMPLETION, ListEntry);
+
+ //
+ // If we have a match, cancel it.
+ //
+ if (pCompletion->ulDriverId == pDriver->ulDriverId)
+ pCompletion->fCanceled = TRUE;
+ }
+ }
+ //
+ // Set this driver's enable mode to ACD_ENABLE_NONE.
+ //
+ KeAcquireSpinLock(&pDriver->SpinLock, &dirql);
+ pDriver->fEnabled = FALSE;
+ KeReleaseSpinLock(&pDriver->SpinLock, dirql);
+ //
+ // Remove this driver from the list.
+ //
+ RemoveEntryList(&pDriver->ListEntry);
+ KeReleaseSpinLock(&AcdSpinLockG, irql);
+ //
+ // No data should be copied back.
+ //
+ pIrp->IoStatus.Information = 0;
+
+ return STATUS_SUCCESS;
+} // AcdUnbind
+
+
+VOID
+AcdPrintAddress(
+ IN PACD_ADDR pAddr
+ )
+{
+#if DBG
+ PUCHAR puc;
+
+ switch (pAddr->fType) {
+ case ACD_ADDR_IP:
+ puc = (PUCHAR)&pAddr->ulIpaddr;
+ AcdPrint(("IP: %d.%d.%d.%d", puc[0], puc[1], puc[2], puc[3]));
+ break;
+ case ACD_ADDR_IPX:
+ AcdPrint((
+ "IPX: %02x:%02x:%02x:%02x:%02x:%02x",
+ pAddr->cNode[0],
+ pAddr->cNode[1],
+ pAddr->cNode[2],
+ pAddr->cNode[3],
+ pAddr->cNode[4],
+ pAddr->cNode[5]));
+ break;
+ case ACD_ADDR_NB:
+ AcdPrint(("NB: %15.15s", pAddr->cNetbios));
+ break;
+ case ACD_ADDR_INET:
+ AcdPrint(("INET: %s", pAddr->szInet));
+ break;
+ default:
+ AcdPrint(("UNKNOWN: ????"));
+ break;
+ }
+#endif
+} // AcdPrintAddress
diff --git a/private/ntos/tdi/acd/debug.h b/private/ntos/tdi/acd/debug.h
new file mode 100644
index 000000000..421c43de6
--- /dev/null
+++ b/private/ntos/tdi/acd/debug.h
@@ -0,0 +1,74 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ debug.h
+
+Abstract:
+
+ Debugging defintions for the Automatic
+ Connection Driver (acd.sys).
+
+Author:
+
+ Anthony Discolo (adiscolo) 3-Aug-1995
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#ifndef _ACDDBG_
+#define _ACDDBG_
+
+//
+// Debug tracing flags.
+//
+// To enable debug tracing for a module, set the
+// appropriate bit in ntinit\AcdDebugG.
+//
+#if DBG
+
+#define ACD_DEBUG_IOCTL 0x00000001 // ntdisp.c/AcdDispatchDeviceControl()
+#define ACD_DEBUG_OPENCOUNT 0x00000002 // ntdisp.c/Acd{Open,Close}()
+#define ACD_DEBUG_TIMER 0x00000004 // timer.c
+#define ACD_DEBUG_CONNECTION 0x00000008 // api.c/AcdStartConnection()
+#define ACD_DEBUG_WORKER 0x00000010 // api.c/AcdNotificationRequestThread()
+#define ACD_DEBUG_RESET 0x00000020 // api.c/AcdReset()
+#define ACD_DEBUG_MEMORY 0x80000000 // memory alloc/free
+
+#define IF_ACDDBG(flag) if (AcdDebugG & flag)
+#define AcdPrint(many_args) DbgPrint many_args
+
+#define ALLOCATE_MEMORY(fObject, pObject) \
+ pObject = AllocateObjectMemory(fObject); \
+ IF_ACDDBG(ACD_DEBUG_MEMORY) \
+ AcdPrint(("ALLOCATE_MEMORY: %s(%d): fObject=%d, pObject=0x%x\n", __FILE__, __LINE__, fObject, pObject))
+
+#define FREE_MEMORY(pObject) \
+ IF_ACDDBG(ACD_DEBUG_MEMORY) \
+ AcdPrint(("FREE_MEMORY: %s(%d): pObject=0x%x\n", __FILE__, __LINE__, pObject)); \
+ FreeObjectMemory(pObject)
+
+extern ULONG AcdDebugG;
+
+#else
+
+#define IF_ACDDBG(flag) if (0)
+#define AcdPrint(many_args)
+
+#define ALLOCATE_MEMORY(fObject, pObject) \
+ pObject = AllocateObjectMemory(fObject);
+
+#define FREE_MEMORY(pObject) \
+ FreeObjectMemory(pObject)
+
+#endif
+
+
+#endif // _ACDDBG_
diff --git a/private/ntos/tdi/acd/makefile b/private/ntos/tdi/acd/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/tdi/acd/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/tdi/acd/mem.c b/private/ntos/tdi/acd/mem.c
new file mode 100644
index 000000000..e3c9166c1
--- /dev/null
+++ b/private/ntos/tdi/acd/mem.c
@@ -0,0 +1,289 @@
+/*++
+
+Copyright(c) 1995 Microsoft Corporation
+
+MODULE NAME
+ table.c
+
+ABSTRACT
+ Generic hash table manipulation routines.
+
+AUTHOR
+ Anthony Discolo (adiscolo) 28-Jul-1995
+
+REVISION HISTORY
+
+--*/
+
+#include <ndis.h>
+#include <cxport.h>
+#include <tdi.h>
+#include <tdikrnl.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <acd.h>
+#include <acdapi.h>
+
+#include "acddefs.h"
+#include "mem.h"
+#include "debug.h"
+
+//
+// The maximum number of allocated
+// objects we allocate from outside
+// our zones.
+//
+#define MAX_ALLOCATED_OBJECTS 100
+
+//
+// Rounding up macro.
+//
+#define ROUNDUP(n, b) (((n) + ((b) - 1)) & ~((b) - 1))
+
+//
+// Map an object type to a zone.
+//
+#define OBJECT_INFO(fObject) \
+ (fObject < ACD_OBJECT_MAX) ? &AcdObjectInfoG[fObject] : &AcdObjectInfoG[ACD_OBJECT_MAX]
+
+//
+// The spin lock for this module.
+//
+KSPIN_LOCK AcdMemSpinLockG;
+
+//
+// Zone-based object information. One zone
+// per object type.
+//
+typedef struct _OBJECT_INFORMATION {
+ ZONE_HEADER zone;
+ ULONG ulSize; // object size
+ ULONG ulTag; // ExAllocateFromPoolWithTag() tag
+ ULONG ulCurrent; // # currently allocated in zone
+ ULONG ulTotal; // total # zone allocations
+} OBJECT_INFORMATION, *POBJECT_INFORMATION;
+
+OBJECT_INFORMATION AcdObjectInfoG[ACD_OBJECT_MAX + 1];
+
+//
+// Pool-based object allocation. This is for
+// objects that don't fit into any of the zones,
+// or when the zone is full.
+//
+typedef struct _POOL_INFORMATION {
+ ULONG cbMin; // minimum size
+ ULONG cbMax; // maximum size
+ ULONG ulCurrent; // # current allocations
+ ULONG ulTotal; // total allocations
+ ULONG ulFailures; // total failures
+} POOL_INFORMATION, *PPOOL_INFORMATION;
+
+POOL_INFORMATION AcdPoolInfoG;
+
+
+
+VOID
+InitializeObjectAllocator()
+{
+ NTSTATUS status;
+ PVOID pMem;
+ ULONG ulSize;
+
+ KeInitializeSpinLock(&AcdMemSpinLockG);
+ //
+ // Initialize zone 0 (ACD_OBJECT_CONNECTION).
+ //
+ AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulTag = 'NdcA';
+ AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulSize =
+ ROUNDUP(sizeof (ACD_CONNECTION), 8);
+ ulSize = PAGE_SIZE;
+ pMem = ExAllocatePoolWithTag(
+ NonPagedPool,
+ ulSize,
+ AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulTag);
+ ASSERT(pMem != NULL);
+ status = ExInitializeZone(
+ &AcdObjectInfoG[ACD_OBJECT_CONNECTION].zone,
+ AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulSize,
+ pMem,
+ ulSize);
+ IF_ACDDBG(ACD_DEBUG_MEMORY) {
+ AcdPrint((
+ "InitializeObjectAllocator: zone 0 created: blksiz=%d, size=%d (status=%d)\n",
+ AcdObjectInfoG[ACD_OBJECT_CONNECTION].ulSize,
+ ulSize,
+ status));
+ }
+ //
+ // Initialize zone 1 (ACD_OBJECT_COMPLETION).
+ //
+ AcdObjectInfoG[ACD_OBJECT_MAX].ulTag = 'MdcA';
+ //
+ // Allow for up to 6 parameters to a completion
+ // request (6 used by tcpip.sys).
+ //
+ AcdObjectInfoG[ACD_OBJECT_MAX].ulSize =
+ ROUNDUP(sizeof (ACD_COMPLETION) + (6 * sizeof (PVOID)), 8);
+ ulSize = ROUNDUP(6 * AcdObjectInfoG[ACD_OBJECT_MAX].ulSize, PAGE_SIZE),
+ pMem = ExAllocatePoolWithTag(
+ NonPagedPool,
+ ulSize,
+ AcdObjectInfoG[ACD_OBJECT_MAX].ulTag);
+ ASSERT(pMem != NULL);
+ status = ExInitializeZone(
+ &AcdObjectInfoG[ACD_OBJECT_MAX].zone,
+ AcdObjectInfoG[ACD_OBJECT_MAX].ulSize,
+ pMem,
+ ulSize);
+ IF_ACDDBG(ACD_DEBUG_MEMORY) {
+ AcdPrint((
+ "InitializeObjectAllocator: zone 1 created: blksiz=%d size=%d (status=%d)\n",
+ AcdObjectInfoG[ACD_OBJECT_MAX].ulSize,
+ ulSize,
+ status));
+ }
+ //
+ // Initialize the pool info.
+ //
+ AcdPoolInfoG.cbMin = 0xffffffff;
+ AcdPoolInfoG.cbMax = 0;
+ AcdPoolInfoG.ulCurrent = 0;
+ AcdPoolInfoG.ulTotal = 0;
+ AcdPoolInfoG.ulFailures = 0;
+} // InitializeObjectAllocator
+
+
+
+PVOID
+AllocateObjectMemory(
+ IN ULONG fObject
+ )
+{
+ KIRQL irql;
+ POBJECT_INFORMATION pObjectInfo = OBJECT_INFO(fObject);
+ PVOID pObject;
+ ULONG cbBytes = 0, ulTag;
+ static ULONG nAllocations = 0;
+
+ KeAcquireSpinLock(&AcdMemSpinLockG, &irql);
+ //
+ // If the zone is full, or the object
+ // size is greater than the zone object size,
+ // then use the pool allocator.
+ //
+ if (fObject > pObjectInfo->zone.BlockSize) {
+ cbBytes = fObject;
+ ulTag = 'PdcA';
+ }
+ else if (ExIsFullZone(&pObjectInfo->zone)) {
+ cbBytes = pObjectInfo->ulSize;
+ ulTag = pObjectInfo->ulTag;
+ }
+ if (cbBytes) {
+ //
+ // Limit memory usage under stress.
+ // If we have more than 100 outstanding
+ // requests, then we start dropping
+ // them.
+ //
+ if (AcdPoolInfoG.ulCurrent < MAX_ALLOCATED_OBJECTS)
+ pObject = ExAllocatePoolWithTag(NonPagedPool, cbBytes, ulTag);
+ else {
+ pObject = NULL;
+ AcdPoolInfoG.ulFailures++;
+ goto done;
+ }
+ if (cbBytes < AcdPoolInfoG.cbMin)
+ AcdPoolInfoG.cbMin = cbBytes;
+ if (cbBytes > AcdPoolInfoG.cbMax)
+ AcdPoolInfoG.cbMax = cbBytes;
+ AcdPoolInfoG.ulCurrent++;
+ AcdPoolInfoG.ulTotal++;
+ IF_ACDDBG(ACD_DEBUG_MEMORY) {
+ AcdPrint((
+ "AllocateObjectMemory: allocated type %d from pool: pObject=0x%x\n",
+ fObject,
+ pObject));
+ }
+ }
+ else {
+ pObject = ExAllocateFromZone(&pObjectInfo->zone);
+ pObjectInfo->ulCurrent++;
+ pObjectInfo->ulTotal++;
+ IF_ACDDBG(ACD_DEBUG_MEMORY) {
+ AcdPrint((
+ "AllocateObjectMemory: allocated type %d from zone: pObject=0x%x\n",
+ fObject,
+ pObject));
+ }
+ }
+#if DBG
+ IF_ACDDBG(ACD_DEBUG_MEMORY) {
+ INT i;
+
+ if (!(++nAllocations % 10)) {
+ for (i = 0; i <= ACD_OBJECT_MAX; i++) {
+ AcdPrint((
+ "Zone %d: ulCurrent=%d, ulTotal=%d\n",
+ i,
+ AcdObjectInfoG[i].ulCurrent,
+ AcdObjectInfoG[i].ulTotal));
+ }
+ AcdPrint((
+ "Pool: ulCurrent=%d, ulTotal=%d\n",
+ AcdPoolInfoG.ulCurrent,
+ AcdPoolInfoG.ulTotal));
+ }
+ }
+#endif
+done:
+ KeReleaseSpinLock(&AcdMemSpinLockG, irql);
+
+ return pObject;
+} // AllocateObjectMemory
+
+
+
+VOID
+FreeObjectMemory(
+ IN PVOID pObject
+ )
+{
+ KIRQL irql;
+ INT i;
+ POBJECT_INFORMATION pObjectInfo;
+
+ KeAcquireSpinLock(&AcdMemSpinLockG, &irql);
+ for (i = 0; i <= ACD_OBJECT_MAX; i++) {
+ pObjectInfo = &AcdObjectInfoG[i];
+
+ if (ExIsObjectInFirstZoneSegment(&pObjectInfo->zone, pObject)) {
+ ExFreeToZone(&pObjectInfo->zone, pObject);
+ pObjectInfo->ulCurrent--;
+ IF_ACDDBG(ACD_DEBUG_MEMORY) {
+ AcdPrint((
+ "FreeObjectMemory: freed type %d into zone: pObject=0x%x\n",
+ i,
+ pObject));
+ }
+ goto done;
+ }
+ }
+ ExFreePool(pObject);
+ AcdPoolInfoG.ulCurrent--;
+ IF_ACDDBG(ACD_DEBUG_MEMORY) {
+ AcdPrint((
+ "FreeObjectMemory: freed into pool: pObject=0x%x\n",
+ pObject));
+ }
+done:
+ KeReleaseSpinLock(&AcdMemSpinLockG, irql);
+} // FreeObjectMemory
+
+
+
+VOID
+FreeObjectAllocator()
+{
+ // Apparently, we can't do this?
+} // FreeObjectAllocator
diff --git a/private/ntos/tdi/acd/mem.h b/private/ntos/tdi/acd/mem.h
new file mode 100644
index 000000000..3811029e7
--- /dev/null
+++ b/private/ntos/tdi/acd/mem.h
@@ -0,0 +1,41 @@
+/*++
+
+Copyright(c) 1995 Microsoft Corporation
+
+MODULE NAME
+ mem.h
+
+ABSTRACT
+ Header file for memory allocation routines.
+
+AUTHOR
+ Anthony Discolo (adiscolo) 18-Aug-1995
+
+REVISION HISTORY
+
+--*/
+
+//
+// Pre-defined object types.
+// Any other value represents a
+// byte count.
+//
+#define ACD_OBJECT_CONNECTION 0
+#define ACD_OBJECT_MAX 1
+
+VOID
+InitializeObjectAllocator();
+
+PVOID
+AllocateObjectMemory(
+ IN ULONG fObject
+ );
+
+VOID
+FreeObjectMemory(
+ IN PVOID pObject
+ );
+
+VOID
+FreeObjectAllocator();
+
diff --git a/private/ntos/tdi/acd/ntdisp.c b/private/ntos/tdi/acd/ntdisp.c
new file mode 100644
index 000000000..5388656d2
--- /dev/null
+++ b/private/ntos/tdi/acd/ntdisp.c
@@ -0,0 +1,411 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ ntdisp.c
+
+Abstract:
+
+ NT specific routines for dispatching and handling automatic
+ connection notification IRPs.
+
+ The basic architecture involves a user address space,
+ a network transport, and this driver.
+
+ The user address space is responsible for creating a
+ new network connection given a notification from this
+ driver (IOCTL_ACD_NOTIFICATION). When it gets a
+ notification, it is also responsible for pinging the
+ this driver (IOCTL_ACD_KEEPALIVE) so it can be guaranteed
+ the connection is progressing. Once the connection is
+ created, it informs this driver of the success or
+ failure of the connection attempt (IOCTL_ACD_CONNECTION).
+
+ Network transports are responsible for informing this
+ driver of network unreachable errors via TdiConnect()
+ or TdiSendDatagram(). When this happens, the transport
+ is responsible for dequeueing the send request from any
+ of its internal queues and enqueueing the request in
+ this driver (AcdWaitForCompletion()), supplying a callback
+ to be called when the connection has been completed.
+
+Author:
+
+ Anthony Discolo (adiscolo) 18-Apr-1995
+
+Revision History:
+
+--*/
+#include <ndis.h>
+#include <cxport.h>
+#include <tdikrnl.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <acd.h>
+
+#include "acdapi.h"
+#include "debug.h"
+
+//
+// Driver reference count
+//
+ULONG ulAcdOpenCountG;
+
+//
+// Imported routines
+//
+NTSTATUS
+AcdEnable(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+VOID
+AcdCancelNotifications();
+
+NTSTATUS
+AcdWaitForNotification(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdConnectionInProgress(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdSignalCompletion(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdConnectAddress(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+VOID
+AcdReset();
+
+NTSTATUS
+AcdGetAddressAttributes(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdSetAddressAttributes(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+//
+// Internal function prototypes
+//
+NTSTATUS
+AcdCreate(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdDispatchDeviceControl(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdDispatchInternalDeviceControl(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdCleanup(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdClose(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdBind(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+NTSTATUS
+AcdUnbind(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ );
+
+//
+// All of this code is pageable.
+//
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, AcdCreate)
+#pragma alloc_text(PAGE, AcdDispatchDeviceControl)
+#pragma alloc_text(PAGE, AcdDispatchInternalDeviceControl)
+#pragma alloc_text(PAGE, AcdCleanup)
+#pragma alloc_text(PAGE, AcdClose)
+#endif // ALLOC_PRAGMA
+
+
+
+NTSTATUS
+AcdCreate(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+{
+ ulAcdOpenCountG++;
+ IF_ACDDBG(ACD_DEBUG_OPENCOUNT) {
+ AcdPrint(("AcdCreate: ulAcdOpenCountG=%d\n", ulAcdOpenCountG));
+ }
+ return STATUS_SUCCESS;
+} // AcdCreate
+
+
+
+NTSTATUS
+AcdDispatchDeviceControl(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+{
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+ //
+ // Set this in advance. Any IOCTL dispatch routine that cares about it
+ // will modify it itself.
+ //
+ pIrp->IoStatus.Information = 0;
+
+ switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode) {
+ case IOCTL_ACD_RESET:
+ IF_ACDDBG(ACD_DEBUG_IOCTL) {
+ AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_RESET\n"));
+ }
+ AcdReset();
+ status = STATUS_SUCCESS;
+ break;
+ case IOCTL_ACD_ENABLE:
+ IF_ACDDBG(ACD_DEBUG_IOCTL) {
+ AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_ENABLE\n"));
+ }
+ //
+ // Enable/disable requests to/from the driver.
+ //
+ status = AcdEnable(pIrp, pIrpSp);
+ break;
+ case IOCTL_ACD_NOTIFICATION:
+ IF_ACDDBG(ACD_DEBUG_IOCTL) {
+ AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_NOTIFICATION\n"));
+ }
+ //
+ // This irp will be completed upon the
+ // next connection attempt to
+ // allow a user-space process to attempt
+ // to make a connection.
+ //
+ status = AcdWaitForNotification(pIrp, pIrpSp);
+ break;
+ case IOCTL_ACD_KEEPALIVE:
+ IF_ACDDBG(ACD_DEBUG_IOCTL) {
+ AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_KEEPALIVE\n"));
+ }
+ //
+ // Inform the driver that the connection
+ // is in the process of being created.
+ //
+ status = AcdConnectionInProgress(pIrp, pIrpSp);
+ break;
+ case IOCTL_ACD_COMPLETION:
+ IF_ACDDBG(ACD_DEBUG_IOCTL) {
+ AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_COMPLETION\n"));
+ }
+ //
+ // Complete all pending irps that initially
+ // encountered a network unreachable error,
+ // and have been waiting for a connection to be
+ // made.
+ //
+ status = AcdSignalCompletion(pIrp, pIrpSp);
+ break;
+ case IOCTL_ACD_CONNECT_ADDRESS:
+ IF_ACDDBG(ACD_DEBUG_IOCTL) {
+ AcdPrint(("AcdDispatchDeviceControl: IOCTL_ACD_CONNECT_ADDRESS\n"));
+ }
+ //
+ // This allows a user space application to
+ // generate the same automatic connection
+ // mechanism as a transport protocol.
+ //
+ status = AcdConnectAddress(pIrp, pIrpSp);
+ break;
+ default:
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (status != STATUS_PENDING) {
+ pIrp->IoStatus.Status = status;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+
+ return status;
+} // AcdDispatchDeviceControl
+
+
+
+NTSTATUS
+AcdDispatchInternalDeviceControl(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+ //
+ // Set this in advance. Any IOCTL dispatch routine that cares about it
+ // will modify it itself.
+ //
+ pIrp->IoStatus.Information = 0;
+
+ switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode) {
+ case IOCTL_INTERNAL_ACD_BIND:
+ IF_ACDDBG(ACD_DEBUG_IOCTL) {
+ AcdPrint(("AcdDispatchInternalDeviceControl: IOCTL_INTERNAL_ACD_BIND\n"));
+ }
+ //
+ // Transfer entrypoints to client.
+ //
+ status = AcdBind(pIrp, pIrpSp);
+ break;
+ case IOCTL_INTERNAL_ACD_UNBIND:
+ IF_ACDDBG(ACD_DEBUG_IOCTL) {
+ AcdPrint(("AcdDispatchInternalDeviceControl: IOCTL_INTERNAL_ACD_UNBIND\n"));
+ }
+ //
+ // Remove any pending requests from
+ // this driver.
+ //
+ status = AcdUnbind(pIrp, pIrpSp);
+ break;
+ default:
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (status != STATUS_PENDING) {
+ pIrp->IoStatus.Status = status;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+
+ return status;
+} // AcdDispatchInternalDeviceControl
+
+
+
+NTSTATUS
+AcdCleanup(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+{
+ return STATUS_SUCCESS;
+} // AcdCleanup
+
+
+
+NTSTATUS
+AcdClose(
+ IN PIRP pIrp,
+ IN PIO_STACK_LOCATION pIrpSp
+ )
+{
+ ulAcdOpenCountG--;
+ IF_ACDDBG(ACD_DEBUG_OPENCOUNT) {
+ AcdPrint(("AcdClose: ulAcdOpenCountG=%d\n", ulAcdOpenCountG));
+ }
+ if (!ulAcdOpenCountG)
+ AcdReset();
+ return STATUS_SUCCESS;
+} // AcdClose
+
+
+
+NTSTATUS
+AcdDispatch(
+ IN PDEVICE_OBJECT pDeviceObject,
+ IN PIRP pIrp
+ )
+
+/*++
+
+DESCRIPTION
+ This is the dispatch routine for the network connection
+ notification driver.
+
+ARGUMENTS
+ pDeviceObject: a pointer to device object for target device
+
+ pIrp: a pointer to I/O request packet
+
+Return Value:
+ NTSTATUS
+
+--*/
+
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION pIrpSp;
+
+ UNREFERENCED_PARAMETER(pDeviceObject);
+
+ pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
+
+ switch (pIrpSp->MajorFunction) {
+ case IRP_MJ_CREATE:
+ status = AcdCreate(pIrp, pIrpSp);
+ break;
+ case IRP_MJ_DEVICE_CONTROL:
+ return AcdDispatchDeviceControl(pIrp, pIrpSp);
+ case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+ return AcdDispatchInternalDeviceControl(pIrp, pIrpSp);
+ case IRP_MJ_CLEANUP:
+ status = AcdCleanup(pIrp, pIrpSp);
+ break;
+ case IRP_MJ_CLOSE:
+ status = AcdClose(pIrp, pIrpSp);
+ break;
+ default:
+ DbgPrint("AcdDispatch: Invalid major function %lx\n",
+ pIrpSp->MajorFunction);
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (status != STATUS_PENDING) {
+ pIrp->IoStatus.Status = status;
+ pIrp->IoStatus.Information = 0;
+
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+
+ return status;
+} // AcdDispatch
+
diff --git a/private/ntos/tdi/acd/ntinit.c b/private/ntos/tdi/acd/ntinit.c
new file mode 100644
index 000000000..24ad73ef0
--- /dev/null
+++ b/private/ntos/tdi/acd/ntinit.c
@@ -0,0 +1,223 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ntinit.c
+
+Abstract:
+
+ NT specific routines for loading and configuring the
+ automatic connection notification driver (acd.sys).
+
+Author:
+
+ Anthony Discolo (adiscolo) 18-Apr-1995
+
+Revision History:
+
+--*/
+#include <ndis.h>
+#include <cxport.h>
+#include <tdi.h>
+#include <tdikrnl.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <acd.h>
+
+#include "acdapi.h"
+#include "acddefs.h"
+#include "mem.h"
+#include "debug.h"
+
+
+//
+// Global variables
+//
+#if DBG
+ULONG AcdDebugG = 0x0; // see debug.h for flags
+#endif
+
+PDRIVER_OBJECT pAcdDriverObjectG;
+PDEVICE_OBJECT pAcdDeviceObjectG;
+
+HANDLE hSignalNotificationThreadG;
+
+//
+// Imported routines
+//
+VOID
+AcdNotificationRequestThread(
+ PVOID context
+ );
+
+//
+// External function prototypes
+//
+NTSTATUS
+AcdDispatch(
+ IN PDEVICE_OBJECT pDeviceObject,
+ IN PIRP pIrp
+ );
+
+VOID
+AcdConnectionTimer(
+ IN PDEVICE_OBJECT pDeviceObject,
+ IN PVOID pContext
+ );
+
+//
+// Internal function prototypes
+//
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT pDriverObject,
+ IN PUNICODE_STRING pRegistryPath
+ );
+
+BOOLEAN
+GetComputerName(
+ IN PUCHAR szName,
+ IN USHORT cbName
+ );
+
+VOID
+AcdUnload(
+ IN PDRIVER_OBJECT pDriverObject
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, AcdUnload)
+#endif // ALLOC_PRAGMA
+
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT pDriverObject,
+ IN PUNICODE_STRING pRegistryPath
+ )
+
+/*++
+
+DESCRIPTION
+ Initialization routine for the network connection notification driver.
+ It creates the device object and initializes the driver.
+
+ARGUMENTS
+ pDriverObject: a pointer to the driver object created by the system.
+
+ pRegistryPath - the name of the configuration node in the registry.
+
+RETURN VALUE
+ The final status from the initialization operation.
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING deviceName;
+ ULONG i;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ PDEVICE_OBJECT pDeviceObject;
+ PFILE_OBJECT pFileObject;
+
+ //
+ // Initialize the spin lock.
+ //
+ KeInitializeSpinLock(&AcdSpinLockG);
+ //
+ // Initialize the notification and completion
+ // connection queues.
+ //
+ InitializeListHead(&AcdNotificationQueueG);
+ InitializeListHead(&AcdCompletionQueueG);
+ InitializeListHead(&AcdConnectionQueueG);
+ InitializeListHead(&AcdDriverListG);
+ //
+ // Initialize our zone allocator.
+ //
+ InitializeObjectAllocator();
+ //
+ // Create the device object.
+ //
+ pAcdDriverObjectG = pDriverObject;
+ RtlInitUnicodeString(&deviceName, ACD_DEVICE_NAME);
+ status = IoCreateDevice(
+ pDriverObject,
+ 0,
+ &deviceName,
+ FILE_DEVICE_ACD,
+ 0,
+ FALSE,
+ &pAcdDeviceObjectG);
+
+ if (!NT_SUCCESS(status)) {
+ DbgPrint(
+ "AcdDriverEntry: IoCreateDevice failed (status=0x%x)\n",
+ status);
+ return status;
+ }
+ //
+ // Initialize the driver object.
+ //
+ //pDriverObject->DriverUnload = AcdUnload;
+ pDriverObject->DriverUnload = NULL;
+ for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
+ pDriverObject->MajorFunction[i] = AcdDispatch;
+ pDriverObject->FastIoDispatch = NULL;
+ //
+ // Initialize the connection timer. This is
+ // used to make sure pending requests aren't
+ // blocked forever because the user-space
+ // process died trying to make a connection.
+ //
+ IoInitializeTimer(pAcdDeviceObjectG, AcdConnectionTimer, NULL);
+ //
+ // Create the worker thread. We need
+ // a thread because these operations can occur at
+ // DPC irql.
+ //
+ KeInitializeEvent(
+ &AcdRequestThreadEventG,
+ NotificationEvent,
+ FALSE);
+ status = PsCreateSystemThread(
+ &hSignalNotificationThreadG,
+ THREAD_ALL_ACCESS,
+ NULL,
+ NULL,
+ NULL,
+ AcdNotificationRequestThread,
+ NULL);
+ if (!NT_SUCCESS(status)) {
+ DbgPrint(
+ "AcdDriverEntry: PsCreateSystemThread failed (status=0x%x)\n",
+ status);
+ return status;
+ }
+
+ return STATUS_SUCCESS;
+} // DriverEntry
+
+
+
+VOID
+AcdUnload(
+ IN PDRIVER_OBJECT pDriverObject
+ )
+{
+ NTSTATUS status;
+
+ //
+ // BUGBUG: Make sure to unlink all driver
+ // blocks before unloading!
+ //
+ IoDeleteDevice(pAcdDeviceObjectG);
+ //
+ // Free zone allocator.
+ //
+ FreeObjectAllocator();
+} // AcdUnload
diff --git a/private/ntos/tdi/acd/rasacd.rc b/private/ntos/tdi/acd/rasacd.rc
new file mode 100644
index 000000000..d8c0d68bf
--- /dev/null
+++ b/private/ntos/tdi/acd/rasacd.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_NETWORK
+#define VER_FILEDESCRIPTION_STR "RAS Automatic Connection Driver"
+#define VER_INTERNALNAME_STR "rasacd.sys"
+#define VER_ORIGINALFILENAME_STR "rasacd.sys"
+
+#include "common.ver"
+
diff --git a/private/ntos/tdi/acd/request.c b/private/ntos/tdi/acd/request.c
new file mode 100644
index 000000000..7976e733d
--- /dev/null
+++ b/private/ntos/tdi/acd/request.c
@@ -0,0 +1,303 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ request.c
+
+Abstract:
+
+ Worker thread for the automatic connection driver.
+
+Author:
+
+ Anthony Discolo (adiscolo) 17-Apr-1995
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#include <ndis.h>
+#include <cxport.h>
+#include <tdi.h>
+#include <tdikrnl.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <acd.h>
+
+#include "acdapi.h"
+#include "acddefs.h"
+#include "mem.h"
+#include "debug.h"
+
+//
+// External declarations
+//
+VOID AcdPrintAddress(
+ IN PACD_ADDR pAddr
+ );
+
+
+
+VOID
+ProcessCompletion(
+ IN PACD_COMPLETION pCompletion,
+ IN KIRQL irqlCancel,
+ IN KIRQL irqlLock
+ )
+{
+ PLIST_ENTRY pHead;
+ KIRQL irql;
+ PIRP pIrp;
+ PIO_STACK_LOCATION pIrpSp;
+ PACD_NOTIFICATION pNotification;
+
+ ASSERT(!IsListEmpty(&AcdNotificationQueueG));
+ //
+ // Complete the next irp in the
+ // AcdNotificationQueueG queue. These
+ // represent the ioctl completions the
+ // system service has posted. Completing
+ // this request will start the system service
+ // to create a new RAS connection.
+ // Logically, there is always just one.
+ //
+ pHead = RemoveHeadList(&AcdNotificationQueueG);
+ pIrp = CONTAINING_RECORD(pHead, IRP, Tail.Overlay.ListEntry);
+ pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
+ //
+ // Disable the irp's cancel routine.
+ //
+ IoSetCancelRoutine(pIrp, NULL);
+ //
+ // Copy the success flag and the address into the
+ // system buffer. This will get copied into the
+ // user's buffer on return.
+ //
+ pNotification = (PACD_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer;
+ RtlCopyMemory(
+ pNotification,
+ &pCompletion->notif,
+ sizeof (ACD_NOTIFICATION));
+ IF_ACDDBG(ACD_DEBUG_WORKER) {
+ AcdPrint(("AcdNotificationRequestThread: "));
+ AcdPrintAddress(&pCompletion->notif.addr);
+ AcdPrint((", ulFlags=0x%x\n", pCompletion->notif.ulFlags));
+ }
+ //
+ // We can release both the cancel lock
+ // and our lock now.
+ //
+ KeReleaseSpinLock(&AcdSpinLockG, irqlLock);
+ IoReleaseCancelSpinLock(irqlCancel);
+ //
+ // Set the status code and the number
+ // of bytes to be copied back to the user
+ // buffer.
+ //
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = sizeof (ACD_NOTIFICATION);
+ //
+ // Complete the irp.
+ //
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+} // ProcessCompletion
+
+
+
+VOID
+AcdNotificationRequestThread(
+ PVOID context
+ )
+
+/*++
+
+DESCRIPTION
+ This thread handles the notification that an automatic
+ connection may need to be initiated. This needs to
+ happen in a separate thread, because the notification
+ may occur at DPC irql.
+
+ARGUMENTS
+ None.
+
+RETURN VALUE
+ None.
+
+--*/
+
+{
+ KIRQL irql, irql2;
+ PLIST_ENTRY pEntry, pEntry2;
+ PACD_CONNECTION pConnection;
+ PACD_COMPLETION pCompletion;
+ BOOLEAN bStartTimer, bStopTimer;
+
+ UNREFERENCED_PARAMETER(context);
+
+ IoStartTimer(pAcdDeviceObjectG);
+
+ for (;;) {
+ bStartTimer = bStopTimer = FALSE;
+ //
+ // Acquire our lock.
+ //
+ IoAcquireCancelSpinLock(&irql);
+ KeAcquireSpinLock(&AcdSpinLockG, &irql2);
+ //
+ // If there are no irps to complete,
+ // then go back to sleep.
+ //
+ if (IsListEmpty(&AcdNotificationQueueG)) {
+ IF_ACDDBG(ACD_DEBUG_WORKER) {
+ AcdPrint(("AcdNotificationRequestThread: no ioctl to complete\n"));
+ }
+ KeReleaseSpinLock(&AcdSpinLockG, irql2);
+ IoReleaseCancelSpinLock(irql);
+ goto again;
+ }
+ //
+ // Search for connections that haven't
+ // been processed yet.
+ //
+ for (pEntry = AcdConnectionQueueG.Flink;
+ pEntry != &AcdConnectionQueueG;
+ pEntry = pEntry->Flink)
+ {
+ pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry);
+
+ //
+ // Don't issue a request to the service
+ // for more than one simultaneous connection.
+ //
+ IF_ACDDBG(ACD_DEBUG_WORKER) {
+ AcdPrint((
+ "AcdNotificationRequestThread: pConnection=0x%x, fNotif=%d, fCompleting=%d\n",
+ pConnection,
+ pConnection->fNotif,
+ pConnection->fCompleting));
+ }
+ if (pConnection->fNotif)
+ break;
+ //
+ // Skip all connections that are in
+ // the process of being completed.
+ //
+ if (pConnection->fCompleting)
+ continue;
+ //
+ // Make sure there is at least one
+ // request in this connection that
+ // hasn't been canceled.
+ //
+ for (pEntry2 = pConnection->CompletionList.Flink;
+ pEntry2 != &pConnection->CompletionList;
+ pEntry2 = pEntry2->Flink)
+ {
+ pCompletion = CONTAINING_RECORD(pEntry2, ACD_COMPLETION, ListEntry);
+
+ if (!pCompletion->fCanceled) {
+ IF_ACDDBG(ACD_DEBUG_WORKER) {
+ AcdPrint((
+ "AcdNotificationRequestThread: starting pConnection=0x%x, pCompletion=0x%x\n",
+ pConnection,
+ pCompletion));
+ }
+ pConnection->fNotif = TRUE;
+ //
+ // This call releases both the cancel lock
+ // and our lock.
+ //
+ ProcessCompletion(pCompletion, irql, irql2);
+ //
+ // Start the connection timer.
+ //
+ bStartTimer = TRUE;
+ //
+ // We can only process one completion
+ // at a time.
+ //
+ goto again;
+ }
+ }
+ }
+ //
+ // Complete other requests.
+ //
+ if (!IsListEmpty(&AcdCompletionQueueG)) {
+ pEntry = RemoveHeadList(&AcdCompletionQueueG);
+ pCompletion = CONTAINING_RECORD(pEntry, ACD_COMPLETION, ListEntry);
+
+ IF_ACDDBG(ACD_DEBUG_WORKER) {
+ AcdPrint((
+ "AcdNotificationRequestThread: starting pCompletion=0x%x\n",
+ pCompletion));
+ }
+ //
+ // This call releases both the cancel lock
+ // and our lock.
+ //
+ ProcessCompletion(pCompletion, irql, irql2);
+ //
+ // We are done with the completion,
+ // so we can free the memory now.
+ //
+ FREE_MEMORY(pCompletion);
+ //
+ // We can only process one completion
+ // at a time.
+ //
+ goto again;
+
+ }
+ //
+ // If there are no connections pending,
+ // then stop the connection timer.
+ //
+ if (IsListEmpty(&AcdConnectionQueueG))
+ bStopTimer = TRUE;
+ //
+ // Release our lock.
+ //
+ KeReleaseSpinLock(&AcdSpinLockG, irql2);
+ IoReleaseCancelSpinLock(irql);
+again:
+ //
+ // Start or stop the timer, depending
+ // on what we found while we had the
+ // spinlock. We can't hold our spin
+ // lock when we call the Io*Timer
+ // routines.
+ //
+#ifdef notdef
+ if (bStopTimer)
+ IoStopTimer(pAcdDeviceObjectG);
+ else if (bStartTimer)
+ IoStartTimer(pAcdDeviceObjectG);
+#endif
+ //
+ // Wait for something to do. This event
+ // will be signaled by AcdSignalNotification().
+ //
+ IF_ACDDBG(ACD_DEBUG_WORKER) {
+ AcdPrint(("AcdNotificationRequestThread: waiting on AcdPendingCompletionEventG\n"));
+ }
+ KeWaitForSingleObject(
+ &AcdRequestThreadEventG,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(&AcdRequestThreadEventG);
+ IF_ACDDBG(ACD_DEBUG_WORKER) {
+ AcdPrint(("AcdNotificationRequestThread: AcdPendingCompletionEventG signalled\n"));
+ }
+ }
+} // AcdNotificationRequestThread
+
+
diff --git a/private/ntos/tdi/acd/request.h b/private/ntos/tdi/acd/request.h
new file mode 100644
index 000000000..4d2c69824
--- /dev/null
+++ b/private/ntos/tdi/acd/request.h
@@ -0,0 +1,31 @@
+/*++
+
+Copyright(c) 1995 Microsoft Corporation
+
+MODULE NAME
+ request.h
+
+ABSTRACT
+ Header file for completion queue routines.
+
+AUTHOR
+ Anthony Discolo (adiscolo) 18-Aug-1995
+
+REVISION HISTORY
+
+--*/
+
+PACD_COMPLETION GetNextRequest();
+
+BOOLEAN
+EqualAddress(
+ PACD_ADDR pszAddr1,
+ PACD_ADDR pszAddr2
+ );
+
+PACD_COMPLETION GetNextRequestAddress(
+ IN PACD_ADDR pszAddr
+ );
+
+PACD_COMPLETION GetCurrentRequest();
+
diff --git a/private/ntos/tdi/acd/sources b/private/ntos/tdi/acd/sources
new file mode 100644
index 000000000..b787b8419
--- /dev/null
+++ b/private/ntos/tdi/acd/sources
@@ -0,0 +1,46 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=
+MINORCOMP=
+
+TARGETNAME=rasacd
+TARGETPATH=obj
+TARGETTYPE=DRIVER
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+INCLUDES=..\inc;..\..\inc;..\..\..\inc;..\..\..\..\inc
+
+C_DEFINES=-DNT -DNETSCAPE_HACK
+
+SOURCES= \
+ ntinit.c \
+ ntdisp.c \
+ api.c \
+ request.c \
+ timer.c \
+ mem.c \
+ rasacd.rc
+
diff --git a/private/ntos/tdi/acd/table.c b/private/ntos/tdi/acd/table.c
new file mode 100644
index 000000000..0846b97e6
--- /dev/null
+++ b/private/ntos/tdi/acd/table.c
@@ -0,0 +1,286 @@
+/*++
+
+Copyright(c) 1995 Microsoft Corporation
+
+MODULE NAME
+ table.c
+
+ABSTRACT
+ Generic hash table manipulation routines.
+
+AUTHOR
+ Anthony Discolo (adiscolo) 28-Jul-1995
+
+REVISION HISTORY
+
+--*/
+
+#include <ndis.h>
+#include <cxport.h>
+#include <tdi.h>
+#include <tdikrnl.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <acd.h>
+#include <acdapi.h>
+
+#include "table.h"
+#include "acddefs.h"
+#include "mem.h"
+#include "debug.h"
+
+
+
+PHASH_TABLE
+NewTable()
+{
+ PHASH_TABLE pTable;
+ INT i;
+
+ ALLOCATE_MEMORY(sizeof (HASH_TABLE), pTable);
+ if (pTable == NULL) {
+ DbgPrint("AcdNewTable: ExAllocatePool failed\n");
+ return NULL;
+ }
+ KeInitializeSpinLock(&pTable->SpinLock);
+ for (i = 0; i < NBUCKETS; i++)
+ InitializeListHead(&pTable->ListEntry[i]);
+
+ return pTable;
+} // NewTable
+
+
+
+VOID
+FreeHashTableEntry(
+ PHASH_ENTRY pHashEntry
+ )
+{
+ FREE_MEMORY(pHashEntry);
+} // FreeHashTableEntry
+
+
+
+VOID
+ClearTable(
+ PHASH_TABLE pTable
+ )
+{
+ KIRQL irql;
+ INT i;
+ PLIST_ENTRY pHead;
+ PHASH_ENTRY pHashEntry;
+
+ KeAcquireSpinLock(&pTable->SpinLock, &irql);
+ for (i = 0; i < NBUCKETS; i++) {
+ while (!IsListEmpty(&pTable->ListEntry[i])) {
+ pHead = RemoveHeadList(&pTable->ListEntry[i]);
+ pHashEntry = CONTAINING_RECORD(pHead, HASH_ENTRY, ListEntry);
+
+ FreeHashTableEntry(pHashEntry);
+ }
+ }
+ KeReleaseSpinLock(&pTable->SpinLock, irql);
+} // ClearTable
+
+
+
+VOID
+FreeTable(
+ PHASH_TABLE pTable
+ )
+{
+ ClearTable(pTable);
+ FREE_MEMORY(pTable);
+} // FreeTable
+
+
+
+VOID
+EnumTable(
+ IN PHASH_TABLE pTable,
+ IN PHASH_TABLE_ENUM_PROC pProc,
+ IN PVOID pArg
+ )
+{
+ INT i;
+ PLIST_ENTRY pEntry;
+ PHASH_ENTRY pHashEntry;
+ KIRQL irql;
+
+ KeAcquireSpinLock(&pTable->SpinLock, &irql);
+ for (i = 0; i < NBUCKETS; i++) {
+ for (pEntry = pTable->ListEntry[i].Flink;
+ pEntry != &pTable->ListEntry[i];
+ pEntry = pEntry->Flink)
+ {
+ pHashEntry = CONTAINING_RECORD(pEntry, HASH_ENTRY, ListEntry);
+
+ //
+ // If the enumerator procedure
+ // returns FALSE, terminate the
+ // enumeration.
+ //
+ if (!pProc(pArg, &pHashEntry->szKey, pHashEntry->ulData))
+ goto done;
+ }
+ }
+done:
+ KeReleaseSpinLock(&pTable->SpinLock, irql);
+} // EnumTable
+
+
+
+INT
+HashString(
+ IN PACD_ADDR pszKey
+ )
+{
+ ULONG ulHashValue = 0;
+ CHAR ch;
+ PCSZ p = (PCSZ)pszKey;
+
+ while (*p != L'\0') {
+ ch = tolower(*p);
+ ulHashValue += (INT)(ch) * (INT)(ch);
+ p++;
+ }
+
+ return (INT)(ulHashValue % NBUCKETS);
+} // HashString
+
+
+
+BOOLEAN
+IsEqualKey(
+ PACD_ADDR pszKey1,
+ PACD_ADDR pszKey2
+ )
+{
+ BOOLEAN fFound;
+
+ fFound = (BOOLEAN)RtlEqualMemory(pszKey1, pszKey2, sizeof (ACD_ADDR));
+ IF_ACDDBG(ACD_DEBUG_TABLE) {
+ AcdPrint(("AcdIsEqualKey(%s, %s) returns %d\n", pszKey1, pszKey2, fFound));
+ }
+ return fFound;
+} // IsEqualKey
+
+
+
+PHASH_ENTRY
+GetTableEntryNL(
+ IN PHASH_TABLE pTable,
+ IN PACD_ADDR pszKey
+ )
+{
+ INT nBucket = HashString(pszKey);
+ PLIST_ENTRY pEntry;
+ PHASH_ENTRY pHashEntry;
+
+ for (pEntry = pTable->ListEntry[nBucket].Flink;
+ pEntry != &pTable->ListEntry[nBucket];
+ pEntry = pEntry->Flink)
+ {
+ pHashEntry = CONTAINING_RECORD(pEntry, HASH_ENTRY, ListEntry);
+
+ if (IsEqualKey(&pHashEntry->szKey, pszKey)) {
+ IF_ACDDBG(ACD_DEBUG_TABLE) {
+ AcdPrint(("AcdGetTableEntryNL(0x%x, %s) returns 0x%x\n", pTable, pszKey, pHashEntry));
+ }
+ return pHashEntry;
+ }
+ }
+
+ IF_ACDDBG(ACD_DEBUG_TABLE) {
+ AcdPrint(("AcdGetTableEntryNL(0x%x, %s) returns NULL\n", pTable, pszKey));
+ }
+ return NULL;
+} // GetTableEntryNL
+
+
+
+BOOLEAN
+GetTableEntry(
+ IN PHASH_TABLE pTable,
+ IN PACD_ADDR pszKey,
+ OUT PULONG pulData
+ )
+{
+ KIRQL irql;
+ PHASH_ENTRY pHashEntry;
+
+ KeAcquireSpinLock(&pTable->SpinLock, &irql);
+ pHashEntry = GetTableEntryNL(pTable, pszKey);
+ KeReleaseSpinLock(&pTable->SpinLock, irql);
+
+ if (pHashEntry != NULL) {
+ if (pulData != NULL)
+ *pulData = pHashEntry->ulData;
+ return TRUE;
+ }
+
+ return FALSE;
+} // GetTableEntry
+
+
+
+BOOLEAN
+PutTableEntry(
+ IN PHASH_TABLE pTable,
+ IN PACD_ADDR pszKey,
+ IN ULONG ulData
+ )
+{
+ KIRQL irql;
+ BOOLEAN fSuccess = FALSE;
+ INT nBucket = HashString(pszKey);
+ PHASH_ENTRY pHashEntry;
+
+ IF_ACDDBG(ACD_DEBUG_TABLE) {
+ AcdPrint(("AcdPutTableEntry(0x%x, %s)\n", pTable, pszKey));
+ }
+
+ KeAcquireSpinLock(&pTable->SpinLock, &irql);
+
+ pHashEntry = GetTableEntryNL(pTable, pszKey);
+ if (pHashEntry == NULL) {
+ ALLOCATE_MEMORY(ACD_OBJECT_HASHENTRY, pHashEntry);
+ if (pHashEntry == NULL) {
+ DbgPrint("PutTableEntry: ExAllocatePool failed\n");
+ goto done;
+ }
+ RtlCopyMemory(pHashEntry->szKey, pszKey, sizeof (ACD_ADDR));
+ InsertHeadList(
+ &pTable->ListEntry[nBucket],
+ &pHashEntry->ListEntry);
+ }
+ pHashEntry->ulData = ulData;
+ fSuccess = TRUE;
+
+done:
+ KeReleaseSpinLock(&pTable->SpinLock, irql);
+ return fSuccess;
+} // PutTableEntry
+
+
+
+BOOLEAN
+DeleteTableEntry(
+ IN PHASH_TABLE pTable,
+ IN PACD_ADDR pszKey
+ )
+{
+ KIRQL irql;
+ PHASH_ENTRY pHashEntry;
+
+ KeAcquireSpinLock(&pTable->SpinLock, &irql);
+ pHashEntry = GetTableEntryNL(pTable, pszKey);
+ if (pHashEntry != NULL) {
+ RemoveEntryList(&pHashEntry->ListEntry);
+ FreeHashTableEntry(pHashEntry);
+ }
+ KeReleaseSpinLock(&pTable->SpinLock, irql);
+
+ return (pHashEntry != NULL);
+} // DeleteTableEntry
diff --git a/private/ntos/tdi/acd/table.h b/private/ntos/tdi/acd/table.h
new file mode 100644
index 000000000..17a5ef1a0
--- /dev/null
+++ b/private/ntos/tdi/acd/table.h
@@ -0,0 +1,77 @@
+/*++
+
+Copyright(c) 1995 Microsoft Corporation
+
+MODULE NAME
+ table.h
+
+ABSTRACT
+ Header file for generic hash table routines.
+
+AUTHOR
+ Anthony Discolo (adiscolo) 28-Jul-1995
+
+REVISION HISTORY
+
+--*/
+
+//
+// Number of hash table buckets.
+//
+#define NBUCKETS 13
+
+//
+// Generic hash table structure.
+//
+typedef struct _HASH_TABLE {
+ LIST_ENTRY ListEntry[NBUCKETS];
+ KSPIN_LOCK SpinLock;
+} HASH_TABLE, *PHASH_TABLE;
+
+//
+// Hash table enumerator procedure.
+// Returns TRUE to continue enumeration,
+// FALSE to terminate enumeration.
+//
+typedef BOOLEAN (*PHASH_TABLE_ENUM_PROC)(PVOID, PACD_ADDR, ULONG);
+
+
+PHASH_TABLE
+NewTable();
+
+VOID
+ClearTable(
+ IN PHASH_TABLE pTable
+ );
+
+VOID
+FreeTable(
+ IN PHASH_TABLE pTable
+ );
+
+VOID
+EnumTable(
+ IN PHASH_TABLE pTable,
+ IN PHASH_TABLE_ENUM_PROC pProc,
+ IN PVOID pArg
+ );
+
+BOOLEAN
+GetTableEntry(
+ IN PHASH_TABLE pTable,
+ IN PACD_ADDR pszKey,
+ OUT PULONG pulData
+ );
+
+BOOLEAN
+PutTableEntry(
+ IN PHASH_TABLE pTable,
+ IN PACD_ADDR pszKey,
+ IN ULONG ulData
+ );
+
+BOOLEAN
+DeleteTableEntry(
+ IN PHASH_TABLE pTable,
+ IN PACD_ADDR pszKey
+ );
diff --git a/private/ntos/tdi/acd/timer.c b/private/ntos/tdi/acd/timer.c
new file mode 100644
index 000000000..cf4f3ab63
--- /dev/null
+++ b/private/ntos/tdi/acd/timer.c
@@ -0,0 +1,145 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ timer.c
+
+Abstract:
+
+ Timer thread to monitor connection progress in the
+ automatic connection driver (acd.sys).
+
+Author:
+
+ Anthony Discolo (adiscolo) 25-Apr-1995
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#include <ndis.h>
+#include <cxport.h>
+#include <tdi.h>
+#include <tdikrnl.h>
+#include <tdistat.h>
+#include <tdiinfo.h>
+#include <acd.h>
+
+#include "acdapi.h"
+#include "table.h"
+#include "acddefs.h"
+#include "debug.h"
+
+//
+// Imported routines.
+//
+VOID
+AcdSignalCompletionCommon(
+ IN PACD_CONNECTION pConnection,
+ IN BOOLEAN fSuccess
+ );
+
+//
+// Keep track how long the user-space
+// process has been attempting a connection.
+//
+#define ACD_MAX_TIMER_CALLS 3*60 // 3 minutes
+
+//
+// We give the user-space process
+// some slack on missed pings.
+//
+#define ACD_MAX_MISSED_PINGS 40 // 20 seconds
+
+
+
+VOID
+AcdConnectionTimer(
+ IN PDEVICE_OBJECT pDeviceObject,
+ IN PVOID pContext
+ )
+{
+ PLIST_ENTRY pEntry;
+ PACD_CONNECTION pConnection;
+ BOOLEAN bCancel = FALSE;
+
+ //
+ // Acquire the spin lock.
+ // We're guaranteed to be at DPC
+ // since this is a timer routine.
+ //
+ KeAcquireSpinLockAtDpcLevel(&AcdSpinLockG);
+ //
+ // If the user-space process responsible
+ // for creating the connection hasn't
+ // pinged us in a while, or if it hasn't
+ // created a connection in 3 minutes,
+ // cancel all the pending requests.
+ //
+ for (pEntry = AcdConnectionQueueG.Flink;
+ pEntry != &AcdConnectionQueueG;
+ pEntry = pEntry->Flink)
+ {
+ pConnection = CONTAINING_RECORD(pEntry, ACD_CONNECTION, ListEntry);
+
+ IF_ACDDBG(ACD_DEBUG_TIMER) {
+ PACD_COMPLETION pCompletion;
+
+ AcdPrint((
+ "AcdConnectionTimer: pConnection=0x%x, fNotif=%d, szAddr=",
+ pConnection,
+ pConnection->fNotif));
+ pCompletion = CONTAINING_RECORD(pConnection->CompletionList.Flink, ACD_COMPLETION, ListEntry);
+ AcdPrintAddress(&pCompletion->notif.addr);
+ AcdPrint((", nTimerCalls=%d, nMissedPings=%d\n",
+ pConnection->ulTimerCalls,
+ pConnection->ulMissedPings));
+ }
+ //
+ // If we haven't reported the connection to
+ // user space yet, or it is in the process of
+ // being completed, then don't time it out.
+ //
+ if (!pConnection->fNotif || pConnection->fCompleting)
+ continue;
+
+ pConnection->ulTimerCalls++;
+ if (pConnection->fProgressPing)
+ pConnection->ulMissedPings = 0;
+ else
+ pConnection->ulMissedPings++;
+ if (pConnection->ulTimerCalls >= ACD_MAX_TIMER_CALLS ||
+ pConnection->ulMissedPings >= ACD_MAX_MISSED_PINGS)
+ {
+ IF_ACDDBG(ACD_DEBUG_TIMER) {
+ AcdPrint((
+ "AcdConnectionTimer: canceling pConnection=0x%x\n",
+ pConnection));
+ }
+ //
+ // Set the completion-in-progress flag so
+ // this request cannot be completed after
+ // we release the spin lock.
+ //
+ pConnection->fCompleting = TRUE;
+ bCancel = TRUE;
+ break;
+ }
+ }
+ //
+ // Release the spin lock.
+ //
+ KeReleaseSpinLockFromDpcLevel(&AcdSpinLockG);
+ //
+ // We now process all the canceled requests.
+ //
+ if (bCancel)
+ AcdSignalCompletionCommon(pConnection, FALSE);
+} // AcdConnectionTimer
+