/* 
 * Mach Operating System
 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

/*** TCA 100 ATM NETWORK INTERFACE  ***/

#ifndef STUB
#include <chips/tca100_if.h>
# else
#include "tca100_if.h"
#endif

#define SMALL_WINDOW_SIZE (BOM_DATA_SIZE + EOM_DATA_SIZE)
#define INITIAL_WINDOW_SIZE BOM_DATA_SIZE
#define CONTINUATION_WINDOW_SIZE (71 * COM_DATA_SIZE)
#define FINAL_WINDOW_SIZE (70 * COM_DATA_SIZE + EOM_DATA_SIZE)
#define MAX_LONG_RX 2
#define MAX_LONG_TX 5
#define BASE_TIME_OUT 5
#define DELAYED_TIME_OUT 15
#define MAX_RETRY 3
#define POLL_LIMIT 100000
#define POLL_IDLE_TIME 1
#define POLL_CELL_TIME 8

#define TCA_SYNCH  0xfc00
#define TCA_ACK  (NW_SUCCESS << 10)
#define TCA_NAK  (NW_FAILURE << 10)
#define TCA_OVR  (NW_OVERRUN << 10)
#define TCA_SEQ  (NW_INCONSISTENCY << 10)

int tca100_verbose = 0;

int tick[MAX_DEV];

nw_control_s nw_tx_control[MAX_DEV][MAX_LONG_TX];
nw_control_s nw_rx_control[MAX_DEV][MAX_LONG_RX];

int long_tx_count[MAX_DEV], long_rx_count[MAX_DEV];

nw_tx_header_t delayed_tx_first[MAX_DEV], delayed_tx_last[MAX_DEV];
nw_rx_header_t delayed_rx_first[MAX_DEV], delayed_rx_last[MAX_DEV];

nw_tcb tct[MAX_EP];

u_int MTU[] = {9244, 65528, 65532, 65528};
u_int MTU_URGENT[] = {32, 28, 32, 28};

nw_dev_entry_s tca100_entry_table = {
  tca100_initialize, tca100_status, spans_timer_sweep, tca100_timer_sweep,
  tca100_poll, tca100_send, tca100_rpc, spans_input, spans_open, spans_accept,
  spans_close, spans_add, spans_drop};

typedef enum {
  ATM_HEADER,
  SAR_HEADER,
  SAR_TRAILER,
  CS_HEADER,
  CS_TRAILER,
  FRAME_ERROR,
  DELIVERY_ERROR,
  SYNCH_ERROR,
  SEQ_ERROR,
  OVERRUN_ERROR,
  RX_RETRANSMISSION,
  TX_RETRANSMISSION
} tca_error;

int tca_ec[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

nw_result tca100_initialize(int dev) {
  nw_result rc;
  int i;

  rc = spans_initialize(dev);
  if (rc = NW_SUCCESS) {
    tick[dev] = 0;
    for (i = 0; i < MAX_LONG_TX; i++)
      nw_tx_control[dev][i].ep = 0;
    long_tx_count[dev] = 0;
    delayed_tx_first[dev] = delayed_tx_last[dev] = NULL;
    for (i = 0; i < MAX_LONG_RX; i++)
      nw_rx_control[dev][i].ep = 0;
    long_rx_count[dev] = 0;
    delayed_rx_first[dev] = delayed_rx_last[dev] = NULL;
    for (i = 0; i < MAX_EP; i++) {
      tct[i].rx_sar_header = 0;
      tct[i].rx_control = NULL;
      tct[i].tx_queued_count = 0;
      tct[i].tx_control = NULL;
    }
    rc = NW_SUCCESS;
  }
  return rc;
}

nw_result tca100_status(int dev) {
  nw_result rc;
  atm_device_t atmp;
  u_int status;

  atmp = (atm_device_t) devct[dev].addr;
  status = atmp->sreg;
  if (status & RX_NO_CARRIER) {
    atmp->creg_set = CR_RX_RESET;
    atmp->creg_clr = ~CR_RX_RESET;
    atmp->creg_set = CR_RX_ENABLE;
    atmp->sreg = 0;
    rc = NW_NO_CARRIER;
  } else if (status & RX_CELL_LOST) {
    atmp->sreg = RX_COUNT_INTR;
    rc = NW_OVERRUN;
  } else {
    rc = NW_SUCCESS;
  }
  return rc;
}


void tca100_synch_send(int dev, nw_tcb_t tcb, u_int reply) {
  vol_u_int *tx_fifo = &((atm_device_t) devct[dev].addr)->txfifo[0];
  
#ifdef TRACING
  printf("Synch sent %x\n", reply);
#endif

  tx_fifo[0] = tcb->tx_atm_header;
  tx_fifo[1] = SSM;
  tx_fifo[2] = HTONL(8);
  tx_fifo[3] = HTONL(NW_SYNCHRONIZATION);
  tx_fifo[4] = htonl(reply);
  tx_fifo[5] = HTONL(8);
  tx_fifo[6] = 0;
  tx_fifo[7] = 0;
  tx_fifo[8] = 0;
  tx_fifo[9] = 0;
  tx_fifo[10] = 0;
  tx_fifo[11] = 0;
  tx_fifo[12] = 0;
  tx_fifo[13] = SYNCH_SEGMENT_TRAILER;
}

#define broken_cell_mend(length) {    \
  missing = length;                                         \
  while (missing != 0) {                                    \
    if (missing > block_count) {                            \
      limit = 0;                                            \
      missing -= block_count;                               \
    } else {                                                \
      limit = block_count - missing;                        \
      missing = 0;                                          \
    }                                                       \
    while (block_count > limit) {                           \
      t1 = block[0];                                        \
      block++;                                              \
      tx_fifo[1] = t1;                                      \
      block_count -= 4;                                     \
    }                                                       \
    if (block_count == 0) {                                 \
      ecb->tx_current = tx_header = ecb->tx_current->next;  \
      if (tx_header != NULL) {                              \
        block_count = tx_header->block_length;              \
        block = (vol_u_int *) tx_header->block;             \
      }                                                     \
    }                                                       \
  }                                                         \
}  


