1464 lines
39 KiB
C
1464 lines
39 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
Copyright (c) 1991 Nokia Data Systems AB
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
dlcdrvr.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains code which implements the NT DLC driver API
|
|||
|
using the generic data link module.
|
|||
|
|
|||
|
Contents:
|
|||
|
DriverEntry
|
|||
|
CreateAdapterFileContext
|
|||
|
CleanupAdapterFileContext
|
|||
|
DlcDriverUnload
|
|||
|
CloseAdapterFileContext
|
|||
|
DlcKillFileContext
|
|||
|
DlcDeviceIoControl
|
|||
|
DlcCompleteIoRequest
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Antti Saarenheimo 8-Jul-1991
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#define INCLUDE_IO_BUFFER_SIZE_TABLE // includes the io buffer sizes
|
|||
|
|
|||
|
#include <dlc.h>
|
|||
|
#include "dlcreg.h"
|
|||
|
#include "dbgmsg.h"
|
|||
|
|
|||
|
#define DEFINE_DLC_DIAGNOSTICS
|
|||
|
#include "dlcdebug.h"
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
The queueing of asynchronous commands (21 Oct. 1991)
|
|||
|
----------------------------------------------------
|
|||
|
|
|||
|
DLC driver uses basically three different methods to queue and
|
|||
|
complete its asyncronous commands:
|
|||
|
|
|||
|
1. Using LLC request handle
|
|||
|
|
|||
|
In this case the LLC driver takes care of the command queueing.
|
|||
|
This method is used by:
|
|||
|
- all transmit commands
|
|||
|
- all close commands
|
|||
|
- dlc connect
|
|||
|
- dlc disconnect
|
|||
|
|
|||
|
2. DLC (READ / RECEIVE) Command queue (FIFO)
|
|||
|
|
|||
|
The read and receive commands are saved to the command completion
|
|||
|
queue, that is is circular single enty link list.
|
|||
|
The events are handled by the oldest pending command.
|
|||
|
These commands also check the event queue and the command
|
|||
|
is queued only if there are no pending events.
|
|||
|
|
|||
|
3. Timer queue
|
|||
|
|
|||
|
Timer queue is a null terminated single entry link list.
|
|||
|
They are sorted by the relative expiration time. The timer
|
|||
|
tick completes all timer commands having the same expiration
|
|||
|
time. The expiration times are relative to all previous
|
|||
|
commands. For example, timer commands having expiration times 1, 2 and 3
|
|||
|
would all have 1 tick count in the queue. Thus the timer tick
|
|||
|
needs to increment only one tick from the beginning of the list
|
|||
|
and complete all timer commands having zero expiration time.
|
|||
|
When command is cancelled, its tick count must be added
|
|||
|
to the next element in the queue.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
New stuff in Feb-20-1992
|
|||
|
|
|||
|
|
|||
|
(reason: with a free build (because it is so fast) I run out of
|
|||
|
non-paged pool and several problems did arise in DLC driver:
|
|||
|
commands were sometimes lost, DLC.RESET did never complete and
|
|||
|
the adapter close
|
|||
|
|
|||
|
|
|||
|
The recovery rules when no non-paged memory is available:
|
|||
|
|
|||
|
1. The adapter close (or file context close by handle) must work always =>
|
|||
|
the adapter close packet must be in the file context.
|
|||
|
|
|||
|
2. The commands must allocate all resources needed to complete the
|
|||
|
command before they return a pending status (transmit!), if that
|
|||
|
cannot be done then the command must fail immediately.
|
|||
|
|
|||
|
3. The received data must not be acknowledged before all resources
|
|||
|
have been allocated to receive it.
|
|||
|
|
|||
|
4. Probablem: we may lose an important link state change indication if we
|
|||
|
cannot allocate a packet for it. For example, the application may
|
|||
|
not know, that the link is in the local busy state. The dlc status events
|
|||
|
of quite many link stations (255) may also very soon eat all non-paged
|
|||
|
pool, if they are not read by client. A static event packet would
|
|||
|
prevent that.
|
|||
|
A solution: a static event indication packet
|
|||
|
in the dlc object, connect and disconnect confirmation would be handled
|
|||
|
as now (they are indicated in the command completion entry).
|
|||
|
The indication status would be reset, when it is read from the command
|
|||
|
queue. The same event indication may have several flags.
|
|||
|
|
|||
|
|
|||
|
(Do first 1, 2, 3 and 4, make the test even more stressing for non-paged
|
|||
|
pool and test then what happens. Then we may fix the bug in the buffer
|
|||
|
pool shrinkin and implement dynamic packet pools).
|
|||
|
|
|||
|
The long term solutions:
|
|||
|
|
|||
|
Dynamic memory management in dlc! The current dlc memory management
|
|||
|
is very fast and the top memory consumption is minimal (without the
|
|||
|
default 33% overhead of binary buddy algorithm), but it never release
|
|||
|
the resources, that it has once allocated.
|
|||
|
|
|||
|
|
|||
|
1. The packet pools should release the extra memory when they are not
|
|||
|
needed any more, implementation: each memory block allocated for
|
|||
|
the packet pool has a reference count, that memory block is deallocated
|
|||
|
when the reference count is zero. This cleanup could be done once in
|
|||
|
a second. The algorithm scans the free list of packets, removes the
|
|||
|
packet from the free list, if the reference count of free packets
|
|||
|
is the same as the total packet count on a memory block.
|
|||
|
The memory blocks can be relesed in the next loop while the block itself
|
|||
|
is disconnected from the single entry list of all memory blocks in
|
|||
|
the packet pool.
|
|||
|
|
|||
|
2. The buffer pool memory management should also be able to shrink the
|
|||
|
number of locked pages (there must be a bug in the current implementation)
|
|||
|
AND also to free all MDLs and extra packets, when the buffer pool pages
|
|||
|
are unlocked.
|
|||
|
|
|||
|
|
|||
|
3. Data link driver should not allocated any memory resources (except
|
|||
|
packet pools to send its own frames). The objects should be created
|
|||
|
by in the dlc driver => all extra resources are released when
|
|||
|
a dlc driver is released (actually not a big deal, because dynamic
|
|||
|
packet pool management fixes the problem with the link stations).
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
// Local IOCTL dispatcher table:
|
|||
|
// ***************************************************
|
|||
|
// THE ORDER OF THESE FUNCTIONS MUST BE THE SAME AS
|
|||
|
// THE IOCTL COMMAND CODES IN NTDDDLC.H
|
|||
|
// ***************************************************
|
|||
|
|
|||
|
static PFDLC_COMMAND_HANDLER DispatchTable[IOCTL_DLC_LAST_COMMAND] = {
|
|||
|
DlcReadRequest,
|
|||
|
DlcReceiveRequest,
|
|||
|
DlcTransmit,
|
|||
|
DlcBufferFree,
|
|||
|
DlcBufferGet,
|
|||
|
DlcBufferCreate,
|
|||
|
DirSetExceptionFlags,
|
|||
|
DlcCloseStation, // DLC.CLOSE.STATION
|
|||
|
DlcConnectStation,
|
|||
|
DlcFlowControl,
|
|||
|
DlcOpenLinkStation,
|
|||
|
DlcReset,
|
|||
|
DlcReadCancel,
|
|||
|
DlcReceiveCancel,
|
|||
|
DlcQueryInformation,
|
|||
|
DlcSetInformation,
|
|||
|
DirTimerCancel,
|
|||
|
DirTimerCancelGroup,
|
|||
|
DirTimerSet,
|
|||
|
DlcOpenSap,
|
|||
|
DlcCloseStation, // DLC.CLOSE.SAP
|
|||
|
DirOpenDirect,
|
|||
|
DlcCloseStation, // DIR.CLOSE.DIRECT
|
|||
|
DirOpenAdapter,
|
|||
|
DirCloseAdapter,
|
|||
|
DlcReallocate,
|
|||
|
DlcReadRequest,
|
|||
|
DlcReceiveRequest,
|
|||
|
DlcTransmit,
|
|||
|
DlcCompleteCommand
|
|||
|
};
|
|||
|
|
|||
|
USHORT aSpecialOutputBuffers[3] = {
|
|||
|
sizeof(LLC_READ_OUTPUT_PARMS),
|
|||
|
sizeof(PVOID), // pFirstBuffer
|
|||
|
sizeof(UCHAR) // TransmitFrameStatus
|
|||
|
};
|
|||
|
|
|||
|
NDIS_SPIN_LOCK DlcDriverLock;
|
|||
|
|
|||
|
#ifdef LOCK_CHECK
|
|||
|
|
|||
|
LONG DlcDriverLockLevel = 0;
|
|||
|
|
|||
|
ULONG __line = 0;
|
|||
|
PCHAR __file = NULL;
|
|||
|
LONG __last = 1;
|
|||
|
HANDLE __process = (HANDLE)0;
|
|||
|
HANDLE __thread = (HANDLE)0;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
#if LLC_DBG
|
|||
|
|
|||
|
extern PVOID pAdapters;
|
|||
|
|
|||
|
ULONG AllocatedNonPagedPool = 0;
|
|||
|
ULONG LockedPageCount = 0;
|
|||
|
ULONG AllocatedMdlCount = 0;
|
|||
|
ULONG AllocatedPackets = 0;
|
|||
|
ULONG cExAllocatePoolFailed = 0;
|
|||
|
ULONG FailedMemoryLockings = 0;
|
|||
|
NDIS_SPIN_LOCK MemCheckLock;
|
|||
|
|
|||
|
ULONG cFramesReceived = 0;
|
|||
|
ULONG cFramesIndicated = 0;
|
|||
|
ULONG cFramesReleased = 0;
|
|||
|
|
|||
|
ULONG cLockedXmitBuffers = 0;
|
|||
|
ULONG cUnlockedXmitBuffers = 0;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//UINT InputIndex = 0;
|
|||
|
//LLC_SM_TRACE aLast[LLC_INPUT_TABLE_SIZE];
|
|||
|
|
|||
|
#if DBG & DLC_TRACE_ENABLED
|
|||
|
|
|||
|
UINT LlcTraceIndex = 0;
|
|||
|
UCHAR LlcTraceTable[LLC_TRACE_TABLE_SIZE];
|
|||
|
|
|||
|
#endif // DBG & DLC_TRACE_ENABLED
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// prototypes
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
LinkFileContext(
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext
|
|||
|
);
|
|||
|
|
|||
|
PDLC_FILE_CONTEXT
|
|||
|
UnlinkFileContext(
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// global data
|
|||
|
//
|
|||
|
|
|||
|
BOOLEAN MemoryLockFailed = FALSE; // this limits unneceasary memory locks
|
|||
|
KSPIN_LOCK DlcSpinLock; // syncnhronize the final cleanup
|
|||
|
PDEVICE_OBJECT ThisDeviceContext; // required for unloading driver
|
|||
|
|
|||
|
//
|
|||
|
// we now maintain a singly-linked list of FILE_CONTEXTs for debug and retail
|
|||
|
// versions
|
|||
|
//
|
|||
|
|
|||
|
SINGLE_LIST_ENTRY FileContexts = {NULL};
|
|||
|
KSPIN_LOCK FileContextsLock;
|
|||
|
KIRQL PreviousIrql;
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
BOOLEAN Prolix;
|
|||
|
MEMORY_USAGE DriverMemoryUsage;
|
|||
|
MEMORY_USAGE DriverStringUsage; // how much string does it take to hang a DLC driver?
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// external data
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// functions
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DriverEntry(
|
|||
|
IN PDRIVER_OBJECT pDriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called when the I/O subsystem loads the DLC driver
|
|||
|
|
|||
|
This routine performs the initialization of NT DLC API driver.
|
|||
|
Eventually this should be called after the first reference to
|
|||
|
DLC driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDriverObject - Pointer to driver object created by the system
|
|||
|
RegistryPath - The name of DLC's node in the registry
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the final status from the initialization operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PDEVICE_OBJECT pDeviceObject;
|
|||
|
UNICODE_STRING DriverName;
|
|||
|
|
|||
|
ASSUME_IRQL(PASSIVE_LEVEL);
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (Prolix) {
|
|||
|
DbgPrint("DLC.DriverEntry\n");
|
|||
|
}
|
|||
|
KeInitializeSpinLock(&DriverMemoryUsage.SpinLock);
|
|||
|
KeInitializeSpinLock(&DriverStringUsage.SpinLock);
|
|||
|
InitializeMemoryPackage();
|
|||
|
#endif
|
|||
|
|
|||
|
KeInitializeSpinLock(&FileContextsLock);
|
|||
|
|
|||
|
//
|
|||
|
// load any initialization-time parameters from the registry
|
|||
|
//
|
|||
|
|
|||
|
DlcRegistryInitialization(RegistryPath);
|
|||
|
LoadDlcConfiguration();
|
|||
|
|
|||
|
//
|
|||
|
// LLC init makes ourselves known to the NDIS wrapper,
|
|||
|
// but we don't yet bind to any NDIS driver (don't know even the name)
|
|||
|
//
|
|||
|
|
|||
|
Status = LlcInitialize();
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create the DLC device object. For now, we simply create \Device\Dlc
|
|||
|
// using a Unicode string. In the future we may need to load an ACL
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString(&DriverName, DD_DLC_DEVICE_NAME);
|
|||
|
|
|||
|
//
|
|||
|
// Create the device object for DLC driver, we don't have any
|
|||
|
// device specific data, because DLC needs only one device context.
|
|||
|
// Thus it can just use statics and globals.
|
|||
|
//
|
|||
|
|
|||
|
Status = IoCreateDevice(pDriverObject,
|
|||
|
0,
|
|||
|
&DriverName,
|
|||
|
FILE_DEVICE_DLC,
|
|||
|
FILE_DEVICE_SECURE_OPEN,
|
|||
|
FALSE,
|
|||
|
&pDeviceObject
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// need to keep a pointer to device context for IoDeleteDevice
|
|||
|
//
|
|||
|
|
|||
|
ThisDeviceContext = pDeviceObject;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// DLC driver never calls other device drivers: 1 I/O stack in IRP is enough
|
|||
|
//
|
|||
|
|
|||
|
pDeviceObject->StackSize = 1;
|
|||
|
pDeviceObject->Flags |= DO_DIRECT_IO;
|
|||
|
|
|||
|
KeInitializeSpinLock(&DlcSpinLock);
|
|||
|
|
|||
|
NdisAllocateSpinLock(&DlcDriverLock);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the driver object with this driver's entry points.
|
|||
|
//
|
|||
|
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_CREATE] = CreateAdapterFileContext;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = CloseAdapterFileContext;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = CleanupAdapterFileContext;
|
|||
|
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DlcDeviceIoControl;
|
|||
|
pDriverObject->DriverUnload = DlcDriverUnload;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
CreateAdapterFileContext(
|
|||
|
IN PDEVICE_OBJECT pDeviceObject,
|
|||
|
IN PIRP pIrp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called when a handle to the DLC driver is opened (via
|
|||
|
NtCreateFile)
|
|||
|
|
|||
|
The Create function creates file context for a DLC application.
|
|||
|
A DLC application needs at least one file context for each
|
|||
|
network adapter it is using. The DLC file contexts may share
|
|||
|
the same buffer pool, but otherwise they are totally isolated
|
|||
|
from each other.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Pointer to the device object for this driver
|
|||
|
Irp - Pointer to the request packet representing the I/O request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
PDLC_FILE_CONTEXT pFileContext;
|
|||
|
PIO_STACK_LOCATION pIrpSp;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|||
|
|
|||
|
ASSUME_IRQL(PASSIVE_LEVEL);
|
|||
|
|
|||
|
#if LLC_DBG == 2
|
|||
|
PrintMemStatus();
|
|||
|
#endif
|
|||
|
|
|||
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
pIrp->IoStatus.Information = 0;
|
|||
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|||
|
|
|||
|
pFileContext = (PDLC_FILE_CONTEXT)ALLOCATE_ZEROMEMORY_DRIVER(sizeof(DLC_FILE_CONTEXT));
|
|||
|
if (pFileContext == NULL) {
|
|||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto ErrorExit2;
|
|||
|
}
|
|||
|
|
|||
|
// #126745: Enqueue after initialization
|
|||
|
/* //
|
|||
|
// add this file context to our global list of opened file contexts
|
|||
|
//
|
|||
|
|
|||
|
LinkFileContext(pFileContext); */
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
//
|
|||
|
// record who owns this memory usage and add it to our global list of
|
|||
|
// memory usages
|
|||
|
//
|
|||
|
|
|||
|
pFileContext->MemoryUsage.Owner = (PVOID)pFileContext;
|
|||
|
pFileContext->MemoryUsage.OwnerObjectId = FileContextObject;
|
|||
|
LinkMemoryUsage(&pFileContext->MemoryUsage);
|
|||
|
#endif
|
|||
|
|
|||
|
pIrpSp->FileObject->FsContext = pFileContext;
|
|||
|
pFileContext->FileObject = pIrpSp->FileObject;
|
|||
|
|
|||
|
InitializeListHead(&pFileContext->EventQueue);
|
|||
|
InitializeListHead(&pFileContext->CommandQueue);
|
|||
|
InitializeListHead(&pFileContext->ReceiveQueue);
|
|||
|
InitializeListHead(&pFileContext->FlowControlQueue);
|
|||
|
|
|||
|
//
|
|||
|
// create pool of command/event packets
|
|||
|
//
|
|||
|
|
|||
|
pFileContext->hPacketPool = CREATE_PACKET_POOL_FILE(DlcPacketPoolObject,
|
|||
|
sizeof(DLC_PACKET),
|
|||
|
8
|
|||
|
);
|
|||
|
if (pFileContext->hPacketPool == NULL) {
|
|||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto ErrorExit1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// create pool of DLC-level SAP/LINK/DIRECT objects
|
|||
|
//
|
|||
|
|
|||
|
pFileContext->hLinkStationPool = CREATE_PACKET_POOL_FILE(DlcLinkPoolObject,
|
|||
|
sizeof(DLC_OBJECT),
|
|||
|
4
|
|||
|
);
|
|||
|
if (pFileContext->hLinkStationPool == NULL) {
|
|||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto ErrorExit1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// add this file context to our global list of opened file contexts
|
|||
|
//
|
|||
|
LinkFileContext(pFileContext);
|
|||
|
|
|||
|
//
|
|||
|
// set the file context reference count to 1 - this file context is ALIVE!
|
|||
|
//
|
|||
|
|
|||
|
ReferenceFileContext(pFileContext);
|
|||
|
|
|||
|
//
|
|||
|
// the call to open a handle to the driver may have succeeded, but we don't
|
|||
|
// yet have an open adapter context
|
|||
|
//
|
|||
|
|
|||
|
pFileContext->State = DLC_FILE_CONTEXT_CLOSED;
|
|||
|
ALLOCATE_SPIN_LOCK(&pFileContext->SpinLock);
|
|||
|
|
|||
|
KeInitializeEvent(&pFileContext->CleanupEvent, SynchronizationEvent, FALSE);
|
|||
|
|
|||
|
ErrorExit1:
|
|||
|
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
|
|||
|
DELETE_PACKET_POOL_FILE(&pFileContext->hLinkStationPool);
|
|||
|
DELETE_PACKET_POOL_FILE(&pFileContext->hPacketPool);
|
|||
|
CHECK_MEMORY_RETURNED_FILE();
|
|||
|
|
|||
|
// UnlinkFileContext(pFileContext);
|
|||
|
|
|||
|
#if DBG
|
|||
|
// UnlinkMemoryUsage(&pFileContext->MemoryUsage);
|
|||
|
#endif
|
|||
|
|
|||
|
FREE_MEMORY_DRIVER(pFileContext);
|
|||
|
}
|
|||
|
|
|||
|
ErrorExit2:
|
|||
|
|
|||
|
pIrp->IoStatus.Status = Status;
|
|||
|
DlcCompleteIoRequest(pIrp, FALSE);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
CleanupAdapterFileContext(
|
|||
|
IN PDEVICE_OBJECT pDeviceObject,
|
|||
|
IN PIRP pIrp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called when the last reference to an open file handle is
|
|||
|
removed. This is an opportunity, given us by the I/O subsystem, to ensure
|
|||
|
that all pending I/O requests for the file object being closed have been
|
|||
|
completed
|
|||
|
|
|||
|
The routine checks, that the file context is really closed.
|
|||
|
Otherwise it executes a panic closing of all resources in
|
|||
|
the same way as in the DirCloseAdapter call.
|
|||
|
It happens when an application makes process exit without
|
|||
|
calling the DirCloseAdapter.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Pointer to the device object for this driver
|
|||
|
Irp - Pointer to the request packet representing the I/O request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION pIrpSp;
|
|||
|
PDLC_FILE_CONTEXT pFileContext;
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|||
|
|
|||
|
DIAG_FUNCTION("CleanupAdapterFileContext");
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (Prolix) {
|
|||
|
DbgPrint("CleanupAdapterFileContext\n");
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
pIrp->IoStatus.Information = 0;
|
|||
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|||
|
|
|||
|
pFileContext = pIrpSp->FileObject->FsContext;
|
|||
|
|
|||
|
//
|
|||
|
// We may have a pending close or Initialize operation going on
|
|||
|
//
|
|||
|
|
|||
|
ACQUIRE_DRIVER_LOCK();
|
|||
|
|
|||
|
ENTER_DLC(pFileContext);
|
|||
|
|
|||
|
KeResetEvent(&pFileContext->CleanupEvent);
|
|||
|
|
|||
|
if (pFileContext->State == DLC_FILE_CONTEXT_OPEN) {
|
|||
|
//
|
|||
|
// as for Ioctl processing, add 2 to the file context reference count
|
|||
|
// The combination of the dereference below, the completion of the
|
|||
|
// close adapter call and the processing of a close IRP will cause the
|
|||
|
// file context to be destroyed
|
|||
|
//
|
|||
|
|
|||
|
ReferenceFileContextByTwo(pFileContext);
|
|||
|
Status = DirCloseAdapter(pIrp,
|
|||
|
pFileContext,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
#if LLC_DBG
|
|||
|
if (Status != STATUS_PENDING) {
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// We always return a pending status from DirCloseAdapter
|
|||
|
//
|
|||
|
|
|||
|
MY_ASSERT(Status == STATUS_PENDING);
|
|||
|
|
|||
|
DereferenceFileContext(pFileContext);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remove the original DLC_FILE_CONTEXT reference.
|
|||
|
//
|
|||
|
|
|||
|
DereferenceFileContext(pFileContext);
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
RELEASE_DRIVER_LOCK();
|
|||
|
|
|||
|
//
|
|||
|
// Wait for all references to the DLC_FILE_CONTEXT to be removed. When
|
|||
|
// the last reference is removed, DlcKillFileContext is called which will
|
|||
|
// clean up most of the file context's resources and then set the event.
|
|||
|
// CloseAdapterFileContext/IRP_MJ_CLOSE will free the actual memory
|
|||
|
// for the file context.
|
|||
|
//
|
|||
|
|
|||
|
KeWaitForSingleObject(
|
|||
|
&pFileContext->CleanupEvent,
|
|||
|
UserRequest,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
|
|||
|
DlcCompleteIoRequest(pIrp, FALSE);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DlcDriverUnload(
|
|||
|
IN PDRIVER_OBJECT pDeviceObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This functions is called when a called is made to the I/O subsystem to
|
|||
|
remove the DLC driver
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Pointer to the device object for this driver.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|||
|
|
|||
|
ASSUME_IRQL(PASSIVE_LEVEL);
|
|||
|
|
|||
|
DEBUGMSG(DBG_INIT,
|
|||
|
(TEXT("DlcDriverUnload(%#x)\n"), pDeviceObject));
|
|||
|
|
|||
|
LlcTerminate();
|
|||
|
DlcRegistryTermination();
|
|||
|
|
|||
|
CHECK_MEMORY_RETURNED_DRIVER();
|
|||
|
CHECK_STRING_RETURNED_DRIVER();
|
|||
|
CHECK_DRIVER_MEMORY_USAGE(TRUE);
|
|||
|
|
|||
|
NdisFreeSpinLock(&DlcDriverLock);
|
|||
|
|
|||
|
//
|
|||
|
// now tell I/O subsystem that this device context is no longer current
|
|||
|
//
|
|||
|
|
|||
|
IoDeleteDevice(ThisDeviceContext);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
CloseAdapterFileContext(
|
|||
|
IN PDEVICE_OBJECT pDeviceObject,
|
|||
|
IN PIRP pIrp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when the file object reference count is zero. The file
|
|||
|
object is really being deleted by the I/O subsystem. The file context had
|
|||
|
better be closed by now (should have been cleared out by Cleanup)
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Pointer to the device object for this driver
|
|||
|
Irp - Pointer to the request packet representing the I/O request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION pIrpSp;
|
|||
|
PDLC_FILE_CONTEXT pFileContext;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|||
|
|
|||
|
DIAG_FUNCTION("CloseAdapterFileContext");
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (Prolix) {
|
|||
|
DbgPrint("CloseAdapterFileContext\n");
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
pIrp->IoStatus.Information = 0;
|
|||
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|||
|
pFileContext = pIrpSp->FileObject->FsContext;
|
|||
|
|
|||
|
//
|
|||
|
// The original reference was removed in CleanupAdapterFileContext and
|
|||
|
// blocked until all references were removed and the file context
|
|||
|
// resources cleaned up, except for the following.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(pFileContext->ReferenceCount == 0);
|
|||
|
ASSERT(UnlinkFileContext(pFileContext) == NULL);
|
|||
|
|
|||
|
pIrpSp->FileObject->FsContext = NULL;
|
|||
|
DEALLOCATE_SPIN_LOCK(&pFileContext->SpinLock);
|
|||
|
FREE_MEMORY_DRIVER(pFileContext);
|
|||
|
|
|||
|
//
|
|||
|
// complete the Close IRP
|
|||
|
//
|
|||
|
|
|||
|
DlcCompleteIoRequest(pIrp, FALSE);
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (Prolix) {
|
|||
|
CHECK_DRIVER_MEMORY_USAGE(FALSE);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DlcKillFileContext(
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Called when the reference count on a file context structure is decremented
|
|||
|
to zero. Frees all memory owned by the file context and removes it from the
|
|||
|
file context list.
|
|||
|
|
|||
|
After this function, no references to the file context structure can be made!
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pFileContext - pointer to DLC file context structure to kill
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KIRQL irql;
|
|||
|
PVOID pBindingContext;
|
|||
|
|
|||
|
ASSUME_IRQL(DISPATCH_LEVEL);
|
|||
|
|
|||
|
// Shouldn't need lock since only called when reference count is 0.
|
|||
|
// ENTER_DLC(pFileContext);
|
|||
|
|
|||
|
//
|
|||
|
// delete all events on the file context event list before we delete the
|
|||
|
// packet pool
|
|||
|
//
|
|||
|
|
|||
|
PurgeDlcEventQueue(pFileContext);
|
|||
|
PurgeDlcFlowControlQueue(pFileContext);
|
|||
|
|
|||
|
DELETE_PACKET_POOL_FILE(&pFileContext->hPacketPool);
|
|||
|
DELETE_PACKET_POOL_FILE(&pFileContext->hLinkStationPool);
|
|||
|
|
|||
|
// LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
|
|||
|
pBindingContext = pFileContext->pBindingContext;
|
|||
|
|
|||
|
//
|
|||
|
// Finally, close the NDIS adapter. we have already disabled all
|
|||
|
// indications from it
|
|||
|
//
|
|||
|
|
|||
|
if (pBindingContext) {
|
|||
|
|
|||
|
//
|
|||
|
// RLF 04/26/94
|
|||
|
//
|
|||
|
// We need to call LlcDisableAdapter here to terminate the DLC timer
|
|||
|
// if it is not already terminated. Else we can end up with the timer
|
|||
|
// still in the adapter's tick list (if there are other bindings to
|
|||
|
// the adapter), and sooner or later that will cause an access
|
|||
|
// violation, followed very shortly thereafter by a blue screen
|
|||
|
//
|
|||
|
|
|||
|
LlcDisableAdapter(pBindingContext);
|
|||
|
LlcCloseAdapter(pBindingContext, TRUE);
|
|||
|
}
|
|||
|
|
|||
|
CHECK_MEMORY_RETURNED_FILE();
|
|||
|
|
|||
|
UnlinkFileContext(pFileContext);
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
UnlinkMemoryUsage(&pFileContext->MemoryUsage);
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
KeSetEvent(&pFileContext->CleanupEvent, 0, FALSE);
|
|||
|
|
|||
|
#if LLC_DBG
|
|||
|
|
|||
|
if ((LockedPageCount != 0
|
|||
|
|| AllocatedMdlCount != 0
|
|||
|
|| AllocatedNonPagedPool != 0)
|
|||
|
&& pAdapters == NULL) {
|
|||
|
DbgPrint("DLC.CloseAdapterFileContext: Error: Resources not released\n");
|
|||
|
//PrintMemStatus();
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
FailedMemoryLockings = 0;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DlcDeviceIoControl(
|
|||
|
IN PDEVICE_OBJECT pDeviceContext,
|
|||
|
IN PIRP pIrp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine dispatches DLC requests to different handlers based
|
|||
|
on the minor IOCTL function code in the IRP's current stack location.
|
|||
|
In addition to cracking the minor function code, this routine also
|
|||
|
reaches into the IRP and passes the packetized parameters stored there
|
|||
|
as parameters to the various DLC request handlers so that they are
|
|||
|
not IRP-dependent.
|
|||
|
|
|||
|
DlcDeviceControl and LlcReceiveIndication are the most time critical
|
|||
|
procedures in DLC. This code has been optimized for the asynchronous
|
|||
|
command (read and transmit)
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceContext - Pointer to the device object for this driver (unused)
|
|||
|
pIrp - Pointer to the request packet representing the I/O request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
Success - STATUS_SUCCESS
|
|||
|
The I/O request has been successfully completed
|
|||
|
|
|||
|
STATUS_PENDING
|
|||
|
The I/O request has been submitted and will be completed
|
|||
|
asynchronously
|
|||
|
|
|||
|
Failure - DLC_STATUS_XXX
|
|||
|
LLC_STATUS_XXX
|
|||
|
The I/O request has been completed, but an error occurred
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT TmpIndex;
|
|||
|
PDLC_FILE_CONTEXT pFileContext; // FsContext in FILE_OBJECT.
|
|||
|
PIO_STACK_LOCATION pIrpSp;
|
|||
|
ULONG ioControlCode;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(pDeviceContext);
|
|||
|
|
|||
|
ASSUME_IRQL(PASSIVE_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// Make sure status information is consistent every time
|
|||
|
//
|
|||
|
|
|||
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the current stack location in the IRP. This is where
|
|||
|
// the function codes and parameters are stored.
|
|||
|
//
|
|||
|
|
|||
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|||
|
|
|||
|
//
|
|||
|
// Branch to the appropriate request handler, but do first
|
|||
|
// preliminary checking of the input and output buffers,
|
|||
|
// the size of the request block is performed here so that it is known
|
|||
|
// in the handlers that the minimum input parameters are readable. It
|
|||
|
// is *not* determined here whether variable length input fields are
|
|||
|
// passed correctly; this is a check which must be made within each routine.
|
|||
|
//
|
|||
|
|
|||
|
ioControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
|
|||
|
|
|||
|
// Check the complete IoControl Code
|
|||
|
|
|||
|
switch (ioControlCode) {
|
|||
|
case IOCTL_DLC_READ:
|
|||
|
case IOCTL_DLC_RECEIVE:
|
|||
|
case IOCTL_DLC_TRANSMIT:
|
|||
|
case IOCTL_DLC_BUFFER_FREE:
|
|||
|
case IOCTL_DLC_BUFFER_GET:
|
|||
|
case IOCTL_DLC_BUFFER_CREATE:
|
|||
|
case IOCTL_DLC_SET_EXCEPTION_FLAGS:
|
|||
|
case IOCTL_DLC_CLOSE_STATION:
|
|||
|
case IOCTL_DLC_CONNECT_STATION:
|
|||
|
case IOCTL_DLC_FLOW_CONTROL:
|
|||
|
case IOCTL_DLC_OPEN_STATION:
|
|||
|
case IOCTL_DLC_RESET:
|
|||
|
case IOCTL_DLC_READ_CANCEL:
|
|||
|
case IOCTL_DLC_RECEIVE_CANCEL:
|
|||
|
case IOCTL_DLC_QUERY_INFORMATION:
|
|||
|
case IOCTL_DLC_SET_INFORMATION:
|
|||
|
case IOCTL_DLC_TIMER_CANCEL:
|
|||
|
case IOCTL_DLC_TIMER_CANCEL_GROUP:
|
|||
|
case IOCTL_DLC_TIMER_SET:
|
|||
|
case IOCTL_DLC_OPEN_SAP:
|
|||
|
case IOCTL_DLC_CLOSE_SAP:
|
|||
|
case IOCTL_DLC_OPEN_DIRECT:
|
|||
|
case IOCTL_DLC_CLOSE_DIRECT:
|
|||
|
case IOCTL_DLC_OPEN_ADAPTER:
|
|||
|
case IOCTL_DLC_CLOSE_ADAPTER:
|
|||
|
case IOCTL_DLC_REALLOCTE_STATION:
|
|||
|
case IOCTL_DLC_READ2:
|
|||
|
case IOCTL_DLC_RECEIVE2:
|
|||
|
case IOCTL_DLC_TRANSMIT2:
|
|||
|
case IOCTL_DLC_COMPLETE_COMMAND:
|
|||
|
case IOCTL_DLC_TRACE_INITIALIZE:
|
|||
|
TmpIndex = (((USHORT)ioControlCode) >> 2) & 0x0fff;
|
|||
|
break;
|
|||
|
default:
|
|||
|
TmpIndex = IOCTL_DLC_LAST_COMMAND;
|
|||
|
}
|
|||
|
|
|||
|
// TmpIndex = (((USHORT)ioControlCode) >> 2) & 0x0fff;
|
|||
|
if (TmpIndex >= IOCTL_DLC_LAST_COMMAND) {
|
|||
|
|
|||
|
pIrp->IoStatus.Information = 0;
|
|||
|
|
|||
|
// DlcCompleteIoRequest(pIrp, FALSE);
|
|||
|
// Don't call DlcCompleteIoRequest, it tries to free MDLs we haven't yet allocated
|
|||
|
// Instead of putting some more checks in DlcCompleteIoRequest, complete request here itself
|
|||
|
|
|||
|
pIrp->IoStatus.Status = DLC_STATUS_INVALID_COMMAND;
|
|||
|
SetIrpCancelRoutine(pIrp, FALSE);
|
|||
|
IoCompleteRequest(pIrp, (CCHAR)IO_NETWORK_INCREMENT);
|
|||
|
return DLC_STATUS_INVALID_COMMAND;
|
|||
|
}
|
|||
|
|
|||
|
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength
|
|||
|
< (ULONG)aDlcIoBuffers[TmpIndex].InputBufferSize
|
|||
|
||
|
|||
|
|
|||
|
pIrpSp->Parameters.DeviceIoControl.OutputBufferLength
|
|||
|
< (ULONG)aDlcIoBuffers[TmpIndex].OutputBufferSize) {
|
|||
|
|
|||
|
//
|
|||
|
// This error code should never be returned to user
|
|||
|
// If this happpens, then there is something wrong with ACSLAN
|
|||
|
//
|
|||
|
|
|||
|
pIrp->IoStatus.Information = 0;
|
|||
|
|
|||
|
// DlcCompleteIoRequest(pIrp, FALSE);
|
|||
|
// Don't call DlcCompleteIoRequest, it tries to free MDLs we haven't yet allocated
|
|||
|
// Instead of putting some more checks in DlcCompleteIoRequest, complete request here itself
|
|||
|
|
|||
|
pIrp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
SetIrpCancelRoutine(pIrp, FALSE);
|
|||
|
IoCompleteRequest(pIrp, (CCHAR)IO_NETWORK_INCREMENT);
|
|||
|
return STATUS_BUFFER_TOO_SMALL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save the length of the actual output buffer to Information field.
|
|||
|
// This number of bytes will be copied back to user buffer.
|
|||
|
//
|
|||
|
|
|||
|
pIrp->IoStatus.Information = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|||
|
|
|||
|
//
|
|||
|
// there are 3 cases of asynchronous commands where we need to lock extra
|
|||
|
// user memory for returned information. This goes in the parameter table
|
|||
|
// which can be anywhere in user memory (ie not near the CCB):
|
|||
|
//
|
|||
|
// TRANSMIT
|
|||
|
// - TRANSMIT_FS - a single byte!
|
|||
|
//
|
|||
|
// RECEIVE
|
|||
|
// - FIRST_BUFFER - a DWORD - pointer to the first received frame
|
|||
|
//
|
|||
|
// READ
|
|||
|
// - the entire parameter table needs to be locked. Virtually all
|
|||
|
// the fields are output. Still, this is only a max of 30 bytes
|
|||
|
//
|
|||
|
|
|||
|
if (TmpIndex <= IOCTL_DLC_TRANSMIT_INDEX) {
|
|||
|
|
|||
|
PVOID pDestination;
|
|||
|
PNT_DLC_PARMS pDlcParms;
|
|||
|
|
|||
|
pDlcParms = (PNT_DLC_PARMS)pIrp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Get the pointer of output parameters in user memory.
|
|||
|
// Note that we are not accessing anything in user address space.
|
|||
|
//
|
|||
|
|
|||
|
switch (TmpIndex) {
|
|||
|
case IOCTL_DLC_READ_INDEX:
|
|||
|
pDestination = &pDlcParms->Async.Ccb.u.pParameterTable->Read.uchEvent;
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_DLC_RECEIVE_INDEX:
|
|||
|
pDestination = &pDlcParms->Async.Ccb.u.pParameterTable->Receive.pFirstBuffer;
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_DLC_TRANSMIT_INDEX:
|
|||
|
pDestination = &pDlcParms->Async.Ccb.u.pParameterTable->Transmit.uchTransmitFs;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// allocate another MDL for the 1, 4, or 30 byte parameter table and lock
|
|||
|
// the page(s!)
|
|||
|
//
|
|||
|
|
|||
|
pDlcParms->Async.Ccb.u.pMdl = AllocateProbeAndLockMdl(
|
|||
|
pDestination,
|
|||
|
aSpecialOutputBuffers[TmpIndex]
|
|||
|
);
|
|||
|
if (pDlcParms->Async.Ccb.u.pMdl == NULL) {
|
|||
|
pIrp->IoStatus.Status = DLC_STATUS_MEMORY_LOCK_FAILED;
|
|||
|
DlcCompleteIoRequest(pIrp, FALSE);
|
|||
|
return DLC_STATUS_MEMORY_LOCK_FAILED;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
pFileContext = (PDLC_FILE_CONTEXT)pIrpSp->FileObject->FsContext;
|
|||
|
|
|||
|
ACQUIRE_DRIVER_LOCK();
|
|||
|
|
|||
|
ENTER_DLC(pFileContext);
|
|||
|
|
|||
|
//
|
|||
|
// We must leave immediately, if the reference counter is zero
|
|||
|
// or if we have a pending close or Initialize operation going on.
|
|||
|
// (This is not 100% safe, if app would create a file context,
|
|||
|
// open adapter, close adapter and immediately would close it again
|
|||
|
// when the previous command is pending, but that cannot be happen
|
|||
|
// with dlcapi.dll)
|
|||
|
//
|
|||
|
|
|||
|
if ((pFileContext->ReferenceCount == 0)
|
|||
|
|| ((pFileContext->State != DLC_FILE_CONTEXT_OPEN)
|
|||
|
&& (TmpIndex != IOCTL_DLC_OPEN_ADAPTER_INDEX))) {
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
RELEASE_DRIVER_LOCK();
|
|||
|
|
|||
|
pIrp->IoStatus.Status = LLC_STATUS_ADAPTER_CLOSED;
|
|||
|
DlcCompleteIoRequest(pIrp, FALSE);
|
|||
|
return LLC_STATUS_ADAPTER_CLOSED;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
DLC_TRACE('F');
|
|||
|
|
|||
|
//
|
|||
|
// set the default IRP cancel routine. We are not going to handle
|
|||
|
// transmit cases now
|
|||
|
//
|
|||
|
|
|||
|
//SetIrpCancelRoutine(pIrp,
|
|||
|
// (BOOLEAN)
|
|||
|
// !( (ioControlCode == IOCTL_DLC_TRANSMIT)
|
|||
|
// || (ioControlCode == IOCTL_DLC_TRANSMIT2) )
|
|||
|
// );
|
|||
|
|
|||
|
//
|
|||
|
// and set the irp I/O status to pending
|
|||
|
//
|
|||
|
|
|||
|
IoMarkIrpPending(pIrp);
|
|||
|
|
|||
|
//
|
|||
|
// The reason why we add 2 here is that during the processing of the
|
|||
|
// current IRP we may complete the request, causing us to decrement the
|
|||
|
// reference counter on the file context. If we just incremented by 1
|
|||
|
// here, the decrement could cause a pending close IRP to be allowed to
|
|||
|
// delete the file context while we are still using it
|
|||
|
//
|
|||
|
|
|||
|
ReferenceFileContextByTwo(pFileContext);
|
|||
|
|
|||
|
//
|
|||
|
// Irp and IrpSp are used just as in NBF
|
|||
|
//
|
|||
|
|
|||
|
Status = DispatchTable[TmpIndex](
|
|||
|
pIrp,
|
|||
|
pFileContext,
|
|||
|
(PNT_DLC_PARMS)pIrp->AssociatedIrp.SystemBuffer,
|
|||
|
pIrpSp->Parameters.DeviceIoControl.InputBufferLength,
|
|||
|
pIrpSp->Parameters.DeviceIoControl.OutputBufferLength
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// ensure the function returned with the correct IRQL
|
|||
|
//
|
|||
|
|
|||
|
ASSUME_IRQL(DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// the following error codes are valid:
|
|||
|
//
|
|||
|
// STATUS_PENDING
|
|||
|
// The request has been accepted
|
|||
|
// The driver will complete the request asynchronously
|
|||
|
// The output CCB should contain 0xFF in its status field (unless
|
|||
|
// already completed)
|
|||
|
//
|
|||
|
// STATUS_SUCCESS
|
|||
|
// The request has successfully completed synchronously
|
|||
|
// The output CCB should contain 0x00 in its status field
|
|||
|
//
|
|||
|
// 0x6001 - 0x6069
|
|||
|
// 0x6080 - 0x6081
|
|||
|
// 0x60A1 - 0x60A3
|
|||
|
// 0x60C0 - 0x60CB
|
|||
|
// 0x60FF
|
|||
|
// The request has failed with a DLC-specific error
|
|||
|
// The error code is converted to a DLC status code (-0x6000) and
|
|||
|
// the output CCB status field is set to the DLC status code
|
|||
|
// No asynchronous completion will be taken for this request
|
|||
|
//
|
|||
|
|
|||
|
if (Status != STATUS_PENDING) {
|
|||
|
|
|||
|
DLC_TRACE('G');
|
|||
|
|
|||
|
pIrpSp->Control &= ~SL_PENDING_RETURNED;
|
|||
|
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
|
|||
|
PNT_DLC_PARMS pDlcParms = (PNT_DLC_PARMS)pIrp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
if (Status >= DLC_STATUS_ERROR_BASE && Status < DLC_STATUS_MAX_ERROR) {
|
|||
|
Status -= DLC_STATUS_ERROR_BASE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// RLF 04/20/94
|
|||
|
//
|
|||
|
// make sure the CCB has the correct value written to it on
|
|||
|
// output if we're not returning pending status
|
|||
|
//
|
|||
|
|
|||
|
pDlcParms->Async.Ccb.uchDlcStatus = (UCHAR)Status;
|
|||
|
|
|||
|
//
|
|||
|
// the CCB request has failed. Make sure the pNext field is reset
|
|||
|
//
|
|||
|
|
|||
|
if ((pIrpSp->Parameters.DeviceIoControl.IoControlCode & 3) == METHOD_OUT_DIRECT) {
|
|||
|
|
|||
|
//
|
|||
|
// the CCB address may actually be unaligned DOS CCB1
|
|||
|
//
|
|||
|
|
|||
|
LLC_CCB UNALIGNED * pCcb;
|
|||
|
|
|||
|
pCcb = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
|
|||
|
|
|||
|
if (pCcb) {
|
|||
|
pCcb->pNext = NULL;
|
|||
|
}
|
|||
|
// Failure case. Don't override previous failure status.
|
|||
|
// It is likely STATUS_INSUFFICIENT_RESOURCES.
|
|||
|
} else {
|
|||
|
pDlcParms->Async.Ccb.pCcbAddress = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (ioControlCode != IOCTL_DLC_RESET) {
|
|||
|
|
|||
|
//
|
|||
|
// DLC.RESET returns an immediate status and does not complete
|
|||
|
// asynchronously
|
|||
|
//
|
|||
|
|
|||
|
DereferenceFileContextByTwo(pFileContext);
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// everything else that returns a non-pending status completes
|
|||
|
// asynchronously, which also causes the other reference count
|
|||
|
// to be removed
|
|||
|
//
|
|||
|
|
|||
|
DereferenceFileContext(pFileContext);
|
|||
|
}
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
RELEASE_DRIVER_LOCK();
|
|||
|
|
|||
|
//
|
|||
|
// RLF 06/07/93
|
|||
|
//
|
|||
|
// if the request is DLC.RESET, the IRP will have already been
|
|||
|
// completed if we're here, so don't complete it again (else we'll
|
|||
|
// bugcheck)
|
|||
|
//
|
|||
|
|
|||
|
if (ioControlCode != IOCTL_DLC_RESET) {
|
|||
|
DlcCompleteIoRequest(pIrp, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DLC_TRACE('H');
|
|||
|
|
|||
|
//
|
|||
|
// Reallocate the buffer pool size, if a threshold has been exceeded
|
|||
|
//
|
|||
|
|
|||
|
if (BufferPoolCheckThresholds(pFileContext->hBufferPool)) {
|
|||
|
|
|||
|
ReferenceBufferPool(pFileContext);
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
#if DBG
|
|||
|
BufferPoolExpand(pFileContext, (PDLC_BUFFER_POOL)pFileContext->hBufferPool);
|
|||
|
#else
|
|||
|
BufferPoolExpand((PDLC_BUFFER_POOL)pFileContext->hBufferPool);
|
|||
|
#endif
|
|||
|
|
|||
|
ENTER_DLC(pFileContext);
|
|||
|
|
|||
|
DereferenceBufferPool(pFileContext);
|
|||
|
}
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
//
|
|||
|
// if this dereference causes the count to go to 0, the file context
|
|||
|
// will be destroyed. Implicitly we must be closing the adapter and
|
|||
|
// have received a close IRP for this to happen
|
|||
|
//
|
|||
|
|
|||
|
DereferenceFileContext(pFileContext);
|
|||
|
|
|||
|
RELEASE_DRIVER_LOCK();
|
|||
|
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DlcCompleteIoRequest(
|
|||
|
IN PIRP pIrp,
|
|||
|
IN BOOLEAN InCancel
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine completes the given DLC IRP
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pIrp - Pointer to the request packet representing the I/O request.
|
|||
|
InCancel - TRUE if called on Irp cancel path
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// we are about to complete this IRP - remove the cancel routine. The check
|
|||
|
// stops us spinning forever if this function is called from within an IRP
|
|||
|
// cancellation
|
|||
|
//
|
|||
|
|
|||
|
if (!InCancel) {
|
|||
|
SetIrpCancelRoutine(pIrp, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// unlock and free any MDLs we allocated
|
|||
|
//
|
|||
|
|
|||
|
if (IoGetCurrentIrpStackLocation(pIrp)->MajorFunction == IRP_MJ_DEVICE_CONTROL
|
|||
|
&& IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.IoControlCode <= IOCTL_DLC_TRANSMIT) {
|
|||
|
|
|||
|
//
|
|||
|
// We enter here only if something has gone wrong in the main
|
|||
|
// function of an async operation => the status field and
|
|||
|
// next pointer will be updated synchronously.
|
|||
|
// On the other hand, all other async functions having no output
|
|||
|
// parameters except CCB status and next pointer are upated
|
|||
|
// by the normal code path. They should just copy
|
|||
|
// back the pending status and next pointer pointing to CCB itself.
|
|||
|
// That should not affect anything, because the DLL will update
|
|||
|
// those fields, when we return synchronous status
|
|||
|
//
|
|||
|
|
|||
|
PNT_DLC_PARMS pDlcParms = (PNT_DLC_PARMS)pIrp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
if (pDlcParms->Async.Ccb.u.pMdl != NULL) {
|
|||
|
UnlockAndFreeMdl(pDlcParms->Async.Ccb.u.pMdl);
|
|||
|
}
|
|||
|
}
|
|||
|
IoCompleteRequest(pIrp, (CCHAR)IO_NETWORK_INCREMENT);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
LinkFileContext(
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext
|
|||
|
)
|
|||
|
{
|
|||
|
KeAcquireSpinLock(&FileContextsLock, &PreviousIrql);
|
|||
|
PushEntryList(&FileContexts, &pFileContext->List);
|
|||
|
KeReleaseSpinLock(&FileContextsLock, PreviousIrql);
|
|||
|
}
|
|||
|
|
|||
|
PDLC_FILE_CONTEXT
|
|||
|
UnlinkFileContext(
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext
|
|||
|
)
|
|||
|
{
|
|||
|
PSINGLE_LIST_ENTRY p, prev = (PSINGLE_LIST_ENTRY)&FileContexts;
|
|||
|
|
|||
|
KeAcquireSpinLock(&FileContextsLock, &PreviousIrql);
|
|||
|
for (p = FileContexts.Next; p && p != (PSINGLE_LIST_ENTRY)pFileContext; ) {
|
|||
|
prev = p;
|
|||
|
p = p->Next;
|
|||
|
}
|
|||
|
if (p) {
|
|||
|
prev->Next = p->Next;
|
|||
|
// } else {
|
|||
|
//
|
|||
|
//#if DBG
|
|||
|
// DbgPrint("DLC.UnlinkFileContext: Error: FILE_CONTEXT @%08X not on list??\n",
|
|||
|
// pFileContext
|
|||
|
// );
|
|||
|
//#endif
|
|||
|
//
|
|||
|
}
|
|||
|
KeReleaseSpinLock(&FileContextsLock, PreviousIrql);
|
|||
|
|
|||
|
return (PDLC_FILE_CONTEXT)p;
|
|||
|
}
|