text: /*-------------------------------------------------------------------
    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;
  }
  