nw_result tca100_window_send(int dev, nw_ecb_t ecb, nw_tcb_t tcb,
			     boolean_t initial) {
  nw_result rc;
  register vol_u_int *tx_fifo = &((atm_device_t) devct[dev].addr)->txfifo[0];
  register vol_u_int *block;
  register u_int block_count, msg_count;
  register int com_count;
  int eom_count;
  register u_int atm_header, sar_header, sar_trailer;
  u_int cs_header, end_count;
  int limit, missing;
  register u_int t1, t2;
  nw_tx_header_t tx_header;
  nw_options options;

  atm_header = tcb->tx_atm_header;
  if (initial) {
    sar_header = tcb->tx_sar_header & MID;
    tx_header = ecb->tx_initial;
    block = (vol_u_int *) tx_header->block;
    block_count = tx_header->block_length;
    options = tx_header->options;
    msg_count = tx_header->msg_length;
    if (ecb->protocol == NW_LINE)
      msg_count += 4;
    if (options == NW_URGENT)
      msg_count += 4;
    cs_header = ecb->protocol | (sar_header & 0xff) << 8 |
      (msg_count & 0xff00) << 8 | msg_count << 24;
    tcb->tx_cs_header = cs_header;

    if (msg_count <= SSM_DATA_SIZE) {      /*Single segment message*/
      tx_fifo[0] = atm_header;
      sar_trailer = (msg_count + 8) << 26;
      tx_fifo[1] = SSM | sar_header;      /*Sequence number 0 is implicit*/
      end_count = msg_count + 4;
      tx_fifo[1] = cs_header;
      if (options == NW_URGENT) {
	tx_fifo[1] = HTONL(NW_URGENT);
	msg_count -= 4;
      }
      if (ecb->protocol == NW_LINE) {
	tx_fifo[1] = tx_header->peer.local_ep >> 8 |
	             (tx_header->peer.local_ep & 0x00ff) << 8 |
	             (tx_header->peer.remote_ep & 0xff00) << 8 |
		     tx_header->peer.remote_ep << 24;
	msg_count -= 4;
      }
      if (ecb->protocol == NW_SEQ_PACKET) {
	tcb->tx_synch = 0;
      } else {
	tcb->tx_synch = -1;
      }
      goto EOM_payload;
      
    } else {	                          /*Beginning of message*/
      tx_fifo[0] = atm_header;
      sar_trailer = FULL_SEGMENT_TRAILER;
      tx_fifo[1] = BOM | sar_header;      /*Sequence number 0 is implicit*/
      tx_fifo[2] = cs_header;
      if (block_count < BOM_DATA_SIZE) {
	if (ecb->protocol == NW_LINE) {
	  t1 = tx_header->peer.local_ep >> 8 |
	       (tx_header->peer.local_ep & 0x00ff) << 8 |
	       (tx_header->peer.remote_ep & 0xff00) << 8 |
	       tx_header->peer.remote_ep << 24;
	  missing = BOM_DATA_SIZE - 4;
	  tx_fifo[3] = t1;
	} else {
	  missing = BOM_DATA_SIZE;
	}
	broken_cell_mend(missing);
      } else {
	if (ecb->protocol == NW_LINE) {
	  t1 = tx_header->peer.local_ep >> 8 |
	       (tx_header->peer.local_ep & 0x00ff) << 8 |
	       (tx_header->peer.remote_ep & 0xff00) << 8 |
	       tx_header->peer.remote_ep << 24;
	} else {
	  t1 = block[0];
	  block_count -= 4;
	  block++;
	}
	t2 = block[0];
	tx_fifo[3] = t1;
	tx_fifo[4] = t2;
	t1 = block[1];
	t2 = block[2];
	tx_fifo[5] = t1;
	tx_fifo[6] = t2;
	t1 = block[3];
	t2 = block[4];
	tx_fifo[7] = t1;
	tx_fifo[8] = t2;
	t1 = block[5];
	t2 = block[6];
	tx_fifo[9] = t1;
	tx_fifo[10] = t2;
	t1 = block[7];
	t2 = block[8];
	tx_fifo[11] = t1;
	tx_fifo[12] = t2;
	block_count -= (BOM_DATA_SIZE - 4);
	block += 9;
      }
      if (ecb->protocol == NW_RAW) {
	msg_count -= BOM_DATA_SIZE;
	com_count = msg_count / COM_DATA_SIZE;
	msg_count = msg_count % COM_DATA_SIZE;
	eom_count = 1;
	tcb->tx_synch = -1;
      } else if (msg_count > SMALL_WINDOW_SIZE) {
	com_count = eom_count = 0;
	tcb->tx_synch = msg_count;
	msg_count -= BOM_DATA_SIZE;       
      } else {
	com_count = 0;
	eom_count = 1;
	if (ecb->protocol == NW_SEQ_PACKET) {
	  tcb->tx_synch = 0;
	} else {
	  tcb->tx_synch = -1;
	}
	msg_count -= BOM_DATA_SIZE;       
      }
      tx_fifo[13] = sar_trailer;
      sar_header += SEQ_INC;
    }

  } else {
    sar_header = tcb->tx_sar_header;
    sar_trailer = FULL_SEGMENT_TRAILER;
    block = (vol_u_int *) tcb->tx_p;
    block_count = tcb->tx_block_count;
    msg_count = tcb->tx_msg_count;
    if (msg_count > FINAL_WINDOW_SIZE) {
      com_count = (CONTINUATION_WINDOW_SIZE / COM_DATA_SIZE);
      eom_count = 0;
      tcb->tx_synch = msg_count;
      msg_count -= CONTINUATION_WINDOW_SIZE;
    } else {
      com_count = msg_count / COM_DATA_SIZE;
      msg_count = msg_count % COM_DATA_SIZE;
      eom_count = 1;
      if (ecb->protocol == NW_SEQ_PACKET) {
	tcb->tx_synch = 0;
      } else {
	tcb->tx_synch = -1;
      }
    }
  }

  while (com_count-- > 0) {                 /*Continuation of message*/
    tx_fifo[0] = atm_header;
    tx_fifo[1] = sar_header;               /*COM is 0 and is implicit*/
    if (block_count >= COM_DATA_SIZE) {
      t1 = block[0];
      t2 = block[1];
      tx_fifo[2] = t1;
      tx_fifo[3] = t2;
      t1 = block[2];
      t2 = block[3];
      tx_fifo[4] = t1;
      tx_fifo[5] = t2;
      t1 = block[4];
      t2 = block[5];
      tx_fifo[6] = t1;
      tx_fifo[7] = t2;
      t1 = block[6];
      t2 = block[7];
      tx_fifo[8] = t1;
      tx_fifo[9] = t2;
      t1 = block[8];
      t2 = block[9];
      tx_fifo[10] = t1;
      tx_fifo[11] = t2;
      t1 = block[10];
      block_count -= COM_DATA_SIZE;
      tx_fifo[12] = t1;
      tx_fifo[13] = sar_trailer;
      block += 11;
      sar_header = (sar_header + SEQ_INC) & (SEQ_NO | MID);
    } else {
      broken_cell_mend(COM_DATA_SIZE);
      tx_fifo[13] = sar_trailer;
      sar_header = (sar_header + SEQ_INC) & (SEQ_NO | MID);
    }
  }

  if (eom_count != 0) {                               /*End of message*/
    tx_fifo[0] = atm_header;
    tx_fifo[1] = EOM | sar_header;
    end_count = msg_count;
    sar_trailer = (msg_count + 4) << 26;

 EOM_payload:
    if (block_count >= msg_count) {
      if (msg_count & 0x4) {
	t1 = block[0];
	tx_fifo[1] = t1;
      }
      block = (vol_u_int *) ((char *) block + msg_count);
      switch (msg_count >> 3) {
      case 5:
	t1 = block[-10];
	t2 = block[-9];
	tx_fifo[1] = t1;
	tx_fifo[1] = t2;
      case 4:
	t1 = block[-8];
	t2 = block[-7];
	tx_fifo[1] = t1;
	tx_fifo[1] = t2;
      case 3:
	t1 = block[-6];
	t2 = block[-5];
	tx_fifo[1] = t1;
	tx_fifo[1] = t2;
      case 2:
	t1 = block[-4];
	t2 = block[-3];
	tx_fifo[1] = t1;
	tx_fifo[1] = t2;
      case 1:
	t1 = block[-2];
	t2 = block[-1];
	tx_fifo[1] = t1;
	tx_fifo[1] = t2;
      }
      msg_count = 0;
    } else {
      broken_cell_mend(msg_count);
      msg_count = 0;
    }

  EOM_cs_trailer:
    tx_fifo[1] = tcb->tx_cs_header;
    switch (end_count) {
    case 0: tx_fifo[1] = 0;
    case 4: tx_fifo[1] = 0;
    case 8: tx_fifo[1] = 0;
    case 12: tx_fifo[1] = 0;
    case 16: tx_fifo[1] = 0;
    case 20: tx_fifo[1] = 0;
    case 24: tx_fifo[1] = 0;
    case 28: tx_fifo[1] = 0;
    case 32: tx_fifo[1] = 0;
    case 36: tx_fifo[1] = 0;
    }
    tx_fifo[13] = sar_trailer;
  }

  if (tcb->tx_synch == -1) { 

#ifdef TRACING
    printf("Final window sent\n");
#endif

    sar_header = (sar_header + MID_INC) & MID;
    if (sar_header == 0)
      sar_header = 1;
    tcb->tx_sar_header = sar_header;
    rc = NW_SUCCESS;
  } else {

#ifdef TRACING
    printf("Window synch at %x\n", msg_count);
#endif

    tcb->tx_sar_header = sar_header;
    tcb->tx_p = (u_int *) block;
    tcb->tx_block_count = block_count;
    tcb->tx_msg_count = msg_count;
    rc = NW_SYNCH;
  }
  return rc;
}

