windows-nt/Source/XPSP1/NT/net/dlc/driver/dlcdrvr.c
2020-09-26 16:20:57 +08:00

1464 lines
39 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}