windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/atkdrvr.c

935 lines
21 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
atkdrvr.c
Abstract:
This module implements Appletalk Transport Provider driver interfaces
for NT
Author:
Jameel Hyder (jameelh@microsoft.com)
Nikhil Kamkolkar (nikhilk@microsoft.com)
Revision History:
19 Jun 1992 Initial Version
Notes: Tab stop: 4
--*/
#include <atalk.h>
#pragma hdrstop
// File module number for errorlogging
#define FILENUM ATKDRVR
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGEINIT, AtalkCleanup)
#pragma alloc_text(PAGE, atalkUnload)
#pragma alloc_text(PAGE, AtalkDispatchCreate)
#pragma alloc_text(PAGE, AtalkDispatchCleanup)
#pragma alloc_text(PAGE, AtalkDispatchClose)
#pragma alloc_text(PAGE, AtalkDispatchDeviceControl)
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This is the initialization routine for the Windows NT Appletalk
driver. This routine creates the device object for the Atalk
device and performs all other driver initialization.
Arguments:
DriverObject - Pointer to driver object created by the system.
RegistryPath- Path to the root of the section in the registry for this
driver
Return Value:
The function value is the final status from the initialization operation. If
this is not STATUS_SUCCESS the driver will not load.
--*/
{
NTSTATUS status;
UNICODE_STRING deviceName;
USHORT i, j;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("Appletalk DriverEntry - Entered !!!\n"));
TdiInitialize();
INITIALIZE_SPIN_LOCK(&AtalkStatsLock);
INITIALIZE_SPIN_LOCK(&AtalkSktCacheLock);
INITIALIZE_SPIN_LOCK(&ArapSpinLock);
#if DBG
INITIALIZE_SPIN_LOCK(&AtalkDebugSpinLock);
#endif
// Initialize event for locking/unlocking pageable sections. Set it to signalled state
// so that the first wait is satisfied.
KeInitializeMutex(&AtalkPgLkMutex, 0xFFFF);
// Create the device object. (IoCreateDevice zeroes the memory
// occupied by the object.)
for (i = 0; i < ATALK_NO_DEVICES; i++)
{
RtlInitUnicodeString(&deviceName, AtalkDeviceNames[i]);
status = IoCreateDevice(
DriverObject, // DriverObject
ATALK_DEV_EXT_LEN, // DeviceExtension
&deviceName, // DeviceName
FILE_DEVICE_NETWORK, // DeviceType
FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics
(BOOLEAN)FALSE, // Exclusive
(PDEVICE_OBJECT *) &AtalkDeviceObject[i]); // DeviceObject
if (!NT_SUCCESS(status))
{
LOG_ERROR(EVENT_ATALK_CANT_CREATE_DEVICE, status, NULL, 0);
// Delete all the devices created so far, if any
for (j = 0; j < i; j++)
{
IoDeleteDevice((PDEVICE_OBJECT)AtalkDeviceObject[j]);
}
return status;
}
// Assumption:
// 'i' will correspond to the Device type in the ATALK_DEVICE_TYPE enum
AtalkDeviceObject[i]->Ctx.adc_DevType = (ATALK_DEV_TYPE)i;
// Initialize the provider info and statistics structures for this device
AtalkQueryInitProviderInfo((ATALK_DEV_TYPE)i,
&AtalkDeviceObject[i]->Ctx.adc_ProvInfo);
#if 0
// NOTE: Implement
AtalkQueryInitProviderStatistics((ATALK_DEV_TYPE)i,
&AtalkDeviceObject[i]->Ctx.adc_ProvStats);
#endif
}
// Initialize the driver object for this driver's entry points.
DriverObject->MajorFunction[IRP_MJ_CREATE] = AtalkDispatchCreate;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = AtalkDispatchCleanup;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = AtalkDispatchClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AtalkDispatchDeviceControl;
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = AtalkDispatchInternalDeviceControl;
DriverObject->DriverUnload = atalkUnload;
// Get lock handles to all the conditional pageable sections
AtalkLockInit(&AtalkPgLkSection[NBP_SECTION], AtalkNbpAction);
AtalkLockInit(&AtalkPgLkSection[ZIP_SECTION], AtalkZipGetMyZone);
AtalkLockInit(&AtalkPgLkSection[TDI_SECTION], AtalkTdiCleanupAddress);
AtalkLockInit(&AtalkPgLkSection[ATP_SECTION], AtalkAtpCloseAddress);
AtalkLockInit(&AtalkPgLkSection[ASP_SECTION], AtalkAspCloseAddress);
AtalkLockInit(&AtalkPgLkSection[PAP_SECTION], AtalkPapCleanupAddress);
AtalkLockInit(&AtalkPgLkSection[ASPC_SECTION], AtalkAspCCloseAddress);
AtalkLockInit(&AtalkPgLkSection[ADSP_SECTION], AtalkAdspCleanupAddress);
AtalkLockInit(&AtalkPgLkSection[ROUTER_SECTION], AtalkRtmpPacketInRouter);
AtalkLockInit(&AtalkPgLkSection[INIT_SECTION], AtalkInitRtmpStartProcessingOnPort);
AtalkLockInit(&AtalkPgLkSection[ARAP_SECTION], ArapExchangeParms);
AtalkLockInit(&AtalkPgLkSection[PPP_SECTION], AllocPPPConn);
AtalkLockInitIfNecessary();
status = AtalkInitializeTransport(DriverObject, RegistryPath);
AtalkUnlockInitIfNecessary();
if (!NT_SUCCESS(status))
{
#if DBG
// Make sure we are not unloading with any locked sections
for (i = 0; i < LOCKABLE_SECTIONS; i++)
{
ASSERT (AtalkPgLkSection[i].ls_LockCount == 0);
}
#endif
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("DriverEntry: AtalkInitializeTransport failed %lx\n",status));
}
else
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("DriverEntry: AtalkInitializeTransport complete %lx\n",status));
}
return status;
} // DriverEntry
NTSTATUS
AtalkDispatchCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
This is the dispatch routine for Create functions for the Appletalk driver.
Arguments:
DeviceObject - Pointer to device object for target device
pIrp - Pointer to I/O request packet
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION pIrpSp;
PFILE_FULL_EA_INFORMATION ea;
INT createObject;
TA_APPLETALK_ADDRESS tdiAddress;
CONNECTION_CONTEXT connectionContext;
PATALK_DEV_OBJ atalkDeviceObject;
UCHAR protocolType, socketType;
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_INFO,
("AtalkDispatchCreate: entered for irp %lx\n", pIrp));
// Make sure status information is consistent every time.
IoMarkIrpPending(pIrp);
pIrp->IoStatus.Status = STATUS_PENDING;
pIrp->IoStatus.Information = 0;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
// Both opens must complete synchronously. It is possible we return
// status_pending to the system, but it will not return to the caller
// until the call actually completes. In our case, we block until the
// actions are complete. So we can be assured that we can complete the irp
// upon return from these calls.
createObject = AtalkIrpGetEaCreateType(pIrp);
ea = (PFILE_FULL_EA_INFORMATION)pIrp->AssociatedIrp.SystemBuffer;
switch (createObject)
{
case TDI_TRANSPORT_ADDRESS_FILE :
if (ea->EaValueLength < sizeof(TA_APPLETALK_ADDRESS))
{
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_ERR,
("AtalkDispatchCreate: addr size %d\n", ea->EaValueLength));
status = STATUS_EA_LIST_INCONSISTENT;
break;
}
// We have the AtalkTdiOpenAddress routine look at only the first
// address in the list of addresses by casting the passed address
// to TA_APPLETALK_ADDRESS.
RtlCopyMemory(
&tdiAddress,
(PBYTE)(&ea->EaName[ea->EaNameLength+1]),
sizeof(TA_APPLETALK_ADDRESS));
// Also, get the protocol type field for the socket
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_INFO,
("AtalkDispatchCreate: Remaining File Name : %S\n",
&pIrpSp->FileObject->FileName));
if (!NT_SUCCESS(AtalkGetProtocolSocketType(&atalkDeviceObject->Ctx,
&pIrpSp->FileObject->FileName,
&protocolType,
&socketType)))
{
status = STATUS_NO_SUCH_DEVICE;
break;
}
status = AtalkTdiOpenAddress(
pIrp,
pIrpSp,
&tdiAddress,
protocolType,
socketType,
&atalkDeviceObject->Ctx);
break;
case TDI_CONNECTION_FILE :
if (ea->EaValueLength < sizeof(CONNECTION_CONTEXT))
{
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_ERR,
("AtalkDispatchCreate: Context size %d\n", ea->EaValueLength));
status = STATUS_EA_LIST_INCONSISTENT;
break;
}
RtlCopyMemory(&connectionContext,
&ea->EaName[ea->EaNameLength+1],
sizeof(CONNECTION_CONTEXT));
status = AtalkTdiOpenConnection(pIrp,
pIrpSp,
connectionContext,
&atalkDeviceObject->Ctx);
break;
case TDI_CONTROL_CHANNEL_FILE :
status = AtalkTdiOpenControlChannel(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
default:
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_ERR,
("AtalkDispatchCreate: unknown EA passed!\n"));
status = STATUS_INVALID_EA_NAME;
break;
}
// Successful completion.
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_INFO,
("AtalkDispatchCreate complete irp %lx status %lx\n", pIrp, status));
if (NT_SUCCESS(status))
INTERLOCKED_INCREMENT_LONG(&AtalkHandleCount, &AtalkStatsLock);
if (status != STATUS_PENDING)
{
pIrpSp->Control &= ~SL_PENDING_RETURNED;
ASSERT (status != STATUS_PENDING);
TdiCompleteRequest(pIrp, status);
}
return status;
} // AtalkDispatchCreate
NTSTATUS
AtalkDispatchCleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
This is the dispatch routine for Cleanup functions for the Appletalk driver.
Arguments:
DeviceObject - Pointer to device object for target device
pIrp - Pointer to I/O request packet
Return Value:
NTSTATUS -- Indicates whether the request was successfully
started/completed
--*/
{
NTSTATUS status;
PATALK_DEV_OBJ atalkDeviceObject;
PIO_STACK_LOCATION pIrpSp;
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_INFO,
("AtalkDispatchCleanup: entered irp %lx\n", pIrp));
// Make sure status information is consistent every time.
IoMarkIrpPending (pIrp);
pIrp->IoStatus.Status = STATUS_PENDING;
pIrp->IoStatus.Information = 0;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
switch ((ULONG_PTR)(pIrpSp->FileObject->FsContext2) & 0xFF)
{
case TDI_TRANSPORT_ADDRESS_FILE :
status = AtalkTdiCleanupAddress(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_CONNECTION_FILE :
status = AtalkTdiCleanupConnection(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_CONTROL_CHANNEL_FILE :
status = STATUS_SUCCESS;
break;
default:
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_ERR,
("AtalkDispatchCleaup: Invalid object %s\n",
pIrpSp->FileObject->FsContext));
status = STATUS_INVALID_HANDLE;
break;
}
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_INFO,
("AtalkDispatchCleanup complete irp %lx status %lx\n", pIrp, status));
if (status != STATUS_PENDING)
{
pIrpSp->Control &= ~SL_PENDING_RETURNED;
ASSERT (status != STATUS_PENDING);
TdiCompleteRequest(pIrp, status);
}
return(status);
} // AtalkDispatchCleanup
NTSTATUS
AtalkDispatchClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
This is the dispatch routine for Close functions for the Appletalk driver.
Arguments:
DeviceObject - Pointer to device object for target device
irp - Pointer to I/O request packet
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION pIrpSp;
PATALK_DEV_OBJ atalkDeviceObject;
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_INFO,
("AtalkDispatchClose: entered for IRP %lx\n", pIrp));
// Make sure status information is consistent every time.
IoMarkIrpPending(pIrp);
pIrp->IoStatus.Status = STATUS_PENDING;
pIrp->IoStatus.Information = 0;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
switch ((ULONG_PTR)(pIrpSp->FileObject->FsContext2) & 0xFF)
{
case TDI_TRANSPORT_ADDRESS_FILE :
status = AtalkTdiCloseAddress(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_CONNECTION_FILE :
status = AtalkTdiCloseConnection(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_CONTROL_CHANNEL_FILE :
status = AtalkTdiCloseControlChannel(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
default:
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_ERR,
("AtalkDispatchClose: Invalid object %s\n",
pIrpSp->FileObject->FsContext));
status = STATUS_INVALID_HANDLE;
break;
}
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_INFO,
("AtalkDispatchClose complete irp %lx status %lx\n", pIrp, status));
if (status != STATUS_PENDING)
{
pIrpSp->Control &= ~SL_PENDING_RETURNED;
ASSERT (status != STATUS_PENDING);
TdiCompleteRequest(pIrp, status);
}
INTERLOCKED_DECREMENT_LONG(&AtalkHandleCount, &AtalkStatsLock);
return(status);
} // AtalkDispatchClose
NTSTATUS
AtalkDispatchDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
This is the dispatch routine for Device Control functions for the Appletalk driver.
Arguments:
DeviceObject - Pointer to device object for target device
pIrp - Pointer to I/O request packet
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
PATALK_DEV_OBJ atalkDeviceObject;
PIO_STACK_LOCATION pIrpSp;
ULONG IoControlCode;
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_INFO,
("AtalkDispatchDeviceControl: irp %lx\n", pIrp));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
IoControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
//
// if it's a request from ARAP, process it here and return
//
if (IoControlCode > IOCTL_ARAP_START && IoControlCode < IOCTL_ARAP_END)
{
status = ArapProcessIoctl(pIrp);
return(status);
}
// Do a map and call the internal device io control function.
// That will also perform the completion.
status = TdiMapUserRequest(DeviceObject,
pIrp,
pIrpSp);
if (status == STATUS_SUCCESS)
{
status = AtalkDispatchInternalDeviceControl(
DeviceObject,
pIrp);
//
// AtalkDispatchInternalDeviceControl expects to complete the
// irp
//
}
else
{
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_WARN,
("AtalkDispatchDeviceControl: TdiMap failed %lx\n", status));
pIrpSp->Control &= ~SL_PENDING_RETURNED;
ASSERT (status != STATUS_PENDING);
TdiCompleteRequest(pIrp, status);
}
return(status);
} // AtalkDispatchDeviceControl
NTSTATUS
AtalkDispatchInternalDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
This is the dispatch routine for Internal Device Control functions
for the Appletalk driver.
Arguments:
DeviceObject - Pointer to device object for target device
pIrp - Pointer to I/O request packet
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION pIrpSp;
PATALK_DEV_OBJ atalkDeviceObject;
KIRQL oldIrql;
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_INFO,
("AtalkDispatchInternalDeviceControl entered for IRP %lx\n", pIrp));
// Make sure status information is consistent every time.
IoMarkIrpPending (pIrp);
pIrp->IoStatus.Status = STATUS_PENDING;
pIrp->IoStatus.Information = 0;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
IoAcquireCancelSpinLock(&oldIrql);
if (!pIrp->Cancel)
{
IoSetCancelRoutine(pIrp, (PDRIVER_CANCEL)AtalkTdiCancel);
}
else
{
IoReleaseCancelSpinLock(oldIrql);
status = STATUS_CANCELLED;
TdiCompleteRequest(pIrp, status);
return(status);
}
IoReleaseCancelSpinLock(oldIrql);
// Branch to the appropriate request handler.
switch (pIrpSp->MinorFunction)
{
case TDI_ACCEPT:
status = AtalkTdiAccept(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_RECEIVE_DATAGRAM:
status = AtalkTdiReceiveDgram(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_SEND_DATAGRAM:
status = AtalkTdiSendDgram(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_SET_EVENT_HANDLER:
status = AtalkTdiSetEventHandler(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_RECEIVE:
status = AtalkTdiReceive(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_SEND:
status = AtalkTdiSend(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_ACTION:
ASSERT(pIrp->MdlAddress != NULL);
status = AtalkTdiAction(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_ASSOCIATE_ADDRESS:
status = AtalkTdiAssociateAddress(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_DISASSOCIATE_ADDRESS:
status = AtalkTdiDisassociateAddress(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_CONNECT:
status = AtalkTdiConnect(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_DISCONNECT:
status = AtalkTdiDisconnect(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_LISTEN:
status = AtalkTdiListen(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_QUERY_INFORMATION:
ASSERT(pIrp->MdlAddress != NULL);
status = AtalkTdiQueryInformation(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
case TDI_SET_INFORMATION:
status = AtalkTdiSetInformation(pIrp,
pIrpSp,
&atalkDeviceObject->Ctx);
break;
default:
// Something we don't know about was submitted.
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_ERR,
("AtalkDispatchInternal: fnct %lx\n", pIrpSp->MinorFunction));
status = STATUS_INVALID_DEVICE_REQUEST;
}
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_INFO,
("AtalkDispatchInternal complete irp %lx status %lx\n", pIrp, status));
// Return the immediate status code to the caller.
if (status != STATUS_PENDING)
{
pIrpSp->Control &= ~SL_PENDING_RETURNED;
// Complete the request, this will also dereference it.
pIrp->CancelRoutine = NULL;
ASSERT (status != STATUS_PENDING);
TdiCompleteRequest(pIrp, status);
}
return status;
} // AtalkDispatchInternalDeviceControl
VOID
atalkUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This is the unload routine for the Appletalk driver.
NOTE: Unload will not be called until all the handles have been
closed successfully. We just shutdown all the ports, and do
misc. cleanup.
Arguments:
DriverObject - Pointer to driver object for this driver.
Return Value:
None.
--*/
{
UNREFERENCED_PARAMETER (DriverObject);
AtalkBindnUnloadStates |= ATALK_UNLOADING;
// if we hit that timing window where binding or PnP event is going on,
// sleep (for a second each time) until that action completes
while (AtalkBindnUnloadStates & (ATALK_BINDING | ATALK_PNP_IN_PROGRESS))
{
AtalkSleep(1000);
}
AtalkLockInitIfNecessary();
AtalkCleanup();
AtalkUnlockInitIfNecessary();
#if DBG
{
int i;
// Make sure we are not unloading with any locked sections
for (i = 0; i < LOCKABLE_SECTIONS; i++)
{
ASSERT (AtalkPgLkSection[i].ls_LockCount == 0);
}
}
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("Appletalk driver unloaded\n"));
#endif
} // atalkUnload
VOID
AtalkCleanup(
VOID
)
/*++
Routine Description:
This is synchronous and will block until Unload Completes
Arguments:
None.
Return Value:
None.
--*/
{
PPORT_DESCRIPTOR pPortDesc;
LONG i;
KIRQL OldIrql;
// Stop the timer subsystem
AtalkTimerFlushAndStop();
ASSERT(KeGetCurrentIrql() == LOW_LEVEL);
ACQUIRE_SPIN_LOCK(&AtalkPortLock, &OldIrql);
// Shut down all ports
while ((pPortDesc = AtalkPortList) != NULL)
{
RELEASE_SPIN_LOCK(&AtalkPortLock, OldIrql);
AtalkPortShutdown(pPortDesc);
ACQUIRE_SPIN_LOCK(&AtalkPortLock, &OldIrql);
}
RELEASE_SPIN_LOCK(&AtalkPortLock, OldIrql);
if (AtalkRegPath.Buffer != NULL)
{
AtalkFreeMemory(AtalkRegPath.Buffer);
}
if (AtalkDefaultPortName.Buffer != NULL)
{
AtalkFreeMemory(AtalkDefaultPortName.Buffer);
}
for (i = 0; i < ATALK_NO_DEVICES; i++)
{
//
// Delete all the devices created
//
IoDeleteDevice((PDEVICE_OBJECT)AtalkDeviceObject[i]);
}
// Deinitialize the Block Package
AtalkDeInitMemorySystem();
// Check if routing is on, if so unlock the router code now
if (AtalkRouter)
AtalkUnlockRouterIfNecessary();
// Free the rtmp tables
AtalkRtmpInit(FALSE);
// Free the zip tables
AtalkZipInit(FALSE);
// Release ndis resources (buffer/packet pools)
AtalkNdisReleaseResources();
// Deregister the protocol from ndis if handle is non-null
if (AtalkNdisProtocolHandle != (NDIS_HANDLE)NULL)
AtalkNdisDeregisterProtocol();
ASSERT(AtalkStatistics.stat_CurAllocSize == 0);
ASSERT(AtalkDbgMdlsAlloced == 0);
ASSERT(AtalkDbgIrpsAlloced == 0);
#ifdef PROFILING
ASSERT(AtalkStatistics.stat_CurAllocCount == 0);
ASSERT(AtalkStatistics.stat_CurMdlCount == 0);
#endif
}