nw_result tca100_send(nw_ep ep, nw_tx_header_t header, nw_options options) {
  nw_result rc;
  int i, dev;
  nw_ecb_t ecb;
  nw_tcb_t tcb;
  nw_control_t control;
  nw_tx_header_t tx_header, tx_previous;

  dev = NW_DEVICE(header->peer.rem_addr_1);
  ecb = &ect[ep];
  tcb = &tct[ep];
  if ((options == NW_URGENT && header->msg_length >
       MTU_URGENT[ecb->protocol]) || header->msg_length > MTU[ecb->protocol]) {
    rc = NW_BAD_LENGTH;
  } else if (tcb->tx_queued_count != 0 ||
	     (ecb->protocol != NW_RAW &&
	      long_tx_count[dev] >= MAX_LONG_TX &&
	      (header->msg_length > SMALL_WINDOW_SIZE ||
	       ecb->protocol == NW_SEQ_PACKET))) {
    if (options == NW_URGENT && tcb->tx_queued_count != 0) {
      tx_header = delayed_tx_first[dev];
      tx_previous = NULL;
      while (tx_header != NULL && tx_header->sender != ep) {
	tx_previous = tx_header;
	tx_header = tx_header->next;
      }
      if (tx_previous == NULL) 
	delayed_tx_first[dev] = header;
      else 
	tx_previous->next = header;
      while (header->next != NULL)
	header = header->next;
      header->next = tx_header;
    } else {
      if (delayed_tx_first[dev] == NULL)
	delayed_tx_first[dev] = header;
      else
	delayed_tx_last[dev]->next = header;
      delayed_tx_last[dev] = header;
    }
    tcb->tx_queued_count++;
    rc = NW_QUEUED;

#ifdef TRACING
    printf("Send enqueued ep %d\n", ep);
#endif
    
  } else {


#ifdef TRACING
    printf("Send ep %d\n", ep);
#endif

    ecb->tx_initial = ecb->tx_current = header;
    rc = tca100_window_send(dev, ecb, tcb, TRUE);
    if (rc == NW_SUCCESS) {
      while (header != NULL) {
	if (header->buffer != NULL)
	  nc_buffer_deallocate(ep, header->buffer);
	header = header->next;
      }
      nc_tx_header_deallocate(ecb->tx_initial);
      ecb->tx_initial = ecb->tx_current = NULL;
    } else {
      control = &nw_tx_control[dev][0];
      while (control->ep != 0) 
	control++; 
      control->ep = ep;
      control->time_out = tick[dev] + BASE_TIME_OUT;
      control->retry = 0;
      tcb->reply = TCA_SYNCH;
      tcb->tx_control = control;
      tcb->tx_queued_count++;
      if (long_tx_count[dev] + long_rx_count[dev] == 0)
	nc_fast_timer_set(dev);
      long_tx_count[dev]++;
    }
  }
  return rc;
}


