|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
llcsmsb.c
Abstract:
The module implements the subroutines used by a IEEE 802.2 compatible state machine.
To understand the procedure of this module, you should read Chapters 11 and 12 in IBM Token-Ring Architecture Reference.
The procedures in this module can be called only when SendSpinLock is set.
Contents: SaveStatusChangeEvent ResendPackets UpdateVa UpdateVaChkpt AdjustWw SendAck QueueCommandCompletion (DynamicWindowAlgorithm)
Author:
Antti Saarenheimo (o-anttis) 23-MAY-1991
Revision History:
--*/
#include <llc.h>
//
// private prototypes
//
VOID DynamicWindowAlgorithm( IN OUT PDATA_LINK pLink // data link station strcuture
);
//
// functions
//
VOID SaveStatusChangeEvent( IN PDATA_LINK pLink, IN PUCHAR puchLlcHdr, IN BOOLEAN boolResponse )
/*++
Routine Description:
Procedure saves Status Change event of the link to the event queue. to be indicated later to upper protocol.
Arguments:
pLink - LLC link station object
puchLlcHdr - the received LLC header
boolResponse - flag set if the received frame was response
Return Value:
None
--*/
{ UINT Event; PEVENT_PACKET pEvent; PVOID hClientHandle; PADAPTER_CONTEXT pAdapterContext = pLink->Gen.pAdapterContext;
ASSUME_IRQL(DISPATCH_LEVEL);
//
// Set the ethernet header length (== type) the same as
// in the current receive frame, that was either the first SABME or
// any response to it, that opened the link connection.
//
if ((pLink->DlcStatus.StatusCode & (CONFIRM_CONNECT | LLC_INDICATE_CONNECT_REQUEST)) && pAdapterContext->RcvLanHeaderLength != pLink->cbLanHeaderLength && pLink->Gen.pLlcBinding->EthernetType == LLC_ETHERNET_TYPE_AUTO) { pLink->cbLanHeaderLength = (UCHAR)pLink->Gen.pAdapterContext->RcvLanHeaderLength; }
//
// Handle first the disconnect/connect complete
//
if (pLink->DlcStatus.StatusCode & (CONFIRM_CONNECT | CONFIRM_DISCONNECT | CONFIRM_CONNECT_FAILED)) {
//
// We cannot indicate any events to non-existing stations
//
if (pLink->Gen.hClientHandle != NULL) { if (pLink->DlcStatus.StatusCode & CONFIRM_DISCONNECT) { QueueCommandCompletion((PLLC_OBJECT)pLink, LLC_DISCONNECT_COMPLETION, STATUS_SUCCESS ); } if (pLink->DlcStatus.StatusCode & (CONFIRM_CONNECT | CONFIRM_CONNECT_FAILED)) {
UINT Status;
if (pLink->DlcStatus.StatusCode & CONFIRM_CONNECT) {
//
// Set the T1 timeout for the first checkpointing state.
// This value will be changed when we have got the response
// to the first poll, but the initial value is big to
// be able to run DLC over a WAN connection
//
pLink->AverageResponseTime = 100; // 100 * 40 = 4 seconds
pLink->Flags |= DLC_FIRST_POLL; InitializeLinkTimers(pLink); Status = STATUS_SUCCESS; } else { Status = DLC_STATUS_CONNECT_FAILED; } QueueCommandCompletion((PLLC_OBJECT)pLink, LLC_CONNECT_COMPLETION, Status ); } } pLink->DlcStatus.StatusCode &= ~(CONFIRM_CONNECT | CONFIRM_DISCONNECT | CONFIRM_CONNECT_FAILED); }
if (pLink->DlcStatus.StatusCode != 0) { if (pLink->DlcStatus.StatusCode & INDICATE_FRMR_SENT) {
#if LLC_DBG
PrintLastInputs("FRMR SENT!\n", pLink); #endif
pLink->DlcStatus.FrmrData.Command = puchLlcHdr[2]; pLink->DlcStatus.FrmrData.Ctrl = puchLlcHdr[3]; if ((pLink->DlcStatus.FrmrData.Command & LLC_S_U_TYPE_MASK) == LLC_U_TYPE) { pLink->DlcStatus.FrmrData.Ctrl = 0; } pLink->DlcStatus.FrmrData.Vs = pLink->Vs; pLink->DlcStatus.FrmrData.Vr = pLink->Vr | boolResponse; } else if (pLink->DlcStatus.StatusCode & INDICATE_FRMR_RECEIVED) {
#if LLC_DBG
PrintLastInputs("FRMR RECEIVED!\n", pLink); DbgBreakPoint(); #endif
LlcMemCpy(&pLink->DlcStatus.FrmrData, &puchLlcHdr[3], sizeof(LLC_FRMR_INFORMATION) ); }
//
// A remote connect request may have created a link station
// in link driver. The upper protocol must be able to separate
// sap handle from the data link
//
if (pLink->Gen.hClientHandle == NULL) {
//
// Indicate the event on the sap, because the upper protocol
// has not yet any link station create for this link, because
// it has been created remotely.
//
hClientHandle = pLink->pSap->Gen.hClientHandle, Event = LLC_STATUS_CHANGE_ON_SAP; } else {
//
// Indicate the event on the link station
//
hClientHandle = pLink->Gen.hClientHandle, Event = LLC_STATUS_CHANGE; }
//
// The indications of the received SABMEs must be queued,
// but all other events are indicated directy to
// the upper protocol, because those indications must never
// be lost because of an out of memory condition.
//
if (pLink->DlcStatus.StatusCode & INDICATE_CONNECT_REQUEST) {
pEvent = ALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool);
if (pEvent != NULL) { LlcInsertTailList(&pAdapterContext->QueueEvents, pEvent); pEvent->pBinding = pLink->Gen.pLlcBinding; pEvent->hClientHandle = hClientHandle; pEvent->Event = Event; pEvent->pEventInformation = (PVOID)&pLink->DlcStatus;
//
// RLF 11/18/92
//
// INDICATE_CONNECT_REQUEST is generated when we receive a
// SABME for a station in the DISCONNECTED state. However,
// we need to generate either INDICATE_CONNECT_REQUEST (0x0400)
// or INDICATE_RESET (0x0800) depending on whether the SABME
// created the link station or whether it was created by a
// DLC.OPEN.STATION at this end. pLink->RemoteOpen is TRUE if
// the link was created due to receipt of the SABME
// This routine is only called by RunStateMachine and
// INDICATE_CONNECT_REQUEST is never combined with any other
// status codes
//
//pEvent->SecondaryInfo = pLink->DlcStatus.StatusCode;
pEvent->SecondaryInfo = pLink->RemoteOpen ? INDICATE_CONNECT_REQUEST : INDICATE_RESET; } } else {
//
// We must do this with a locked SendSpinLock, because
// otherwise somebody might delete the link, while
// we are still using it.
// THIS IS ACTAULLY QUITE DIRTY (callback with locked
// spinlocks), BUT WE CANNOT USE THE PACKET POOLS WHEN
// WE INDICATE AN EVENT, THAT MUST NOT BE LOST!
//
pLink->Gen.pLlcBinding->pfEventIndication( pLink->Gen.pLlcBinding->hClientContext, hClientHandle, Event, (PVOID)&pLink->DlcStatus, pLink->DlcStatus.StatusCode ); }
//
// We must cancel all queued transmit commands, if the link
// is lost, disconnected or reset.
//
if (pLink->DlcStatus.StatusCode & (INDICATE_LINK_LOST | INDICATE_DM_DISC_RECEIVED | INDICATE_FRMR_RECEIVED | INDICATE_FRMR_SENT | INDICATE_RESET)) {
CancelTransmitCommands((PLLC_OBJECT)pLink, DLC_STATUS_LINK_NOT_TRANSMITTING); }
//
// Reset the status code!
//
pLink->DlcStatus.StatusCode = 0; } }
VOID ResendPackets( IN OUT PDATA_LINK pLink // data link strcuture
)
/*++
Routine Description:
Function initializes the send process to resend the rejected packets and resets the adaptive working window variables. The operations defined in IBM state machine are: Vs=Nr, Ww=1, Ia_Ct=0, but this also resorts the packet queue.
Arguments:
pLink - LLC link station object
Return Value:
None
--*/
{ PADAPTER_CONTEXT pAdapterContext = pLink->Gen.pAdapterContext;
//
// Complete all frames, that were acknowledged by the reject (if any)
//
if (pLink->Nr != pLink->Va) { DynamicWindowAlgorithm(pLink); }
if ( (pLink->Vs != pLink->VsMax) && (((pLink->Vs < pLink->VsMax) && (pLink->Nr >= pLink->Vs) && (pLink->Nr <= pLink->VsMax) ) || (!((pLink->Vs > pLink->VsMax) && (pLink->Nr > pLink->VsMax) && (pLink->Nr < pLink->Vs)) ) ) ) { return; }
//
// Move all rejected packets from the queue sent packets back
// to the send queue. We have already completed all acknowledged
// packets => we can take the packet from the tail and put them
// to the head of the send queue.
// We can trust, that the reject window is correct, because
// Nr has been checked before the state machine was called.
// (note: the counters are a modulo 256, but we use bytes).
//
for (;pLink->Vs != pLink->Nr; pLink->Vs -= 2) {
PLLC_PACKET pPacket;
if (!IsListEmpty(&pLink->SentQueue) ){
pLink->Statistics.I_FrameTransmissionErrors++; if (pLink->Statistics.I_FrameTransmissionErrors == 0x80) { pLink->DlcStatus.StatusCode |= INDICATE_DLC_COUNTER_OVERFLOW; }
pPacket = (PLLC_PACKET)LlcRemoveTailList(&pLink->SentQueue);
LlcInsertHeadList(&pLink->SendQueue.ListHead, pPacket);
} }
//
// The procedure starts the send process only if it has been
// enabled by the state machine. Only StartSendProcessLocked
// may start the process, if it has been locked by
// StopSendProcess
//
StartSendProcess(pAdapterContext, pLink);
//
// Reset the current window (Vs=Nr, Ww=1, Ia_Ct=0)
//
pLink->Ww = 2; pLink->Ia_Ct = 0; }
VOID UpdateVa( IN OUT PDATA_LINK pLink // data link station strcuture
)
/*++
Routine Description:
Function updates Va (last valid Nr received) and makes also some other actions needed in the normal receive operations.
Arguments:
pLink - LLC link station object
Return Value:
None
--*/
{ //
// Reset the initilization state variable
//
pLink->Vi = 0;
//
// Update the receive state variable Va (the last valid received
// frame), but update some Ww variables before that.
//
if (pLink->Nr != pLink->Va) { DynamicWindowAlgorithm(pLink);
//
// T1 reply timer must be running as far as there are
// out (or sent) any unacknowledged frames.
// Ti timer must be stopped whenever T1 is running and vice versa
//
if (pLink->Nr != pLink->Vs) {
//
// There still are some unacknowledged frames,
// start or restart the reply timer.
//
StartTimer(&pLink->T1); // reply timer
StopTimer(&pLink->Ti); } else {
//
// All sent frames have been acknowledged,
// => We may stop the reply timer.
//
StopTimer(&pLink->T1); // reply timer
StartTimer(&pLink->Ti); }
//
// Reset the I- frame retry counter whenever we do
// any kind of progress
//
pLink->Is_Ct = pLink->N2; } }
VOID UpdateVaChkpt( IN OUT PDATA_LINK pLink // data link station strcuture
)
/*++
Routine Description:
Function updates Va (last valid Nr received) and makes also some other actions needed in the check point receive operations.
Arguments:
pLink - LLC link station object
Return Value:
None
--*/
{ UCHAR OrginalWw = pLink->Ww;
//
// Reset the initilization state variable
//
pLink->Vi = 0;
//
// Update the receive state variable Va (the last valid received
// frame), but update the acknowledged frames counter before that.
// That counter is used by the Adaptive window algorithm.
//
if (pLink->Nr != pLink->Va) {
//
// Run adaptive transmit window (TW/T1) algorithm.
//
if (pLink->Ww == pLink->TW) {
//
// Update the counters of adaptive transmit window algorithm,
// We need (LLC_MAX_T1_TO_I_RATIO/2) successful transmit using
// the full window size, before we again try increase the
// maximum transmit window size.
//
pLink->FullWindowTransmits += pLink->Ww; if ((UINT)pLink->FullWindowTransmits >= LLC_MAX_T1_TO_I_RATIO) { pLink->FullWindowTransmits = 2; if (pLink->TW < pLink->MaxOut) { pLink->TW += 2; } } } DynamicWindowAlgorithm(pLink);
//
// Reset the I- frame and Poll retry counters whenever
// we do any kind of progress with the acknowledged I-frames.
//
pLink->P_Ct = pLink->N2; pLink->Is_Ct = pLink->N2; }
//
// Stop the reply timer, if we are not waiting
// anything else from the other side.
//
if (pLink->Nr != pLink->Vs) {
//
// There still are some unacknowledged frames,
// start or restart the reply timer.
//
StartTimer(&pLink->T1); // reply timer
StopTimer(&pLink->Ti); } else {
//
// All sent frames have been acknowledged,
// => We may stop the reply timer.
//
StopTimer(&pLink->T1); // reply timer
StartTimer(&pLink->Ti); }
//
// Bugfix in 3-3-1992, Vp (!= pLInk->Vp) seems to be wrong here,
// because in many cases it is not set when a checkpointing state is
// entered. In the chekpointing state Vs=Vp, because the
// send process is always stopped in our implementation,
// when a checkpointing state is entered.
// Why do we actually need the Vp? A: It's needed to prevent
// forever looping between chekpointing and open states.
//
if (pLink->Nr != pLink->Vs) {
//
// We use a very simple adaptive transmit window (TW/T1) algorithm:
//
// TW is set the same as the last successful Working window
// size (Ww), whenever T1 has been lost. We increase TW after
// a constant number of full window transmits.
//
// The more complicated TW/T1 algorithms usually work worse
// produce more code and decrease the performace, but this algorithm
// is quite vulnerable to unreliable media (=> TW=1, a lot of T1
// timeouts). A better algorithm could also try to increase TW,
// if the ratio of T1 timeouts to transferred I- frames increases
// when the TW is decreased. I will leave this matter to my
// successors (AS 19-MAR-1992).
//
// Another problem in this algorithm is the too fast increasing
// of big working windows (Ww). In that case Ww is incremented by
// more than 1 => the other side may lose I-frames before I-c1.
// This is not very serious situation, we reset working window to 1
// and start it again.
//
//
// Update the transmit window always after a T1 timeout.
//
if (pLink->P_Ct < pLink->N2) {
//
// Reset the maximum transmit window size whenever
// we have lost the last I-C1 (poll).
// In the first time the current windows size
// becomes the maximum windows size (we never hit
// the maximum tranmit window size, if the other
// size have receive problems). This algorithm assumes,
// that we have otherwise very reliable network.
//
if (OrginalWw > 2) { pLink->TW = (UCHAR)(OrginalWw - 2); } else if (pLink->TW > 2) {
//
// We may have already reset Ww because of REJ of a
// I-c0 before the actual poll, that was lost also.
// In that case we don't have any idea of the actual
// window size, but we decrement TW in any case.
//
pLink->TW -= 2; } pLink->FullWindowTransmits = 2; } ResendPackets(pLink); } }
VOID AdjustWw( IN OUT PDATA_LINK pLink // data link strcuture
)
/*++
Routine Description:
Procedure adjust the working window of a data link station.
Arguments:
pLink - LLC link station object
Nr - NR of the received LLC LDPU
Return Value:
None
--*/
{ //
// Update the receive state variable Va (the last valid received
// frame), but update some Ww variables before that.
//
if (pLink->Nr != pLink->Va) { DynamicWindowAlgorithm(pLink);
//
// Reset the I- frame and Poll retry counters whenever
// we do any kind of progress with the acknowledged I-frames.
//
pLink->P_Ct = pLink->N2; pLink->Is_Ct = pLink->N2; } }
VOID SendAck( IN OUT PDATA_LINK pLink )
/*++
Routine Description:
Procedure sends the ack, if the received unacknowledged frames counter expires and stops the acknowledge delay timer (T2). Otherwise it start (or restarts) the acknowledge delay timer.
Arguments:
pLink - LLC link station object
Return Value:
Returns the token of the next sent command frame.
--*/
{ pLink->Ir_Ct--; if (pLink->Ir_Ct == 0) { pLink->Ir_Ct = pLink->N3; // MaxIn
StopTimer(&pLink->T2);
//
// Send RR-r0 to acknowledge the response
//
SendLlcFrame(pLink, (UCHAR)DLC_RR_TOKEN); } else { StartTimer(&pLink->T2); } }
VOID QueueCommandCompletion( IN PLLC_OBJECT pLlcObject, IN UINT CompletionCode, IN UINT Status )
/*++
Routine Description:
The function queues a command completion (if there was an allcoated packet in the completion queue).
Arguments:
pLlcObject - LLC object (link, sap or direct) CompletionCode - command completion code returned to upper protocol Status - returned status
Return Value:
None -
--*/
{ PLLC_PACKET *ppPacket;
//
// Search the command from the completion list.
// (use the "address of address" scanning to take the
// searched element from the middle of one way linked list)
//
ppPacket = &pLlcObject->Gen.pCompletionPackets; while (*ppPacket != NULL && (*ppPacket)->Data.Completion.CompletedCommand != CompletionCode) { ppPacket = &(*ppPacket)->pNext; } if (*ppPacket != NULL) {
PLLC_PACKET pPacket = *ppPacket;
*ppPacket = pPacket->pNext;
pPacket->pBinding = pLlcObject->Gen.pLlcBinding; pPacket->Data.Completion.Status = Status; pPacket->Data.Completion.CompletedCommand = CompletionCode; pPacket->Data.Completion.hClientHandle = pLlcObject->Gen.hClientHandle;
#if LLC_DBG
pPacket->pNext = NULL; #endif
LlcInsertTailList(&pLlcObject->Gen.pAdapterContext->QueueCommands, pPacket); } }
VOID DynamicWindowAlgorithm( IN OUT PDATA_LINK pLink // data link station strcuture
)
/*++
Routine Description:
The function runs the dynamic window algorithm and updates the dynamic window size of used by the link's send process. This routine also completes the acknowledged transmissions.
Arguments:
pLink - LLC link station object
Return Value:
None
--*/
{ PADAPTER_CONTEXT pAdapterContext;
//
// Run Dynamic Window algorithm of IBM TR Architecture Ref:
//
// if (Working window less that the maximum window)
// then
// The Acknowledged frame count += The acknowledged frames
//
// if (The Acknowledged frame count >
// packets to be aknowledged before next increment)
// then
// Increment the working window
// endif
// endif
//
if (pLink->Ww < pLink->TW) {
//
// The Acknowledged frame count += The acknowledged frames
// (handle the wrap around of UCHAR counters)
//
if (pLink->Va > pLink->Nr) { pLink->Ia_Ct += (256 + pLink->Nr) - pLink->Va; } else { pLink->Ia_Ct += pLink->Nr - pLink->Va; }
//
// if (The Acknowledged frame count
// > packets to be aknowledged before next increment)
// then
// Increment the working window
// endif
//
if (pLink->Ia_Ct > pLink->Nw) {
USHORT usWw;
usWw = (USHORT)(pLink->Ww + (pLink->Ia_Ct / pLink->Nw) * 2); pLink->Ia_Ct = pLink->Ia_Ct % pLink->Nw; if (usWw > pLink->TW) { pLink->Ww = pLink->TW; } else { pLink->Ww = (UCHAR)usWw; } } }
//
// Complete all acknowledged I-frame packets
//
pAdapterContext = pLink->Gen.pAdapterContext; for (; pLink->Va != pLink->Nr; pLink->Va += 2) {
PLLC_PACKET pPacket;
MY_ASSERT(!IsListEmpty(&pLink->SentQueue));
if (IsListEmpty(&pLink->SentQueue)) { return; }
pPacket = LlcRemoveHeadList(&pLink->SentQueue);
pPacket->Data.Completion.Status = STATUS_SUCCESS; pPacket->Data.Completion.CompletedCommand = LLC_SEND_COMPLETION; pPacket->Data.Completion.hClientHandle = pPacket->Data.Xmit.pLlcObject->Gen.hClientHandle;
//
// We use extra status bits to indicate, when I- packet has been both
// completed by NDIS and acknowledged by the other side of the link
// connection. An I- packet can be queued to the completion queue by
// the second quy (either state machine or SendCompletion handler)
// only when the first guy has set completed its work.
// An I packet could be acknowledged by the other side before
// its completion is indicated by NDIS. Dlc Driver deallocates
// the packet immediately, when Llc driver completes the acknowledged
// packet => possible data corruption (if packet is reused before
// NDIS has completed it). This is probably not possible in a
// single processor NT- system, but very possible in multiprocessor
// NT or systems without a single level DPC queue (like OS/2 and DOS).
//
pPacket->CompletionType &= ~LLC_I_PACKET_UNACKNOWLEDGED; if (pPacket->CompletionType == LLC_I_PACKET_COMPLETE) { LlcInsertTailList(&pAdapterContext->QueueCommands, pPacket); }
//
// Increment counter, when the I- frame has
// succesfully received and acknowledged by the other side.
// We must also send status changes indication, when
// the USHORT counter hits the half way.
//
pLink->Statistics.I_FramesTransmitted++; if (pLink->Statistics.I_FramesTransmitted == 0x8000) { pLink->DlcStatus.StatusCode |= INDICATE_DLC_COUNTER_OVERFLOW; } pLink->pSap->Statistics.FramesTransmitted++; } }
|