1092 lines
30 KiB
C
1092 lines
30 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
Copyright (c) 1991 Nokia Data Systems Ab
|
||
|
||
Module Name:
|
||
|
||
llclink.c
|
||
|
||
Abstract:
|
||
|
||
The module implements the open, connect and close primitives
|
||
for a link station object. The link stations have also been
|
||
initialized within this module.
|
||
|
||
Contents:
|
||
LlcOpenLinkStation
|
||
LlcConnectStation
|
||
InitiateAsyncLinkCommand
|
||
LlcDisconnectStation
|
||
LlcFlowControl
|
||
LinkFlowControl
|
||
SearchLinkAddress
|
||
SetLinkParameters
|
||
CheckLinkParameters
|
||
CopyLinkParameters
|
||
CopyNonZeroBytes
|
||
RunInterlockedStateMachineCommand
|
||
|
||
Author:
|
||
|
||
Antti Saarenheimo (o-anttis) 28-MAY-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <llc.h>
|
||
|
||
|
||
DLC_STATUS
|
||
LlcOpenLinkStation(
|
||
IN PLLC_SAP pSap,
|
||
IN UCHAR DestinationSap,
|
||
IN PUCHAR pDestinationAddress OPTIONAL,
|
||
IN PUCHAR pReceivedLanHeader OPTIONAL,
|
||
IN PVOID hClientStation,
|
||
OUT PVOID* phLlcHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
creates a DATA_LINK structure and fills it in. Called either as a result
|
||
of receiving a SABME, or via DLC.OPEN.STATION
|
||
|
||
This operation is the same as ACTIVATE_LS primitive in IBM TR Arch. ref.
|
||
|
||
Arguments:
|
||
|
||
pSap - pointer to SAP
|
||
DestinationSap - remote SAP number
|
||
pDestinationAddress - remote node address. If this function is being called
|
||
as a result of receiving a SABME for a new link then
|
||
this parameter is NULL
|
||
pReceivedLanHeader - LAN header as received off the wire, containing source
|
||
and destination adapter addresses, optional source
|
||
routing and source and destination SAPs
|
||
hClientStation - handle (address) of LLC client's link station object
|
||
phLlcHandle - pointer to returned handle (address) to LLC DATA_LINK
|
||
object
|
||
|
||
Return Value:
|
||
|
||
DLC_STATUS
|
||
Success - STATUS_SUCCESS
|
||
link station has been opened successfully
|
||
Failure - DLC_STATUS_INVALID_SAP_VALUE
|
||
the link station already exists or the SAP is really invalid.
|
||
DLC_NO_MEMORY
|
||
there was no free preallocated link station
|
||
|
||
--*/
|
||
|
||
{
|
||
PDATA_LINK pLink;
|
||
PDATA_LINK* ppLink;
|
||
PADAPTER_CONTEXT pAdapterContext = pSap->Gen.pAdapterContext;
|
||
DLC_STATUS LlcStatus = STATUS_SUCCESS;
|
||
UINT AddressTranslation;
|
||
|
||
//
|
||
// We need a temporary buffer to build lan header for link,
|
||
// because user may use different ndis medium from network.
|
||
//
|
||
|
||
UCHAR auchTempBuffer[32];
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
LlcZeroMem(auchTempBuffer, sizeof(auchTempBuffer));
|
||
|
||
if (pSap->Gen.ObjectType != LLC_SAP_OBJECT) {
|
||
return DLC_STATUS_INVALID_SAP_VALUE;
|
||
}
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->ObjectDataBase);
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
pLink = (PDATA_LINK)ALLOCATE_PACKET_LLC_LNK(pAdapterContext->hLinkPool);
|
||
|
||
if (pLink == NULL) {
|
||
LlcStatus = DLC_STATUS_NO_MEMORY;
|
||
goto ErrorExit;
|
||
}
|
||
|
||
//
|
||
// This reference keeps the object alive, until it is dereferenced
|
||
// in the delete.
|
||
//
|
||
|
||
ReferenceObject(pLink);
|
||
|
||
//
|
||
// LLC driver have two different address formats:
|
||
//
|
||
// 1. External format of the binding (ethernet or token-ring,
|
||
// DLC driver uses always token-ring format, the ethernet
|
||
// support is compiled in conditionally.
|
||
//
|
||
// 2. Internal send format (always the actual lan type,
|
||
// ethernet, dix or tokenring). The user provides link
|
||
// address in its own mode and we must build the actual
|
||
// lan link header from it.
|
||
//
|
||
|
||
if (pDestinationAddress != NULL) {
|
||
|
||
//
|
||
// link created by DLC.CONNECT.STATION
|
||
//
|
||
|
||
AddressTranslation = pSap->Gen.pLlcBinding->AddressTranslation;
|
||
LlcBuildAddress(pSap->Gen.pLlcBinding->NdisMedium,
|
||
pDestinationAddress,
|
||
NULL,
|
||
auchTempBuffer
|
||
);
|
||
} else {
|
||
|
||
//
|
||
// link created by incoming SABME
|
||
//
|
||
|
||
pLink->Flags |= DLC_ACTIVE_REMOTE_CONNECT_REQUEST;
|
||
AddressTranslation = pAdapterContext->AddressTranslationMode;
|
||
LlcBuildAddressFromLanHeader(pAdapterContext->NdisMedium,
|
||
pReceivedLanHeader,
|
||
auchTempBuffer
|
||
);
|
||
}
|
||
|
||
//
|
||
// We want to use always DIX lan headers in the token-ring case
|
||
//
|
||
|
||
if (AddressTranslation == LLC_SEND_802_5_TO_802_3) {
|
||
AddressTranslation = LLC_SEND_802_5_TO_DIX;
|
||
} else if (AddressTranslation == LLC_SEND_802_3_TO_802_3) {
|
||
AddressTranslation = LLC_SEND_802_3_TO_DIX;
|
||
}
|
||
|
||
//
|
||
// Now we can build the actual network header for the sending
|
||
// (this same routine build lan header also for all
|
||
// other packet types)
|
||
//
|
||
|
||
pLink->cbLanHeaderLength = CopyLanHeader(AddressTranslation,
|
||
auchTempBuffer,
|
||
pAdapterContext->NodeAddress,
|
||
pLink->auchLanHeader,
|
||
pAdapterContext->ConfigInfo.SwapAddressBits
|
||
);
|
||
|
||
//
|
||
// We always build a DIX header but it is only used when the Ethernet type
|
||
// is actually DIX
|
||
//
|
||
|
||
if (pAdapterContext->NdisMedium == NdisMedium802_3
|
||
&& pSap->Gen.pLlcBinding->EthernetType != LLC_ETHERNET_TYPE_DIX) {
|
||
pLink->cbLanHeaderLength = 14;
|
||
}
|
||
|
||
//
|
||
// Save the client handle, but reset and initailize everything else.
|
||
// The link must be ready for any kind extrnal inputs when
|
||
// we will connenct it to the hash table of the link stations.
|
||
// (actually that's not true now, because we init the link to
|
||
// LINK_CLOSED state, but we may change the state machine.
|
||
// It would be a different thing with a 802.2 state machine)
|
||
//
|
||
|
||
pLink->Gen.ObjectType = LLC_LINK_OBJECT;
|
||
|
||
//
|
||
// RLF 07/22/92. The link state should be DISCONNECTED so that we can
|
||
// accept incoming SABMEs for this SAP/link station. This is also
|
||
// according to IBM LAN Tech. Ref. p. 2-33. It is safe to set the
|
||
// DISCONNECTED state now because we have the send and object database
|
||
// spin locks, so we can't get interrupted by NDIS driver
|
||
//
|
||
|
||
//
|
||
// RLF 08/13/92. Ho Hum. This still isn't correct - we must put the link
|
||
// into different states depending on how its being opened - DISCONNECTED
|
||
// if the upper layers are creating the link, or LINK_CLOSED if we're
|
||
// creating the link as a result of receiving a SABME. Use pReceivedLanHeader
|
||
// as a flag: DLC calls this routine with this parameter set to NULL
|
||
//
|
||
|
||
////pLink->State = LINK_CLOSED;
|
||
//pLink->State = DISCONNECTED;
|
||
|
||
pLink->State = pReceivedLanHeader ? LINK_CLOSED : DISCONNECTED;
|
||
|
||
//
|
||
// RLF 10/01/92. We need some way of knowing that the link station was
|
||
// created by receiving a SABME. We need this to decide what to do with
|
||
// the source routing info in a subsequent DLC.CONNECT.STATION command.
|
||
// This field used to be Reserved
|
||
//
|
||
|
||
pLink->RemoteOpen = hClientStation == NULL;
|
||
|
||
//
|
||
// RLF 05/09/94
|
||
//
|
||
// We set the framing type to unspecified. This field is only used if the
|
||
// adapter was opened in AUTO mode. It will be set to 802.3 or DIX by the
|
||
// SABME received processing (new link created by remote station) or when
|
||
// the first UA is received in response to the 2 SABMEs we send out (802.3
|
||
// and DIX)
|
||
//
|
||
|
||
pLink->FramingType = (ULONG)LLC_SEND_UNSPECIFIED;
|
||
|
||
pLink->Gen.hClientHandle = hClientStation;
|
||
pLink->Gen.pAdapterContext = pAdapterContext;
|
||
pLink->pSap = pSap;
|
||
pLink->Gen.pLlcBinding = pSap->Gen.pLlcBinding;
|
||
|
||
//
|
||
// Save the node addresses used in link station
|
||
//
|
||
|
||
pDestinationAddress = pLink->auchLanHeader;
|
||
if (pAdapterContext->NdisMedium == NdisMedium802_5) {
|
||
pDestinationAddress += 2;
|
||
} else if (pAdapterContext->NdisMedium == NdisMediumFddi) {
|
||
++pDestinationAddress;
|
||
}
|
||
|
||
memcpy(pLink->LinkAddr.Node.auchAddress, pDestinationAddress, 6);
|
||
|
||
//
|
||
// RLF 03/24/93
|
||
//
|
||
// if we're talking Ethernet or DIX, we need to report the bit-flipped
|
||
// address at the DLC interface
|
||
//
|
||
|
||
SwapMemCpy((BOOLEAN)((AddressTranslation == LLC_SEND_802_3_TO_DIX)
|
||
|| (pAdapterContext->NdisMedium == NdisMediumFddi)),
|
||
pLink->DlcStatus.auchRemoteNode,
|
||
pDestinationAddress,
|
||
6
|
||
);
|
||
|
||
//
|
||
// Four different local saps: my code would need a cleanup
|
||
// (four bytes doesn't use very much memory on the other hand)
|
||
//
|
||
|
||
pLink->LinkAddr.Address.DestSap = pLink->DlcStatus.uchRemoteSap = pLink->Dsap = DestinationSap;
|
||
pLink->LinkAddr.Address.SrcSap = pLink->DlcStatus.uchLocalSap = pLink->Ssap = (UCHAR)pSap->SourceSap;
|
||
pLink->DlcStatus.hLlcLinkStation = (PVOID)pLink;
|
||
pLink->SendQueue.pObject = pLink;
|
||
InitializeListHead(&pLink->SendQueue.ListHead);
|
||
InitializeListHead(&pLink->SentQueue);
|
||
pLink->Flags |= DLC_SEND_DISABLED;
|
||
|
||
//
|
||
// The next procedure returns the pointer of the slot for the
|
||
// new link station pointer. The address may be in the
|
||
// hash table or it may be the address of pRigth or pLeft
|
||
// field within another link station structure.
|
||
//
|
||
|
||
ppLink = SearchLinkAddress(pAdapterContext, pLink->LinkAddr);
|
||
|
||
//
|
||
// this link station must not yet be in the table
|
||
// of active link stations. If its slot is
|
||
// empty, then save the new link station to the
|
||
// list of the active link stations.
|
||
//
|
||
|
||
if (*ppLink != NULL) {
|
||
LlcStatus = DLC_STATUS_INVALID_SAP_VALUE;
|
||
|
||
DEALLOCATE_PACKET_LLC_LNK(pAdapterContext->hLinkPool, pLink);
|
||
|
||
} else {
|
||
pLink->Gen.pNext = (PLLC_OBJECT)pSap->pActiveLinks;
|
||
pSap->pActiveLinks = pLink;
|
||
|
||
//
|
||
// Set the default link parameters,
|
||
// Note: This creates the timer ticks. They must
|
||
// be deleted with the terminate timer function,
|
||
// when the link station is closed.
|
||
//
|
||
|
||
LlcStatus = SetLinkParameters(pLink, (PUCHAR)&pSap->DefaultParameters);
|
||
if (LlcStatus != STATUS_SUCCESS) {
|
||
|
||
//
|
||
// We may have been started T1 and T2 timers.
|
||
//
|
||
|
||
TerminateTimer(pAdapterContext, &pLink->T1);
|
||
TerminateTimer(pAdapterContext, &pLink->T2);
|
||
|
||
DEALLOCATE_PACKET_LLC_LNK(pAdapterContext->hLinkPool, pLink);
|
||
|
||
} else {
|
||
|
||
//
|
||
// N2 is never initilaized by IBM state machine, when
|
||
// the link is created by a remote connect request.
|
||
// The link can be killed by this combination of state
|
||
// transmitions:
|
||
// (LINK_OPENED),
|
||
// (RNR-r => REMOTE_BUSY),
|
||
// (RR-c => CHECKPOINTING)
|
||
// (T1 timeout => DISCONNECTED) !!!!!
|
||
//
|
||
// This will fix the bug in IBM state machine:
|
||
//
|
||
|
||
pLink->P_Ct = pLink->N2;
|
||
*ppLink = *phLlcHandle = (PVOID)pLink;
|
||
pAdapterContext->ObjectCount++;
|
||
}
|
||
}
|
||
|
||
ErrorExit:
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->ObjectDataBase);
|
||
|
||
return LlcStatus;
|
||
}
|
||
|
||
|
||
VOID
|
||
LlcBindLinkStation(
|
||
IN PDATA_LINK pStation,
|
||
IN PVOID hClientHandle
|
||
)
|
||
{
|
||
pStation->Gen.hClientHandle = hClientHandle;
|
||
}
|
||
|
||
|
||
VOID
|
||
LlcConnectStation(
|
||
IN PDATA_LINK pStation,
|
||
IN PLLC_PACKET pPacket,
|
||
IN PVOID pSourceRouting OPTIONAL,
|
||
IN PUSHORT pusMaxInformationField
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The upper level protocol may call this primitive to initiate
|
||
the connection negotiation with a remote link station,
|
||
to accept the connection request or to reconnect a link station
|
||
that have been disconnected for some reason with a new source
|
||
routing information.
|
||
The command is completed asynchronously and the status
|
||
is returned as an event.
|
||
|
||
The primitive is the same as SET_ABME primitive in "IBM TR Architecture
|
||
reference".
|
||
The function implements also CONNECT_REQUEST and CONNECT_RESPONSE
|
||
primitives of IEEE 802.2.
|
||
|
||
Arguments:
|
||
|
||
pStation - address of link station
|
||
pPacket - command completion packet
|
||
pSourceRouting - optional source routing information. This must
|
||
be NULL if the source routing information is not
|
||
used
|
||
pusMaxInformationField - the maximum data size possible to use with this
|
||
connection. The source routing bridges may
|
||
decrease the maximum information field size.
|
||
Otherwise the maximum length is used
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NDIS_MEDIUM NdisMedium = pStation->Gen.pAdapterContext->NdisMedium;
|
||
|
||
if (pSourceRouting) {
|
||
|
||
//
|
||
// We first read the destination address from the
|
||
// lan header and then extend the source routing
|
||
// field in the LAN header of link.
|
||
//
|
||
|
||
if (NdisMedium == NdisMedium802_5) {
|
||
|
||
//
|
||
// RLF 10/01/92. If RemoteOpen is TRUE then the link was opened
|
||
// due to receiving a SABME and we ignore the source routing info
|
||
// (we already got it from the SABME packet)
|
||
//
|
||
|
||
if (!pStation->RemoteOpen) {
|
||
pStation->cbLanHeaderLength = (UCHAR)LlcBuildAddress(
|
||
NdisMedium,
|
||
&pStation->auchLanHeader[2],
|
||
pSourceRouting,
|
||
pStation->auchLanHeader
|
||
);
|
||
}
|
||
} else {
|
||
pSourceRouting = NULL;
|
||
}
|
||
}
|
||
*pusMaxInformationField = LlcGetMaxInfoField(NdisMedium,
|
||
pStation->Gen.pLlcBinding,
|
||
pStation->auchLanHeader
|
||
);
|
||
pStation->MaxIField = *pusMaxInformationField;
|
||
pStation->Flags &= ~DLC_ACTIVE_REMOTE_CONNECT_REQUEST;
|
||
|
||
//
|
||
// Activate the link station at first, the remotely connected
|
||
// link station is already active and in that case the state
|
||
// machine return logical error from ACTIVATE_LS input.
|
||
//
|
||
|
||
RunInterlockedStateMachineCommand(pStation, ACTIVATE_LS);
|
||
InitiateAsyncLinkCommand(pStation, pPacket, SET_ABME, LLC_CONNECT_COMPLETION);
|
||
}
|
||
|
||
VOID
|
||
InitiateAsyncLinkCommand(
|
||
IN PDATA_LINK pLink,
|
||
IN PLLC_PACKET pPacket,
|
||
IN UINT StateMachineCommand,
|
||
IN UINT CompletionCode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initiates or removes an LLC link. We have a link station in the DISCONNECTED
|
||
state. We are either sending a SABME or DISC
|
||
|
||
Arguments:
|
||
|
||
pLink - pointer to LLC link station structure ('object')
|
||
pPacket - pointer to packet to use for transmission
|
||
StateMachineCommand - command given to the state machine
|
||
CompletionCode - completion command type returned asynchronously
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PADAPTER_CONTEXT pAdapterContext = pLink->Gen.pAdapterContext;
|
||
UINT Status;
|
||
|
||
//
|
||
// link will return an error status if it is already connected.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
AllocateCompletionPacket(pLink, CompletionCode, pPacket);
|
||
|
||
//
|
||
// After RunStateMachineCommand
|
||
// the link may be deleted in any time by an NDIS command completion
|
||
// indication (send or receive) => we must not use link after this
|
||
//
|
||
|
||
Status = RunStateMachineCommand(pLink, StateMachineCommand);
|
||
|
||
//
|
||
// IBM state machine does not stop the send process => we
|
||
// must do it here we will get a system bug check.
|
||
//
|
||
|
||
if (StateMachineCommand == SET_ADM) {
|
||
DisableSendProcess(pLink);
|
||
}
|
||
|
||
//
|
||
// disconnect or connect commands may fail, because there are
|
||
// not enough memory to allocate packets for them.
|
||
// In that case we must complete the command here with an error code.
|
||
//
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
QueueCommandCompletion((PLLC_OBJECT)pLink, CompletionCode, Status);
|
||
}
|
||
|
||
BackgroundProcessAndUnlock(pAdapterContext);
|
||
}
|
||
|
||
|
||
VOID
|
||
LlcDisconnectStation(
|
||
IN PDATA_LINK pLink,
|
||
IN PLLC_PACKET pPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The primtive initiates the disconnection handshaking. The upper
|
||
protocol must wait LLC_EVENT_DISCONNECTED event before it can close
|
||
the link station. The link station must either be closed or
|
||
reconnected after a disconnection event. The DLC driver
|
||
disconnects the link only when it is closed.
|
||
|
||
This operation is the same as SET_ADM primitive in IBM TR Arch. ref.
|
||
|
||
Arguments:
|
||
|
||
hStation - link station handle.
|
||
|
||
hRequestHandle - opaque handle returned when the command completes
|
||
|
||
Return Value:
|
||
|
||
None
|
||
Complete always asynchronously by calling the
|
||
command completion routine.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// We don't want send yet another DM, if the link station has
|
||
// already disconnected. We don't modify the state machine,
|
||
// because the state machine should be as orginal as possible.
|
||
//
|
||
|
||
if (pLink->State == DISCONNECTED) {
|
||
pPacket->Data.Completion.CompletedCommand = LLC_DISCONNECT_COMPLETION;
|
||
pPacket->Data.Completion.Status = STATUS_SUCCESS;
|
||
pLink->Gen.pLlcBinding->pfCommandComplete(
|
||
pLink->Gen.pLlcBinding->hClientContext,
|
||
pLink->Gen.hClientHandle,
|
||
pPacket
|
||
);
|
||
} else {
|
||
InitiateAsyncLinkCommand(
|
||
pLink,
|
||
pPacket,
|
||
SET_ADM,
|
||
LLC_DISCONNECT_COMPLETION
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
DLC_STATUS
|
||
LlcFlowControl(
|
||
IN PLLC_OBJECT pStation,
|
||
IN UCHAR FlowControlState
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The primitive sets or resets the local busy state of a single
|
||
link station or all link stations of a sap.
|
||
The routine also maintains the local busy user
|
||
and local busy buffer states, that are returned in link station
|
||
status query, because the IBM state machine support only one buffer
|
||
busy state.
|
||
|
||
|
||
Arguments:
|
||
|
||
pStation - link station handle.
|
||
FlowControlState - new flow control command bits set for the link station.
|
||
The parameter is a bit field:
|
||
0 => Sets LOCAL_BUSY_USER state
|
||
0x80 => resets LOCAL_BUSY_USER state
|
||
0x40 => resets LOCAL_BUSY_BUFFER state
|
||
0xC0 => resets both local busy states
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
|
||
--*/
|
||
|
||
{
|
||
PDATA_LINK pLink;
|
||
UINT Status = STATUS_SUCCESS;
|
||
PADAPTER_CONTEXT pAdapterContext = pStation->Gen.pAdapterContext;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
//
|
||
// We must prevent any LLC object to be deleted, while we
|
||
// are updating the flow control states
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->ObjectDataBase);
|
||
|
||
if (pStation->Gen.ObjectType != LLC_LINK_OBJECT) {
|
||
if (pStation->Gen.ObjectType == LLC_SAP_OBJECT) {
|
||
for (pLink = pStation->Sap.pActiveLinks;
|
||
pLink != NULL;
|
||
pLink = (PDATA_LINK)pLink->Gen.pNext) {
|
||
Status |= LinkFlowControl(pLink, FlowControlState);
|
||
}
|
||
} else {
|
||
Status = DLC_STATUS_INVALID_STATION_ID;
|
||
}
|
||
} else {
|
||
Status = LinkFlowControl((PDATA_LINK)pStation, FlowControlState);
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->ObjectDataBase);
|
||
|
||
BackgroundProcess(pAdapterContext);
|
||
return Status;
|
||
}
|
||
|
||
|
||
DLC_STATUS
|
||
LinkFlowControl(
|
||
IN PDATA_LINK pLink,
|
||
IN UCHAR FlowControlState
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The primitive sets or resets the local busy state for a
|
||
single link. The routine also maintains the local busy user
|
||
and local busy buffer states.
|
||
This level do not care about the interlocking.
|
||
It is done on the upper level.
|
||
|
||
Arguments:
|
||
|
||
hStation - link station handle.
|
||
FlowControlState - new flow control command bits set for the link station.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
|
||
--*/
|
||
|
||
{
|
||
if ((FlowControlState & 0x80) == 0) {
|
||
|
||
//
|
||
// Bit5 is used as an undocumented flag, that sets
|
||
// the link local busy buffer state. We need this
|
||
// in the DOS DLC emulation.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&pLink->Gen.pAdapterContext->SendSpinLock);
|
||
|
||
if (FlowControlState == LLC_SET_LOCAL_BUSY_BUFFER) {
|
||
pLink->Flags |= DLC_LOCAL_BUSY_BUFFER;
|
||
} else {
|
||
pLink->Flags |= DLC_LOCAL_BUSY_USER;
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&pLink->Gen.pAdapterContext->SendSpinLock);
|
||
|
||
return RunInterlockedStateMachineCommand(pLink, ENTER_LCL_Busy);
|
||
} else {
|
||
|
||
//
|
||
// Optimize the buffer enabling, because RECEICE for a
|
||
// SAP station should disable any non user busy states of
|
||
// all link stations defined for sap (may take a long
|
||
// time if a sap has very may links)
|
||
//
|
||
|
||
if (FlowControlState == LLC_RESET_LOCAL_BUSY_BUFFER) {
|
||
FlowControlState = DLC_LOCAL_BUSY_BUFFER;
|
||
} else {
|
||
FlowControlState = DLC_LOCAL_BUSY_USER;
|
||
}
|
||
if (pLink->Flags & FlowControlState) {
|
||
|
||
ACQUIRE_SPIN_LOCK(&pLink->Gen.pAdapterContext->SendSpinLock);
|
||
|
||
pLink->Flags &= ~FlowControlState;
|
||
|
||
RELEASE_SPIN_LOCK(&pLink->Gen.pAdapterContext->SendSpinLock);
|
||
|
||
if ((pLink->Flags & (DLC_LOCAL_BUSY_USER | DLC_LOCAL_BUSY_BUFFER)) == 0) {
|
||
return RunInterlockedStateMachineCommand(pLink, EXIT_LCL_Busy);
|
||
}
|
||
} else {
|
||
return DLC_STATUS_LINK_PROTOCOL_ERROR;
|
||
}
|
||
}
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
#if LLC_DBG >= 2
|
||
|
||
PDATA_LINK
|
||
SearchLink(
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
IN LAN802_ADDRESS LanAddr
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine searches a link from the hash table.
|
||
All links in the same hash node has been saved to a simple
|
||
link list.
|
||
|
||
Note: the full link address is actually 61 bits long =
|
||
7 (SSAP) + 7 (DSAP) + 47 (any non-broadcast source address).
|
||
We save the address information into two ULONGs, that are used
|
||
in the actual search. The hash key will be calculated by xoring
|
||
all 8 bytes in the address.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - MAC adapter context of data link driver
|
||
|
||
LanAddr - the complete 64 bit address of link (48 bit source addr + saps)
|
||
|
||
Return Value:
|
||
PDATA_LINK - pointer to LLC link object or NULL if not found
|
||
|
||
--*/
|
||
{
|
||
USHORT usHash;
|
||
PDATA_LINK pLink;
|
||
|
||
// this is a very simple hash algorithm, but result is modified
|
||
// by all bits => it should be good enough for us.
|
||
usHash =
|
||
LanAddr.aus.Raw[0] ^ LanAddr.aus.Raw[1] ^
|
||
LanAddr.aus.Raw[2] ^ LanAddr.aus.Raw[3];
|
||
|
||
pLink =
|
||
pAdapterContext->aLinkHash[
|
||
((((PUCHAR)&usHash)[0] ^ ((PUCHAR)&usHash)[1]) % LINK_HASH_SIZE)];
|
||
|
||
//
|
||
// Search the first matching link in the link list.
|
||
//
|
||
while (pLink != NULL &&
|
||
(pLink->LinkAddr.ul.Low != LanAddr.ul.Low ||
|
||
pLink->LinkAddr.ul.High != LanAddr.ul.High))
|
||
{
|
||
pLink = pLink->pNextNode;
|
||
}
|
||
return pLink;
|
||
}
|
||
#endif
|
||
|
||
PDATA_LINK*
|
||
SearchLinkAddress(
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
IN LAN802_ADDRESS LanAddr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine searches the address of a link pointer in the hash table.
|
||
All links in the same hash node has been saved to a simple
|
||
link list.
|
||
|
||
Note: the full link address is actually 61 bits long =
|
||
7 (SSAP) + 7 (DSAP) + 47 (any non-broadcast source address).
|
||
We save the address information into two ULONGs, that are used
|
||
in the actual search. The hash key will be calculated by xoring
|
||
all 8 bytes in the address.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - MAC adapter context of data link driver
|
||
LanAddr - the complete 64 bits address of link (48 bit source addr + saps)
|
||
|
||
Return Value:
|
||
|
||
PDATA_LINK - pointer to LLC link object or NULL if not found
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT usHash;
|
||
PDATA_LINK *ppLink;
|
||
|
||
//
|
||
// this is a very simple hash algorithm, but result is modified
|
||
// by all bits => it should be quite good enough
|
||
//
|
||
|
||
usHash = LanAddr.aus.Raw[0]
|
||
^ LanAddr.aus.Raw[1]
|
||
^ LanAddr.aus.Raw[2]
|
||
^ LanAddr.aus.Raw[3];
|
||
|
||
ppLink = &pAdapterContext->aLinkHash[((((PUCHAR)&usHash)[0]
|
||
^ ((PUCHAR)&usHash)[1])
|
||
% LINK_HASH_SIZE)];
|
||
|
||
//
|
||
// BUG-BUG-BUG Check, that the C- compliler produces optimal
|
||
// dword compare for this.
|
||
//
|
||
|
||
while (*ppLink != NULL
|
||
&& ((*ppLink)->LinkAddr.ul.Low != LanAddr.ul.Low
|
||
|| (*ppLink)->LinkAddr.ul.High != LanAddr.ul.High)) {
|
||
ppLink = &(*ppLink)->pNextNode;
|
||
}
|
||
return ppLink;
|
||
}
|
||
|
||
DLC_STATUS
|
||
SetLinkParameters(
|
||
IN OUT PDATA_LINK pLink,
|
||
IN PUCHAR pNewParameters
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Updates new parameters for a link station and reinitializes the
|
||
timers and window counters.
|
||
|
||
Arguments:
|
||
|
||
pLink - LLC link station object
|
||
pNewParameters - new parameters set to a link station
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DLC_STATUS LlcStatus;
|
||
USHORT MaxInfoField;
|
||
|
||
CopyLinkParameters((PUCHAR)&pLink->TimerT1,
|
||
pNewParameters,
|
||
(PUCHAR)&pLink->pSap->DefaultParameters
|
||
);
|
||
|
||
//
|
||
// The application cannot set bigger information field than
|
||
// supported by adapter and source routing bridges.
|
||
//
|
||
|
||
MaxInfoField = LlcGetMaxInfoField(pLink->Gen.pAdapterContext->NdisMedium,
|
||
pLink->Gen.pLlcBinding,
|
||
pLink->auchLanHeader
|
||
);
|
||
if (pLink->MaxIField > MaxInfoField) {
|
||
pLink->MaxIField = MaxInfoField;
|
||
}
|
||
|
||
//
|
||
// The initial transmit and receive window size (Ww) has
|
||
// a fixed initial value, because it is dynamic, but we must
|
||
// set it always smaller than maxout.
|
||
// The maxin value is fixed. The dynamic management of N3
|
||
// is not really worth of the effort. By default, when it is
|
||
// set to maximum 127, the sender searches the optimal window
|
||
// size using the pool-bit.
|
||
//
|
||
|
||
pLink->N3 = pLink->RW;
|
||
pLink->Ww = 16; // 8 * 2;
|
||
pLink->MaxOut *= 2;
|
||
pLink->TW = pLink->MaxOut;
|
||
if (pLink->TW < pLink->Ww) {
|
||
pLink->Ww = pLink->TW;
|
||
}
|
||
LlcStatus = InitializeLinkTimers(pLink);
|
||
return LlcStatus;
|
||
}
|
||
|
||
DLC_STATUS
|
||
CheckLinkParameters(
|
||
PDLC_LINK_PARAMETERS pParms
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Procedure checks the new parameters to be set for a link and returns
|
||
error status if any of them is invalid.
|
||
|
||
Arguments:
|
||
|
||
pLink - LLC link station object
|
||
|
||
pNewParameters - new parameters set to a link station
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// These maximum values have been defined in IBM LAN Tech-Ref
|
||
//
|
||
|
||
if (pParms->TimerT1 > 10
|
||
|| pParms->TimerT2 > 10
|
||
|| pParms->TimerTi > 10
|
||
|| pParms->MaxOut > 127
|
||
|| pParms->MaxIn > 127
|
||
|| pParms->TokenRingAccessPriority > 3) {
|
||
return DLC_STATUS_PARMETERS_EXCEEDED_MAX;
|
||
} else {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Copies all non-null new link parameters, the default values are
|
||
// used when the new values are nul. Used by SetLinkParameters and
|
||
// and SetInfo call of sap station.
|
||
//
|
||
|
||
VOID
|
||
CopyLinkParameters(
|
||
OUT PUCHAR pOldParameters,
|
||
IN PUCHAR pNewParameters,
|
||
IN PUCHAR pDefaultParameters
|
||
)
|
||
{
|
||
//
|
||
// We must use the default value, if somebody has set nul.
|
||
// All parameters are UCHARs => we can do the check for a byte stream
|
||
//
|
||
|
||
CopyNonZeroBytes(pOldParameters,
|
||
pNewParameters,
|
||
pDefaultParameters,
|
||
sizeof(DLC_LINK_PARAMETERS) - sizeof(USHORT)
|
||
);
|
||
|
||
//
|
||
// The information field is the only non-UCHAR value among the
|
||
// link station parameters.
|
||
//
|
||
|
||
if (((PDLC_LINK_PARAMETERS)pNewParameters)->MaxInformationField != 0) {
|
||
((PDLC_LINK_PARAMETERS)pOldParameters)->MaxInformationField =
|
||
((PDLC_LINK_PARAMETERS)pNewParameters)->MaxInformationField;
|
||
} else if (((PDLC_LINK_PARAMETERS)pOldParameters)->MaxInformationField == 0) {
|
||
((PDLC_LINK_PARAMETERS)pOldParameters)->MaxInformationField =
|
||
((PDLC_LINK_PARAMETERS)pDefaultParameters)->MaxInformationField;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
CopyNonZeroBytes(
|
||
OUT PUCHAR pOldParameters,
|
||
IN PUCHAR pNewParameters,
|
||
IN PUCHAR pDefaultParameters,
|
||
IN UINT Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Copies and filters DLC parameter values. If the value is 0, the corresponding
|
||
default is used, else the supplied value
|
||
|
||
Arguments:
|
||
|
||
pOldParameters - pointer to set of UCHAR values
|
||
pNewParameters - pointer to array of output values
|
||
pDefaultParameters - pointer to corresponding default values
|
||
Length - size of values in bytes (UCHARs)
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
UINT i;
|
||
|
||
for (i = 0; i < Length; i++) {
|
||
if (pNewParameters[i] != 0) {
|
||
pOldParameters[i] = pNewParameters[i];
|
||
} else if (pOldParameters[i] == 0) {
|
||
pOldParameters[i] = pDefaultParameters[i];
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
UINT
|
||
RunInterlockedStateMachineCommand(
|
||
IN PDATA_LINK pStation,
|
||
IN USHORT Command
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Runs the state machine for a link, within the adapter's SendSpinLock (&
|
||
therefore at DPC)
|
||
|
||
Arguments:
|
||
|
||
pStation - pointer to DATA_LINK structure describing this link station
|
||
Command - state machine command to be run
|
||
|
||
Return Value:
|
||
|
||
UINT
|
||
Status from RunStateMachineCommand
|
||
|
||
--*/
|
||
|
||
{
|
||
UINT Status;
|
||
|
||
ACQUIRE_SPIN_LOCK(&pStation->Gen.pAdapterContext->SendSpinLock);
|
||
|
||
Status = RunStateMachineCommand(pStation, Command);
|
||
|
||
RELEASE_SPIN_LOCK(&pStation->Gen.pAdapterContext->SendSpinLock);
|
||
|
||
return Status;
|
||
}
|