nw_result tx_slot_free(int dev, nw_control_t control) {
  nw_result rc;
  nw_tcb_t tcb;
  nw_ecb_t ecb;
  nw_tx_header_t tx_header;
  nw_ep ep;

  tcb = &tct[control->ep];
  tcb->tx_control = NULL;                               
  tcb->tx_queued_count--;                             
  do {                                                            
    tx_header = delayed_tx_first[dev];                            
    if (tx_header == NULL) {                                      
      control->ep = 0;                                            
      long_tx_count[dev]--;                                       
      rc = NW_FAILURE;                                            
    } else {
      ep = tx_header->sender;

#ifdef TRACING
    printf("Send dequeued ep %d\n", ep);
#endif

      ecb = &ect[ep];
      tcb = &tct[ep];
      ecb->tx_initial = ecb->tx_current =  tx_header;
      while (tx_header->next != NULL &&                           
	     tx_header->next->msg_length == 0) {                  
	tx_header = tx_header->next;                              
      }                                                         
      delayed_tx_first[dev] = tx_header->next;                    
      if (tx_header->next == NULL)                                
	delayed_tx_last[dev] = NULL;                              
      tx_header->next = NULL;                                     
      rc = tca100_window_send(dev, ecb, tcb, TRUE); 
      if (rc == NW_SYNCH) {                                         
	control->ep = ep;                            
	tcb->tx_control = control;                
	tcb->reply = TCA_SYNCH;                                 
	control->time_out = tick[dev] + BASE_TIME_OUT;              
	control->retry = 0;                                         
      }                                                             
    }                                                               
  } while (rc == NW_SUCCESS);                                       
  return rc;
}

nw_result rx_slot_free(int dev, nw_control_t control) {
  nw_result rc;
  nw_rx_header_t rx_header;
  nw_ep ep;
  nw_tcb_t tcb;

  if (control == NULL) {   
    rc = NW_SUCCESS;
  } else {
    tct[control->ep].rx_control = NULL;                               
    while ((rx_header = delayed_rx_first[dev]) != NULL &&             
	   tick[dev] >= rx_header->time_stamp) {                      
      delayed_rx_first[dev] = rx_header->next;                        
      nc_buffer_deallocate(rx_header->buffer->peer.local_ep,          
			   rx_header->buffer);                        
      ep = rx_header->receiver;
      tcb = &tct[ep];
      tcb->rx_sar_header = SSM | (tcb->rx_sar_header & MID);         
      nc_rx_header_deallocate(rx_header);                             
    }                                                                 
    if (rx_header == NULL) {                                          
      delayed_rx_last[dev] = NULL;                                    
      control->ep = 0;                                                
      long_rx_count[dev]--;                                           
      rc = NW_FAILURE;
    } else {                                                          
      delayed_rx_first[dev] = rx_header->next;                        
      if (rx_header->next == NULL)                                    
	delayed_rx_last[dev] = NULL;                                  
      ep = rx_header->receiver;
      tcb = &tct[ep];
      tca100_synch_send(dev, tcb, rx_header->reply);
      control->ep = ep;
      control->time_out = tick[dev] + BASE_TIME_OUT;                  
      tcb->rx_control = control;                  
      nc_rx_header_deallocate(rx_header);                             
    }                                                                 
  }
}


