2084 lines
63 KiB
C
2084 lines
63 KiB
C
|
// Copyright (c) 1997, Microsoft Corporation, all rights reserved
|
||
|
// Copyright (c) 1997, Parallel Technologies, Inc., all rights reserved
|
||
|
//
|
||
|
// mp.c
|
||
|
// RAS DirectParallel WAN mini-port/call-manager driver
|
||
|
// Mini-port routines
|
||
|
//
|
||
|
// 01/07/97 Steve Cobb
|
||
|
// 09/15/97 Jay Lowe, Parallel Technologies, Inc.
|
||
|
|
||
|
|
||
|
#include "ptiwan.h"
|
||
|
#include "ptilink.h"
|
||
|
|
||
|
// The adapter control block address is recorded in this global as a debugging
|
||
|
// aid. This global must not be read by any code.
|
||
|
//
|
||
|
ADAPTERCB* g_pDebugAdapter;
|
||
|
|
||
|
// Default settings for the NDIS_WAN_CO_INFO capabilities of an adapter.
|
||
|
//
|
||
|
static NDIS_WAN_CO_INFO g_infoDefaults =
|
||
|
{
|
||
|
PTI_MaxFrameSize, // MaxFrameSize
|
||
|
1, // MaxSendWindow (placeholder)
|
||
|
PPP_FRAMING // FramingBits
|
||
|
| PPP_COMPRESS_ADDRESS_CONTROL
|
||
|
| PPP_COMPRESS_PROTOCOL_FIELD,
|
||
|
0, // DesiredACCM
|
||
|
};
|
||
|
|
||
|
// String constants for Win9x UNIMODEM emulation
|
||
|
//
|
||
|
CHAR g_szClient[] = "CLIENT";
|
||
|
#define CLIENTLEN 6
|
||
|
CHAR g_szClientServer[] = "CLIENTSERVER";
|
||
|
#define CLIENTSERVERLEN 12
|
||
|
|
||
|
// Async framing definitions.
|
||
|
//
|
||
|
#define PPPFLAGBYTE 0x7E
|
||
|
#define PPPESCBYTE 0x7D
|
||
|
|
||
|
#if DBG
|
||
|
BOOLEAN g_fAssumeWin9x = FALSE;
|
||
|
BOOLEAN g_fNoAccmFastPath = FALSE;
|
||
|
#endif
|
||
|
|
||
|
NDIS_PNP_CAPABILITIES PnpCaps =
|
||
|
{
|
||
|
0, // Flags
|
||
|
{
|
||
|
NdisDeviceStateUnspecified,
|
||
|
NdisDeviceStateUnspecified,
|
||
|
NdisDeviceStateUnspecified
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Local prototypes (alphabetically)
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
AsyncFromHdlcFraming(
|
||
|
IN UCHAR* pInBuf,
|
||
|
IN ULONG ulInBufLen,
|
||
|
OUT UCHAR* pOutBuf,
|
||
|
OUT ULONG* pulOutBufLen,
|
||
|
IN ULONG ulAccmMask );
|
||
|
|
||
|
USHORT
|
||
|
CalculatePppFcs(
|
||
|
IN UCHAR* pBuf,
|
||
|
IN ULONG ulBufLen );
|
||
|
|
||
|
VOID
|
||
|
FreeAdapter(
|
||
|
IN ADAPTERCB* pAdapter );
|
||
|
|
||
|
NDIS_STATUS
|
||
|
RegistrySettings(
|
||
|
IN OUT ADAPTERCB* pAdapter,
|
||
|
IN NDIS_HANDLE WrapperConfigurationContext );
|
||
|
|
||
|
BOOLEAN
|
||
|
HdlcFromAsyncFraming(
|
||
|
IN UCHAR* pInBuf,
|
||
|
IN ULONG ulInBufLen,
|
||
|
OUT UCHAR* pOutBuf,
|
||
|
OUT ULONG* pulOutBufLen );
|
||
|
|
||
|
NDIS_STATUS
|
||
|
QueryInformation(
|
||
|
IN ADAPTERCB* pAdapter,
|
||
|
IN VCCB* pLink,
|
||
|
IN NDIS_OID Oid,
|
||
|
IN PVOID InformationBuffer,
|
||
|
IN ULONG InformationBufferLength,
|
||
|
OUT PULONG BytesWritten,
|
||
|
OUT PULONG BytesNeeded );
|
||
|
|
||
|
NDIS_STATUS
|
||
|
SetInformation(
|
||
|
IN ADAPTERCB* pAdapter,
|
||
|
IN VCCB* pLink,
|
||
|
IN NDIS_OID Oid,
|
||
|
IN PVOID InformationBuffer,
|
||
|
IN ULONG InformationBufferLength,
|
||
|
OUT PULONG BytesRead,
|
||
|
OUT PULONG BytesNeeded );
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Mini-port handlers
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
NDIS_STATUS
|
||
|
PtiInit(
|
||
|
OUT PNDIS_STATUS OpenErrorStatus,
|
||
|
OUT PUINT SelectedMediumIndex,
|
||
|
IN PNDIS_MEDIUM MediumArray,
|
||
|
IN UINT MediumArraySize,
|
||
|
IN NDIS_HANDLE MiniportAdapterHandle,
|
||
|
IN NDIS_HANDLE WrapperConfigurationContext )
|
||
|
|
||
|
// Standard 'MiniportInitialize' routine called by NDIS to initialize a
|
||
|
// new WAN adapter. See DDK doc. The driver will receive no requests
|
||
|
// until this initialization has completed.
|
||
|
//
|
||
|
{
|
||
|
NDIS_STATUS status;
|
||
|
ADAPTERCB* pAdapter;
|
||
|
|
||
|
TRACE( TL_N, TM_Init, ( "PtiInit" ) );
|
||
|
|
||
|
#ifdef TESTMODE
|
||
|
DbgBreakPoint();
|
||
|
#endif
|
||
|
|
||
|
status = *OpenErrorStatus = NDIS_STATUS_SUCCESS;
|
||
|
|
||
|
// Find the medium index in the array of media, looking for the only one
|
||
|
// we support, 'NdisMediumCoWan'.
|
||
|
//
|
||
|
{
|
||
|
UINT i;
|
||
|
|
||
|
for (i = 0; i < MediumArraySize; ++i)
|
||
|
{
|
||
|
if (MediumArray[ i ] == NdisMediumCoWan)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i >= MediumArraySize)
|
||
|
{
|
||
|
TRACE( TL_A, TM_Init, ( "medium?" ) );
|
||
|
return NDIS_STATUS_FAILURE;
|
||
|
}
|
||
|
|
||
|
*SelectedMediumIndex = i;
|
||
|
}
|
||
|
|
||
|
// Allocate and zero a control block for the new adapter.
|
||
|
//
|
||
|
pAdapter = ALLOC_NONPAGED( sizeof(*pAdapter), MTAG_ADAPTERCB );
|
||
|
TRACE( TL_N, TM_Init, ( "PtiInit: pAdapter=$%p", pAdapter ) );
|
||
|
if (!pAdapter)
|
||
|
{
|
||
|
return NDIS_STATUS_RESOURCES;
|
||
|
}
|
||
|
NdisZeroMemory( pAdapter, sizeof(*pAdapter) );
|
||
|
|
||
|
// The adapter control block address is recorded in 'g_pDebugAdapter' as a
|
||
|
// debugging aid only. This global is not to be read by any code.
|
||
|
//
|
||
|
g_pDebugAdapter = pAdapter;
|
||
|
|
||
|
// Set a marker for easier memory dump browsing and future assertions.
|
||
|
//
|
||
|
pAdapter->ulTag = MTAG_ADAPTERCB;
|
||
|
|
||
|
// Save the NDIS handle associated with this adapter for use in future
|
||
|
// NdixXxx calls.
|
||
|
//
|
||
|
pAdapter->MiniportAdapterHandle = MiniportAdapterHandle;
|
||
|
|
||
|
// Copy defaults NDISWAN information. Some of these are updated below.
|
||
|
//
|
||
|
NdisMoveMemory( &pAdapter->info, &g_infoDefaults, sizeof(pAdapter->info) );
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Read/write this adapter's registry settings.
|
||
|
//
|
||
|
status = RegistrySettings(
|
||
|
pAdapter,
|
||
|
WrapperConfigurationContext );
|
||
|
|
||
|
if (status != NDIS_STATUS_SUCCESS)
|
||
|
{
|
||
|
// Set 'usMaxVcs' to 0 as an indication to FreeAdapter that the
|
||
|
// lookaside lists and pools were not initialized.
|
||
|
//
|
||
|
pAdapter->usMaxVcs = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Initialize lookaside lists, buffer pools, and packet pool. On NT,
|
||
|
// lookaside depths are optimized by the system based on usage
|
||
|
// regardless of the depth set, but choose something reasonable
|
||
|
// anyway.
|
||
|
//
|
||
|
{
|
||
|
NdisInitializeNPagedLookasideList(
|
||
|
&pAdapter->llistWorkItems,
|
||
|
NULL, NULL, 0,
|
||
|
sizeof(NDIS_WORK_ITEM),
|
||
|
MTAG_WORKITEM,
|
||
|
4 );
|
||
|
|
||
|
NdisInitializeNPagedLookasideList(
|
||
|
&pAdapter->llistVcs,
|
||
|
NULL, NULL, 0,
|
||
|
sizeof(VCCB),
|
||
|
MTAG_VCCB,
|
||
|
4 );
|
||
|
|
||
|
InitBufferPool(
|
||
|
&pAdapter->poolFrameBuffers,
|
||
|
PTI_FrameBufferSize,
|
||
|
0, 10, 0,
|
||
|
TRUE, MTAG_FBUFPOOL );
|
||
|
|
||
|
InitPacketPool(
|
||
|
&pAdapter->poolPackets,
|
||
|
0, 0, 10, 0,
|
||
|
MTAG_PACKETPOOL );
|
||
|
}
|
||
|
|
||
|
// Inform NDIS of the attributes of our adapter. Set the
|
||
|
// 'MiniportAdapterContext' returned to us by NDIS when it calls our
|
||
|
// handlers to the address of our adapter control block. Turn off
|
||
|
// hardware oriented timeouts.
|
||
|
//
|
||
|
NdisMSetAttributesEx(
|
||
|
MiniportAdapterHandle,
|
||
|
(NDIS_HANDLE)pAdapter,
|
||
|
(UINT)-1,
|
||
|
NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT
|
||
|
| NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT,
|
||
|
NdisInterfaceInternal );
|
||
|
|
||
|
// Register the address family of our call manager with NDIS for the
|
||
|
// newly bound adapter. We use the mini-port form of
|
||
|
// RegisterAddressFamily instead of the protocol form, though that
|
||
|
// would also work. With the protocol form, our internal call manager
|
||
|
// would have to go thru NDIS to talk to the mini-port instead of just
|
||
|
// calling directly. Since the DirectParallel call manager is not
|
||
|
// useful with anything but the DirectParallel mini-port, this would be a waste.
|
||
|
// The mini-port form also causes the call manager VC context to
|
||
|
// automatically map to the mini-port VC context, which is exactly
|
||
|
// what we want.
|
||
|
//
|
||
|
// NDIS notifies all call manager clients of the new family we
|
||
|
// register. The TAPI proxy is the only client expected to be
|
||
|
// interested. NDISWAN will receive the notification, but ignore it
|
||
|
// and wait for the TAPI proxy to notify it of the proxied version.
|
||
|
//
|
||
|
{
|
||
|
NDIS_CALL_MANAGER_CHARACTERISTICS ncmc;
|
||
|
CO_ADDRESS_FAMILY family;
|
||
|
|
||
|
NdisZeroMemory( &family, sizeof(family) );
|
||
|
family.MajorVersion = NDIS_MajorVersion;
|
||
|
family.MinorVersion = NDIS_MinorVersion;
|
||
|
family.AddressFamily = CO_ADDRESS_FAMILY_TAPI_PROXY;
|
||
|
|
||
|
NdisZeroMemory( &ncmc, sizeof(ncmc) );
|
||
|
ncmc.MajorVersion = NDIS_MajorVersion;
|
||
|
ncmc.MinorVersion = NDIS_MinorVersion;
|
||
|
ncmc.CmCreateVcHandler = PtiCmCreateVc;
|
||
|
ncmc.CmDeleteVcHandler = PtiCmDeleteVc;
|
||
|
ncmc.CmOpenAfHandler = PtiCmOpenAf;
|
||
|
ncmc.CmCloseAfHandler = PtiCmCloseAf;
|
||
|
ncmc.CmRegisterSapHandler = PtiCmRegisterSap;
|
||
|
ncmc.CmDeregisterSapHandler = PtiCmDeregisterSap;
|
||
|
ncmc.CmMakeCallHandler = PtiCmMakeCall;
|
||
|
ncmc.CmCloseCallHandler = PtiCmCloseCall;
|
||
|
ncmc.CmIncomingCallCompleteHandler = PtiCmIncomingCallComplete;
|
||
|
// no CmAddPartyHandler
|
||
|
// no CmDropPartyHandler
|
||
|
ncmc.CmActivateVcCompleteHandler = PtiCmActivateVcComplete;
|
||
|
ncmc.CmDeactivateVcCompleteHandler = PtiCmDeactivateVcComplete;
|
||
|
ncmc.CmModifyCallQoSHandler = PtiCmModifyCallQoS;
|
||
|
ncmc.CmRequestHandler = PtiCmRequest;
|
||
|
|
||
|
TRACE( TL_I, TM_Mp, ( "PtiInit: NdisMCmRegAf" ) );
|
||
|
status = NdisMCmRegisterAddressFamily(
|
||
|
MiniportAdapterHandle, &family, &ncmc, sizeof(ncmc) );
|
||
|
TRACE( TL_I, TM_Mp, ( "PtiInit: NdisMCmRegAf=$%x", status ) );
|
||
|
}
|
||
|
}
|
||
|
while (FALSE);
|
||
|
|
||
|
if (status == NDIS_STATUS_SUCCESS)
|
||
|
{
|
||
|
// Add a reference that will eventually be removed by an NDIS call to
|
||
|
// the LmpHalt handler.
|
||
|
//
|
||
|
ReferenceAdapter( pAdapter );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Failed, so undo whatever portion succeeded.
|
||
|
//
|
||
|
if (pAdapter)
|
||
|
{
|
||
|
FreeAdapter( pAdapter );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TRACE( TL_V, TM_Init, ( "PtiInit: Exit: status=$%x", status ) );
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PtiHalt(
|
||
|
IN NDIS_HANDLE MiniportAdapterContext )
|
||
|
|
||
|
// Standard 'MiniportHalt' routine called by NDIS to deallocate all
|
||
|
// resources attached to the adapter. NDIS does not make any other calls
|
||
|
// for this mini-port adapter during or after this call. NDIS will not
|
||
|
// call this routine when packets indicated as received have not been
|
||
|
// returned, or when any VC is created and known to NDIS. Runs at PASSIVE
|
||
|
// IRQL.
|
||
|
//
|
||
|
{
|
||
|
ADAPTERCB* pAdapter;
|
||
|
|
||
|
TRACE( TL_N, TM_Mp, ( "PtiHalt" ) );
|
||
|
|
||
|
pAdapter = (ADAPTERCB* )MiniportAdapterContext;
|
||
|
if (pAdapter->ulTag != MTAG_ADAPTERCB)
|
||
|
{
|
||
|
ASSERT( !"Atag?" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
DereferenceAdapter( pAdapter );
|
||
|
|
||
|
TRACE( TL_V, TM_Mp, ( "PtiHalt: Exit" ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
NDIS_STATUS
|
||
|
PtiReset(
|
||
|
OUT PBOOLEAN AddressingReset,
|
||
|
IN NDIS_HANDLE MiniportAdapterContext )
|
||
|
|
||
|
// Standard 'MiniportReset' routine called by NDIS to reset the driver's
|
||
|
// software state.
|
||
|
//
|
||
|
{
|
||
|
TRACE( TL_N, TM_Mp, ( "PtiReset" ) );
|
||
|
return NDIS_STATUS_NOT_RESETTABLE;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PtiReturnPacket(
|
||
|
IN NDIS_HANDLE MiniportAdapterContext,
|
||
|
IN PNDIS_PACKET Packet )
|
||
|
|
||
|
// Standard 'MiniportReturnPacket' routine called by NDIS when a packet
|
||
|
// used to indicate a receive has been released by the driver above.
|
||
|
//
|
||
|
{
|
||
|
VCCB* pVc;
|
||
|
CHAR* pBuffer;
|
||
|
ADAPTERCB* pAdapter;
|
||
|
NDIS_BUFFER* pTrimmedBuffer;
|
||
|
PACKETHEAD* pHead;
|
||
|
PACKETPOOL* pPool;
|
||
|
|
||
|
TRACE( TL_V, TM_Recv, ( "PtiReturnPacket" ) );
|
||
|
|
||
|
// Unpack the context information we stashed earlier.
|
||
|
//
|
||
|
pHead = *((PACKETHEAD** )(&Packet->MiniportReserved[ 0 ]));
|
||
|
pBuffer = *((CHAR** )(&Packet->MiniportReserved[ sizeof(VOID*) ]));
|
||
|
|
||
|
// Find the adapter from the PACKETHEAD address.
|
||
|
//
|
||
|
pPool = PacketPoolFromPacketHead( pHead );
|
||
|
pAdapter = CONTAINING_RECORD( pPool, ADAPTERCB, poolPackets );
|
||
|
ASSERT( pAdapter->ulTag == MTAG_ADAPTERCB );
|
||
|
|
||
|
// Free the descriptor created by NdisCopyBuffer.
|
||
|
//
|
||
|
NdisUnchainBufferAtFront( Packet, &pTrimmedBuffer );
|
||
|
if (pTrimmedBuffer)
|
||
|
{
|
||
|
NdisFreeBuffer( pTrimmedBuffer );
|
||
|
}
|
||
|
|
||
|
// Free the buffer and packet back to the pools.
|
||
|
//
|
||
|
FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE );
|
||
|
FreePacketToPool( &pAdapter->poolPackets, pHead, TRUE );
|
||
|
|
||
|
TRACE( TL_V, TM_Recv, ( "PtiReturnPacket: Exit" ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
NDIS_STATUS
|
||
|
PtiCoActivateVc(
|
||
|
IN NDIS_HANDLE MiniportVcContext,
|
||
|
IN OUT PCO_CALL_PARAMETERS CallParameters )
|
||
|
|
||
|
// Standard 'MiniportCoActivateVc' routine called by NDIS in response to a
|
||
|
// protocol's request to activate a virtual circuit.
|
||
|
//
|
||
|
{
|
||
|
ASSERT( !"PtiCoActVc?" );
|
||
|
return NDIS_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
NDIS_STATUS
|
||
|
PtiCoDeactivateVc(
|
||
|
IN NDIS_HANDLE MiniportVcContext )
|
||
|
|
||
|
// Standard 'MiniportCoDeactivateVc' routine called by NDIS in response to
|
||
|
// a protocol's request to de-activate a virtual circuit.
|
||
|
//
|
||
|
{
|
||
|
ASSERT( !"PtiCoDeactVc?" );
|
||
|
return NDIS_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PtiCoSendPackets(
|
||
|
IN NDIS_HANDLE MiniportVcContext,
|
||
|
IN PPNDIS_PACKET PacketArray,
|
||
|
IN UINT NumberOfPackets )
|
||
|
|
||
|
// Standard 'MiniportCoSendPackets' routine called by NDIS in response to
|
||
|
// a protocol's request to send packets on a virtual circuit.
|
||
|
//
|
||
|
{
|
||
|
UINT i;
|
||
|
NDIS_STATUS status;
|
||
|
NDIS_PACKET** ppPacket;
|
||
|
VCCB* pVc;
|
||
|
ADAPTERCB* pAdapter;
|
||
|
ULONG ulLength;
|
||
|
NDIS_PACKET* pPacket;
|
||
|
PNDIS_BUFFER pBuffer;
|
||
|
PVOID pFrameVirtualAddress;
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
|
||
|
TRACE( TL_V, TM_Send,
|
||
|
( "PtiCoSendPackets: pVc=$%p, nPackets=$%x",
|
||
|
MiniportVcContext, NumberOfPackets ) );
|
||
|
|
||
|
pVc = (VCCB* )MiniportVcContext;
|
||
|
ASSERT( pVc->ulTag == MTAG_VCCB );
|
||
|
pAdapter = pVc->pAdapter;
|
||
|
|
||
|
ReferenceVc( pVc );
|
||
|
|
||
|
for (i = 0, ppPacket = PacketArray;
|
||
|
i < NumberOfPackets;
|
||
|
++i, ++ppPacket)
|
||
|
{
|
||
|
NDIS_PACKET* pPacket = *ppPacket;
|
||
|
|
||
|
if (ReferenceCall( pVc ))
|
||
|
{
|
||
|
// Send the packet and call NdisMCoSendComplete to notify caller
|
||
|
//
|
||
|
NDIS_SET_PACKET_STATUS( pPacket, NDIS_STATUS_PENDING );
|
||
|
|
||
|
// Request the first buffer descriptor
|
||
|
//
|
||
|
NdisQueryPacket( pPacket, NULL, NULL, &pBuffer, NULL );
|
||
|
|
||
|
// While pBuffer <> NULL
|
||
|
do
|
||
|
{
|
||
|
UCHAR* pAsyncBuf;
|
||
|
ULONG ulAsyncLen;
|
||
|
|
||
|
// request buffer address and length
|
||
|
//
|
||
|
NdisQueryBuffer( pBuffer,
|
||
|
&pFrameVirtualAddress,
|
||
|
&ulLength );
|
||
|
|
||
|
if (IsWin9xPeer( pVc ))
|
||
|
{
|
||
|
pAsyncBuf = (UCHAR* )
|
||
|
GetBufferFromPool( &pAdapter->poolFrameBuffers );
|
||
|
|
||
|
if (!pAsyncBuf)
|
||
|
{
|
||
|
status = NDIS_STATUS_FAILURE;
|
||
|
TRACE( TL_A, TM_Send, ( "PtiSP: !pAsyncBuf" ) );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
AsyncFromHdlcFraming(
|
||
|
pFrameVirtualAddress, ulLength,
|
||
|
pAsyncBuf, &ulAsyncLen,
|
||
|
pVc->linkinfo.SendACCM );
|
||
|
|
||
|
pFrameVirtualAddress = pAsyncBuf;
|
||
|
ulLength = ulAsyncLen;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pAsyncBuf = NULL;
|
||
|
}
|
||
|
|
||
|
// send the buffer
|
||
|
//
|
||
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
||
|
status = (NDIS_STATUS) PtiWrite( pVc->PtiExtension,
|
||
|
pFrameVirtualAddress,
|
||
|
ulLength,
|
||
|
PID_STANDARD );
|
||
|
KeLowerIrql(oldIrql);
|
||
|
|
||
|
TRACE( TL_N, TM_Send,
|
||
|
( "PtiCoSendPackets=%x: $%x bytes", status, ulLength ) );
|
||
|
|
||
|
#ifdef TESTMODE
|
||
|
if ( g_ulTraceMask & TM_Data )
|
||
|
{
|
||
|
if (pFrameVirtualAddress != NULL) {
|
||
|
Dump( pFrameVirtualAddress, ulLength, 0, TRUE );
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
if (pAsyncBuf)
|
||
|
{
|
||
|
FreeBufferToPool(
|
||
|
&pAdapter->poolFrameBuffers, pAsyncBuf, TRUE );
|
||
|
}
|
||
|
|
||
|
if (!NT_SUCCESS(status)){
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// get next pBuffer
|
||
|
//
|
||
|
NdisGetNextBuffer( pBuffer, &pBuffer );
|
||
|
|
||
|
// With current NDISWAN behavior only one NDIS_BUFFER will
|
||
|
// ever be received. If multiples are received, we need to
|
||
|
// coalesce the chained buffers into an input buffer for the
|
||
|
// call to AsyncFromHdlcFraming above. For that matter, this
|
||
|
// would send partial PPP frames, which, it seems to me, would
|
||
|
// be discarded as fragments on the other end. Tony, am I
|
||
|
// wrong? To avoid a useless copy, we will skip that for now,
|
||
|
// but acknowledge here that the current code is not strictly
|
||
|
// correct. (SLC)
|
||
|
//
|
||
|
ASSERT( !pBuffer );
|
||
|
}
|
||
|
while ( pBuffer != NULL );
|
||
|
|
||
|
NDIS_SET_PACKET_STATUS( pPacket, status );
|
||
|
TRACE( TL_V, TM_Send,
|
||
|
( "PtiCoSendPackets: NdisMCoSendComp: status=$%x", status ) );
|
||
|
NdisMCoSendComplete( status, pVc->NdisVcHandle, pPacket );
|
||
|
TRACE( TL_V, TM_Send,
|
||
|
( "PtiCoSendPackets: NdisMCoSendComp done" ) );
|
||
|
|
||
|
pVc->ulTotalPackets++;
|
||
|
DereferenceCall( pVc );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRACE( TL_A, TM_Send, ( "Send to inactive call ignored" ) );
|
||
|
NDIS_SET_PACKET_STATUS( pPacket, NDIS_STATUS_FAILURE );
|
||
|
NdisMCoSendComplete( status, pVc->NdisVcHandle, pPacket );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DereferenceVc( pVc );
|
||
|
TRACE( TL_V, TM_Send, ( "PtiCoSendPackets: Exit" ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
NDIS_STATUS
|
||
|
PtiCoRequest(
|
||
|
IN NDIS_HANDLE MiniportAdapterContext,
|
||
|
IN NDIS_HANDLE MiniportVcContext,
|
||
|
IN OUT PNDIS_REQUEST NdisRequest )
|
||
|
|
||
|
// Standard 'MiniportCoRequestHandler' routine called by NDIS in response
|
||
|
// to a protocol's request information from the mini-port. Unlike the
|
||
|
// Query/SetInformation handlers that this routine obsoletes, requests are
|
||
|
// not serialized.
|
||
|
//
|
||
|
{
|
||
|
ADAPTERCB* pAdapter;
|
||
|
VCCB* pVc;
|
||
|
NDIS_STATUS status;
|
||
|
|
||
|
TRACE( TL_N, TM_Mp, ( "PtiCoReq" ) );
|
||
|
|
||
|
pAdapter = (ADAPTERCB* )MiniportAdapterContext;
|
||
|
if (pAdapter->ulTag != MTAG_ADAPTERCB)
|
||
|
{
|
||
|
ASSERT( !"Atag?" );
|
||
|
return NDIS_STATUS_INVALID_DATA;
|
||
|
}
|
||
|
|
||
|
pVc = (VCCB* )MiniportVcContext;
|
||
|
if (pVc && pVc->ulTag != MTAG_VCCB)
|
||
|
{
|
||
|
ASSERT( !"Vtag?" );
|
||
|
return NDIS_STATUS_INVALID_DATA;
|
||
|
}
|
||
|
|
||
|
switch (NdisRequest->RequestType)
|
||
|
{
|
||
|
case NdisRequestQueryInformation:
|
||
|
{
|
||
|
status = QueryInformation(
|
||
|
pAdapter,
|
||
|
pVc,
|
||
|
NdisRequest->DATA.QUERY_INFORMATION.Oid,
|
||
|
NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
|
||
|
NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
|
||
|
&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
|
||
|
&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case NdisRequestSetInformation:
|
||
|
{
|
||
|
status = SetInformation(
|
||
|
pAdapter,
|
||
|
pVc,
|
||
|
NdisRequest->DATA.SET_INFORMATION.Oid,
|
||
|
NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
|
||
|
NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
|
||
|
&NdisRequest->DATA.SET_INFORMATION.BytesRead,
|
||
|
&NdisRequest->DATA.SET_INFORMATION.BytesNeeded );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
status = NDIS_STATUS_NOT_SUPPORTED;
|
||
|
TRACE( TL_A, TM_Mp, ( "PtiCoReq: type=%d unsupported", NdisRequest->RequestType ) );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TRACE( TL_V, TM_Mp, ( "PtiCoReq: Exit: $%x", status ) );
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Callback routines ... called by the PtiLink layer below
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
PVOID
|
||
|
PtiCbGetReadBuffer(
|
||
|
IN PVOID Context,
|
||
|
OUT PULONG BufferSize,
|
||
|
OUT PVOID* RequestContext )
|
||
|
|
||
|
// PtiLink is requesting a read buffer, get one and return it
|
||
|
// This is a the start of a receive event ...
|
||
|
{
|
||
|
VCCB* pVc;
|
||
|
ADAPTERCB* pAdapter;
|
||
|
PCHAR pBuffer;
|
||
|
|
||
|
TRACE( TL_V, TM_Spec, ( "PtiCbGetReadBuffer: pVc=$%p", Context ) );
|
||
|
|
||
|
pVc = (VCCB* )Context;
|
||
|
if (pVc->ulTag != MTAG_VCCB)
|
||
|
{
|
||
|
ASSERT( !"Vtag?" );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pAdapter = pVc->pAdapter;
|
||
|
|
||
|
// the pVc is our context for use of this buffer
|
||
|
//
|
||
|
*RequestContext = pVc;
|
||
|
|
||
|
// Give caller the length of this buffer
|
||
|
//
|
||
|
*BufferSize = PTI_FrameBufferSize;
|
||
|
|
||
|
// ask for a buffer, caller must check for NULL
|
||
|
//
|
||
|
pBuffer = GetBufferFromPool( &pAdapter->poolFrameBuffers );
|
||
|
|
||
|
TRACE( TL_V, TM_Spec,
|
||
|
( "PtiCbGetReadBuffer: Exit: Issuing pBuffer=$%p", pBuffer ) );
|
||
|
|
||
|
// return the buffer to the caller
|
||
|
// this is a raw system va
|
||
|
//
|
||
|
return pBuffer;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PtiRx(
|
||
|
IN PVOID Context,
|
||
|
IN PVOID pBuffer,
|
||
|
IN NTSTATUS Status,
|
||
|
IN ULONG ulLength,
|
||
|
IN PVOID RequestContext )
|
||
|
|
||
|
// Ptilink has completed a read, i.e., receive complete
|
||
|
// buffer now belongs to this layer
|
||
|
//
|
||
|
// Context -- is the pVC
|
||
|
//
|
||
|
// pBuffer -- is the pointer to buffer previously allocated
|
||
|
// to the PtiLink driver via the PtiCbGetReadBuffer function
|
||
|
//
|
||
|
// Status -- one of: NT_SUCCESS = good packet received
|
||
|
// DATA_OVERRUN = header failure
|
||
|
// BUFFER_TOO_SMALL= pBuffer is too small to receive packet
|
||
|
//
|
||
|
// ulLength - packet length
|
||
|
//
|
||
|
// RequestContext -- don't care
|
||
|
//
|
||
|
// General Note: PtiLink below us sends and receives link manangement
|
||
|
// packets using the Our and His structures ... link management packets to
|
||
|
// not flow through here. Link events are announced to us via our
|
||
|
// registered callback (PtiCbLinkEventHandler) below. We have nothing to
|
||
|
// do with Tx/Rx of link pkts.
|
||
|
//
|
||
|
{
|
||
|
VCCB* pVc;
|
||
|
ADAPTERCB* pAdapter;
|
||
|
NDIS_STATUS status;
|
||
|
NDIS_STATUS writestatus;
|
||
|
NDIS_PACKET* pPacket;
|
||
|
NDIS_BUFFER* pNdisBuffer;
|
||
|
PACKETHEAD* pHead;
|
||
|
ULONGLONG ullTimeReceived;
|
||
|
KIRQL oldIrql;
|
||
|
UCHAR* pHdlcBuf;
|
||
|
ULONG ulHdlcLen;
|
||
|
UCHAR* pTmp;
|
||
|
|
||
|
TRACE( TL_N, TM_Recv, ( "PtiRx=%x: bytes=$%x", Status, ulLength ) );
|
||
|
TRACE( TL_V, TM_Recv, ( "PtiRx=%x, pVc=$%p, pBuf=$%p, bytes=$%x",
|
||
|
Status, Context, pBuffer, ulLength ) );
|
||
|
|
||
|
pVc = (VCCB* )Context;
|
||
|
if (pVc->ulTag != MTAG_VCCB)
|
||
|
{
|
||
|
ASSERT( !"Vtag?" );
|
||
|
return;
|
||
|
}
|
||
|
ReferenceVc( pVc );
|
||
|
|
||
|
pAdapter = pVc->pAdapter;
|
||
|
|
||
|
// NOT A REAL DATA PACKET
|
||
|
// return any buffers used for non-data or losing reads
|
||
|
//
|
||
|
if ( !NT_SUCCESS( Status ) ){
|
||
|
TRACE( TL_A, TM_Pool, ( "PtiRx: Status != SUCCESS, freeing buffer", Status ) );
|
||
|
|
||
|
if ( pBuffer != NULL ) {
|
||
|
FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE );
|
||
|
}
|
||
|
|
||
|
DereferenceVc( pVc );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef TESTMODE
|
||
|
if ( g_ulTraceMask & TM_Data )
|
||
|
{
|
||
|
if (pBuffer != NULL) {
|
||
|
Dump( pBuffer, ulLength, 0, TRUE );
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// INCOMING CALL ... NO VC EXISTS YET for this incoming data packet
|
||
|
//
|
||
|
if (ReferenceSap( pAdapter ))
|
||
|
{
|
||
|
if (!(ReadFlags( &pAdapter->pListenVc->ulFlags ) & VCBF_CallInProgress))
|
||
|
{
|
||
|
// Setting in Listen VC here.
|
||
|
//
|
||
|
SetFlags( &pAdapter->pListenVc->ulFlags, VCBF_CallInProgress );
|
||
|
|
||
|
// This is the start of an incoming call which may also start via
|
||
|
// LINK_OPEN event to PtiCbLinkEventHandler
|
||
|
//
|
||
|
// Ignore this packet and proceed to dispatch an incoming call
|
||
|
//
|
||
|
TRACE( TL_V, TM_Recv, ( "PtiRx: Incoming call", Status ) );
|
||
|
|
||
|
// Free the buffer associated with this read ... we throw away the
|
||
|
// data thus losing one packet off the front of an attempt to
|
||
|
// connect, unless the LPKT_OPEN function beats us (a LPKT_OPEN
|
||
|
// notification occurs before first data packet is received ... it
|
||
|
// could happen either way.)
|
||
|
//
|
||
|
if (pBuffer != NULL ) {
|
||
|
FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE );
|
||
|
}
|
||
|
|
||
|
// set up a VC for the incoming call
|
||
|
//
|
||
|
SetupVcAsynchronously( pAdapter );
|
||
|
DereferenceVc( pVc );
|
||
|
DereferenceSap( pAdapter );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
DereferenceSap( pAdapter );
|
||
|
}
|
||
|
|
||
|
// NOW HAVE A REAL DATA PACKET
|
||
|
|
||
|
if (ReferenceCall( pVc ))
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
if (IsWin9xPeer( pVc ))
|
||
|
{
|
||
|
if (pVc->ulTotalPackets < 4)
|
||
|
{
|
||
|
// If packet matches "CLIENT", we emit one saying
|
||
|
// "CLIENTSERVER"
|
||
|
//
|
||
|
// If packet matches "CLIENTSERVER", throw it away
|
||
|
//
|
||
|
// This hack emulates the Win9x UNIMODEM behavior which is
|
||
|
// required to allow Win9x systems to connect to us.
|
||
|
//
|
||
|
// Also, it appears that sending the "CLIENT" packet up
|
||
|
// the stack causes RasTapi to disconnect us immediately.
|
||
|
// It wants to see PPP?
|
||
|
//
|
||
|
|
||
|
if ( StrCmp(
|
||
|
pBuffer, g_szClientServer, CLIENTSERVERLEN ) == 0 )
|
||
|
{
|
||
|
// throw away packets containing "CLIENTSERVER"
|
||
|
//
|
||
|
FreeBufferToPool(
|
||
|
&pAdapter->poolFrameBuffers, pBuffer, TRUE );
|
||
|
TRACE( TL_V, TM_Recv,
|
||
|
( "PtiRx: CLIENTSERVER ignored", Status ) );
|
||
|
pVc->ulTotalPackets++;
|
||
|
break;
|
||
|
}
|
||
|
else if ( StrCmp(
|
||
|
pBuffer, g_szClient, CLIENTLEN ) == 0 )
|
||
|
{
|
||
|
// when we see "CLIENT", throw away and respond
|
||
|
// "CLIENTSERVER".
|
||
|
//
|
||
|
TRACE( TL_V, TM_Recv, ( "PtiRx: See CLIENT", Status ) );
|
||
|
|
||
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
||
|
writestatus = (NDIS_STATUS) PtiWrite(
|
||
|
pVc->PtiExtension,
|
||
|
g_szClientServer,
|
||
|
CLIENTSERVERLEN,
|
||
|
PID_STANDARD );
|
||
|
KeLowerIrql(oldIrql);
|
||
|
|
||
|
FreeBufferToPool(
|
||
|
&pAdapter->poolFrameBuffers, pBuffer, TRUE );
|
||
|
TRACE( TL_V, TM_Recv,
|
||
|
( "PtiRx: CLIENTSERVER sent", Status ) );
|
||
|
pVc->ulTotalPackets++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Un-byte-stuff the received buffer into a second buffer,
|
||
|
// then swap it with the received buffer.
|
||
|
//
|
||
|
pHdlcBuf = (UCHAR* )
|
||
|
GetBufferFromPool( &pAdapter->poolFrameBuffers );
|
||
|
if (!pHdlcBuf)
|
||
|
{
|
||
|
FreeBufferToPool(
|
||
|
&pAdapter->poolFrameBuffers, pBuffer, TRUE );
|
||
|
TRACE( TL_A, TM_Recv, ( "PtiRx: !Alloc HDLC" ) );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
HdlcFromAsyncFraming(
|
||
|
pBuffer, ulLength, pHdlcBuf, &ulHdlcLen );
|
||
|
|
||
|
pTmp = pBuffer;
|
||
|
pBuffer = pHdlcBuf;
|
||
|
ulLength = ulHdlcLen;
|
||
|
FreeBufferToPool( &pAdapter->poolFrameBuffers, pTmp, TRUE );
|
||
|
}
|
||
|
|
||
|
// Note the time if client's call parameters indicated interest in
|
||
|
// time received.
|
||
|
//
|
||
|
if (ReadFlags( &pVc->ulFlags ) & VCBF_IndicateTimeReceived)
|
||
|
{
|
||
|
NdisGetCurrentSystemTime( (LARGE_INTEGER* )&ullTimeReceived );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ullTimeReceived = 0;
|
||
|
}
|
||
|
|
||
|
TRACE( TL_V, TM_Recv,
|
||
|
( "PtiRx: Rx Packet: nBytes=$%x", ulLength ) );
|
||
|
|
||
|
// Get a packet from the packet pool
|
||
|
//
|
||
|
pPacket = GetPacketFromPool( &pAdapter->poolPackets, &pHead );
|
||
|
if (!pPacket)
|
||
|
{
|
||
|
// Packet descriptor pool is maxed.
|
||
|
//
|
||
|
ASSERT( !"GetPfP?" );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Hook the NDIS_BUFFER to the packet. The "copy" here refers to
|
||
|
// descriptor information only. The packet data is not copied.
|
||
|
//
|
||
|
NdisCopyBuffer(
|
||
|
&status,
|
||
|
&pNdisBuffer,
|
||
|
PoolHandleForNdisCopyBufferFromBuffer( pBuffer ),
|
||
|
NdisBufferFromBuffer( pBuffer ),
|
||
|
0,
|
||
|
ulLength );
|
||
|
|
||
|
if (status != STATUS_SUCCESS)
|
||
|
{
|
||
|
// Can't get a MDL which likely means the system is toast.
|
||
|
//
|
||
|
FreePacketToPool( &pAdapter->poolPackets, pHead, TRUE );
|
||
|
TRACE( TL_A, TM_Recv, ( "NdisCopyBuffer=%08x?", status ) );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
NdisChainBufferAtFront( pPacket, pNdisBuffer );
|
||
|
|
||
|
// Stash the time the packet was received in the packet.
|
||
|
//
|
||
|
NDIS_SET_PACKET_TIME_RECEIVED( pPacket, ullTimeReceived );
|
||
|
|
||
|
// Pre-set the packet to success, since a random value of
|
||
|
// NDIS_STATUS_RESOURCES would prevent our ReturnPackets handler
|
||
|
// from getting called.
|
||
|
//
|
||
|
NDIS_SET_PACKET_STATUS( pPacket, NDIS_STATUS_SUCCESS );
|
||
|
|
||
|
// Stash our context information with the packet for clean-up use
|
||
|
// in PtiReturnPacket, then indicate the packet to NDISWAN.
|
||
|
//
|
||
|
*((PACKETHEAD** )(&pPacket->MiniportReserved[ 0 ])) = pHead;
|
||
|
*((CHAR** )(&pPacket->MiniportReserved[ sizeof(VOID*) ])) = pBuffer;
|
||
|
|
||
|
TRACE( TL_V, TM_Recv,
|
||
|
( "PtiRx: NdisMCoIndRecPkt: hVc=$%p, pPacket=$%p, len=$%x",
|
||
|
pVc->NdisVcHandle, pPacket, ulLength ) );
|
||
|
|
||
|
NdisMCoIndicateReceivePacket( pVc->NdisVcHandle, &pPacket, 1 );
|
||
|
|
||
|
TRACE( TL_V, TM_Recv, ( "PtiRx: NdisMCoIndRecPkt done" ) );
|
||
|
|
||
|
// Tell NDIS our "receive process" is complete. Since we deal
|
||
|
// with one packet at a time and NDISWAN does also, this doesn't
|
||
|
// accomplish anything, but the consensus is it's bad form to omit
|
||
|
// it.
|
||
|
//
|
||
|
TRACE( TL_V, TM_Recv, ( "PtiRx: NdisMCoRecComp" ) );
|
||
|
NdisMCoReceiveComplete( pAdapter->MiniportAdapterHandle );
|
||
|
TRACE( TL_V, TM_Recv, ( "PtiRx: NdisMCoRecComp done" ) );
|
||
|
pVc->ulTotalPackets++;
|
||
|
}
|
||
|
while (FALSE);
|
||
|
|
||
|
DereferenceCall( pVc );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRACE( TL_A, TM_Recv, ( "Receive on inactive call ignored" ) );
|
||
|
}
|
||
|
|
||
|
DereferenceVc( pVc );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PtiCbLinkEventHandler(
|
||
|
IN PVOID Context,
|
||
|
IN ULONG PtiLinkEventId,
|
||
|
IN ULONG PtiLinkEventData )
|
||
|
|
||
|
// Ptilink is reporting a link management event (Link up or down)
|
||
|
//
|
||
|
{
|
||
|
VCCB* pVc;
|
||
|
ADAPTERCB* pAdapter;
|
||
|
|
||
|
pVc = (VCCB* )Context;
|
||
|
if (pVc->ulTag != MTAG_VCCB)
|
||
|
{
|
||
|
ASSERT( !"Vtag?" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pAdapter = pVc->pAdapter;
|
||
|
|
||
|
switch (PtiLinkEventId)
|
||
|
{
|
||
|
case PTILINK_LINK_UP:
|
||
|
{
|
||
|
TRACE( TL_A, TM_Cm, ( "LinkEvent: LINK UP, pVc=$%p", pVc ) );
|
||
|
|
||
|
// peer is initiating a call (also happens in PtiRx)
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case PTILINK_LINK_DOWN:
|
||
|
{
|
||
|
TRACE( TL_A, TM_Cm, ( "LinkEvent: LINK DOWN, pVc=$%p", pVc ) );
|
||
|
|
||
|
// peer is closing a call
|
||
|
//
|
||
|
if (pVc == pAdapter->pListenVc)
|
||
|
{
|
||
|
TRACE( TL_A, TM_Cm,
|
||
|
( "LinkEvent: LINK DOWN on ListenVc ignored" ) );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
NdisAcquireSpinLock( &pVc->lockV );
|
||
|
{
|
||
|
pVc->status = NDIS_STATUS_TAPI_DISCONNECTMODE_NORMAL;
|
||
|
CallTransitionComplete( pVc );
|
||
|
}
|
||
|
NdisReleaseSpinLock( &pVc->lockV );
|
||
|
|
||
|
TRACE( TL_A, TM_Cm, ( "LinkEvent: LINK DOWN causing disconnect" ) );
|
||
|
|
||
|
CompleteVc( pVc );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
TRACE( TL_A, TM_Cm,
|
||
|
( "LinkEvent: Bad LinkEvent = %d", PtiLinkEventId ) );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Mini-port utility routines (alphabetically)
|
||
|
// Some are used externally
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
AsyncFromHdlcFraming(
|
||
|
IN UCHAR* pInBuf,
|
||
|
IN ULONG ulInBufLen,
|
||
|
OUT UCHAR* pOutBuf,
|
||
|
OUT ULONG* pulOutBufLen,
|
||
|
IN ULONG ulAccmMask )
|
||
|
|
||
|
// Make a copy of PPP HDLC framed data buffer 'pInBuf' of length
|
||
|
// 'ulInBufLen' bytes in caller's 'pOutBuf' buffer, converting to
|
||
|
// byte-stuffed asynchronous PPP framed format in the process.
|
||
|
// 'POutBufLen' is the length in bytes of the returned output buffer. Due
|
||
|
// to the byte stuffing, caller must allow for up to twice the length of
|
||
|
// 'pInfBuf'. 'UlAccmMask' is the bitmask of characters to be byte
|
||
|
// stuffed.
|
||
|
//
|
||
|
// With current implementation, user must allow 2 extra bytes at the end
|
||
|
// of the input buffer for stashing the FCS during byte-stuffing.
|
||
|
//
|
||
|
// This routine is adapted from the ASYNCMAC AssemblePppFrame routine, as
|
||
|
// is the following description, which in turn was lifted from RFC 1331
|
||
|
// (May 1992). The PPP frame NDISWAN passes us for sends is the data from
|
||
|
// the Address field through the Information field inclusive, without any
|
||
|
// byte stuffing, of course.
|
||
|
//
|
||
|
// Asynchronously framed PPP packet:
|
||
|
//
|
||
|
// +----------+----------+----------+----------+------------...
|
||
|
// | Flag | Address | Control | Protocol | Information
|
||
|
// | 01111110 | 11111111 | 00000011 | 16 bits | *
|
||
|
// +----------+----------+----------+----------+------------...
|
||
|
// ...---+----------+----------+-----------------
|
||
|
// | FCS | Flag | Inter-frame Fill
|
||
|
// | 16 bits | 01111110 | or next Address
|
||
|
// ...---+----------+----------+-----------------
|
||
|
//
|
||
|
// Frame Check Sequence (FCS) Field
|
||
|
//
|
||
|
// The Frame Check Sequence field is normally 16 bits (two octets). The
|
||
|
// use of other FCS lengths may be defined at a later time, or by prior
|
||
|
// agreement.
|
||
|
//
|
||
|
// The FCS field is calculated over all bits of the Address, Control,
|
||
|
// Protocol and Information fields not including any start and stop bits
|
||
|
// (asynchronous) and any bits (synchronous) or octets (asynchronous)
|
||
|
// inserted for transparency. This does not include the Flag Sequences
|
||
|
// or the FCS field itself. The FCS is transmitted with the coefficient
|
||
|
// of the highest term first.
|
||
|
//
|
||
|
// Note: When octets are received which are flagged in the Async-
|
||
|
// Control-Character-Map, they are discarded before calculating the
|
||
|
// FCS. See the description in Appendix A.
|
||
|
//
|
||
|
// On asynchronous links, a character stuffing procedure is used.
|
||
|
// The Control Escape octet is defined as binary 01111101
|
||
|
// (hexadecimal 0x7d) where the bit positions are numbered 87654321
|
||
|
// (not 76543210, BEWARE).
|
||
|
//
|
||
|
// After FCS computation, the transmitter examines the entire frame
|
||
|
// between the two Flag Sequences. Each Flag Sequence, Control
|
||
|
// Escape octet and octet with value less than hexadecimal 0x20 which
|
||
|
// is flagged in the Remote Async-Control-Character-Map is replaced
|
||
|
// by a two octet sequence consisting of the Control Escape octet and
|
||
|
// the original octet with bit 6 complemented (i.e., exclusive-or'd
|
||
|
// with hexadecimal 0x20).
|
||
|
//
|
||
|
// Prior to FCS computation, the receiver examines the entire frame
|
||
|
// between the two Flag Sequences. Each octet with value less than
|
||
|
// hexadecimal 0x20 is checked. If it is flagged in the Local
|
||
|
// Async-Control-Character-Map, it is simply removed (it may have
|
||
|
// been inserted by intervening data communications equipment). For
|
||
|
// each Control Escape octet, that octet is also removed, but bit 6
|
||
|
// of the following octet is complemented. A Control Escape octet
|
||
|
// immediately preceding the closing Flag Sequence indicates an
|
||
|
// invalid frame.
|
||
|
//
|
||
|
// Note: The inclusion of all octets less than hexadecimal 0x20
|
||
|
// allows all ASCII control characters [10] excluding DEL (Delete)
|
||
|
// to be transparently communicated through almost all known data
|
||
|
// communications equipment.
|
||
|
//
|
||
|
//
|
||
|
// The transmitter may also send octets with value in the range 0x40
|
||
|
// through 0xff (except 0x5e) in Control Escape format. Since these
|
||
|
// octet values are not negotiable, this does not solve the problem
|
||
|
// of receivers which cannot handle all non-control characters.
|
||
|
// Also, since the technique does not affect the 8th bit, this does
|
||
|
// not solve problems for communications links that can send only 7-
|
||
|
// bit characters.
|
||
|
//
|
||
|
// A few examples may make this more clear. Packet data is
|
||
|
// transmitted on the link as follows:
|
||
|
//
|
||
|
// 0x7e is encoded as 0x7d, 0x5e.
|
||
|
// 0x7d is encoded as 0x7d, 0x5d.
|
||
|
//
|
||
|
// 0x01 is encoded as 0x7d, 0x21.
|
||
|
//
|
||
|
// Some modems with software flow control may intercept outgoing DC1
|
||
|
// and DC3 ignoring the 8th (parity) bit. This data would be
|
||
|
// transmitted on the link as follows:
|
||
|
//
|
||
|
// 0x11 is encoded as 0x7d, 0x31.
|
||
|
// 0x13 is encoded as 0x7d, 0x33.
|
||
|
// 0x91 is encoded as 0x7d, 0xb1.
|
||
|
// 0x93 is encoded as 0x7d, 0xb3.
|
||
|
//
|
||
|
{
|
||
|
USHORT usFcs;
|
||
|
UCHAR* pIn;
|
||
|
UCHAR* pOut;
|
||
|
ULONG ulInBytesLeft;
|
||
|
|
||
|
pIn = pInBuf;
|
||
|
ulInBytesLeft = ulInBufLen;
|
||
|
pOut = pOutBuf;
|
||
|
|
||
|
// Calculate the frame check sequence on the data.
|
||
|
//
|
||
|
TRACE( TL_I, TM_Data, ( "AfromH (send) dump:" ) );
|
||
|
DUMPB( TL_I, TM_Data, pInBuf, ulInBufLen );
|
||
|
usFcs = CalculatePppFcs( pInBuf, ulInBufLen );
|
||
|
usFcs ^= 0xFFFF;
|
||
|
|
||
|
// Add the calculated FCS. Added to the input buffer for convenience as
|
||
|
// it must be byte-stuffed along with the other data, though this uglies
|
||
|
// the interface a bit.
|
||
|
//
|
||
|
pIn[ ulInBytesLeft ] = (UCHAR )usFcs;
|
||
|
++ulInBytesLeft;
|
||
|
pIn[ ulInBytesLeft ] = (UCHAR )(usFcs >> 8);
|
||
|
++ulInBytesLeft;
|
||
|
|
||
|
// Add the initial flag byte.
|
||
|
//
|
||
|
*pOut = PPPFLAGBYTE;
|
||
|
++pOut;
|
||
|
|
||
|
// Because an empty control character mask is common, an optimized loop is
|
||
|
// provided in that case.
|
||
|
//
|
||
|
if (ulAccmMask
|
||
|
#ifdef TESTMODE
|
||
|
|| g_fNoAccmFastPath
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
// Have bitmask...slower path.
|
||
|
//
|
||
|
while (ulInBytesLeft--)
|
||
|
{
|
||
|
UCHAR uch;
|
||
|
|
||
|
uch = *pIn;
|
||
|
++pIn;
|
||
|
|
||
|
if (((uch < 0x20) && ((1 << uch) & ulAccmMask))
|
||
|
|| (uch == PPPESCBYTE) || (uch == PPPFLAGBYTE))
|
||
|
{
|
||
|
// Byte stuff the character.
|
||
|
//
|
||
|
*pOut = PPPESCBYTE;
|
||
|
++pOut;
|
||
|
*pOut = uch ^ 0x20;
|
||
|
++pOut;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Copy the character as is.
|
||
|
//
|
||
|
*pOut = uch;
|
||
|
++pOut;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No bitmask...fast path.
|
||
|
//
|
||
|
while (ulInBytesLeft--)
|
||
|
{
|
||
|
UCHAR uch;
|
||
|
|
||
|
uch = *pIn;
|
||
|
++pIn;
|
||
|
|
||
|
if ((uch == PPPESCBYTE) || (uch == PPPFLAGBYTE))
|
||
|
{
|
||
|
// Byte stuff the character.
|
||
|
//
|
||
|
*pOut = PPPESCBYTE;
|
||
|
++pOut;
|
||
|
*pOut = uch ^ 0x20;
|
||
|
++pOut;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Copy the character as is.
|
||
|
//
|
||
|
*pOut = uch;
|
||
|
++pOut;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add the trailing flag byte.
|
||
|
//
|
||
|
*pOut = PPPFLAGBYTE;
|
||
|
++pOut;
|
||
|
|
||
|
// Calculate length of output.
|
||
|
//
|
||
|
*pulOutBufLen = (ULONG )(pOut - pOutBuf);
|
||
|
}
|
||
|
|
||
|
|
||
|
USHORT
|
||
|
CalculatePppFcs(
|
||
|
IN UCHAR* pBuf,
|
||
|
IN ULONG ulBufLen )
|
||
|
|
||
|
// Return the PPP Frame Check Sequence on 'ulBufLen' bytes starting at
|
||
|
// 'pBuf'.
|
||
|
//
|
||
|
// (Taken from ASYNCMAC)
|
||
|
//
|
||
|
{
|
||
|
static USHORT ausFcsTable[ 256 ] =
|
||
|
{
|
||
|
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
|
||
|
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
|
||
|
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
|
||
|
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
|
||
|
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
|
||
|
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
|
||
|
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
|
||
|
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
|
||
|
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
|
||
|
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
|
||
|
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
|
||
|
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
|
||
|
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
|
||
|
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
|
||
|
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
|
||
|
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
|
||
|
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
|
||
|
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
|
||
|
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
|
||
|
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
|
||
|
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
|
||
|
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
|
||
|
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
|
||
|
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
|
||
|
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
|
||
|
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
|
||
|
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
|
||
|
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
|
||
|
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
|
||
|
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
|
||
|
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
|
||
|
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
|
||
|
};
|
||
|
|
||
|
register USHORT usFcs;
|
||
|
|
||
|
usFcs = 0xFFFF;
|
||
|
while (ulBufLen--)
|
||
|
{
|
||
|
usFcs = (usFcs >> 8) ^ ausFcsTable[ (usFcs ^ (USHORT )*pBuf) & 0xFF ];
|
||
|
++pBuf;
|
||
|
}
|
||
|
|
||
|
return usFcs;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
DereferenceAdapter(
|
||
|
IN ADAPTERCB* pAdapter )
|
||
|
|
||
|
// Removes a reference from the adapter control block 'pAdapter', and when
|
||
|
// frees the adapter resources when the last reference is removed.
|
||
|
//
|
||
|
{
|
||
|
LONG lRef;
|
||
|
|
||
|
lRef = NdisInterlockedDecrement( &pAdapter->lRef );
|
||
|
|
||
|
TRACE( TL_N, TM_Ref, ( "DerefA to %d", lRef ) );
|
||
|
ASSERT( lRef >= 0 );
|
||
|
|
||
|
if (lRef == 0)
|
||
|
{
|
||
|
FreeAdapter( pAdapter );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
DereferenceVc(
|
||
|
IN VCCB* pVc )
|
||
|
|
||
|
// Removes a reference to the VC control block 'pVc', and when frees the
|
||
|
// block when the last reference is removed.
|
||
|
//
|
||
|
{
|
||
|
LONG lRef;
|
||
|
|
||
|
lRef = NdisInterlockedDecrement( &pVc->lRef );
|
||
|
|
||
|
TRACE( TL_N, TM_Ref, ( "DerefVc to %d", lRef ) );
|
||
|
ASSERT( lRef >= 0 );
|
||
|
|
||
|
if (lRef == 0)
|
||
|
{
|
||
|
ADAPTERCB* pAdapter;
|
||
|
|
||
|
// now for an interesting bit ...
|
||
|
//
|
||
|
// if we have a listenVc allocated, then revert to using that
|
||
|
//
|
||
|
pAdapter = pVc->pAdapter;
|
||
|
if (pAdapter->ulTag != MTAG_ADAPTERCB)
|
||
|
{
|
||
|
ASSERT( !"Atag?" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pAdapter->pListenVc && pAdapter->pListenVc->hPtiLink)
|
||
|
{
|
||
|
TRACE( TL_V, TM_Mp,
|
||
|
( "DerefVc: Reverting to pVc=$%p", pAdapter->pListenVc ) );
|
||
|
|
||
|
ClearFlags( &pAdapter->pListenVc->ulFlags, VCBF_CallInProgress );
|
||
|
|
||
|
// reregister using the listen Vc
|
||
|
//
|
||
|
TRACE( TL_V, TM_Mp, ( "DerefVc: RegCb pLV=$%p",
|
||
|
pAdapter->pListenVc ) );
|
||
|
PtiRegisterCallbacks(pAdapter->pListenVc->Extension, // the PTILINKx extension
|
||
|
PtiCbGetReadBuffer, // our get buffer routine
|
||
|
PtiRx, // our receive complete routine
|
||
|
PtiCbLinkEventHandler, // our link event handler
|
||
|
pAdapter->pListenVc); // our new context
|
||
|
}
|
||
|
|
||
|
// Can make these assumptions because NDIS will not call the delete-VC
|
||
|
// handler while the VC is active. All the nasty VC clean up occurs
|
||
|
// before the VC is deactivated and the call closed.
|
||
|
//
|
||
|
pVc->ulTag = MTAG_FREED;
|
||
|
FREE_VCCB( pAdapter, pVc );
|
||
|
DereferenceAdapter( pAdapter );
|
||
|
TRACE( TL_I, TM_Mp, ( "pVc=$%p freed", pVc ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
FreeAdapter(
|
||
|
IN ADAPTERCB* pAdapter )
|
||
|
|
||
|
// Frees all resources allocated for adapter 'pAdapter', including
|
||
|
// 'pAdapter' itself.
|
||
|
//
|
||
|
{
|
||
|
BOOLEAN fSuccess;
|
||
|
|
||
|
// Setting 'usMaxVcs' to 0 is PtiInitialize's way of telling us that the
|
||
|
// lookaside lists and pools were not initialized.
|
||
|
//
|
||
|
if (pAdapter->usMaxVcs)
|
||
|
{
|
||
|
NdisDeleteNPagedLookasideList( &pAdapter->llistWorkItems );
|
||
|
NdisDeleteNPagedLookasideList( &pAdapter->llistVcs );
|
||
|
}
|
||
|
|
||
|
TRACE( TL_V, TM_Mp, ( "FreeAdapter" ) );
|
||
|
|
||
|
pAdapter->ulTag = MTAG_FREED;
|
||
|
FREE_NONPAGED( pAdapter );
|
||
|
}
|
||
|
|
||
|
|
||
|
NDIS_STATUS
|
||
|
RegistrySettings(
|
||
|
IN OUT ADAPTERCB* pAdapter,
|
||
|
IN NDIS_HANDLE WrapperConfigurationContext )
|
||
|
|
||
|
// Read this mini-port's registry settings into 'pAdapter' fields. Also
|
||
|
// writes registry values read by RASTAPI, overriding SETUPs.
|
||
|
// 'WrapperConfigurationContext' is the handle to passed to
|
||
|
// MiniportInitialize.
|
||
|
//
|
||
|
{
|
||
|
NDIS_STATUS status;
|
||
|
NDIS_HANDLE hCfg;
|
||
|
NDIS_CONFIGURATION_PARAMETER* pncp;
|
||
|
|
||
|
NdisOpenConfiguration( &status, &hCfg, WrapperConfigurationContext );
|
||
|
if (status != NDIS_STATUS_SUCCESS)
|
||
|
{
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// The delay in milliseconds to wait for PARPORT to initialize all the
|
||
|
// parallel ports. With PnP there is no deterministic time at which
|
||
|
// to do this.
|
||
|
//
|
||
|
{
|
||
|
NDIS_STRING nstr = NDIS_STRING_CONST( "ParportDelayMs" );
|
||
|
|
||
|
NdisReadConfiguration(
|
||
|
&status, &pncp, hCfg, &nstr, NdisParameterInteger );
|
||
|
|
||
|
if (status == NDIS_STATUS_SUCCESS)
|
||
|
{
|
||
|
pAdapter->ulParportDelayMs = pncp->ParameterData.IntegerData;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Default is 3 seconds.
|
||
|
//
|
||
|
pAdapter->ulParportDelayMs = 3000;
|
||
|
status = NDIS_STATUS_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The secondary delay in milliseconds to wait for PARPORT to
|
||
|
// initialize all the parallel ports, if there are no ports after the
|
||
|
// initial delay above.
|
||
|
//
|
||
|
{
|
||
|
NDIS_STRING nstr = NDIS_STRING_CONST( "ExtraParportDelayMs" );
|
||
|
|
||
|
NdisReadConfiguration(
|
||
|
&status, &pncp, hCfg, &nstr, NdisParameterInteger );
|
||
|
|
||
|
if (status == NDIS_STATUS_SUCCESS)
|
||
|
{
|
||
|
pAdapter->ulExtraParportDelayMs =
|
||
|
pncp->ParameterData.IntegerData;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Default is 30 seconds.
|
||
|
//
|
||
|
pAdapter->ulExtraParportDelayMs = 30000;
|
||
|
status = NDIS_STATUS_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The number of VCs we must be able to provide.
|
||
|
//
|
||
|
{
|
||
|
#if 0
|
||
|
NDIS_STRING nstr = NDIS_STRING_CONST( "MaxVcs" );
|
||
|
|
||
|
NdisReadConfiguration(
|
||
|
&status, &pncp, hCfg, &nstr, NdisParameterInteger );
|
||
|
|
||
|
if (status == NDIS_STATUS_SUCCESS)
|
||
|
{
|
||
|
pAdapter->usMaxVcs = (USHORT )pncp->ParameterData.IntegerData;
|
||
|
|
||
|
// Make sure it's a valid value.
|
||
|
//
|
||
|
if (pAdapter->usMaxVcs < 1)
|
||
|
{
|
||
|
status = NDIS_STATUS_INVALID_DATA;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pAdapter->usMaxVcs = 1;
|
||
|
status = NDIS_STATUS_SUCCESS;
|
||
|
}
|
||
|
#else
|
||
|
// Registry value is currently ignored, and hard-coded maximum
|
||
|
// used.
|
||
|
//
|
||
|
pAdapter->usMaxVcs = NPORTS;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
while (FALSE);
|
||
|
|
||
|
NdisCloseConfiguration( hCfg );
|
||
|
|
||
|
TRACE( TL_N, TM_Init,
|
||
|
( "Reg: vcs=%d ppd=%d",
|
||
|
(UINT )pAdapter->usMaxVcs,
|
||
|
(UINT )pAdapter->ulParportDelayMs ) );
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
HdlcFromAsyncFraming(
|
||
|
IN UCHAR* pInBuf,
|
||
|
IN ULONG ulInBufLen,
|
||
|
OUT UCHAR* pOutBuf,
|
||
|
OUT ULONG* pulOutBufLen )
|
||
|
|
||
|
// Make a copy of asynchronously framed PPP data buffer 'pInBuf' of length
|
||
|
// 'ulInBufLen' bytes in caller's 'pOutBuf' buffer, converting to PPP HDLC
|
||
|
// framed format in the process. 'POutBufLen' is the length in bytes of
|
||
|
// the returned output buffer. Caller must allow for up to the length of
|
||
|
// 'pInBuf' in 'pOutBuf'.
|
||
|
//
|
||
|
// Returns true if the packet is valid, false if corrupt.
|
||
|
//
|
||
|
// Adapted from ASYNCMAC's AsyncPPPCompletionRoutine.
|
||
|
//
|
||
|
{
|
||
|
UCHAR* pIn;
|
||
|
UCHAR* pInEnd;
|
||
|
UCHAR* pOut;
|
||
|
USHORT usFcs;
|
||
|
|
||
|
if (ulInBufLen < 5)
|
||
|
{
|
||
|
// Expecting at least 2 flag bytes, 1 data byte, and the FCS.
|
||
|
//
|
||
|
TRACE( TL_A, TM_Mp, ( "HfA: frame too short=%d", ulInBufLen ) );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (pInBuf[ 0 ] != PPPFLAGBYTE)
|
||
|
{
|
||
|
TRACE( TL_A, TM_Mp, ( "HfA: No head flag" ) );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (pInBuf[ ulInBufLen - 1 ] != PPPFLAGBYTE)
|
||
|
{
|
||
|
TRACE( TL_A, TM_Mp, ( "HfA: No tail flag" ) );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pIn = pInBuf + 1;
|
||
|
pInEnd = pInBuf + ulInBufLen - 1;
|
||
|
pOut = pOutBuf;
|
||
|
|
||
|
while (pIn < pInEnd)
|
||
|
{
|
||
|
if (*pIn == PPPESCBYTE)
|
||
|
{
|
||
|
++pIn;
|
||
|
*pOut = *pIn ^ 0x20;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pOut = *pIn;
|
||
|
}
|
||
|
|
||
|
++pOut;
|
||
|
++pIn;
|
||
|
}
|
||
|
|
||
|
*pulOutBufLen = (ULONG )(pOut - pOutBuf - 2);
|
||
|
|
||
|
{
|
||
|
USHORT usCalcFcs;
|
||
|
|
||
|
usFcs = (USHORT )(pOut[ -2 ]) + (USHORT )(pOut[ -1 ] << 8);
|
||
|
usFcs ^= 0xFFFF;
|
||
|
|
||
|
TRACE( TL_I, TM_Data, ( "HfromA (recv) dump:" ) );
|
||
|
DUMPB( TL_I, TM_Data, pOutBuf, *pulOutBufLen );
|
||
|
usCalcFcs = CalculatePppFcs( pOutBuf, *pulOutBufLen );
|
||
|
if (usFcs != usCalcFcs)
|
||
|
{
|
||
|
TRACE( TL_A, TM_Mp, (
|
||
|
"HfA: FCS mismatch, R=$%04x C=$%04x, L=%d",
|
||
|
(INT )usFcs, (INT )usCalcFcs, *pulOutBufLen ) );
|
||
|
return FALSE;
|
||
|
}
|
||
|
#if 0
|
||
|
#ifdef TESTMODE
|
||
|
else
|
||
|
{
|
||
|
TRACE( TL_A, TM_Mp, (
|
||
|
"HfA: Good FCS, R=$%04x C=$%04x, L=%d",
|
||
|
(INT )usFcs, (INT )usCalcFcs, *pulOutBufLen ) );
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
IsWin9xPeer(
|
||
|
IN VCCB* pVc )
|
||
|
|
||
|
// Returns true if the link level has determined that the VC's peer is a
|
||
|
// Win9x box, false otherwise.
|
||
|
//
|
||
|
{
|
||
|
ULONG Platform;
|
||
|
PPTI_EXTENSION pPtiExtension;
|
||
|
|
||
|
#ifdef TESTMODE
|
||
|
if (g_fAssumeWin9x)
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
pPtiExtension = (PPTI_EXTENSION )pVc->PtiExtension;
|
||
|
|
||
|
// try to check the validity of the PtiExtension pointer
|
||
|
//
|
||
|
if ( pPtiExtension == NULL )
|
||
|
{
|
||
|
TRACE( TL_A, TM_Recv, ( "PtiRx: pPtiExtension is NULL!" ) );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Platform = (ULONG) pPtiExtension->His.VerPlat;
|
||
|
|
||
|
TRACE( TL_V, TM_Recv, ( "IsWin9xPeer: far platform=$%x", Platform ) );
|
||
|
|
||
|
if (Platform == PLAT_WIN9X)
|
||
|
{
|
||
|
// Win9x -- we reformat the asynch framing used by Win9x DCC
|
||
|
// and also play the CLIENT->CLIENTSERVER game
|
||
|
//
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// WinNT (or DOS maybe)
|
||
|
//
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
NDIS_STATUS
|
||
|
QueryInformation(
|
||
|
IN ADAPTERCB* pAdapter,
|
||
|
IN VCCB* pVc,
|
||
|
IN NDIS_OID Oid,
|
||
|
IN PVOID InformationBuffer,
|
||
|
IN ULONG InformationBufferLength,
|
||
|
OUT PULONG BytesWritten,
|
||
|
OUT PULONG BytesNeeded )
|
||
|
|
||
|
// Handle QueryInformation requests. Arguments are as for the standard
|
||
|
// NDIS 'MiniportQueryInformation' handler except this routine does not
|
||
|
// count on being serialized with respect to other requests.
|
||
|
//
|
||
|
{
|
||
|
NDIS_STATUS status;
|
||
|
ULONG ulInfo;
|
||
|
VOID* pInfo;
|
||
|
ULONG ulInfoLen;
|
||
|
|
||
|
status = NDIS_STATUS_SUCCESS;
|
||
|
|
||
|
// The cases in this switch statement find or create a buffer containing
|
||
|
// the requested information and point 'pInfo' at it, noting it's length
|
||
|
// in 'ulInfoLen'. Since many of the OIDs return a ULONG, a 'ulInfo'
|
||
|
// buffer is set up as the default.
|
||
|
//
|
||
|
ulInfo = 0;
|
||
|
pInfo = &ulInfo;
|
||
|
ulInfoLen = sizeof(ulInfo);
|
||
|
|
||
|
switch (Oid)
|
||
|
{
|
||
|
case OID_GEN_MAXIMUM_LOOKAHEAD:
|
||
|
{
|
||
|
// Report the maximum number of bytes we can always provide as
|
||
|
// lookahead data on receive indications. We always indicate full
|
||
|
// packets so this is the same as the receive block size. And
|
||
|
// since we always allocate enough for a full packet, the receive
|
||
|
// block size is the same as the frame size.
|
||
|
//
|
||
|
TRACE( TL_N, TM_Mp, ( "QInfo(OID_GEN_MAXIMUM_LOOKAHEAD)" ) );
|
||
|
ulInfo = PTI_MaxFrameSize;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OID_GEN_MAC_OPTIONS:
|
||
|
{
|
||
|
// Report a bitmask defining optional properties of the driver.
|
||
|
//
|
||
|
// NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA promises that our receive
|
||
|
// buffer is not on a device-specific card.
|
||
|
//
|
||
|
// NDIS_MAC_OPTION_TRANSFERS_NOT_PEND promises we won't return
|
||
|
// NDIS_STATUS_PENDING from our TransferData handler which is true
|
||
|
// since we don't have one.
|
||
|
//
|
||
|
TRACE( TL_N, TM_Mp, ( "QInfo(OID_GEN_MAC_OPTIONS)" ) );
|
||
|
ulInfo = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA
|
||
|
| NDIS_MAC_OPTION_TRANSFERS_NOT_PEND;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OID_WAN_MEDIUM_SUBTYPE:
|
||
|
{
|
||
|
// Report the media subtype we support. NDISWAN may use this in
|
||
|
// the future (doesn't now) to provide framing differences for
|
||
|
// different media.
|
||
|
//
|
||
|
TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_MEDIUM_SUBTYPE)" ) );
|
||
|
ulInfo = NdisWanMediumParallel;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OID_WAN_CO_GET_INFO:
|
||
|
{
|
||
|
// Report the capabilities of the adapter.
|
||
|
//
|
||
|
TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_CO_GET_INFO)" ) );
|
||
|
pInfo = &pAdapter->info;
|
||
|
ulInfoLen = sizeof(NDIS_WAN_CO_INFO);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OID_WAN_CO_GET_LINK_INFO:
|
||
|
{
|
||
|
// Report the current state of the link.
|
||
|
//
|
||
|
TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_CO_GET_LINK_INFO)" ) );
|
||
|
|
||
|
if (!pVc)
|
||
|
return NDIS_STATUS_INVALID_DATA;
|
||
|
pInfo = &pVc->linkinfo;
|
||
|
ulInfoLen = sizeof(NDIS_WAN_CO_GET_LINK_INFO);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OID_WAN_CO_GET_COMP_INFO:
|
||
|
{
|
||
|
// Report the type of compression we provide, which is none.
|
||
|
//
|
||
|
TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_CO_GET_COMP_INFO)" ) );
|
||
|
status = NDIS_STATUS_NOT_SUPPORTED;
|
||
|
ulInfoLen = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OID_WAN_CO_GET_STATS_INFO:
|
||
|
{
|
||
|
// Because DirectParallel doesn't do compression, NDISWAN will use
|
||
|
// it's own statistics and not query ours.
|
||
|
//
|
||
|
ASSERT( !"OID_WAN_CO_GET_STATS_INFO?" );
|
||
|
status = NDIS_STATUS_NOT_SUPPORTED;
|
||
|
ulInfoLen = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OID_GEN_SUPPORTED_LIST:
|
||
|
{
|
||
|
static ULONG aulSupportedOids[] = {
|
||
|
OID_GEN_SUPPORTED_LIST,
|
||
|
OID_GEN_MAXIMUM_LOOKAHEAD,
|
||
|
OID_GEN_MAC_OPTIONS,
|
||
|
OID_WAN_MEDIUM_SUBTYPE,
|
||
|
OID_WAN_CO_GET_INFO,
|
||
|
OID_WAN_CO_GET_LINK_INFO,
|
||
|
OID_WAN_CO_SET_LINK_INFO,
|
||
|
OID_CO_TAPI_CM_CAPS,
|
||
|
OID_CO_TAPI_LINE_CAPS,
|
||
|
OID_CO_TAPI_ADDRESS_CAPS,
|
||
|
OID_CO_TAPI_GET_CALL_DIAGNOSTICS
|
||
|
};
|
||
|
|
||
|
TRACE( TL_N, TM_Mp, ( "QInfo(OID_GEN_SUPPORTED_LIST)" ) );
|
||
|
pInfo = aulSupportedOids;
|
||
|
ulInfoLen = sizeof(aulSupportedOids);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OID_PNP_CAPABILITIES:
|
||
|
{
|
||
|
pInfo = &PnpCaps;
|
||
|
ulInfoLen = sizeof(PnpCaps);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OID_PNP_SET_POWER:
|
||
|
break;
|
||
|
case OID_PNP_QUERY_POWER:
|
||
|
break;
|
||
|
case OID_PNP_ENABLE_WAKE_UP:
|
||
|
break;
|
||
|
|
||
|
#if 0
|
||
|
// These OIDs are mandatory according to current doc, but since
|
||
|
// NDISWAN never requests them they are omitted.
|
||
|
//
|
||
|
case OID_GEN_HARDWARE_STATUS:
|
||
|
case OID_GEN_MEDIA_SUPPORTED:
|
||
|
case OID_GEN_MEDIA_IN_USE:
|
||
|
case OID_GEN_MEDIA_IN_USE:
|
||
|
case OID_GEN_MAXIMUM_FRAME_SIZE:
|
||
|
case OID_GEN_LINK_SPEED:
|
||
|
case OID_GEN_TRANSMIT_BUFFER_SPACE:
|
||
|
case OID_GEN_RECEIVE_BUFFER_SPACE:
|
||
|
case OID_GEN_TRANSMIT_BLOCK_SIZE:
|
||
|
case OID_GEN_RECEIVE_BLOCK_SIZE:
|
||
|
case OID_GEN_VENDOR_ID:
|
||
|
case OID_GEN_VENDOR_DESCRIPTION:
|
||
|
case OID_GEN_VENDOR_DRIVER_VERSION:
|
||
|
case OID_GEN_CURRENT_PACKET_FILTER:
|
||
|
case OID_GEN_CURRENT_LOOKAHEAD:
|
||
|
case OID_GEN_DRIVER_VERSION:
|
||
|
case OID_GEN_MAXIMUM_TOTAL_SIZE:
|
||
|
case OID_GEN_MAC_OPTIONS:
|
||
|
case OID_GEN_MEDIA_CONNECT_STATUS:
|
||
|
case OID_GEN_MAXIMUM_SEND_PACKETS:
|
||
|
case OID_WAN_PERMANENT_ADDRESS:
|
||
|
case OID_WAN_CURRENT_ADDRESS:
|
||
|
case OID_WAN_QUALITY_OF_SERVICE:
|
||
|
case OID_WAN_LINE_COUNT:
|
||
|
#endif
|
||
|
default:
|
||
|
{
|
||
|
TRACE( TL_A, TM_Mp, ( "QueryInfo: Oid=$%08x?", Oid ) );
|
||
|
status = NDIS_STATUS_NOT_SUPPORTED; // JAY per SLC
|
||
|
ulInfoLen = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ulInfoLen > InformationBufferLength)
|
||
|
{
|
||
|
// Caller's buffer is too small. Tell him what he needs.
|
||
|
//
|
||
|
*BytesNeeded = ulInfoLen;
|
||
|
status = NDIS_STATUS_INVALID_LENGTH;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Copy the found result to caller's buffer.
|
||
|
//
|
||
|
if (ulInfoLen > 0)
|
||
|
{
|
||
|
NdisMoveMemory( InformationBuffer, pInfo, ulInfoLen );
|
||
|
DUMPDW( TL_N, TM_Mp, pInfo, ulInfoLen );
|
||
|
}
|
||
|
|
||
|
*BytesNeeded = *BytesWritten = ulInfoLen;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ReferenceAdapter(
|
||
|
IN ADAPTERCB* pAdapter )
|
||
|
|
||
|
// Adds areference to the adapter block, 'pAdapter'.
|
||
|
//
|
||
|
{
|
||
|
LONG lRef;
|
||
|
|
||
|
lRef = NdisInterlockedIncrement( &pAdapter->lRef );
|
||
|
|
||
|
TRACE( TL_N, TM_Ref, ( "RefA to %d", lRef ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ReferenceVc(
|
||
|
IN VCCB* pVc )
|
||
|
|
||
|
// Adds a reference to the VC control block 'pVc'.
|
||
|
//
|
||
|
{
|
||
|
LONG lRef;
|
||
|
|
||
|
lRef = NdisInterlockedIncrement( &pVc->lRef );
|
||
|
|
||
|
TRACE( TL_N, TM_Ref, ( "RefVc to %d", lRef ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SendClientString(
|
||
|
IN PVOID pPtiExtension )
|
||
|
|
||
|
// Send "CLIENT" so Win9x, which views us as a NULL modem, is happy.
|
||
|
//
|
||
|
{
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
||
|
PtiWrite( pPtiExtension, g_szClient, CLIENTLEN, PID_STANDARD );
|
||
|
KeLowerIrql(oldIrql);
|
||
|
}
|
||
|
|
||
|
|
||
|
NDIS_STATUS
|
||
|
SetInformation(
|
||
|
IN ADAPTERCB* pAdapter,
|
||
|
IN VCCB* pVc,
|
||
|
IN NDIS_OID Oid,
|
||
|
IN PVOID InformationBuffer,
|
||
|
IN ULONG InformationBufferLength,
|
||
|
OUT PULONG BytesRead,
|
||
|
OUT PULONG BytesNeeded )
|
||
|
|
||
|
// Handle SetInformation requests. Arguments are as for the standard NDIS
|
||
|
// 'MiniportQueryInformation' handler except this routine does not count
|
||
|
// on being serialized with respect to other requests.
|
||
|
//
|
||
|
{
|
||
|
NDIS_STATUS status;
|
||
|
|
||
|
status = NDIS_STATUS_SUCCESS;
|
||
|
|
||
|
switch (Oid)
|
||
|
{
|
||
|
case OID_WAN_CO_SET_LINK_INFO:
|
||
|
{
|
||
|
// Read new link state settings.
|
||
|
//
|
||
|
TRACE( TL_N, TM_Mp, ( "SInfo(OID_WAN_CO_SET_LINK_INFO)" ) );
|
||
|
if (InformationBufferLength < sizeof(NDIS_WAN_CO_SET_LINK_INFO))
|
||
|
{
|
||
|
status = NDIS_STATUS_INVALID_LENGTH;
|
||
|
*BytesRead = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!pVc)
|
||
|
{
|
||
|
return NDIS_STATUS_INVALID_DATA;
|
||
|
}
|
||
|
|
||
|
ASSERT( sizeof(pVc->linkinfo)
|
||
|
== sizeof(NDIS_WAN_CO_SET_LINK_INFO) );
|
||
|
|
||
|
NdisMoveMemory( &pVc->linkinfo, InformationBuffer,
|
||
|
sizeof(pVc->linkinfo) );
|
||
|
DUMPB( TL_N, TM_Mp, &pVc->linkinfo, sizeof(pVc->linkinfo) );
|
||
|
|
||
|
*BytesRead = sizeof(NDIS_WAN_CO_SET_LINK_INFO);
|
||
|
}
|
||
|
|
||
|
*BytesNeeded = sizeof(NDIS_WAN_CO_SET_LINK_INFO);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case OID_WAN_CO_SET_COMP_INFO:
|
||
|
{
|
||
|
// DirectParallel doesn't provide compression.
|
||
|
//
|
||
|
TRACE( TL_N, TM_Mp, ( "SInfo(OID_WAN_CO_SET_COMP_INFO)" ) );
|
||
|
status = NDIS_STATUS_NOT_SUPPORTED;
|
||
|
*BytesRead = *BytesNeeded = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
// These OIDs are mandatory according to current doc, but since
|
||
|
// NDISWAN never requests them they are omitted.
|
||
|
//
|
||
|
case OID_GEN_CURRENT_PACKET_FILTER:
|
||
|
case OID_GEN_CURRENT_LOOKAHEAD:
|
||
|
case OID_GEN_PROTOCOL_OPTIONS:
|
||
|
case OID_WAN_PROTOCOL_TYPE:
|
||
|
case OID_WAN_HEADER_FORMAT:
|
||
|
#endif
|
||
|
default:
|
||
|
{
|
||
|
TRACE( TL_A, TM_Mp, ( "SetInfo: Oid=$%08x?", Oid ) );
|
||
|
status = NDIS_STATUS_NOT_SUPPORTED; // JAY per SLC
|
||
|
*BytesRead = *BytesNeeded = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|