1450 lines
41 KiB
C
1450 lines
41 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
Copyright (c) 1991 Nokia Data Systems
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
vrdlcbuf.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
The module implements the buffer management used by DOS DLC applications
|
|||
|
|
|||
|
Contents:
|
|||
|
InitializeBufferPools
|
|||
|
CreateBufferPool
|
|||
|
DeleteBufferPool
|
|||
|
GetBuffers
|
|||
|
FreeBuffers
|
|||
|
CalculateBufferRequirement
|
|||
|
CopyFrame
|
|||
|
AllBuffersInPool
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Antti Saarenheimo (o-anttis) 26-DEC-1991
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Originally, this code created a list of DOS buffers by keeping the segment
|
|||
|
constant, and updating the offset. For example, if a buffer pool was
|
|||
|
supplied starting at 0x1234:0000, buffers 0x100 bytes long, then the
|
|||
|
chain would be:
|
|||
|
|
|||
|
1234:0000 -> 1234:0100 -> 1234:0200 -> ... -> 0000:0000
|
|||
|
|
|||
|
But it turns out that some DOS DLC apps (Rumba) expect the offset to remain
|
|||
|
constant (at 0), and the segment to change(!). Thus, given the same buffer
|
|||
|
pool address, we would have a chain:
|
|||
|
|
|||
|
1234:0000 -> 1244:0000 -> 1254:0000 -> ... -> 0000:0000
|
|||
|
|
|||
|
As far as DOS apps are concerned, there is no difference, since the
|
|||
|
effective 20-bit address is the same.
|
|||
|
|
|||
|
This is mainly done so that an app can take the USER_OFFSET field of a
|
|||
|
received buffer and glue it to the segment, without having to do any
|
|||
|
arithmetic
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <nt.h>
|
|||
|
#include <ntrtl.h> // ASSERT, DbgPrint
|
|||
|
#include <nturtl.h>
|
|||
|
#include <windows.h>
|
|||
|
#include <softpc.h> // x86 virtual machine definitions
|
|||
|
#include <vrdlctab.h>
|
|||
|
#include <vdmredir.h>
|
|||
|
#include <dlcapi.h> // Official DLC API definition
|
|||
|
#include <ntdddlc.h> // IOCTL commands
|
|||
|
#include <dlcio.h> // Internal IOCTL API interface structures
|
|||
|
#include "vrdlc.h"
|
|||
|
#include "vrdebug.h"
|
|||
|
#include "vrdlcdbg.h"
|
|||
|
|
|||
|
//
|
|||
|
// defines
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// BUFFER_2_SIZE - this is the size of the fixed part of a DOS receive Buffer 2.
|
|||
|
// It just so happens that DOS and NT Buffer 2 (aka Next) are the same size
|
|||
|
//
|
|||
|
|
|||
|
#define BUFFER_2_SIZE sizeof(((PLLC_DOS_BUFFER)0)->Next)
|
|||
|
|
|||
|
//
|
|||
|
// macros
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// BUFFER_1_SIZE - return the size of the fixed part of a DOS receive Buffer 1.
|
|||
|
// The size is dependent on whether the receive options specified contiguous or
|
|||
|
// non-contiguous receive buffers. The size of a DOS Buffer 1 (of either type)
|
|||
|
// is 4 bytes smaller than the equivalent NT Buffer 1 because the NEXT_FRAME
|
|||
|
// field is absent
|
|||
|
//
|
|||
|
|
|||
|
#define BUFFER_1_SIZE(contiguous) ((contiguous) \
|
|||
|
? sizeof(((PLLC_DOS_BUFFER)0)->Contiguous) \
|
|||
|
: sizeof(((PLLC_DOS_BUFFER)0)->NotContiguous))
|
|||
|
|
|||
|
//
|
|||
|
// private prototypes
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// public data
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// private data
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// DOS Buffer Pools - there can be one buffer pool per SAP. Protect access
|
|||
|
// using critical section.
|
|||
|
// There are 256 SAPs max, which breaks down into a maximum of 128 SAPs per
|
|||
|
// adapter. We can accomodate a maximum of 2 adapters - one Token Ring (adapter
|
|||
|
// 0) and one Ether Link (adapter 1)
|
|||
|
//
|
|||
|
|
|||
|
DOS_DLC_BUFFER_POOL aBufferPools[DOS_DLC_MAX_SAPS * DOS_DLC_MAX_ADAPTERS];
|
|||
|
CRITICAL_SECTION BufferSemaphore;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// functions
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
InitializeBufferPools(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Clears all buffer pools - sets structures to 0 - and initializes the buffer
|
|||
|
synchronization semaphore
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT("InitializeBufferPools\n");
|
|||
|
}
|
|||
|
|
|||
|
InitializeCriticalSection(&BufferSemaphore);
|
|||
|
RtlZeroMemory(aBufferPools, sizeof(aBufferPools));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
LLC_STATUS
|
|||
|
CreateBufferPool(
|
|||
|
IN DWORD PoolIndex,
|
|||
|
IN DOS_ADDRESS dpBuffer,
|
|||
|
IN WORD PoolBlocks,
|
|||
|
IN WORD BufferSize
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The function initializes buffer pool for a DLC application.
|
|||
|
DOS DLC applications do not necessarily need to create the
|
|||
|
buffer pool immediately in DlcOpenSap (or DirOpenAdapter).
|
|||
|
We initialize the buffer pool for DOS memory mode using
|
|||
|
parallel pointers in flat and DOS side.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PoolIndex - SAP and adapter number (bit 0 defines 0 or 1 adapter)
|
|||
|
|
|||
|
dpBuffer - DOS pointer, space for the buffer segments. May be 0 in which
|
|||
|
case the app maintains its own buffers, we just get to know
|
|||
|
how many there are
|
|||
|
|
|||
|
PoolBlocks - number of 16 byte blocks which comprise the buffer pool.
|
|||
|
If this is 0 then the default of 256 (*16 = 4096) is used
|
|||
|
|
|||
|
BufferSize - size of an individual buffer in bytes and an integral multiple
|
|||
|
of 16. The minimum size is 80. If it is zero then the default
|
|||
|
of 160 is used
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
LLC_STATUS
|
|||
|
|
|||
|
Success - LLC_STATUS_SUCCESS
|
|||
|
Buffer pool for this SAP has been created
|
|||
|
|
|||
|
Failure - LLC_STATUS_DUPLICATE_COMMAND
|
|||
|
The buffer pool for this SAP already exists
|
|||
|
|
|||
|
LLC_STATUS_INVALID_BUFFER_LENGTH
|
|||
|
The given buffer size is not a multiple of 16 or is less
|
|||
|
than 80 bytes (default minimum buffer size)
|
|||
|
|
|||
|
LLC_STATUS_BUFFER_SIZE_EXCEEDED
|
|||
|
The buffer pool isn't big enough to hold 1 buffer of the
|
|||
|
requested size
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
WORD BufferSizeInBlocks;
|
|||
|
WORD BufferCount;
|
|||
|
DWORD i;
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT4("CreateBufferPool(PoolIndex=%#x, dpBuffer=%#x, PoolBlocks=%d, BufferSize=%d)\n",
|
|||
|
PoolIndex, dpBuffer, PoolBlocks, BufferSize);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// An app may reinitialize the buffer with DIR.MODIFY.OPEN.PARMS but the
|
|||
|
// command must fail, if there are already buffers in the pool. We should
|
|||
|
// also check if there is a pending receive command, but we cannot do it
|
|||
|
// without major changes in the receive handling architecture
|
|||
|
//
|
|||
|
|
|||
|
if (aBufferPools[PoolIndex].BufferSize) {
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT1("CreateBufferPool: already have buffer pool for %#x\n", PoolIndex);
|
|||
|
}
|
|||
|
|
|||
|
return LLC_STATUS_DUPLICATE_COMMAND;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Use the default, if the orginal value is 0
|
|||
|
//
|
|||
|
|
|||
|
if (BufferSize == 0) {
|
|||
|
BufferSize = 160;
|
|||
|
}
|
|||
|
if (PoolBlocks == 0) {
|
|||
|
PoolBlocks = 256;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The buffer size must be at least 80 and an even 16 bytes
|
|||
|
//
|
|||
|
|
|||
|
if ((BufferSize < 80) || (BufferSize % 16)) {
|
|||
|
return LLC_STATUS_INVALID_BUFFER_LENGTH;
|
|||
|
}
|
|||
|
|
|||
|
BufferSizeInBlocks = BufferSize / 16;
|
|||
|
if (BufferSizeInBlocks > PoolBlocks) {
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT("CreateBufferPool: Error: BufferSizeInBlocks > PoolBlocks\n");
|
|||
|
}
|
|||
|
|
|||
|
return LLC_STATUS_BUFFER_SIZE_EXCEEDED;
|
|||
|
}
|
|||
|
|
|||
|
EnterCriticalSection(&BufferSemaphore);
|
|||
|
|
|||
|
//
|
|||
|
// A DLC application may want to manage the buffer pool itself and to
|
|||
|
// provide the receive buffers with FreeBuffers, but the buffer size must
|
|||
|
// always be defined here.
|
|||
|
//
|
|||
|
|
|||
|
aBufferPools[PoolIndex].BufferSize = BufferSize;
|
|||
|
aBufferPools[PoolIndex].dpBuffer = dpBuffer; // may be 0!
|
|||
|
aBufferPools[PoolIndex].BufferCount = 0;
|
|||
|
|
|||
|
//
|
|||
|
// if the app has actually given us a buffer to use then we initialize it
|
|||
|
// else the app must manage its own buffers; we just maintain the metrics.
|
|||
|
// Note that the app's buffer might be aligned any old way, but we have to
|
|||
|
// put up with it at the expense of speed
|
|||
|
//
|
|||
|
|
|||
|
if (dpBuffer) {
|
|||
|
|
|||
|
LPBYTE ptr32;
|
|||
|
|
|||
|
BufferCount = PoolBlocks/BufferSizeInBlocks;
|
|||
|
|
|||
|
//
|
|||
|
// if the number of buffers we can fit in our pool is zero then inform
|
|||
|
// the app that we can't proceed with this request and clear out the
|
|||
|
// information for this buffer pool
|
|||
|
//
|
|||
|
|
|||
|
if (BufferCount == 0) {
|
|||
|
aBufferPools[PoolIndex].BufferSize = 0;
|
|||
|
aBufferPools[PoolIndex].dpBuffer = 0;
|
|||
|
aBufferPools[PoolIndex].BufferCount = 0;
|
|||
|
LeaveCriticalSection(&BufferSemaphore);
|
|||
|
return LLC_STATUS_BUFFER_SIZE_EXCEEDED;
|
|||
|
}
|
|||
|
|
|||
|
aBufferPools[PoolIndex].BufferCount = BufferCount;
|
|||
|
aBufferPools[PoolIndex].MaximumBufferCount = BufferCount;
|
|||
|
|
|||
|
//
|
|||
|
// convert the DOS address to a flat 32-bit pointer
|
|||
|
//
|
|||
|
|
|||
|
ptr32 = (LPBYTE)DOS_PTR_TO_FLAT(dpBuffer);
|
|||
|
|
|||
|
//
|
|||
|
// link the buffers together and initialize the headers. The headers
|
|||
|
// are only intialized with 2 pieces of info: the size of the buffer
|
|||
|
// and the pointer to the next buffer
|
|||
|
//
|
|||
|
|
|||
|
aBufferPools[PoolIndex].dpBuffer = dpBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// update the segment only - leave the offset
|
|||
|
//
|
|||
|
|
|||
|
dpBuffer += BufferSize / 16 * 65536;
|
|||
|
for (i = BufferCount; i; --i) {
|
|||
|
|
|||
|
//
|
|||
|
// do we really need this? I don't think so: looking at the manual,
|
|||
|
// this field is to report size of data received, not size of
|
|||
|
// buffer, which we know anyway
|
|||
|
//
|
|||
|
|
|||
|
//WRITE_WORD(((PLLC_DOS_BUFFER)ptr32)->Next.cbBuffer, BufferSize);
|
|||
|
|
|||
|
//
|
|||
|
// if this is the last buffer then set its NextBuffer field to
|
|||
|
// NULL. All buffers get the size info
|
|||
|
//
|
|||
|
|
|||
|
if (i - 1) {
|
|||
|
WRITE_DWORD(&((PLLC_DOS_BUFFER)ptr32)->Next.pNextBuffer, dpBuffer);
|
|||
|
|
|||
|
//
|
|||
|
// update the segment only - leave the offset
|
|||
|
//
|
|||
|
|
|||
|
dpBuffer += BufferSize / 16 * 65536;
|
|||
|
ptr32 += BufferSize;
|
|||
|
} else {
|
|||
|
WRITE_DWORD(&((PLLC_DOS_BUFFER)ptr32)->Next.pNextBuffer, NULL);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DumpDosDlcBufferPool(&aBufferPools[PoolIndex]);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&BufferSemaphore);
|
|||
|
return LLC_STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DeleteBufferPool(
|
|||
|
IN DWORD PoolIndex
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The function deletes a buffer pool
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PoolIndex - pool index based on SAP and adapter number
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT("DeleteBufferPool\n");
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// DLC.RESET for all adapters calls this 127 times
|
|||
|
//
|
|||
|
|
|||
|
EnterCriticalSection(&BufferSemaphore);
|
|||
|
if (aBufferPools[PoolIndex].BufferSize != 0) {
|
|||
|
RtlZeroMemory(&aBufferPools[PoolIndex], sizeof(aBufferPools[PoolIndex]));
|
|||
|
}
|
|||
|
LeaveCriticalSection(&BufferSemaphore);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
LLC_STATUS
|
|||
|
GetBuffers(
|
|||
|
IN DWORD PoolIndex,
|
|||
|
IN WORD BuffersToGet,
|
|||
|
OUT DPLLC_DOS_BUFFER *pdpBuffer,
|
|||
|
OUT LPWORD pusBuffersLeft,
|
|||
|
IN BOOLEAN PartialList,
|
|||
|
OUT PWORD BuffersGot OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The function allocates DLC buffers. It will allocate buffers as a chain
|
|||
|
if >1 is requested. If PartialList is TRUE then will allocate as many
|
|||
|
buffers as are available up to BuffersToGet and return the number in
|
|||
|
BuffersGot
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PoolIndex - SAP and adapter number
|
|||
|
BuffersToGet - numbers of buffers to get. If this is 0, defaults to 1
|
|||
|
pdpBuffer - the returned link list of LLC buffers
|
|||
|
pusBuffersLeft - returned count of buffers left after this call
|
|||
|
PartialList - TRUE if the caller wants a partial list
|
|||
|
BuffersGot - pointer to returned number of buffers allocated
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
LLC_STATUS
|
|||
|
Success - LLC_STATUS_SUCCESS
|
|||
|
The requested number of buffers have been returned, or a
|
|||
|
number of buffers less than the original request if
|
|||
|
PartialList is TRUE
|
|||
|
|
|||
|
Failure - LLC_STATUS_LOST_DATA_NO_BUFFERS
|
|||
|
The request could not be satistfied - not enough buffers
|
|||
|
in pool
|
|||
|
|
|||
|
LLC_STATUS_INVALID_STATION_ID
|
|||
|
The request was mad to an invalid SAP
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PLLC_DOS_BUFFER pBuffer;
|
|||
|
PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[PoolIndex];
|
|||
|
LLC_STATUS status;
|
|||
|
WORD n;
|
|||
|
WORD bufferSize;
|
|||
|
|
|||
|
#if DBG
|
|||
|
DWORD numBufs = BuffersToGet;
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT2("GetBuffers(PoolIndex=%#02x, BuffersToGet=%d)\n", PoolIndex, BuffersToGet);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
EnterCriticalSection(&BufferSemaphore);
|
|||
|
|
|||
|
//
|
|||
|
// if the caller specified PartialList then return whatever we've got. If
|
|||
|
// whatever we've got is 0 then we'll default it to 1 and fail the allocation
|
|||
|
// since 0 is less than 1
|
|||
|
//
|
|||
|
|
|||
|
if (PartialList) {
|
|||
|
if (pBufferPool->BufferCount < BuffersToGet) {
|
|||
|
BuffersToGet = pBufferPool->BufferCount;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// IBM DLC allows a default value of 1 to be used if the caller specified 0
|
|||
|
//
|
|||
|
|
|||
|
if (!BuffersToGet) {
|
|||
|
++BuffersToGet;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// default the returned DOS buffer chain pointer to NULL
|
|||
|
//
|
|||
|
|
|||
|
*pdpBuffer = 0;
|
|||
|
|
|||
|
//
|
|||
|
// if there are no buffers defined then this is an erroneous request
|
|||
|
//
|
|||
|
|
|||
|
if (pBufferPool->BufferSize) {
|
|||
|
|
|||
|
//
|
|||
|
// calculate the size of the data part of the buffer. We put this value
|
|||
|
// in the LENGTH_IN_BUFFER field
|
|||
|
//
|
|||
|
|
|||
|
bufferSize = pBufferPool->BufferSize
|
|||
|
- (WORD)sizeof(pBuffer->Next);
|
|||
|
|
|||
|
//
|
|||
|
// there may be no buffers left, in which case the next buffer pointer
|
|||
|
// (in DOS 16:16 format) will be 0 (0:0). If, on the other hand, it's
|
|||
|
// not 0 then we're in business: see if we can't allocate the buffers
|
|||
|
// requested
|
|||
|
//
|
|||
|
|
|||
|
if (pBufferPool->dpBuffer && pBufferPool->BufferCount >= BuffersToGet) {
|
|||
|
|
|||
|
pBuffer = (PLLC_DOS_BUFFER)DOS_PTR_TO_FLAT(pBufferPool->dpBuffer);
|
|||
|
*pdpBuffer = pBufferPool->dpBuffer;
|
|||
|
pBufferPool->BufferCount -= BuffersToGet;
|
|||
|
n = BuffersToGet;
|
|||
|
|
|||
|
//
|
|||
|
// Eicon Access wants the size of the buffer in the buffer
|
|||
|
// when it is returned by BUFFER.GET. Oblige
|
|||
|
//
|
|||
|
|
|||
|
WRITE_WORD(&pBuffer->Next.cbBuffer, bufferSize);
|
|||
|
|
|||
|
//
|
|||
|
// we will return a chain of buffers, so we (nicely) terminate it
|
|||
|
// with a NULL for the last NextBuffer field. It doesn't say in
|
|||
|
// the lovely IBM Tech Ref whether this should be done, but its
|
|||
|
// probably the best thing to do. Because this buffer pool lives
|
|||
|
// in DOS memory, we have to use READ_POINTER and WRITE_FAR_POINTER
|
|||
|
// macros, lest we get an alignment fault on RISC
|
|||
|
//
|
|||
|
|
|||
|
for (--BuffersToGet; BuffersToGet; --BuffersToGet) {
|
|||
|
pBuffer = (PLLC_DOS_BUFFER)READ_FAR_POINTER(&(pBuffer->pNext));
|
|||
|
if (pBuffer) {
|
|||
|
|
|||
|
//
|
|||
|
// Eicon Access wants the size of the buffer in the buffer
|
|||
|
// when it is returned by BUFFER.GET. Oblige
|
|||
|
//
|
|||
|
|
|||
|
WRITE_WORD(&pBuffer->Next.cbBuffer, bufferSize);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// set the new buffer pool head
|
|||
|
//
|
|||
|
|
|||
|
pBufferPool->dpBuffer = READ_DWORD(&pBuffer->pNext);
|
|||
|
|
|||
|
//
|
|||
|
// terminate the chain
|
|||
|
//
|
|||
|
|
|||
|
WRITE_FAR_POINTER(&pBuffer->pNext, NULL);
|
|||
|
|
|||
|
status = LLC_STATUS_SUCCESS;
|
|||
|
|
|||
|
#if DBG
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DumpDosDlcBufferChain(*pdpBuffer, numBufs ? numBufs : 1);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// if no buffers are obtained, the returned list is set to 0
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
status = LLC_STATUS_LOST_DATA_NO_BUFFERS;
|
|||
|
n = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// return the number of buffers left after this call. Works if we
|
|||
|
// allocated some or not
|
|||
|
//
|
|||
|
|
|||
|
*pusBuffersLeft = pBufferPool->BufferCount;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// bad SAP - no buffer pool for this one
|
|||
|
//
|
|||
|
|
|||
|
status = LLC_STATUS_INVALID_STATION_ID;
|
|||
|
n = 0;
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&BufferSemaphore);
|
|||
|
|
|||
|
//
|
|||
|
// if BuffersGot was specified then return the number of buffers allocated
|
|||
|
// and chained
|
|||
|
//
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(BuffersGot)) {
|
|||
|
*BuffersGot = n;
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT2("GetBuffers returning status=%x, BuffersLeft=%d\n", status, *pusBuffersLeft);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
LLC_STATUS
|
|||
|
FreeBuffers(
|
|||
|
IN DWORD PoolIndex,
|
|||
|
IN DPLLC_DOS_BUFFER dpBuffer,
|
|||
|
OUT LPWORD pusBuffersLeft
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Free a DOS buffer to a DLC buffer pool
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PoolIndex - SAP and adapter number (bit0 defines 0 or 1 adapter)
|
|||
|
dpBuffer - the released buffers (DOS pointer)
|
|||
|
pusBuffersLeft - the number of buffers left after the free
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DPLLC_DOS_BUFFER dpBase; // DOS pointer
|
|||
|
PLLC_DOS_BUFFER pNextBuffer; // flat NT pointer
|
|||
|
PLLC_DOS_BUFFER pBuffer; // flat NT pointer
|
|||
|
PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[PoolIndex];
|
|||
|
|
|||
|
#if DBG
|
|||
|
int n = 0;
|
|||
|
#endif
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT2("FreeBuffers: PoolIndex=%x dpBuffer=%x\n", PoolIndex, dpBuffer);
|
|||
|
}
|
|||
|
|
|||
|
if (pBufferPool->BufferSize == 0) {
|
|||
|
return LLC_STATUS_INVALID_STATION_ID;
|
|||
|
}
|
|||
|
|
|||
|
EnterCriticalSection(&BufferSemaphore);
|
|||
|
|
|||
|
dpBase = dpBuffer;
|
|||
|
pNextBuffer = pBuffer = DOS_PTR_TO_FLAT(dpBuffer);
|
|||
|
|
|||
|
//
|
|||
|
// the manual says for BUFFER.FREE (p3-4):
|
|||
|
//
|
|||
|
// "When the buffer is placed back in the buffer pool, bytes 4 and 5
|
|||
|
// (buffer length) of the buffer are set to zero."
|
|||
|
//
|
|||
|
// So, we oblige
|
|||
|
//
|
|||
|
|
|||
|
WRITE_WORD(&pBuffer->Next.cbFrame, 0);
|
|||
|
if (pNextBuffer) {
|
|||
|
|
|||
|
//
|
|||
|
// count the number of buffers being freed. Hopefully, the application
|
|||
|
// hasn't chenged over our terminating NULL pointer
|
|||
|
//
|
|||
|
|
|||
|
while (pNextBuffer) {
|
|||
|
++pBufferPool->BufferCount;
|
|||
|
|
|||
|
#if DBG
|
|||
|
++n;
|
|||
|
#endif
|
|||
|
|
|||
|
pBuffer = pNextBuffer;
|
|||
|
pNextBuffer = (PLLC_DOS_BUFFER)READ_FAR_POINTER(&pBuffer->pNext);
|
|||
|
|
|||
|
//
|
|||
|
// see above, about bytes 4 and 5
|
|||
|
//
|
|||
|
|
|||
|
WRITE_WORD(&pBuffer->Next.cbFrame, 0);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// put the freed chain at the head of the list, after linking the
|
|||
|
// buffer currently at the head of the list to the end of the freed
|
|||
|
// chain
|
|||
|
//
|
|||
|
|
|||
|
WRITE_DWORD(&pBuffer->pNext, pBufferPool->dpBuffer);
|
|||
|
pBufferPool->dpBuffer = dpBase;
|
|||
|
|
|||
|
#if DBG
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DumpDosDlcBufferChain(dpBuffer, n);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DPUT("ERROR: App tried to free NULL buffer chain\n");
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
*pusBuffersLeft = pBufferPool->BufferCount;
|
|||
|
|
|||
|
if (pBufferPool->BufferCount > pBufferPool->MaximumBufferCount) {
|
|||
|
pBufferPool->MaximumBufferCount = pBufferPool->BufferCount;
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&BufferSemaphore);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
WORD
|
|||
|
CalculateBufferRequirement(
|
|||
|
IN UCHAR Adapter,
|
|||
|
IN WORD StationId,
|
|||
|
IN PLLC_BUFFER pFrame,
|
|||
|
IN LLC_DOS_PARMS UNALIGNED * pDosParms,
|
|||
|
OUT PWORD BufferSize
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Calculate the number of DOS buffers required to hold the data received into
|
|||
|
an NT buffer. We have to go through this laborious phase because we need to
|
|||
|
know ahead of time if we have enough DOS buffers into which we will receive
|
|||
|
an I-Frame.
|
|||
|
|
|||
|
Also, the size of DOS buffers is fixed, but the size of NT receive frame
|
|||
|
buffers can vary depending on the size of the received frame, the options
|
|||
|
requested and the 'binary buddy' allocator algorithm in the DLC driver. You
|
|||
|
may think that since we specify to the driver the buffer pool size in the
|
|||
|
DLC.OPEN.SAP call that it would allocate buffers of the specified size.
|
|||
|
Well, you'd be wrong: the DLC driver ignores this information and creates
|
|||
|
a buffer pool which can dole out variable size buffers, making my life more
|
|||
|
difficult than it ought to be
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Adapter - which adapter we're receiving from
|
|||
|
StationId - the Station Id we're receiving on
|
|||
|
pFrame - pointer to the received frame in NT buffer
|
|||
|
pDosParms - pointer to the original DOS RECEIVE parameters
|
|||
|
BufferSize - pointer to the returned DOS buffer size
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
WORD
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// pBufferPool points to the DOS buffer pool for this adapter/station ID
|
|||
|
//
|
|||
|
|
|||
|
PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[GET_POOL_INDEX(Adapter, StationId)];
|
|||
|
|
|||
|
//
|
|||
|
// dosUserLength is the USER_LENGTH value the DOS client requested when
|
|||
|
// the RECEIVE was submitted. This value may well be different than the
|
|||
|
// USER_LENGTH in the NT receive frame buffer
|
|||
|
//
|
|||
|
|
|||
|
WORD dosUserLength = READ_WORD(&pDosParms->DosReceive.usUserLength);
|
|||
|
|
|||
|
//
|
|||
|
// buffersRequired is the number of DOS buffers we need to allocate in order
|
|||
|
// to return the received frame. It will be at least 1
|
|||
|
//
|
|||
|
|
|||
|
WORD buffersRequired = 1;
|
|||
|
|
|||
|
//
|
|||
|
// dataSpace is the area in a DOS buffer available for data (after the
|
|||
|
// Buffer 1 or Buffer 2 header and the USER_LENGTH consideration)
|
|||
|
//
|
|||
|
|
|||
|
WORD dataSpace;
|
|||
|
|
|||
|
//
|
|||
|
// dataLeft is the amount of data in an NT buffer needing to be copied to
|
|||
|
// a DOS buffer
|
|||
|
//
|
|||
|
|
|||
|
WORD dataLeft = 0;
|
|||
|
|
|||
|
//
|
|||
|
// calculate the number of DOS buffers required to hold the data frame
|
|||
|
// received into the NT buffer. Note that we can't simply use the size
|
|||
|
// of the received frame because we need the size of the buffer headers
|
|||
|
// and if the NT frame is larger than the DOS buffers then we may end
|
|||
|
// up with more DOS buffers required than NT buffers which in turn
|
|||
|
// results in more overhead which we have to factor in
|
|||
|
//
|
|||
|
|
|||
|
WORD bufferSize = pBufferPool->BufferSize;
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT("CalculateBufferRequirement\n");
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// calculate the amount of space in a DOS buffer after the Buffer 1 structure
|
|||
|
// (contiguous or non-contiguous, smoking or non-smoking, ah, ah, ah ahh-haa)
|
|||
|
// The buffer size MUST be large enough to hold the Buffer 1 overhead. This
|
|||
|
// is a FACT
|
|||
|
//
|
|||
|
|
|||
|
dataSpace = bufferSize
|
|||
|
- (BUFFER_1_SIZE(
|
|||
|
pFrame->Contiguous.uchOptions
|
|||
|
& (LLC_CONTIGUOUS_MAC | LLC_CONTIGUOUS_DATA)
|
|||
|
)
|
|||
|
+ dosUserLength
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// if there is less data space available in the first DOS receive buffer
|
|||
|
// than received data in the NT buffer then our first NT buffer will be
|
|||
|
// mapped to >1 DOS buffers: a Buffer 1 and 1 or more Buffer 2s. This is
|
|||
|
// before we even get to any associated Buffer 2s in the NT receive frame.
|
|||
|
//
|
|||
|
// Also: if the LLC_BREAK option is specified in the receive parameters
|
|||
|
// then the first data buffer will contain the header information. Note:
|
|||
|
// we assume this can only be for NotContiguous data, else how would we
|
|||
|
// know the size of the header information?
|
|||
|
//
|
|||
|
|
|||
|
if (pFrame->Contiguous.uchOptions & LLC_BREAK) {
|
|||
|
if (!(pFrame->Contiguous.uchOptions & (LLC_CONTIGUOUS_MAC | LLC_CONTIGUOUS_DATA))) {
|
|||
|
dataLeft = pFrame->NotContiguous.cbBuffer;
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
else {
|
|||
|
DPUT("CalculateBufferRequirement: Error: invalid options: BREAK && CONTIGUOUS???\n");
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
} else if (dataSpace < pFrame->Contiguous.cbBuffer) {
|
|||
|
dataLeft = pFrame->Contiguous.cbBuffer - dataSpace;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// we have enough space in the DOS buffer to copy all the received data
|
|||
|
// and some more
|
|||
|
//
|
|||
|
|
|||
|
dataSpace -= pFrame->Next.cbBuffer;
|
|||
|
dataLeft = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// if there is more data in the NT buffer than we can fit in a DOS buffer,
|
|||
|
// either because the buffer sizes are different or due to the DOS client
|
|||
|
// requesting the BREAK option, then generate Buffer 2 requirements
|
|||
|
//
|
|||
|
|
|||
|
while (dataLeft) {
|
|||
|
++buffersRequired;
|
|||
|
dataSpace = bufferSize - (BUFFER_2_SIZE + dosUserLength);
|
|||
|
if (dataLeft > dataSpace) {
|
|||
|
dataLeft -= dataSpace;
|
|||
|
dataSpace = 0;
|
|||
|
} else {
|
|||
|
dataSpace -= dataLeft;
|
|||
|
dataLeft = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// if the NT received frame has any associated Buffer 2 structures then
|
|||
|
// calculate the additional buffer requirement. Again, the NT buffers may
|
|||
|
// be a different size(s) than the DOS buffers.
|
|||
|
//
|
|||
|
// At this point, dataSpace is the amount of remaining data area in the
|
|||
|
// previous DOS buffer - Buffer 1 or Buffer 2. Use this before we allocate
|
|||
|
// a new DOS buffer
|
|||
|
//
|
|||
|
|
|||
|
for (pFrame = pFrame->pNext; pFrame; pFrame = pFrame->pNext) {
|
|||
|
if (pFrame->Next.cbBuffer > dataSpace) {
|
|||
|
dataLeft = pFrame->Next.cbBuffer - dataSpace;
|
|||
|
dataSpace = 0;
|
|||
|
while (dataLeft) {
|
|||
|
++buffersRequired;
|
|||
|
dataSpace = bufferSize - (BUFFER_2_SIZE + dosUserLength);
|
|||
|
if (dataLeft > dataSpace) {
|
|||
|
dataLeft -= dataSpace;
|
|||
|
dataSpace = 0;
|
|||
|
} else {
|
|||
|
dataSpace -= dataLeft;
|
|||
|
dataLeft = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// we have enough space in the DOS buffer to copy all the received data
|
|||
|
// and some more
|
|||
|
//
|
|||
|
|
|||
|
dataSpace -= pFrame->Next.cbBuffer;
|
|||
|
dataLeft = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT1("CalculateBufferRequirement: %d buffers required\n", buffersRequired);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// set the output DOS buffer size and return the number of DOS buffers
|
|||
|
// required
|
|||
|
//
|
|||
|
|
|||
|
*BufferSize = bufferSize;
|
|||
|
return buffersRequired;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
LLC_STATUS
|
|||
|
CopyFrame(
|
|||
|
IN PLLC_BUFFER pFrame,
|
|||
|
IN DPLLC_DOS_BUFFER DosBuffers,
|
|||
|
IN WORD UserLength,
|
|||
|
IN WORD BufferSize,
|
|||
|
IN DWORD Flags
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Copies a received NT frame into DOS buffers. We have previously calculated
|
|||
|
the DOS buffer requirement and allocated that requirement
|
|||
|
|
|||
|
We may copy the entire received frame or only part of it. We can only copy
|
|||
|
partial frames if the frame is NOT an I-Frame
|
|||
|
|
|||
|
Notes: 1. the DOS buffer manager returns the orginal DOS 16:16 buffer
|
|||
|
pointers, we must use those original pointers, when the buffers
|
|||
|
are linked to each other.
|
|||
|
|
|||
|
2. We do NOT chain frames - DOS DLC cannot handle frames being
|
|||
|
chained, and there is nothing to be gained by us chaining them
|
|||
|
except that we reduce the number of completed READs. However,
|
|||
|
we still have to generate the same number of simulated hardware
|
|||
|
interrupts to the VDM
|
|||
|
|
|||
|
3. Unlike DOS buffer pools, NT does not deal in buffers of a
|
|||
|
specific size. Rather, it allocates buffers from a pool based
|
|||
|
on a 'binary buddy' algorithm and the size of the data to be
|
|||
|
returned. Therefore, there is no correspondence between the
|
|||
|
size of DOS buffers (for a station) and the NT buffers which
|
|||
|
were used to receive the data
|
|||
|
|
|||
|
4. We only copy the data in this routine - whether it is deferred
|
|||
|
data from some prior local busy state or current data is immaterial
|
|||
|
to this routine. Responsibility for managing current/deferred frames
|
|||
|
is left to the caller
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pFrame - pointer to received frame in NT buffer(s)
|
|||
|
DosBuffers - DOS pointer to chain of DOS receive buffers
|
|||
|
UserLength - the USER_LENGTH value specified in the DOS RECEIVE
|
|||
|
BufferSize - size of a DOS buffer
|
|||
|
Flags - various flags:
|
|||
|
CF_CONTIGUOUS
|
|||
|
Set if this frame is contiguous
|
|||
|
|
|||
|
CF_BREAK
|
|||
|
Set if the DOS client requested that the buffers be
|
|||
|
broken into header, data
|
|||
|
|
|||
|
CF_PARTIAL
|
|||
|
Set if we are copying a partial frame - ok for non
|
|||
|
I-Frames
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
LLC_STATUS
|
|||
|
LLC_STATUS_SUCCESS
|
|||
|
All data copied from NT buffer to DOS buffer(s)
|
|||
|
|
|||
|
LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
|
|||
|
A partial copy was performed. Some data ended up in DOS buffer(s),
|
|||
|
the rest is lost. Cannot be I-Frame!
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// pDosBuffer - pointer to the DOS buffer which is usable in 32-bit mode
|
|||
|
//
|
|||
|
|
|||
|
PLLC_DOS_BUFFER pDosBuffer = (PLLC_DOS_BUFFER)DOS_PTR_TO_FLAT(DosBuffers);
|
|||
|
|
|||
|
//
|
|||
|
// dataSpace - amount of data space available in the current DOS buffer.
|
|||
|
// Initialize it for common Buffer 1 case
|
|||
|
//
|
|||
|
|
|||
|
WORD dataSpace = BufferSize - (WORD)&(((PLLC_BUFFER)0)->Contiguous.pNextFrame);
|
|||
|
|
|||
|
PBYTE pDosData; // pointer to place in DOS buffer where we copy data TO
|
|||
|
PBYTE pNtData; // corresponding place in NT buffer where we copy data FROM
|
|||
|
WORD headerLength; // amount of data in headers
|
|||
|
WORD dataLeft; // amount of data to be copied FROM the NT buffer
|
|||
|
WORD userOffset; // offset of USER_SPACE
|
|||
|
WORD bufferLeft; // amount of data left in current NT buffer
|
|||
|
WORD dataToCopy; // amount of data to copy to DOS buffer
|
|||
|
WORD dataCopied; // amount of data copied to Buffer 1/Buffer 2
|
|||
|
WORD frameLength; // length of entire frame
|
|||
|
|
|||
|
//
|
|||
|
// bufferOffset - used in generating the correct userOffset
|
|||
|
//
|
|||
|
|
|||
|
WORD bufferOffset = LOWORD(DosBuffers);
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT5("CopyFrame: pFrame=%x DosBuffers=%x UserLength=%x Flags=%x pDosBuffer=%x\n",
|
|||
|
pFrame, DosBuffers, UserLength, Flags, pDosBuffer);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// copy the first buffer. If the BREAK option is set then we only copy the
|
|||
|
// header part (ASSUMES NotContiguous)! NB: we KNOW that we can fit at least
|
|||
|
// this amount of data in the DOS buffer. Also: it is safe to use RtlCopyMemory ?
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory(&pDosBuffer->Contiguous.cbFrame,
|
|||
|
&pFrame->Contiguous.cbFrame,
|
|||
|
(DWORD)&(((PLLC_BUFFER)0)->Contiguous.pNextFrame)
|
|||
|
- (DWORD)&(((PLLC_BUFFER)0)->Contiguous.cbFrame)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// pDosData points to the area in the DOS buffer where the LAN header info
|
|||
|
// or data will go, depending on format
|
|||
|
//
|
|||
|
|
|||
|
pDosData = &pDosBuffer->NotContiguous.cbLanHeader;
|
|||
|
|
|||
|
//
|
|||
|
// if the CF_CONTIGUOUS flag is not set in the Flags parameter then this is
|
|||
|
// a NotContiguous frame. We must copy the header in 2 parts, because the
|
|||
|
// NT buffer contains the NEXT_FRAME field which the DOS buffer does not
|
|||
|
//
|
|||
|
|
|||
|
if (!(Flags & CF_CONTIGUOUS)) {
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT("Buffer is NOT CONTIGUOUS\n");
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory(pDosData,
|
|||
|
&pFrame->NotContiguous.cbLanHeader,
|
|||
|
(DWORD)&(((PLLC_BUFFER)0)->NotContiguous.usPadding)
|
|||
|
- (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.cbLanHeader)
|
|||
|
);
|
|||
|
|
|||
|
pDosData += (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.usPadding)
|
|||
|
- (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.cbLanHeader);
|
|||
|
|
|||
|
dataSpace -= (WORD)&(((PLLC_BUFFER)0)->NotContiguous.usPadding)
|
|||
|
- (WORD)&(((PLLC_BUFFER)0)->NotContiguous.cbLanHeader);
|
|||
|
userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->NotContiguous.auchDlcHeader)
|
|||
|
+ sizeof(((PLLC_DOS_BUFFER)0)->NotContiguous.auchDlcHeader);
|
|||
|
|
|||
|
//
|
|||
|
// sanity check
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(userOffset == 58);
|
|||
|
|
|||
|
//
|
|||
|
// amount of data in headers
|
|||
|
//
|
|||
|
|
|||
|
headerLength = pFrame->NotContiguous.cbLanHeader
|
|||
|
+ pFrame->NotContiguous.cbDlcHeader;
|
|||
|
} else {
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT("Buffer is CONTIGUOUS\n");
|
|||
|
}
|
|||
|
|
|||
|
userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->Contiguous.uchAdapterNumber)
|
|||
|
+ sizeof(((PLLC_DOS_BUFFER)0)->Contiguous.uchAdapterNumber);
|
|||
|
|
|||
|
//
|
|||
|
// sanity check
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(userOffset == 20);
|
|||
|
|
|||
|
//
|
|||
|
// no header info in contiguous buffer
|
|||
|
//
|
|||
|
|
|||
|
headerLength = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// if the CF_BREAK flag is set in the Flags parameter then the DOS app
|
|||
|
// requested that the first buffer (presumed NotContiguous) be broken into
|
|||
|
// one buffer containing just the header information and another containing
|
|||
|
// the data. In this case copy no more data to the first buffer
|
|||
|
//
|
|||
|
|
|||
|
if (!(Flags & CF_BREAK)) {
|
|||
|
|
|||
|
//
|
|||
|
// pDosData points at USER_SPACE - offset 58 for NotContiguous buffer,
|
|||
|
// offset 20 for Contiguous buffer. Bump it by USER_LENGTH (still don't
|
|||
|
// know if we ever expect anything meaningful to be placed at USER_SPACE
|
|||
|
// before we give the buffer to DOS
|
|||
|
//
|
|||
|
|
|||
|
pDosData += UserLength;
|
|||
|
|
|||
|
//
|
|||
|
// get in dataSpace the amount of space left in the DOS buffer where
|
|||
|
// we are able to copy data. Assume that UserLength doesn't make this
|
|||
|
// go negative (ie LARGE)
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(dataSpace >= UserLength);
|
|||
|
|
|||
|
dataSpace -= UserLength;
|
|||
|
} else {
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT("Buffer has BREAK\n");
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// the DOS app requested BREAK. Set the count of data in this buffer
|
|||
|
// to 0. Use WRITE_WORD since it may be unaligned. Update the other
|
|||
|
// header fields we can't just copy from the NT buffer
|
|||
|
//
|
|||
|
|
|||
|
WRITE_WORD(&pDosBuffer->NotContiguous.cbBuffer, 0);
|
|||
|
WRITE_WORD(&pDosBuffer->NotContiguous.offUserData, userOffset + bufferOffset);
|
|||
|
WRITE_WORD(&pDosBuffer->NotContiguous.cbUserData, UserLength);
|
|||
|
|
|||
|
//
|
|||
|
// get the next DOS buffer in the list. There may not be one? (Don't
|
|||
|
// expect such a situation)
|
|||
|
//
|
|||
|
|
|||
|
bufferOffset = READ_WORD(&pDosBuffer->pNext);
|
|||
|
pDosBuffer = DOS_PTR_TO_FLAT(pDosBuffer->pNext);
|
|||
|
if (pDosBuffer) {
|
|||
|
userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->Next.cbUserData)
|
|||
|
+ sizeof(((PLLC_DOS_BUFFER)0)->Next.cbUserData);
|
|||
|
|
|||
|
//
|
|||
|
// sanity check
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(userOffset == 12);
|
|||
|
dataSpace = BufferSize - (BUFFER_2_SIZE + UserLength);
|
|||
|
pDosData = (PBYTE)pDosBuffer + BUFFER_2_SIZE + UserLength;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// that was the last buffer. Either there was just header data or
|
|||
|
// this is a partial copy and therefore not an I-Frame
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(DLC_BUFFERS) {
|
|||
|
DPUT("CopyFrame: returning early\n");
|
|||
|
}
|
|||
|
|
|||
|
return (Flags & CF_PARTIAL)
|
|||
|
? LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
|
|||
|
: LLC_STATUS_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// frameLength is length of entire frame - must appear in Buffer 1 and
|
|||
|
// Buffer 2s
|
|||
|
//
|
|||
|
|
|||
|
frameLength = pFrame->Contiguous.cbFrame;
|
|||
|
|
|||
|
//
|
|||
|
// dataLeft is the amount of data left to copy from the frame after the
|
|||
|
// headers have been taken care of. For a Contiguous buffer, this is the
|
|||
|
// same as the length of the frame; for a NotContiguous buffer, this is
|
|||
|
// the length of the frame - the combined length of the LAN and DLC
|
|||
|
// headers
|
|||
|
//
|
|||
|
|
|||
|
dataLeft = frameLength - headerLength;
|
|||
|
|
|||
|
//
|
|||
|
// get pointer to data in NT buffer and the amount of data to copy (from
|
|||
|
// rest of NT frame)
|
|||
|
//
|
|||
|
|
|||
|
pNtData = (PBYTE)pFrame
|
|||
|
+ pFrame->Contiguous.offUserData
|
|||
|
+ pFrame->Contiguous.cbUserData;
|
|||
|
|
|||
|
bufferLeft = pFrame->Contiguous.cbBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// dataCopied is amount of data copied to current buffer (1 or 2) and goes
|
|||
|
// in cbBuffer field (aka LENGTH_IN_BUFFER)
|
|||
|
//
|
|||
|
|
|||
|
dataCopied = 0;
|
|||
|
|
|||
|
//
|
|||
|
// we have copied all the data we could to the first buffer. While there
|
|||
|
// are more DOS buffers, copy data from NT buffer
|
|||
|
//
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// calculate the amount of space available in the current buffer
|
|||
|
//
|
|||
|
|
|||
|
if (dataSpace >= bufferLeft) {
|
|||
|
dataToCopy = bufferLeft;
|
|||
|
dataLeft -= dataToCopy;
|
|||
|
dataSpace -= dataToCopy;
|
|||
|
bufferLeft = 0;
|
|||
|
} else {
|
|||
|
dataToCopy = dataSpace;
|
|||
|
bufferLeft -= dataSpace;
|
|||
|
dataLeft -= dataSpace;
|
|||
|
dataSpace = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// copy the data. This will fill up the current DOS buffer, exhaust the
|
|||
|
// current NT buffer, or both
|
|||
|
//
|
|||
|
|
|||
|
if (dataToCopy) {
|
|||
|
|
|||
|
IF_DEBUG(DLC_RX_DATA) {
|
|||
|
DPUT3("CopyFrame: Copying %d bytes from %x to %x\n", dataToCopy, pNtData, pDosData);
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory(pDosData, pNtData, dataToCopy);
|
|||
|
|
|||
|
//
|
|||
|
// dataCopied accumulates until we fill a DOS buffer
|
|||
|
//
|
|||
|
|
|||
|
dataCopied += dataToCopy;
|
|||
|
|
|||
|
//
|
|||
|
// update to- and from- pointers for next go round loop
|
|||
|
//
|
|||
|
|
|||
|
pDosData += dataToCopy;
|
|||
|
pNtData += dataToCopy;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// we have run out of space in a DOS buffer, or out of data to copy from
|
|||
|
// the NT buffer
|
|||
|
//
|
|||
|
|
|||
|
if (dataLeft) {
|
|||
|
|
|||
|
//
|
|||
|
// we think there is data left to copy
|
|||
|
//
|
|||
|
|
|||
|
if (!bufferLeft) {
|
|||
|
|
|||
|
//
|
|||
|
// finished current NT buffer. Get next one
|
|||
|
//
|
|||
|
|
|||
|
pFrame = pFrame->pNext;
|
|||
|
if (pFrame) {
|
|||
|
bufferLeft = pFrame->Next.cbBuffer;
|
|||
|
pNtData = (PBYTE)pFrame
|
|||
|
+ pFrame->Contiguous.offUserData
|
|||
|
+ pFrame->Contiguous.cbUserData;
|
|||
|
|
|||
|
IF_DEBUG(DLC_RX_DATA) {
|
|||
|
DPUT3("CopyFrame: new NT buffer @%x pNtData @%x bufferLeft=%d\n",
|
|||
|
pFrame, pNtData, bufferLeft);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// no more NT buffers. Is this a partial copy?
|
|||
|
//
|
|||
|
|
|||
|
DPUT("*** ERROR: dataLeft && no more NT buffers ***\n");
|
|||
|
ASSERT(Flags & CF_PARTIAL);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (!dataSpace) {
|
|||
|
|
|||
|
//
|
|||
|
// update the current DOS buffer header (it doesn't matter that
|
|||
|
// we use Contiguous, NotContiguous, or Next: these fields are
|
|||
|
// present in all 3 buffer types)
|
|||
|
//
|
|||
|
|
|||
|
WRITE_WORD(&pDosBuffer->Contiguous.cbFrame, frameLength);
|
|||
|
WRITE_WORD(&pDosBuffer->Contiguous.cbBuffer, dataCopied);
|
|||
|
WRITE_WORD(&pDosBuffer->Contiguous.offUserData, userOffset + bufferOffset);
|
|||
|
WRITE_WORD(&pDosBuffer->Contiguous.cbUserData, UserLength);
|
|||
|
|
|||
|
//
|
|||
|
// and get the next one
|
|||
|
//
|
|||
|
|
|||
|
bufferOffset = READ_WORD(&pDosBuffer->pNext);
|
|||
|
pDosBuffer = DOS_PTR_TO_FLAT(pDosBuffer->pNext);
|
|||
|
|
|||
|
//
|
|||
|
// if we have another DOS buffer, then it is a Next buffer. Get the
|
|||
|
// buffer variables
|
|||
|
//
|
|||
|
|
|||
|
if (pDosBuffer) {
|
|||
|
pDosData = (PBYTE)pDosBuffer + BUFFER_2_SIZE + UserLength;
|
|||
|
userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->Next.cbUserData)
|
|||
|
+ sizeof(((PLLC_DOS_BUFFER)0)->Next.cbUserData);
|
|||
|
|
|||
|
//
|
|||
|
// sanity check
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(userOffset == 12);
|
|||
|
|
|||
|
//
|
|||
|
// get new available space (constant)
|
|||
|
//
|
|||
|
|
|||
|
dataSpace = BufferSize - (BUFFER_2_SIZE + UserLength);
|
|||
|
dataCopied = 0;
|
|||
|
|
|||
|
IF_DEBUG(DLC_RX_DATA) {
|
|||
|
DPUT3("CopyFrame: new DOS buffer @%x pDosData @%x dataSpace=%d\n",
|
|||
|
pDosBuffer, pDosData, dataSpace);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// no more DOS buffers. Is this a partial copy?
|
|||
|
//
|
|||
|
|
|||
|
DPUT("*** ERROR: dataLeft && no more DOS buffers ***\n");
|
|||
|
ASSERT(Flags & CF_PARTIAL);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// update the current DOS buffer header
|
|||
|
//
|
|||
|
|
|||
|
WRITE_WORD(&pDosBuffer->Contiguous.cbFrame, frameLength);
|
|||
|
WRITE_WORD(&pDosBuffer->Contiguous.cbBuffer, dataCopied);
|
|||
|
WRITE_WORD(&pDosBuffer->Contiguous.offUserData, userOffset + bufferOffset);
|
|||
|
WRITE_WORD(&pDosBuffer->Contiguous.cbUserData, UserLength);
|
|||
|
}
|
|||
|
|
|||
|
} while ( dataLeft );
|
|||
|
|
|||
|
//
|
|||
|
// if CF_PARTIAL set then we knew we were copying a partial frame before
|
|||
|
// we got here
|
|||
|
//
|
|||
|
|
|||
|
return (Flags & CF_PARTIAL)
|
|||
|
? LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
|
|||
|
: LLC_STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
AllBuffersInPool(
|
|||
|
IN DWORD PoolIndex
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Returns TRUE if all buffers that a pool has held are currently in the pool.
|
|||
|
|
|||
|
Once a buffer has been added to a pool, it cannot be removed, saved by not
|
|||
|
returning it to the pool. Hence this function will always return TRUE if
|
|||
|
the app is well-behaved and all buffers that have been placed in the pool
|
|||
|
are currently in the pool
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PoolIndex - pool id
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
TRUE - all buffers back
|
|||
|
FALSE - buffer pool currently contains less than full number of buffers
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN result;
|
|||
|
PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[PoolIndex];
|
|||
|
|
|||
|
EnterCriticalSection(&BufferSemaphore);
|
|||
|
result = (pBufferPool->BufferCount == pBufferPool->MaximumBufferCount);
|
|||
|
LeaveCriticalSection(&BufferSemaphore);
|
|||
|
return result;
|
|||
|
}
|