int tca100_poll(int dev) {
  vol_u_int *status = &((atm_device_t) devct[dev].addr)->sreg;
  vol_u_int *ctl_set = &((atm_device_t) devct[dev].addr)->creg_set;
  vol_u_int *rx_counter = &((atm_device_t) devct[dev].addr)->rxcount;
  register vol_u_int *rx_fifo = &((atm_device_t) devct[dev].addr)->rxfifo[0];
  register u_int rx_cell_count;
  register u_int predicted_atm_header = 0;
  register u_int predicted_sar_header;
  u_int atm_header, sar_header, predicted_sar_trailer,
        cs_header, end_count, cs_pad, rx_cell_total, reply,
        block_length, initial_offset;
  register vol_u_int *msg;
  register int msg_count;
  register int next_synch;
  register u_int t1, t2;
  nw_ecb_t ecb, tx_ecb;
  nw_tcb_t new_tcb, tx_tcb;
  nw_tcb dummy_tcb_s;
  nw_tcb_t tcb = &dummy_tcb_s;
  nw_control_t control;
  nw_buffer_t buffer;
  nw_protocol protocol;
  nw_ep lep, rep;
  nw_delivery delivery_type = NW_RECEIVE;
  nw_rx_header_t rx_header;
  nw_tx_header_t tx_header;
  int i;
  u_int tx_seqno, rx_seqno, tx_count, rx_count;

  rx_cell_total = 0;
  while ((rx_cell_count = *rx_counter & RX_COUNTER_MASK) != 0) {
    rx_cell_total += rx_cell_count;
    while (rx_cell_count-- > 0) {
      atm_header = rx_fifo[0];         /*Check ATM header and SAR header*/
      sar_header = (rx_fifo[1] & SAR_HEADER_MASK);
      if (atm_header != predicted_atm_header) {
	                      /*Must be cell from a different connection*/
	if (atm_header & ~(ATM_VPVC_MASK | ATM_HEADER_RSV_BITS)) {
 atm_header_error:
	  tca_ec[ATM_HEADER]++;
	  if (tca100_verbose)
		  printf("ATM header error %x\n", atm_header);
 discard_cell:
	  *((char *) rx_fifo) = 0; 
	  delivery_type = NW_RECEIVE;
	  continue;
	} else {
	  t1 = (atm_header & ATM_VPVC_MASK) >> ATM_VPVC_SHIFT;
	  new_tcb = &tct[t1];
	  ecb = &ect[t1];

	  /*Switch cached connection*/
	  if (new_tcb->rx_sar_header == 0)
	    goto atm_header_error;
	  tcb->rx_sar_header = predicted_sar_header;
	  tcb->rx_p = (u_int *) msg;
	  tcb->rx_count = msg_count;
	  tcb->rx_next_synch = next_synch;
	  predicted_atm_header = atm_header;
	  tcb = new_tcb;
	  predicted_sar_header = tcb->rx_sar_header;
	  msg = tcb->rx_p;
	  msg_count = tcb->rx_count;
	  next_synch = tcb->rx_next_synch;
	}
      }
      
      if (sar_header != predicted_sar_header) {
	if ((sar_header ^ predicted_sar_header) == EOM &&
	    ((predicted_sar_header & BOM) || msg_count <= EOM_DATA_SIZE)) {
                                    /*Difference on end of message bit only*/
	  predicted_sar_header = sar_header;
	} else if (sar_header == SSM) {                             /*MID 0*/
	  cs_header = rx_fifo[2];
	  t1 = rx_fifo[3];
	  if (cs_header == HTONL(8) && t1 == HTONL(NW_SYNCHRONIZATION)) {
	    reply = rx_fifo[4];                                /*Synch cell*/
	    if (rx_fifo[5] != cs_header)
	      goto cs_header_error;
	    cs_pad = rx_fifo[6];
	    t1 = rx_fifo[7];
	    t2 = rx_fifo[8];
	    cs_pad |= t1;
	    cs_pad |= t2;
	    t1 = rx_fifo[9];
	    t2 = rx_fifo[10];
	    cs_pad |= t1;
	    cs_pad |= t2;
	    t1 = rx_fifo[11];
	    t2 = rx_fifo[12];
	    cs_pad |= t1;
	    cs_pad |= t2;
	    t1 = rx_fifo[13];
	    if (cs_pad)
	      goto cs_trailer_error;
	    if ((t1 & SAR_TRAILER_MASK) != SYNCH_SEGMENT_TRAILER)
	      goto sar_trailer_error;
	    if (tcb->tx_control == NULL) {
	      tca_ec[SYNCH_ERROR]++;
	      if (tca100_verbose)
		      printf("Synch error %x\n", ntohl(reply));
	    } else {
	      tcb->reply = ntohl(reply);

#ifdef TRACING
	      printf("Received synch ep %d %x\n", ecb->id, tcb->reply);
#endif

	    }
	    continue;
	  } else if (t1 == HTONL(NW_URGENT)) {              /*Urgent cell*/
	    delivery_type = NW_RECEIVE_URGENT;
	    goto cs_header_check;
	  } else {                                          /*Bad segment*/
	    goto sar_header_error;
	  }
	} else if (!(sar_header & ATM_HEADER_CRC_SYNDROME) &&
		   (sar_header & BOM) && (sar_header & SEQ_NO) == 0) {
	  if ((sar_header & MID) == (predicted_sar_header & MID)) {
	                                                 /*Retransmission*/
	    if (tcb->rx_control != NULL) {
	      tcb->rx_control->ep = 0;
	      long_rx_count[dev]--;
	    }
	    nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep,
				 tcb->rx_buffer);
	    predicted_sar_header = sar_header;
	    tca_ec[RX_RETRANSMISSION]++;
	    if (tca100_verbose)
		    printf("Receiving retransmission ep %d sar %x\n",
			   ecb->id, sar_header); 
	  } else if (predicted_sar_header & BOM) {
                                                    /*Sequence number error*/
	    if (tca100_verbose)
		    printf("Sequence error ep %d pred %x real %x\n", ecb->id,
			   predicted_sar_header, sar_header);
	    if (ecb->protocol == NW_SEQ_PACKET) {
	      reply = 0xffff0000 | TCA_SEQ | (predicted_sar_header & MID);
	      tca100_synch_send(dev, tcb, reply);
	      tca_ec[SEQ_ERROR]++;
	      goto discard_cell;
	    } else {
	      predicted_sar_header = sar_header;
	    }
	  } else {
	    goto sar_header_error;                     /*Badly out of synch*/
	  }
	} else {                                                /*Cell loss*/

 sar_header_error:
	  if (!(predicted_sar_header & BOM)) {
	    rx_slot_free(dev, tcb->rx_control);
	    nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep,
				 tcb->rx_buffer);
	    predicted_sar_header = SSM | (predicted_sar_header & MID);
	  }
	  tca_ec[SAR_HEADER]++;
	  if (tca100_verbose)
		  printf("SAR header error ep %d pred %x real %x\n", ecb->id,
			 predicted_sar_header, sar_header);
	  goto discard_cell;
	}  
      }

      if ((predicted_sar_header & SEG_TYPE) == COM) { 
	                                           /*Continuation of message*/
	if (msg_count <= next_synch) {
	  if (msg_count == next_synch &&
	      msg_count >= CONTINUATION_WINDOW_SIZE) {
	    reply = (msg_count << 16) | TCA_ACK | (predicted_sar_header & MID);
	    tca100_synch_send(dev, tcb, reply);
	    if (msg_count > (CONTINUATION_WINDOW_SIZE + FINAL_WINDOW_SIZE)) {
	      next_synch = msg_count - CONTINUATION_WINDOW_SIZE;
	    } else if (ecb->protocol == NW_SEQ_PACKET) {
	      next_synch = 0;
	    } else {
	      next_synch = -1;
	    }
	    tcb->rx_control->time_out = tick[dev] + BASE_TIME_OUT;
	  } else {
	    rx_slot_free(dev, tcb->rx_control);
	    nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep,
				 tcb->rx_buffer);
	    predicted_sar_header = SSM | (predicted_sar_header & MID);
	    tca_ec[FRAME_ERROR]++;
	    if (tca100_verbose)
		    printf("Frame error ep %d\n", ecb->id);
	    goto discard_cell;
	  }
	}
	t1 = rx_fifo[2];
	t2 = rx_fifo[3];
	msg[0] = t1;
	msg[1] = t2;
	t1 = rx_fifo[4];
	t2 = rx_fifo[5];
	msg[2] = t1;
	msg[3] = t2;
	t1 = rx_fifo[6];
	t2 = rx_fifo[7];
	msg[4] = t1;
	msg[5] = t2;
	t1 = rx_fifo[8];
	t2 = rx_fifo[9];
	msg[6] = t1;
	msg[7] = t2;
	t1 = rx_fifo[10];
	t2 = rx_fifo[11];
	msg[8] = t1;
	msg[9] = t2;
	t1 = rx_fifo[12];
	t2 = rx_fifo[13];
	msg[10] = t1;
	if ((t2 & SAR_TRAILER_MASK) != FULL_SEGMENT_TRAILER) {
	  t1 = t2;
	  goto sar_trailer_error;
	}
	predicted_sar_header = (predicted_sar_header + SEQ_INC) &
	                                                    (SEQ_NO | MID);
	msg_count -= COM_DATA_SIZE;
	msg += 11;

      } else if ((predicted_sar_header & BOM) != 0) {
	cs_header = rx_fifo[2];

 cs_header_check:
	block_length = msg_count = (((cs_header >> 8) & 0xff00) |
				    (cs_header >> 24));
	protocol = cs_header & 0x00ff;
	if (protocol == NW_RAW || protocol == NW_SEQ_PACKET) {
	  lep = ecb->conn->peer.local_ep;
	  rep = ecb->conn->peer.remote_ep;
	  if (delivery_type == NW_RECEIVE)
	    initial_offset = 0;
	  else
	    initial_offset = 4;
	} else {
	  t1 = rx_fifo[3];
	  block_length -= 4;
	  lep = (t1 >> 8) & 0xff00 | t1 >> 24;
	  rep = (t1 & 0xff00) >> 8 | (t1 & 0x00ff) << 8;
	  if (delivery_type == NW_RECEIVE)
	    initial_offset = 4;
	  else
	    initial_offset = 8;
	} 
	if (protocol != ecb->protocol || (protocol == NW_DATAGRAM) ||
	    (protocol == NW_LINE && ect[lep].protocol != NW_DATAGRAM) ||
	    ((predicted_sar_header & 0x00ff) << 8) != (cs_header & 0xff00) ||
	    ((delivery_type != NW_RECEIVE) &&
	     msg_count - initial_offset > MTU_URGENT[protocol]) ||
	    msg_count > MTU[protocol] || (msg_count & 0x3)) {

 cs_header_error:
	  if ((protocol != NW_RAW && msg_count > SMALL_WINDOW_SIZE) ||
	      protocol == NW_SEQ_PACKET) {
	    reply = 0xffff0000 | TCA_NAK | (predicted_sar_header & MID);
	    tca100_synch_send(dev, tcb, reply);
	  }
	  tca_ec[CS_HEADER]++;
	  if (tca100_verbose)
		  printf("CS header error ep %d sar %x cs %x\n", ecb->id,
			 predicted_sar_header, cs_header);
	  goto discard_cell;
	}
	buffer = nc_buffer_allocate(lep, sizeof(nw_buffer_s) + block_length);
	if (buffer == NULL) {
	  if ((protocol != NW_RAW && msg_count > SMALL_WINDOW_SIZE) ||
	      protocol == NW_SEQ_PACKET) {
	    reply = 0xffff0000 | TCA_OVR | (predicted_sar_header & MID);
	    tca100_synch_send(dev, tcb, reply);
	  }
	  tca_ec[OVERRUN_ERROR]++;
	  if (tca100_verbose)
		  printf("Overrun error ep %d\n", ecb->id);
	  goto discard_cell;
	}
	if (protocol == NW_RAW) {
	  next_synch = -1;
	} else if (msg_count > SMALL_WINDOW_SIZE) {
	  reply = (msg_count << 16) | TCA_ACK | (predicted_sar_header & MID);
	  if (long_rx_count[dev] >= MAX_LONG_RX) {
	    rx_header = nc_rx_header_allocate();
	    if (rx_header == NULL) {
	      nc_buffer_deallocate(lep, buffer);
	      tca_ec[OVERRUN_ERROR]++;
	      goto discard_cell;
	    }
	    rx_header->buffer = buffer;
	    rx_header->receiver = ecb->id;
	    rx_header->reply = reply;
	    rx_header->time_stamp = tick[dev] + DELAYED_TIME_OUT;
	    rx_header->next = NULL;
	    if (delayed_rx_last[dev] == NULL)
	      delayed_rx_first[dev] = rx_header;
	    else
	      delayed_rx_last[dev]->next = rx_header;
	    delayed_rx_last[dev] = rx_header;
	  } else {
	    tca100_synch_send(dev, tcb, reply);
	    control = &nw_rx_control[dev][0];
	    while (control->ep != 0)
	      control++;
	    control->ep = ecb->id;
	    control->time_out = tick[dev] + BASE_TIME_OUT;
	    tcb->rx_control = control;
	    if (long_rx_count[dev] + long_tx_count[dev] == 0)
	      nc_fast_timer_set(dev);
	    long_rx_count[dev]++;
	  }
	  if (msg_count > INITIAL_WINDOW_SIZE + FINAL_WINDOW_SIZE)
	    next_synch = msg_count - INITIAL_WINDOW_SIZE;
	  else if (protocol == NW_SEQ_PACKET)
	    next_synch = 0;
	  else
	    next_synch = -1;
	} else if (protocol == NW_SEQ_PACKET) {
	  next_synch = 0;
	} else {
	  next_synch = -1;
	}
	msg = (vol_u_int *) ((char *) buffer + sizeof(nw_buffer_s));
	tcb->rx_cs_header = cs_header;
	tcb->rx_buffer = buffer;
	buffer->buf_next = NULL;
	buffer->msg_seqno = sar_header & MID;
	buffer->block_offset = sizeof(nw_buffer_s);
	buffer->block_length = block_length;
	buffer->peer.rem_addr_1 = ecb->conn->peer.rem_addr_1;
	buffer->peer.rem_addr_2 = ecb->conn->peer.rem_addr_2;
	buffer->peer.local_ep = lep;
	buffer->peer.remote_ep = rep;

	if ((predicted_sar_header & EOM) == 0) {                  /*BOM*/
	  if (initial_offset == 0) {
	    t1 = rx_fifo[3];
	    t2 = rx_fifo[4];
	    msg[0] = t1;
	    msg[1] = t2;
	    msg += 2;
	  } else {
	    msg[0] = rx_fifo[4];
	    msg++;
	  }
	  t1 = rx_fifo[5];
	  t2 = rx_fifo[6];
	  msg[0] = t1;
	  msg[1] = t2;
	  t1 = rx_fifo[7];
	  t2 = rx_fifo[8];
	  msg[2] = t1;
	  msg[3] = t2;
	  t1 = rx_fifo[9];
	  t2 = rx_fifo[10];
	  msg[4] = t1;
	  msg[5] = t2;
	  t1 = rx_fifo[11];
	  t2 = rx_fifo[12];
	  msg[6] = t1;
	  t1 = rx_fifo[13];
	  msg[7] = t2;
	  if ((t1 & SAR_TRAILER_MASK) != FULL_SEGMENT_TRAILER)
	    goto sar_trailer_error;
	  msg_count -= BOM_DATA_SIZE;
	  msg += 8;
	  predicted_sar_header = (predicted_sar_header + SEQ_INC) &
	                                                  (SEQ_NO | MID);

	} else {                                                  /*SSM*/
	  end_count = msg_count + 4;
	  predicted_sar_trailer = (msg_count + 8) << 26;
	  if (delivery_type != NW_RECEIVE) {
	    msg[0] = NW_URGENT;
	    msg++;
	  }
	  msg_count -= initial_offset;
	  goto EOM_payload;
	}
      } else {                                                    /*EOM*/
	end_count = msg_count;
	predicted_sar_trailer = (msg_count + 4) << 26;
	
 EOM_payload:
	if (msg_count & 0x4) {
	  msg[0] = rx_fifo[2];
	}
	msg = (vol_u_int *) ((char *) msg + msg_count);
	/*Fall-through the cases is intentional*/
	switch (msg_count >> 3) {
	case 5:
	  t1 = rx_fifo[2];
	  t2 = rx_fifo[2];
	  msg[-10] = t1;
	  msg[-9] = t2;
	case 4:
	  t1 = rx_fifo[2];
	  t2 = rx_fifo[2];
	  msg[-8] = t1;
	  msg[-7] = t2;
	case 3:
	  t1 = rx_fifo[2];
	  t2 = rx_fifo[2];
	  msg[-6] = t1;
	  msg[-5] = t2;
	case 2:
	  t1 = rx_fifo[2];
	  t2 = rx_fifo[2];
	  msg[-4] = t1;
	  msg[-3] = t2;
	case 1:
	  t1 = rx_fifo[2];
	  t2 = rx_fifo[2];
	  msg[-2] = t1;
	  msg[-1] = t2;
	}

	/*CS trailer should be equal to the CS header, followed by
	  padding zeros*/
	cs_pad = (rx_fifo[2] != tcb->rx_cs_header);
	/*Fall-through the cases is intentional*/
	t1 = t2 = 0;
	switch (end_count) {
	case 0:
	  t1 = rx_fifo[2];
	case 4:
	  t2 = rx_fifo[2];
	  cs_pad |= t1;
	case 8:
	  t1 = rx_fifo[2];
	  cs_pad |= t2;
	case 12:
	  t2 = rx_fifo[2];
	  cs_pad |= t1;
	case 16:
	  t1 = rx_fifo[2];
	  cs_pad |= t2;
	case 20:
	  t2 = rx_fifo[2];
	  cs_pad |= t1;
	case 24:
	  t1 = rx_fifo[2];
	  cs_pad |= t2;
	case 28:
	  t2 = rx_fifo[2];
	  cs_pad |= t1;
	case 32:
	  t1 = rx_fifo[2];
	  cs_pad |= t2;
	case 36:
	  t2 = rx_fifo[2];
	  cs_pad |= t1;
	  cs_pad |= t2;
	}
	t1 = rx_fifo[13];
	if (cs_pad != 0) {
                                 	     /*Errors in CS trailer or pad*/
 cs_trailer_error:
	  tca_ec[CS_TRAILER]++;
	  if (tca100_verbose)
		  printf("CS trailer error ep %d hd %x pad %x\n", ecb->id,
			 tcb->rx_cs_header, cs_pad);
	  goto trailer_error;

	} else if ((t1 & SAR_TRAILER_MASK) != predicted_sar_trailer) {
                                         /*Error in SAR trailer or framing*/
 sar_trailer_error:
	  tca_ec[SAR_TRAILER]++;
	  if (tca100_verbose)
		  printf("SAR trailer error ep %d pred %x real %x\n", ecb->id,
			 predicted_sar_trailer, t1);
	  goto trailer_error;

	} else if (!nc_deliver_result(tcb->rx_buffer->peer.local_ep,
				      delivery_type, (int) tcb->rx_buffer)) {
	  tca_ec[DELIVERY_ERROR]++;
	  if (tca100_verbose)
		  printf("Delivery error ep %d\n", ecb->id);

 trailer_error:
	  if (next_synch >= 0 && !(t1 & HEADER_CRC_ERROR)) {
	    reply = (msg_count << 16) | TCA_NAK | (predicted_sar_header & MID);
	    tca100_synch_send(dev, tcb, reply);
	  }
	  rx_slot_free(dev, tcb->rx_control);
	  nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep,
			       tcb->rx_buffer);
	  predicted_sar_header = SSM | (predicted_sar_header & MID);
	  delivery_type = NW_RECEIVE;
	} else {

#ifdef TRACING
	  printf("Received correctly ep %d\n", ecb->id);
#endif

	  if (next_synch == 0) {
	    reply = TCA_ACK | (predicted_sar_header & MID);
	    tca100_synch_send(dev, tcb, reply);
	  }
	  rx_slot_free(dev, tcb->rx_control);
	  if (delivery_type != NW_RECEIVE) {
	    delivery_type = NW_RECEIVE;
	    predicted_sar_header = SSM | (predicted_sar_header & MID);
	  } else {
	    predicted_sar_header = (predicted_sar_header + MID_INC) & MID;
	    if (predicted_sar_header == 0)
	      predicted_sar_header = 1;
	    predicted_sar_header |= SSM;
	  }
	}
      }
    }

    control = &nw_tx_control[dev][0];
    for (i = 0; i < MAX_LONG_TX; i++) {
      if (control->ep != 0 && tct[control->ep].reply != TCA_SYNCH) {
	tx_ecb = &ect[control->ep];
	tx_tcb = &tct[control->ep];
	rx_seqno = tx_tcb->reply & MID;
	tx_seqno = tx_tcb->tx_sar_header & MID;
	rx_count = tx_tcb->reply >> 16;
	tx_count = tx_tcb->tx_synch;
	reply = tx_tcb->reply & TCA_SYNCH;
	if (reply == TCA_ACK) {
	  if (rx_seqno == tx_seqno && rx_count == tx_count) {
	    if (rx_count == 0) {
#ifdef TRACING
	      printf("Received final ack ep %d\n", tx_ecb->id);
#endif

	      tx_seqno = (tx_seqno + MID_INC) & MID;
	      if (tx_seqno == 0)
		tx_seqno = 1;
	      tx_tcb->tx_sar_header = tx_seqno;
	      tx_slot_free(dev, control);
	      tx_tcb->reply = NW_SUCCESS;
	      nc_deliver_result(tx_ecb->id, NW_SEND, NW_SUCCESS);
	    } else {
	      if (tca100_window_send(dev, tx_ecb, tx_tcb,
				     FALSE) == NW_SUCCESS) {
		nc_deliver_result(control->ep, NW_SEND, NW_SUCCESS);
		tx_tcb->reply = NW_SUCCESS;
		tx_slot_free(dev, control);
	      } else {
		control->time_out = tick[dev] + BASE_TIME_OUT;
		tx_tcb->reply = TCA_SYNCH;
	      }
	    }
	  } else {
	    goto synch_error;
	  }
	} else if (reply == TCA_OVR) {
	  if (rx_seqno == tx_seqno && rx_count == 0xffff &&
	      ((int) tx_ecb->tx_initial->msg_length -
	       (int) tx_tcb->tx_synch) <= (int) SMALL_WINDOW_SIZE) {
	    nc_deliver_result(control->ep, NW_SEND, NW_OVERRUN);
	    tx_tcb->reply = NW_OVERRUN;
	    tx_slot_free(dev, control);
	  } else {
	    goto synch_error;
	  }
	} else if (reply == TCA_NAK) {
	  if (rx_seqno == tx_seqno &&
	      (rx_count == tx_count || (rx_count == 0xffff &&
                 ((int) tx_ecb->tx_initial->msg_length -
		  (int) tx_tcb->tx_synch) <= (int) SMALL_WINDOW_SIZE))) {
	    if (++control->retry < MAX_RETRY) {
	      if (tca100_verbose)
		      printf("Sending retransmission ep %d\n", tx_ecb->id);
	      if (tca100_window_send(dev, tx_ecb, tx_tcb,
				     TRUE) == NW_SUCCESS) {
		nc_deliver_result(control->ep, NW_SEND, NW_SUCCESS);
		tx_tcb->reply = NW_SUCCESS;
		tx_slot_free(dev, control);
	      } else {
		control->time_out = tick[dev] + BASE_TIME_OUT;
		tx_tcb->reply = TCA_SYNCH;
	      }
	      tca_ec[TX_RETRANSMISSION]++;
	    } else {
	      nc_deliver_result(control->ep, NW_SEND, NW_FAILURE);
	      tx_tcb->reply = NW_FAILURE;
	      tx_slot_free(dev, control);
	    }
	  } else {
	    goto synch_error;
	  }
	} else if (reply == TCA_SEQ) {
	  if (rx_count == 0xffff && tx_ecb->protocol == NW_SEQ_PACKET &&
	      ((int) tx_ecb->tx_initial->msg_length -
	       (int) tx_tcb->tx_synch) <= (int) SMALL_WINDOW_SIZE &&
	      rx_seqno == ((((tx_seqno + MID_INC) & MID) == 0) ?
			   1 : tx_seqno + MID_INC)) {
	    tx_tcb->tx_sar_header = rx_seqno;
	    if (tca100_window_send(dev, tx_ecb, tx_tcb, 
				   TRUE) == NW_SUCCESS) {
	      nc_deliver_result(control->ep, NW_SEND, NW_SUCCESS);
	      tx_tcb->reply = NW_SUCCESS;
	      tx_slot_free(dev, control);
	    } else {
	      control->time_out = tick[dev] + BASE_TIME_OUT;
	      tx_tcb->reply = TCA_SYNCH;
	    }
	    tca_ec[TX_RETRANSMISSION]++;
	    if (tca100_verbose)
		    printf("Sending seq retransmission ep %d\n", tx_ecb->id);
	  } else {
	    goto synch_error;
	  }
	} else {
 synch_error:
	  tca_ec[SYNCH_ERROR]++;
	  tx_tcb->reply = NW_FAILURE;
	  if (tca100_verbose)
		  printf("Synch error\n");
	}
      }
      control++;
    }
  }
  *status = ~(RX_COUNT_INTR | RX_EOM_INTR | RX_TIME_INTR);
  tcb->rx_sar_header = predicted_sar_header;
  tcb->rx_p = (u_int *) msg;
  tcb->rx_count = msg_count;
  tcb->rx_next_synch = next_synch;
  *ctl_set = RX_COUNT_INTR;
  return rx_cell_total;
}



