You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1017 lines
30 KiB
1017 lines
30 KiB
/*-------------------------------------------------------------------
|
|
hdlc.c - handle LAN communications. Provide error free transport
|
|
of packets: take care of packet drop detection, retransmition.
|
|
HDLC like services. Layer 2.
|
|
|
|
4-27-98 - adjust for scanrate addition.
|
|
6-17-97 - change link-integrity check code.
|
|
6-17-97 - rewrite sequencing logic, in hdlc_clear_outpkts().
|
|
|
|
Copyright 1996,97 Comtrol Corporation. All rights reserved. Proprietary
|
|
information not permitted for development or use with non-Comtrol products.
|
|
|---------------------------------------------------------------------*/
|
|
#include "precomp.h"
|
|
|
|
//void hdlc_send_ialive(Hdlc *hd);
|
|
int hdlc_send_ack_only(Hdlc *hd);
|
|
static void hdlc_clear_outpkts(Hdlc *hd);
|
|
int hdlc_SendPkt(Hdlc *hd, int pkt_num, int length);
|
|
int hdlc_ctl_SendPkt(Hdlc *hd, int pkt_num, int length);
|
|
|
|
#define Trace1(s,p1) GTrace1(D_Hdlc, sz_modid, s, p1)
|
|
#define TraceStr(s) GTrace(D_Hdlc, sz_modid, s)
|
|
#define TraceErr(s) GTrace(D_Error, sz_modid_err, s)
|
|
static char *sz_modid = {"Hdlc"};
|
|
static char *sz_modid_err = {"Error,Hdlc"};
|
|
|
|
#define DISABLE()
|
|
#define ENABLE()
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_open - setup and initialize a LanPort thing.
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_open(Hdlc *hd, BYTE *box_mac_addr)
|
|
{
|
|
int i;
|
|
NTSTATUS Status;
|
|
PNDIS_BUFFER NdisBuffer;
|
|
|
|
TraceStr("open");
|
|
if (hd->qout.QBase != NULL)
|
|
{
|
|
MyKdPrint(D_Error, ("HDLC already open!\n"))
|
|
return 0;
|
|
}
|
|
|
|
hd->out_snd_index= 0;
|
|
hd->in_ack_index = 0;
|
|
hd->next_in_index = 0;
|
|
hd->rec_ack_timer = 0;
|
|
hd->sender_ack_timer = 0;
|
|
hd->tx_alive_timer = 0;
|
|
hd->rx_alive_timer = 0;
|
|
hd->qout_ctl.QPut = 0;
|
|
hd->qout_ctl.QGet = 0;
|
|
hd->qout_ctl.QSize = 2; // 2 pkts
|
|
hd->qout.QPut = 0;
|
|
hd->qout.QGet = 0;
|
|
hd->qout.QSize = HDLC_TX_PKT_QUEUE_SIZE; // number of iframe pkts
|
|
hd->pkt_window_size = HDLC_TX_PKT_QUEUE_SIZE-2;
|
|
memcpy(hd->dest_addr, box_mac_addr, 6);
|
|
|
|
// default to the first nic card slot, port state handling and nic
|
|
// packet reception handling dynamically figures this out.
|
|
// we should probably set it to null, but I'm afraid of this right now
|
|
#ifdef BREAK_NIC_STUFF
|
|
hd->nic = NULL;
|
|
#else
|
|
hd->nic = &Driver.nics[0];
|
|
#endif
|
|
|
|
// NDIS packets consist of one or more buffer descriptors which point
|
|
// to the actual data. We send or receive single packets made up of
|
|
// 1 or more buffers. A MDL is used as a buffer descriptor under NT.
|
|
|
|
//--------- Allocate a packet pool for our tx packets
|
|
NdisAllocatePacketPool(&Status, &hd->TxPacketPool, HDLC_TX_PKT_QUEUE_SIZE,
|
|
sizeof(PVOID));
|
|
// sizeof(PACKET_RESERVED));
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
hdlc_close(hd);
|
|
return 4;
|
|
}
|
|
|
|
//--------- Allocate a buffer pool for our tx packets
|
|
// we will only use 1 buffer per packet.
|
|
NdisAllocateBufferPool(&Status, &hd->TxBufferPool, HDLC_TX_PKT_QUEUE_SIZE);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
hdlc_close(hd);
|
|
return 5;
|
|
}
|
|
|
|
//-------- create tx data buffer area
|
|
hd->qout.QBase = our_locked_alloc( MAX_PKT_SIZE * HDLC_TX_PKT_QUEUE_SIZE,"hdTX");
|
|
|
|
//-------- form our tx queue packets so they link to our tx buffer area
|
|
for (i=0; i<HDLC_TX_PKT_QUEUE_SIZE; i++)
|
|
{
|
|
// Get a packet from the pool
|
|
NdisAllocatePacket(&Status, &hd->TxPackets[i], hd->TxPacketPool);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
hdlc_close(hd);
|
|
return 8;
|
|
}
|
|
hd->TxPackets[i]->ProtocolReserved[0] = i; // mark with our index
|
|
hd->TxPackets[i]->ProtocolReserved[1] = 0; // free for use
|
|
|
|
// get a buffer for the header
|
|
NdisAllocateBuffer(&Status, &NdisBuffer, hd->TxBufferPool,
|
|
&hd->qout.QBase[MAX_PKT_SIZE * i], 1500);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
hdlc_close(hd);
|
|
return 9;
|
|
}
|
|
// we use only one data buffer per packet
|
|
NdisChainBufferAtFront(hd->TxPackets[i], NdisBuffer);
|
|
}
|
|
|
|
|
|
|
|
//--------- Allocate a packet pool for our tx control packets(2)
|
|
NdisAllocatePacketPool(&Status, &hd->TxCtlPacketPool, 2, sizeof(PVOID));
|
|
// sizeof(PACKET_RESERVED));
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
hdlc_close(hd);
|
|
return 4;
|
|
}
|
|
|
|
//--------- Allocate a buffer pool for our tx ctl packets
|
|
// we will only use 1 buffer per packet.
|
|
NdisAllocateBufferPool(&Status, &hd->TxCtlBufferPool, 2);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
hdlc_close(hd);
|
|
return 5;
|
|
}
|
|
|
|
//-------- create tx control data buffer area
|
|
hd->qout_ctl.QBase = our_locked_alloc( MAX_PKT_SIZE * 2,"hdct");
|
|
|
|
//-------- form our tx queue packets so they link to our tx buffer area
|
|
for (i=0; i<2; i++)
|
|
{
|
|
// Get a packet from the pool
|
|
NdisAllocatePacket(&Status, &hd->TxCtlPackets[i], hd->TxCtlPacketPool);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
hdlc_close(hd);
|
|
return 8;
|
|
}
|
|
hd->TxCtlPackets[i]->ProtocolReserved[0] = i; // mark with our index
|
|
hd->TxCtlPackets[i]->ProtocolReserved[1] = 0; // free for use
|
|
|
|
// get a buffer for the header
|
|
NdisAllocateBuffer(&Status, &NdisBuffer, hd->TxCtlBufferPool,
|
|
&hd->qout_ctl.QBase[MAX_PKT_SIZE * i], 1500);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
hdlc_close(hd);
|
|
return 9;
|
|
}
|
|
// we use only one data buffer per packet
|
|
NdisChainBufferAtFront(hd->TxCtlPackets[i], NdisBuffer);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_close -
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_close(Hdlc *hd)
|
|
{
|
|
TraceStr("close");
|
|
|
|
if (hd->TxPacketPool != NULL)
|
|
NdisFreePacketPool(hd->TxPacketPool);
|
|
hd->TxPacketPool = NULL;
|
|
|
|
if (hd->TxBufferPool != NULL)
|
|
NdisFreeBufferPool(hd->TxBufferPool);
|
|
hd->TxBufferPool = NULL;
|
|
|
|
if (hd->qout.QBase != NULL)
|
|
our_free(hd->qout.QBase, "hdTX");
|
|
hd->qout.QBase = NULL;
|
|
|
|
|
|
//------- close up the control packet buffers
|
|
if (hd->TxCtlPacketPool != NULL)
|
|
NdisFreePacketPool(hd->TxCtlPacketPool);
|
|
hd->TxCtlPacketPool = NULL;
|
|
|
|
if (hd->TxCtlBufferPool != NULL)
|
|
NdisFreeBufferPool(hd->TxCtlBufferPool);
|
|
hd->TxCtlBufferPool = NULL;
|
|
|
|
if (hd->qout_ctl.QBase != NULL)
|
|
our_free(hd->qout_ctl.QBase, "hdct");
|
|
hd->qout_ctl.QBase = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------
|
|
hdlc_validate_rx_pkt - Handle "hdlc" like validation of the
|
|
rx packets from our nic driver.
|
|
Handle checking sequence index byte and return an error if packet
|
|
is out of sequence.
|
|
|-----------------------------------------------------------------*/
|
|
int hdlc_validate_rx_pkt(Hdlc *hd, BYTE *buf)
|
|
{
|
|
#define CONTROL_HEADER buf[0]
|
|
#define SND_INDEX buf[1]
|
|
#define ACK_INDEX buf[2]
|
|
#define PRODUCT_HEADER buf[3]
|
|
|
|
TraceStr("validate");
|
|
switch (CONTROL_HEADER)
|
|
{
|
|
case 1: // 1H=unindex
|
|
TraceStr("val,unindexed");
|
|
break;
|
|
|
|
case 3: // 1H=unindex, 2H=sync_init
|
|
//----- use to re-sync up our index count
|
|
// the vs-1000 device will never do this now, only us(the server) will
|
|
TraceStr("RESYNC");
|
|
hdlc_resync(hd);
|
|
return ERR_CONTROL_PACKET; // control packet, no network data
|
|
|
|
case 0: // normal information frame
|
|
break;
|
|
}
|
|
|
|
if ((CONTROL_HEADER & 1) == 0) // indexed, so validate
|
|
{
|
|
if (hd->rec_ack_timer == 0)
|
|
hd->rec_ack_timer = MIN_ACK_REC_TIME;
|
|
|
|
// now check that packet is syncronized in-order
|
|
// make sure we didn't miss a packet
|
|
if (SND_INDEX != ((BYTE)(hd->next_in_index)) )
|
|
{
|
|
++hd->iframes_outofseq;
|
|
|
|
hd->status |= LST_SEND_ACK; // force an acknowledgement packet
|
|
|
|
TraceErr("bad index");
|
|
return ERR_GET_BAD_INDEX; // error, packet out of sequence
|
|
}
|
|
++hd->unacked_pkts; // when to trip acknowledge at 80% full
|
|
if (hd->unacked_pkts > (hd->pkt_window_size - 1))
|
|
{
|
|
hd->status |= LST_SEND_ACK;
|
|
TraceStr("i_ack");
|
|
}
|
|
|
|
hd->rx_alive_timer = 0; // reset this since we have a good rx-active link
|
|
|
|
++hd->next_in_index; // bump our index count
|
|
TraceStr("iframe OK");
|
|
|
|
} // indexed
|
|
|
|
//---- now grab the packet acknowledged index.
|
|
if (hd->in_ack_index != ACK_INDEX) // only act when changed.
|
|
{
|
|
//--- we can assume this ack-index is a reasonable value
|
|
// since it has gone threw the ethernet checksum.
|
|
hd->in_ack_index = ACK_INDEX; // update our copy
|
|
hd->status |= LST_RECLAIM; // perform reclaim operation
|
|
}
|
|
|
|
return 0; // ok
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_poll - Call at regular interval to handle packet sequencing,
|
|
and packet resending. Called 20 times per second for DOS,embedded,
|
|
for NT called 100 times per sec.
|
|
|--------------------------------------------------------------------------*/
|
|
void hdlc_poll(Hdlc *hd)
|
|
{
|
|
WORD timer;
|
|
|
|
hd->tick_timer += ((WORD) Driver.Tick100usBase);
|
|
if (hd->tick_timer >= 1000) // 1/10th second
|
|
{
|
|
hd->tick_timer = 0;
|
|
|
|
// every 1/10th second
|
|
++hd->tx_alive_timer;
|
|
++hd->rx_alive_timer;
|
|
if ((hd->tx_alive_timer >= KEEP_ALIVE_TIMEOUT) || // about 1 min.
|
|
(hd->rx_alive_timer >= KEEP_ALIVE_TIMEOUT))
|
|
{
|
|
// Rx or Tx or both activity has not happened, or com-link
|
|
// failure has occurred, so send out a iframe to see if
|
|
// we are in failure or just a state of non-activity.
|
|
|
|
// take the biggest timeout value, so we don't have to do
|
|
// the logic twice for each.
|
|
if (hd->tx_alive_timer > hd->rx_alive_timer)
|
|
timer = hd->tx_alive_timer;
|
|
else timer = hd->rx_alive_timer;
|
|
|
|
if (timer == KEEP_ALIVE_TIMEOUT)
|
|
{
|
|
//hdlc_send_ialive(hd); // send out a iframe to get ack back.
|
|
//TraceStr("Snd ialive");
|
|
//----- notify owner to check link
|
|
if (hd->upper_layer_proc != NULL)
|
|
(*hd->upper_layer_proc) (hd->context, EV_L2_CHECK_LINK, 0);
|
|
}
|
|
else if (timer == (KEEP_ALIVE_TIMEOUT * 2))
|
|
{
|
|
// declare a bad connection, bring connection down.
|
|
//----- notify owner that it needs to resync
|
|
if (hd->upper_layer_proc != NULL)
|
|
(*hd->upper_layer_proc) (hd->context, EV_L2_RELOAD, 0);
|
|
|
|
TraceErr("ialive fail");
|
|
|
|
// make sure everything is cleared out, or reset at our level
|
|
hdlc_resync(hd);
|
|
hd->tx_alive_timer = 0;
|
|
hd->rx_alive_timer = 0;
|
|
}
|
|
}
|
|
|
|
if (hd->sender_ack_timer > 0)
|
|
{
|
|
--hd->sender_ack_timer;
|
|
if (hd->sender_ack_timer == 0)
|
|
{
|
|
if (!q_empty(&hd->qout)) // have outpkts waiting for ack.
|
|
{
|
|
TraceStr("Snd timeout");
|
|
++hd->send_ack_timeouts; // statistics: # of send-ack-timeouts
|
|
hdlc_resend_outpkt(hd); // send it out again!
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hd->rec_ack_timer > 0)
|
|
{
|
|
--hd->rec_ack_timer;
|
|
if (hd->rec_ack_timer == 0) // timeout on rec. packet ack.
|
|
{
|
|
++hd->rec_ack_timeouts; // statistics: # of rec-ack-timeouts
|
|
|
|
TraceStr("RecAck timeout");
|
|
if (!q_empty(&hd->qout)) // have outpkts waiting for ack.
|
|
hdlc_resend_outpkt(hd); // send it out again!
|
|
else
|
|
{
|
|
// no iframe packets sent out(piggy back acks on them normally)
|
|
// for REC_ACK_TIME amount, so we have to send out just an
|
|
// acknowledgement packet.
|
|
// arrange for a ack-packet to be sent by setting this bit
|
|
hd->status |= LST_SEND_ACK;
|
|
}
|
|
}
|
|
}
|
|
} // end of 100ms tick period
|
|
|
|
// check if received packets more than 80% of senders capacity, if so
|
|
// send immediate ack.
|
|
if (hd->status & LST_SEND_ACK)
|
|
{
|
|
if (hdlc_send_ack_only(hd) == 0) // ok
|
|
{
|
|
hd->status &= ~LST_SEND_ACK;
|
|
TraceStr("Ack Sent");
|
|
}
|
|
else
|
|
{
|
|
TraceStr("Ack Pkt Busy!");
|
|
}
|
|
}
|
|
|
|
if (hd->status & LST_RECLAIM) // check if we should perform reclaim operation
|
|
hdlc_clear_outpkts(hd);
|
|
|
|
return;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_get_ctl_outpkt - Used to allocate a outgoing control data
|
|
packet, fill in the
|
|
common header elements and return a pointer to the packet, so the
|
|
application can fill in the data in the packet. The caller is then
|
|
expected to send the packet via hdlc_send_ctl_outpkt().
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_get_ctl_outpkt(Hdlc *hd, BYTE **buf)
|
|
{
|
|
BYTE *bptr;
|
|
|
|
TraceStr("get_ctl_outpkt");
|
|
|
|
bptr = &hd->qout_ctl.QBase[(MAX_PKT_SIZE * hd->qout_ctl.QPut)];
|
|
|
|
*buf = &bptr[20]; // return ptr to the sub-packet area
|
|
|
|
if (hd->TxCtlPackets[hd->qout_ctl.QPut]->ProtocolReserved[1] != 0) // free for use
|
|
{
|
|
TraceErr("CPktNotOurs!");
|
|
*buf = NULL;
|
|
return 2; // error, packet is owned, busy
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_get_outpkt - Used to allocate a outgoing data packet, fill in the
|
|
common header elements and return a pointer to the packet, so the
|
|
application can fill in the data in the packet. The caller is then
|
|
expected to send the packet via hdlc_send_outpkt().
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_get_outpkt(Hdlc *hd, BYTE **buf)
|
|
{
|
|
BYTE *bptr;
|
|
|
|
TraceStr("get_outpkt");
|
|
if (hd->status & LST_RECLAIM) // check if we should perform reclaim operation
|
|
hdlc_clear_outpkts(hd);
|
|
|
|
// if indexed, then reduce by one so we always leave one for an
|
|
// unindexed packet.
|
|
if (q_count(&hd->qout) >= hd->pkt_window_size)
|
|
{
|
|
return 1; // no room
|
|
}
|
|
if (hd->TxPackets[hd->qout.QPut]->ProtocolReserved[1] != 0) // free for use
|
|
{
|
|
TraceErr("PktNotOurs!");
|
|
*buf = NULL;
|
|
return 2;
|
|
}
|
|
bptr = &hd->qout.QBase[(MAX_PKT_SIZE * hd->qout.QPut)];
|
|
|
|
*buf = &bptr[20]; // return ptr to the sub-packet area
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_clear_outpkts - go through output queue and re-claim any packet
|
|
buffers which have been acknowledged.
|
|
|--------------------------------------------------------------------------*/
|
|
static void hdlc_clear_outpkts(Hdlc *hd)
|
|
{
|
|
#define NEW_WAY
|
|
|
|
#ifndef NEW_WAY
|
|
int count, get, i, ack_count, ack_get;
|
|
BYTE *tx_base;
|
|
|
|
#define OUT_SNDINDEX tx_base[18]
|
|
#else
|
|
|
|
#define OUT_SNDINDEX_BYTE_OFFSET 18
|
|
#endif
|
|
int put;
|
|
int ack_index;
|
|
|
|
TraceStr("clear_outpkt");
|
|
hd->status &= ~LST_RECLAIM; // clear this flag
|
|
|
|
// in_ack_index is the last packet V(r) acknowledgement, so it
|
|
// is equal to what the other party expects the next rec. pkt
|
|
// index to be.
|
|
if (hd->in_ack_index > 0)
|
|
ack_index = hd->in_ack_index-1;
|
|
else ack_index = 0xff;
|
|
|
|
#ifdef NEW_WAY
|
|
put = hd->qout.QPut;
|
|
// figure out a queue index of the last(most recent) pkt we sent
|
|
// (back up the QPut index)
|
|
|
|
while (put != hd->qout.QGet) // while not end of ack-pending out-packets
|
|
{
|
|
// (back up the QPut index)
|
|
if (put == 0)
|
|
put = HDLC_TX_PKT_QUEUE_SIZE-1;
|
|
else --put;
|
|
|
|
// if ack matches the out_snd_index for this packet
|
|
if (hd->qout.QBase[(MAX_PKT_SIZE * put)+OUT_SNDINDEX_BYTE_OFFSET]
|
|
== ack_index)
|
|
{
|
|
// clear all pending up to this packet by updating the QGet index.
|
|
if (put == (HDLC_TX_PKT_QUEUE_SIZE-1))
|
|
hd->qout.QGet = 0;
|
|
else hd->qout.QGet = (put+1);
|
|
|
|
hd->tx_alive_timer = 0; // reset this since we have a good active link
|
|
|
|
if (q_empty(&hd->qout)) // all packets cleared
|
|
hd->sender_ack_timer = 0; // stop the timeout counter
|
|
break; // bail out of while loop, all done
|
|
}
|
|
}
|
|
#else
|
|
count = q_count(&hd->qout);
|
|
get = hd->qout.QGet;
|
|
ack_count = 0;
|
|
ack_get = get; // acknowledge all up to this point
|
|
|
|
for (i=0; i<count; i++)
|
|
{
|
|
//-- setup a ptr to our first outgoing packet in our resend buffer
|
|
tx_base= &hd->qout.QBase[(MAX_PKT_SIZE * get)];
|
|
++get; // setup for next one
|
|
if (get >= HDLC_TX_PKT_QUEUE_SIZE)
|
|
get = 0;
|
|
|
|
// if the packet is definitely older than our ACK index
|
|
if (OUT_SNDINDEX <= ack_index)
|
|
{
|
|
|
|
++ack_count; // acknowledge all up to this point
|
|
ack_get = get; // acknowledge all up to this point
|
|
}
|
|
// else if roll over cases might exist
|
|
else if (ack_index < HDLC_TX_PKT_QUEUE_SIZE)
|
|
{
|
|
if (OUT_SNDINDEX > HDLC_TX_PKT_QUEUE_SIZE) // roll over case
|
|
{
|
|
++ack_count; // acknowledge all up to this point
|
|
ack_get = get; // acknowledge all up to this point
|
|
}
|
|
else break; // bail from for loop
|
|
}
|
|
else // we are all done, because pkts must be in order
|
|
{
|
|
break; // bail from for loop
|
|
}
|
|
}
|
|
|
|
if (ack_count) // if we did acknowledge(free) some output packets
|
|
{
|
|
hd->tx_alive_timer = 0; // reset this since we have a good active link
|
|
|
|
hd->qout.QGet = ack_get; // update the circular get queue index.
|
|
|
|
if (q_empty(&hd->qout)) // all packets cleared
|
|
hd->sender_ack_timer = 0; // stop the timeout counter
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_resend_outpkt - resend packet(s) due to sequence error. Only indexed
|
|
iframe packets get resent.
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_resend_outpkt(Hdlc *hd)
|
|
{
|
|
BYTE *tx_base;
|
|
int phy_len, count;
|
|
// BYTE *buf;
|
|
// WORD *wptr;
|
|
int get;
|
|
|
|
TraceStr("resend_outpkt");
|
|
if (hd->status & LST_RECLAIM) // check if we should perform reclaim operation
|
|
hdlc_clear_outpkts(hd);
|
|
|
|
count = q_count(&hd->qout);
|
|
get = hd->qout.QGet;
|
|
|
|
if (count == 0)
|
|
return 0; // none to send
|
|
|
|
while (count > 0)
|
|
{
|
|
if (hd->TxPackets[get]->ProtocolReserved[1] == 0) {
|
|
/* Make sure packet has come back from NDIS */
|
|
|
|
/* free to resend */
|
|
// assume indexing used
|
|
tx_base= &hd->qout.QBase[(MAX_PKT_SIZE * get)];
|
|
|
|
++hd->iframes_sent; // statistics
|
|
// get calculated length of packet for resending at out pkt prefix.
|
|
phy_len = hd->phys_outpkt_len[get];
|
|
|
|
// always make the ack as current as possible
|
|
tx_base[19] = hd->next_in_index; // V(r)
|
|
|
|
hdlc_SendPkt(hd, get, phy_len);
|
|
|
|
++hd->iframes_resent; // statistics: # of packets re-sent
|
|
}
|
|
|
|
++get;
|
|
if (get >= HDLC_TX_PKT_QUEUE_SIZE)
|
|
get = 0;
|
|
|
|
--count;
|
|
}
|
|
hd->unacked_pkts = 0;
|
|
|
|
// reset timeout
|
|
hd->sender_ack_timer = (MIN_ACK_REC_TIME * 2);
|
|
|
|
// reset this timer, since we are sending out new ack.
|
|
hd->rec_ack_timer = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_send_ctl_outpkt - App. calls hdlc_get_ctl_outpkt() to get a buffer.
|
|
App then fills buffer and sends it out by calling us.
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_send_ctl_outpkt(Hdlc *hd, int data_len, BYTE *dest_addr)
|
|
{
|
|
BYTE *tx_base;
|
|
int phy_len;
|
|
int get, stat;
|
|
|
|
TraceStr("send_ctl_outpkt");
|
|
get = hd->qout_ctl.QPut;
|
|
|
|
tx_base = &hd->qout_ctl.QBase[(MAX_PKT_SIZE * get)];
|
|
++hd->qout_ctl.QPut;
|
|
if (hd->qout_ctl.QPut >= hd->qout_ctl.QSize)
|
|
hd->qout_ctl.QPut = 0;
|
|
|
|
++hd->ctlframes_sent; // statistics
|
|
|
|
if (dest_addr == NULL)
|
|
memcpy(tx_base, hd->dest_addr, 6); // set dest addr
|
|
else memcpy(tx_base, dest_addr, 6); // set dest addr
|
|
|
|
memcpy(&tx_base[6], hd->nic->address, 6); // set src addr
|
|
|
|
// + 1 for trailing 0(sub-pkt terminating header)
|
|
phy_len = 20 + data_len + 1;
|
|
|
|
// BYTE 12-13: Comtrol PCI ID (11H, FEH), Ethernet Len field
|
|
*((WORD *)&tx_base[12]) = 0xfe11;
|
|
|
|
if (phy_len < 60)
|
|
phy_len = 60;
|
|
|
|
tx_base[14] = ASYNC_PRODUCT_HEADER_ID; // comtrol packet type = driver management, any product.
|
|
tx_base[15] = 0; // conc. index field
|
|
tx_base[16] = ASYNC_FRAME; // ASYNC FRAME(0x55)
|
|
tx_base[17] = 1; // hdlc control field(ctrl-packet)
|
|
tx_base[18] = 0; // V(s), unindexed so mark as 0 to avoid confusion
|
|
tx_base[19] = hd->next_in_index; // V(r), acknowl. field
|
|
tx_base[20+data_len] = 0; // terminating sub-packet type
|
|
|
|
|
|
hd->unacked_pkts = 0; // reset this
|
|
|
|
// reset this timer, since we are sending out new ack.
|
|
hd->rec_ack_timer = 0;
|
|
|
|
stat = hdlc_ctl_SendPkt(hd, get, phy_len);
|
|
|
|
|
|
return stat;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_send_outpkt - App. calls hdlc_get_outpkt() to get a buffer. App then
|
|
fills buffer and sends it out by calling us. This packet sits in
|
|
transmit queue for possible re-send until a packet comes in which
|
|
acknowledges reception of it.
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_send_outpkt(Hdlc *hd, int data_len, BYTE *dest_addr)
|
|
{
|
|
BYTE *tx_base;
|
|
int phy_len;
|
|
int get, stat;
|
|
|
|
TraceStr("send_outpkt");
|
|
get = hd->qout.QPut;
|
|
|
|
tx_base = &hd->qout.QBase[(MAX_PKT_SIZE * get)];
|
|
|
|
++hd->qout.QPut;
|
|
if (hd->qout.QPut >= HDLC_TX_PKT_QUEUE_SIZE)
|
|
hd->qout.QPut = 0;
|
|
// setup this timeout for ack. back.
|
|
hd->sender_ack_timer = (MIN_ACK_REC_TIME * 2);
|
|
|
|
++hd->iframes_sent; // statistics
|
|
|
|
if (dest_addr == NULL)
|
|
memcpy(tx_base, hd->dest_addr, 6); // set dest addr
|
|
else memcpy(tx_base, dest_addr, 6); // set dest addr
|
|
|
|
memcpy(&tx_base[6], hd->nic->address, 6); // set src addr
|
|
|
|
// + 1 for trailing 0(sub-pkt terminating header)
|
|
phy_len = 20 + data_len + 1;
|
|
|
|
// BYTE 12-13: Comtrol PCI ID (11H, FEH), Ethernet Len field
|
|
*((WORD *)&tx_base[12]) = 0xfe11;
|
|
|
|
if (phy_len < 60)
|
|
phy_len = 60;
|
|
|
|
tx_base[14] = ASYNC_PRODUCT_HEADER_ID; // comtrol packet type = driver management, any product.
|
|
tx_base[15] = 0; // conc. index field
|
|
tx_base[16] = ASYNC_FRAME; // ASYNC FRAME(0x55)
|
|
tx_base[17] = 0; // hdlc control field(iframe-packet)
|
|
tx_base[19] = hd->next_in_index; // V(r), acknowl. field
|
|
tx_base[20+data_len] = 0; // terminating sub-packet type
|
|
|
|
// save calculated length of packet for resending at out pkt prefix.
|
|
hd->phys_outpkt_len[get] = phy_len;
|
|
|
|
tx_base[18] = hd->out_snd_index; // V(s)
|
|
hd->out_snd_index++;
|
|
|
|
hd->unacked_pkts = 0; // reset this
|
|
|
|
// reset this timer, since we are sending out new ack.
|
|
hd->rec_ack_timer = 0;
|
|
|
|
stat = hdlc_SendPkt(hd, get, phy_len);
|
|
|
|
|
|
return stat;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
hdlc_ctl_SendPkt - Our send routine.
|
|
|----------------------------------------------------------------------*/
|
|
int hdlc_ctl_SendPkt(Hdlc *hd, int pkt_num, int length)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
|
|
#if DBG
|
|
if (hd == NULL)
|
|
{
|
|
MyKdPrint(D_Error, ("H1\n"))
|
|
TraceErr("Hsnd1a1");
|
|
return 1;
|
|
}
|
|
if (hd->nic == NULL)
|
|
{
|
|
MyKdPrint(D_Error, ("H2\n"))
|
|
TraceErr("Hsnd1a");
|
|
return 1;
|
|
}
|
|
if (hd->nic->TxBufTemp == NULL)
|
|
{
|
|
MyKdPrint(D_Error, ("H3\n"))
|
|
TraceErr("Hsnd1b");
|
|
return 1;
|
|
}
|
|
if (hd->nic->TxPacketsTemp == NULL)
|
|
{
|
|
MyKdPrint(D_Error, ("H4\n"))
|
|
TraceErr("Hsnd1c");
|
|
return 1;
|
|
}
|
|
if (hd->nic->Open == 0)
|
|
{
|
|
MyKdPrint(D_Error, ("H5\n"))
|
|
TraceErr("Hsnd1d");
|
|
return 1;
|
|
}
|
|
#endif
|
|
Trace1("Hsendpkt Nic%d", hd->nic->RefIndex);
|
|
|
|
hd->TxCtlPackets[pkt_num]->Private.TotalLength = length;
|
|
NdisAdjustBufferLength(hd->TxCtlPackets[pkt_num]->Private.Head, length);
|
|
|
|
hd->TxCtlPackets[pkt_num]->ProtocolReserved[1] = 1; // mark as pending
|
|
NdisSend(&Status, hd->nic->NICHandle, hd->TxCtlPackets[pkt_num]);
|
|
if (Status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
TraceStr(" ok");
|
|
hd->TxCtlPackets[pkt_num]->ProtocolReserved[1] = 0; // free for use
|
|
}
|
|
else if (Status == NDIS_STATUS_PENDING)
|
|
{
|
|
TraceStr(" pend");
|
|
// Status = NicWaitForCompletion(nic); // wait for completion
|
|
}
|
|
else
|
|
{
|
|
hd->TxCtlPackets[pkt_num]->ProtocolReserved[1] = 0; // free for use
|
|
TraceErr(" send1A");
|
|
return 1;
|
|
}
|
|
|
|
++hd->nic->pkt_sent; // statistics
|
|
hd->nic->send_bytes += length; // statistics
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
hdlc_SendPkt - Our send routine.
|
|
|----------------------------------------------------------------------*/
|
|
int hdlc_SendPkt(Hdlc *hd, int pkt_num, int length)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
TraceStr("sendpkt");
|
|
|
|
hd->TxPackets[pkt_num]->Private.TotalLength = length;
|
|
NdisAdjustBufferLength(hd->TxPackets[pkt_num]->Private.Head, length);
|
|
|
|
hd->TxPackets[pkt_num]->ProtocolReserved[1] = 1; // mark as pending
|
|
NdisSend(&Status, hd->nic->NICHandle, hd->TxPackets[pkt_num]);
|
|
if (Status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
TraceStr(" ok");
|
|
hd->TxPackets[pkt_num]->ProtocolReserved[1] = 0; // free for use
|
|
}
|
|
else if (Status == NDIS_STATUS_PENDING)
|
|
{
|
|
TraceStr(" pend");
|
|
// Status = NicWaitForCompletion(nic); // wait for completion
|
|
}
|
|
else
|
|
{
|
|
hd->TxPackets[pkt_num]->ProtocolReserved[1] = 0; // free for use
|
|
TraceErr(" send1A");
|
|
return 1;
|
|
}
|
|
|
|
++hd->nic->pkt_sent; // statistics
|
|
hd->nic->send_bytes += length; // statistics
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef COMMENT_OUT
|
|
/*--------------------------------------------------------------------------
|
|
hdlc_send_ialive - Send out a iframe packet which device is required
|
|
to acknowledge(and send iframe back) so that we can determine if he
|
|
is still alive.
|
|
|--------------------------------------------------------------------------*/
|
|
void hdlc_send_ialive(Hdlc *hd)
|
|
{
|
|
int stat;
|
|
BYTE *buf;
|
|
|
|
if (!q_empty(&hd->qout)) // have outpkts waiting for ack.
|
|
{
|
|
hdlc_resend_outpkt(hd); // send it out again!
|
|
}
|
|
else
|
|
{
|
|
stat = hdlc_get_outpkt(hd, &buf);
|
|
if (stat == 0)
|
|
{
|
|
buf[0] = 0; // an empty iframe packet
|
|
buf[1] = 0;
|
|
stat = hdlc_send_outpkt(hd, 1, hd->dest_addr); // send it out!
|
|
if (stat != 0)
|
|
{ TraceErr("2D"); }
|
|
}
|
|
else
|
|
{
|
|
// else we might as well go fishing and forget about this stuff.
|
|
TraceErr("3D");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------------
|
|
hdlc_resync - At appropriate times it is needed to reset the sequence
|
|
indexing logic in order to get the two sides up a talking. On
|
|
startup(either side) or a fatal(long) timeout, it is needed to send
|
|
a message to the other party saying: "reset your packet sequencing
|
|
logic so we can get sync-ed up".
|
|
|--------------------------------------------------------------------------*/
|
|
void hdlc_resync(Hdlc *hd)
|
|
{
|
|
TraceErr("resync");
|
|
//----- flush re-send output buffer
|
|
hd->qout.QPut = 0;
|
|
hd->qout.QGet = 0;
|
|
|
|
//----- flush ctl output buffer
|
|
hd->qout_ctl.QPut = 0;
|
|
hd->qout_ctl.QGet = 0;
|
|
|
|
//----- use to re-sync up our index count
|
|
hd->in_ack_index = 0;
|
|
hd->out_snd_index= 0;
|
|
hd->next_in_index= 0;
|
|
|
|
//----- reset our outgoing packet queue
|
|
hd->sender_ack_timer = 0;
|
|
|
|
hd->unacked_pkts = 0;
|
|
//----- notify owner that it needs to resync
|
|
if (hd->upper_layer_proc != NULL)
|
|
(*hd->upper_layer_proc) (hd->context, EV_L2_RESYNC, 0);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_send_ack_only - Used to recover from timeout condition. Used to
|
|
resend ACK only. Used to send over ACK and index fields in a
|
|
unindexed frame(won't flow off). No data sent along, just HDLC header.
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_send_ack_only(Hdlc *hd)
|
|
{
|
|
int ret_stat;
|
|
BYTE *pkt;
|
|
|
|
TraceStr("send_ack_only");
|
|
if (hdlc_get_ctl_outpkt(hd, &pkt) == 0)
|
|
ret_stat = hdlc_send_ctl_outpkt(hd, 0, NULL);
|
|
else
|
|
ret_stat = 1; // packet is already in use
|
|
|
|
return ret_stat;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_send_raw - Used to send raw ethernets out(non-hdlc).
|
|
Caller has gotten a control packet by hdlc_get_ctl_outpkt()
|
|
and has filled in the header. We just plug in src/dest addr and
|
|
send it out. Used to send out non-hdlc packets, we provide the service
|
|
in hdlc layer because we have the nic buffers already setup, so its
|
|
convienent to implement here.
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_send_raw(Hdlc *hd, int data_len, BYTE *dest_addr)
|
|
{
|
|
BYTE *tx_base;
|
|
int phy_len;
|
|
int get, stat;
|
|
|
|
TraceStr("send_raw");
|
|
get = hd->qout_ctl.QPut;
|
|
|
|
tx_base = &hd->qout_ctl.QBase[(MAX_PKT_SIZE * get)];
|
|
++hd->qout_ctl.QPut;
|
|
if (hd->qout_ctl.QPut >= hd->qout_ctl.QSize)
|
|
hd->qout_ctl.QPut = 0;
|
|
|
|
++hd->rawframes_sent; // statistics
|
|
|
|
if (dest_addr == NULL)
|
|
memcpy(tx_base, hd->dest_addr, 6); // set dest addr
|
|
else memcpy(tx_base, dest_addr, 6); // set dest addr
|
|
|
|
memcpy(&tx_base[6], hd->nic->address, 6); // set src addr
|
|
|
|
// + 1 for trailing 0(sub-pkt terminating header)
|
|
phy_len = 20 + data_len + 1;
|
|
|
|
// BYTE 12-13: Comtrol PCI ID (11H, FEH), Ethernet Len field
|
|
*((WORD *)&tx_base[12]) = 0xfe11;
|
|
|
|
if (phy_len < 60)
|
|
phy_len = 60;
|
|
|
|
stat = hdlc_ctl_SendPkt(hd, get, phy_len);
|
|
|
|
|
|
return stat;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
| hdlc_send_control - Used to send small un-indexed hdlc frames.
|
|
|--------------------------------------------------------------------------*/
|
|
int hdlc_send_control(Hdlc *hd, BYTE *header_data, int header_len,
|
|
BYTE *data, int data_len,
|
|
BYTE *dest_addr)
|
|
{
|
|
BYTE *buf;
|
|
int i,stat;
|
|
BYTE *pkt;
|
|
|
|
i = hdlc_get_ctl_outpkt(hd, &pkt);
|
|
if (i)
|
|
return 1; // error
|
|
|
|
buf = pkt;
|
|
|
|
if (header_len)
|
|
{
|
|
for (i=0; i<header_len; i++)
|
|
buf[i] = header_data[i];
|
|
buf += header_len;
|
|
}
|
|
if (data_len)
|
|
{
|
|
for (i=0; i<data_len; i++)
|
|
buf[i] = data[i];
|
|
buf += data_len;
|
|
}
|
|
|
|
if (dest_addr == NULL)
|
|
{
|
|
stat = hdlc_send_ctl_outpkt(hd, header_len + data_len, hd->dest_addr);
|
|
}
|
|
else
|
|
{
|
|
stat = hdlc_send_ctl_outpkt(hd, header_len + data_len, dest_addr);
|
|
}
|
|
|
|
return stat;
|
|
}
|