893 lines
24 KiB
C
893 lines
24 KiB
C
/*++
|
||
|
||
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++;
|
||
}
|
||
}
|