void tca100_timer_sweep(int dev) {
  int i, rt;
  u_int reply;
  nw_control_t control;
  nw_ecb_t ecb;
  nw_tcb_t tcb;
  nw_tx_header_t tx_header;
  nw_rx_header_t rx_header;

  tick[dev]++;
  control = &nw_rx_control[dev][0];
  for (i = 0; i < MAX_LONG_RX; i++) {
    if (control->ep != 0 && control->time_out < tick[dev]) {
      rx_slot_free(dev, control);
      tcb = &tct[control->ep];
      nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep, tcb->rx_buffer);
      tcb->rx_sar_header = SSM | (tcb->rx_sar_header & MID);
    }
    control++;
  }
  control = &nw_tx_control[dev][0];
  for (i = 0; i < MAX_LONG_TX; i++) {
    if (control->ep != 0 && control->time_out < tick[dev]) {
      ecb = &ect[control->ep];
      tcb = &tct[control->ep];
      if (++control->retry < MAX_RETRY) {
	if (control->retry == 1) 
	  rt = ( /* random() */ + devct[dev].local_addr_2) & 0x000f;
	else
	  rt = ( /* random() */ + devct[dev].local_addr_1
		+ devct[dev].local_addr_2) & 0x00ff;
	control->time_out = tick[dev] + BASE_TIME_OUT + rt;
	tca100_window_send(dev, ecb, tcb, TRUE);
	tca_ec[TX_RETRANSMISSION]++;
      } else {
	nc_deliver_result(control->ep, NW_SEND, NW_TIME_OUT);
	tx_slot_free(dev, control);
      }
    }
    control++;
  }
  if (long_tx_count[dev] + long_rx_count[dev] > 0)
    nc_fast_timer_set(dev);
  else
    tick[dev] = 0;
}

nw_buffer_t tca100_rpc(nw_ep ep, nw_tx_header_t header, nw_options options) {
  nw_result rc;
  nw_buffer_t buf;
  nw_ecb_t ecb;
  nw_tcb_t tcb;
  nw_rx_header_t rx_header;
  int dev, poll_time, ncells;

  tcb = &tct[ep];
  ecb = &ect[header->peer.local_ep];
  dev = NW_DEVICE(header->peer.rem_addr_1);
  if ((rc = tca100_send(ep, header, options)) == NW_BAD_LENGTH) {
    buf = NW_BUFFER_ERROR;
  } else if (rc == NW_QUEUED) {
    buf = NULL;
  } else {
    poll_time = 0;
    if (rc == NW_SYNCH) {
      while (tcb->reply == TCA_SYNCH && poll_time < POLL_LIMIT) {
	ncells = tca100_poll(dev);
	if (ncells == 0)
	  poll_time += POLL_IDLE_TIME;
	else
	  poll_time += ncells * POLL_CELL_TIME;
      }
    }
    if (tcb->reply != NW_SUCCESS) {
      buf = NW_BUFFER_ERROR;
    } else {
      while (ecb->rx_first == NULL && poll_time < POLL_LIMIT) {
	ncells = tca100_poll(dev);
	if (ncells == 0)
	  poll_time += POLL_IDLE_TIME;
	else
	  poll_time += ncells * POLL_CELL_TIME;
      }
      if (ecb->rx_first == NULL) {
	buf = NULL;
      } else {
	rx_header = ecb->rx_first;
	buf = rx_header->buffer;
	ecb->rx_first = rx_header->next;
	if (ecb->rx_first == NULL)
	  ecb->rx_last = NULL;
	nc_rx_header_deallocate(rx_header);
      }
    }
  }

  return buf;
}