/* 
 * 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.
 */

/*** NETWORK INTERFACE IMPLEMENTATION CORE ***/

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

/*** Types and data structures ***/

#if PRODUCTION
#define MAX_HASH 701
#define MAX_HOST 4000
#else
#define MAX_HASH 7
#define MAX_HOST 4
#endif

nw_dev_entry_s nc_failure_entry_table =  {nc_fail, nc_fail,
			   nc_null, nc_null,
			   nc_null_poll, nc_null_send, nc_null_rpc,
			   nc_null_signal, nc_open_fail, nc_accept_fail,
			   nc_close_fail, nc_open_fail, nc_open_fail};
					    
nw_dev_entry_s nc_local_entry_table =  {nc_succeed, nc_succeed,
			   nc_null, nc_null,
			   nc_null_poll, nc_local_send, nc_local_rpc,
			   nc_null_signal, nc_open_fail, nc_accept_fail,
			   nc_close_fail, nc_open_fail, nc_open_fail};


typedef struct {
  nw_address_s address;
  int name_next:16;
  int ip_next:16;
  int nw_next:16;
  nw_ep line:16;
} nw_alist_s, *nw_alist_t;
	       

boolean_t nc_initialized = FALSE;
nw_tx_header_s nw_tx[MAX_EP/2];
nw_tx_header_t nw_free_tx_header;
nw_rx_header_s nw_rx[2*MAX_EP];
nw_rx_header_t nw_free_rx_header;
nw_plist_s nw_peer[MAX_EP];
nw_plist_t nw_free_peer;

nw_devcb devct[MAX_DEV];

nw_ecb ect[MAX_EP];

int nw_free_ep_first, nw_free_ep_last;
int nw_free_line_first, nw_free_line_last;

nw_alist_s nw_address[MAX_HOST];
int nw_free_address;

int nw_name[MAX_HASH];
int nw_ip[MAX_HASH];
int nw_nw[MAX_HASH];

int nw_fast_req;

/*** System-independent functions ***/

void nc_initialize() {
  int ep, last_ep;
  
  if (!nc_initialized) {
    last_ep = sizeof(nw_tx)/sizeof(nw_tx_header_s) - 1;
    for (ep = 0; ep < last_ep; ep++)
      nw_tx[ep].next = &nw_tx[ep+1];
    nw_tx[last_ep].next = NULL;
    nw_free_tx_header = &nw_tx[0];
    last_ep = sizeof(nw_rx)/sizeof(nw_rx_header_s) - 1;
    for (ep = 0; ep < last_ep; ep++)
      nw_rx[ep].next = &nw_rx[ep+1];
    nw_rx[last_ep].next = NULL;
    nw_free_rx_header = &nw_rx[0];
    last_ep = sizeof(nw_peer)/sizeof(nw_plist_s) - 1;
    for (ep = 0; ep < last_ep; ep++)
      nw_peer[ep].next = &nw_peer[ep+1];
    nw_peer[last_ep].next = NULL;
    nw_free_peer = &nw_peer[0];
    for (ep = 0; ep < MAX_DEV; ep++) {
      devct[ep].status = NW_FAILURE;
      devct[ep].type = NW_CONNECTIONLESS;
      devct[ep].addr = NULL;
      devct[ep].local_addr_1 = 0;
      devct[ep].local_addr_2 = 0;
      devct[ep].entry = &nc_failure_entry_table;
      devct[ep].fast_req = 0;
    }
    devct[NW_NULL].status = NW_SUCCESS;
    devct[NW_NULL].entry = &nc_local_entry_table;
    last_ep = sizeof(ect)/sizeof(nw_ecb);
    for (ep = 0; ep < last_ep; ep++) {
      ect[ep].state = NW_INEXISTENT;
      ect[ep].id = ep;
      ect[ep].seqno = 0;
      ect[ep].previous = ep - 1;
      ect[ep].next = ep + 1;
    }
    ect[0].next = ect[0].previous = 0;
    ect[last_ep-1].next = 0;
    nw_free_ep_first = 1;
    nw_free_ep_last = last_ep - 1;
    nw_free_line_first = nw_free_line_last = 0;
    for (ep = 0; ep < MAX_HOST; ep++) {
      nw_address[ep].nw_next = ep + 1;
    }
    nw_address[MAX_HOST - 1].nw_next = -1;
    nw_free_address = 0;
    for (ep = 0; ep < MAX_HASH; ep++) {
      nw_name[ep] = -1;
      nw_ip[ep] = -1;
      nw_nw[ep] = -1;
    }
    nw_fast_req = 0;
    h_initialize();
    nc_initialized = TRUE;
  }
}

nw_tx_header_t nc_tx_header_allocate() {
  nw_tx_header_t header;

  header = nw_free_tx_header;
  if (header != NULL)
    nw_free_tx_header = header->next;
  return header;
}

void nc_tx_header_deallocate(nw_tx_header_t header) {
  nw_tx_header_t first_header;

  first_header = header;
  while (header->next != NULL)
    header = header->next;
  header->next = nw_free_tx_header;
  nw_free_tx_header = first_header;
}

nw_rx_header_t nc_rx_header_allocate() {
  nw_rx_header_t header;

  header = nw_free_rx_header;
  if (header != NULL)
    nw_free_rx_header = header->next;
  return header;
}

void nc_rx_header_deallocate(nw_rx_header_t header) {

  header->next = nw_free_rx_header;
  nw_free_rx_header = header;
}

nw_plist_t nc_peer_allocate() {
  nw_plist_t peer;

  peer = nw_free_peer;
  if (peer != NULL)
    nw_free_peer = peer->next;
  return peer;
}

void nc_peer_deallocate(nw_plist_t peer) {
  nw_plist_t first_peer;

  first_peer = peer;
  while (peer->next != NULL)
    peer = peer->next;
  peer->next = nw_free_peer;
  nw_free_peer = first_peer;
}


nw_result nc_device_register(u_int dev, nw_dev_type type, char *dev_addr,
			     nw_dev_entry_t dev_entry_table) {
  nw_result rc;

  if (dev >= MAX_DEV) {
    rc = NW_FAILURE;
  } else {
    devct[dev].status = NW_SUCCESS;
    devct[dev].type = type;
    devct[dev].addr = dev_addr;
    devct[dev].entry = dev_entry_table;
    devct[dev].fast_req = 0;
    rc = NW_SUCCESS;
  }
  return rc;
}
  
nw_result nc_device_unregister(u_int dev, nw_result status) {
  nw_result rc;

  if (dev >= MAX_DEV) {
    rc = NW_FAILURE;
  } else {
    devct[dev].status = status;
    devct[dev].addr = NULL;
    devct[dev].entry = &nc_failure_entry_table;
    devct[dev].fast_req = 0;
    rc = NW_SUCCESS;
  }
  return rc;
}
  
void nc_slow_sweep() {
  int dev;

  for (dev = 0; dev < MAX_DEV; dev++) {
    if (devct[dev].status == NW_SUCCESS) {
      (*(devct[dev].entry->slow_sweep)) (dev);
    }
  }
}

void nc_fast_timer_set(int dev) {

  devct[dev].fast_req++;
  if (nw_fast_req++ == 0)
    h_fast_timer_set();
}

void nc_fast_timer_reset(int dev) {

  devct[dev].fast_req--;
  if (nw_fast_req-- == 0)
    h_fast_timer_reset();
}


void nc_fast_sweep() {
  int dev;

  for (dev = 0; dev < MAX_DEV; dev++) {
    if (devct[dev].status == NW_SUCCESS &&
	devct[dev].fast_req > 0) {
      devct[dev].fast_req = 0;
      (*(devct[dev].entry->fast_sweep)) (dev);
    }
  }
}

int nc_hash_name(char *cp) {
  int h;
  char ch;
  char *cp_end;

  cp_end = cp + 19;
  *cp_end = '\0';
  h = 0;
  ch = *cp++;
  while (ch != '\0') {
    h = (h << 7) + ch;
    ch = *cp++;
    if (ch != '\0') {
      h = (h << 7) + ch;
      ch = *cp++;
      if (ch != '\0') {
	h = (h << 7) + ch;
	ch = *cp++;
      }
    }
    h %= MAX_HASH;
  }
  return h;
}


nw_result nc_update(nw_update_type up_type, int *up_info) {
  nw_result rc;
  nw_alist_t ad;
  int h, slot, previous_slot, found_slot;
  nw_address_1 n1;
  nw_address_2 n2;

  if (up_type == NW_HOST_ADDRESS_REGISTER) {
    if (nw_free_address == -1) {
      rc = NW_NO_RESOURCES;
    } else {
      slot = nw_free_address;
      ad = &nw_address[slot];
      nw_free_address = ad->nw_next;
      ad->address = *((nw_address_t) up_info);
      h = nc_hash_name(ad->address.name);
      ad->name_next = nw_name[h];
      nw_name[h] = slot;
      h = ad->address.ip_addr % MAX_HASH;
      ad->ip_next = nw_ip[h];
      nw_ip[h] = slot;
      h = (ad->address.nw_addr_1 % MAX_HASH + ad->address.nw_addr_2)
	      % MAX_HASH;
      ad->nw_next = nw_nw[h];
      nw_nw[h] = slot;
      ad->line = 0;
      rc = NW_SUCCESS;
    }
  } else if (up_type == NW_HOST_ADDRESS_UNREGISTER) {
    n1 = ((nw_address_t) up_info)->nw_addr_1;
    n2 = ((nw_address_t) up_info)->nw_addr_2;
    h = (n1 % MAX_HASH + n2) % MAX_HASH;
    slot = nw_nw[h];
    previous_slot = -1;
    ad = &nw_address[slot];
    while (slot != -1 && (ad->address.nw_addr_1 != n1 ||
			  ad->address.nw_addr_2 != n2)) {
      previous_slot = slot;
      slot = ad->nw_next;
      ad = &nw_address[slot];
    }
    if (slot == -1) {
      rc = NW_BAD_ADDRESS;
    } else {
      if (previous_slot == -1)
	nw_nw[h] = ad->nw_next;
      else
	nw_address[previous_slot].nw_next = ad->nw_next;
      ad->nw_next = nw_free_address;
      nw_free_address = slot;
      found_slot = slot;
      if (ad->address.ip_addr != 0) {
	h = ad->address.ip_addr % MAX_HASH;
	slot = nw_ip[h];
	previous_slot = -1;
	while (slot != -1 && slot != found_slot) {
	  previous_slot = slot;
	  slot = nw_address[slot].ip_next;
	}
	if (slot == found_slot) {
	  if (previous_slot == -1)
	    nw_ip[h] = ad->ip_next;
	  else
	    nw_address[previous_slot].ip_next = ad->ip_next;
	}
      }
      if (ad->address.name[0] != '\0') {
	h = nc_hash_name(ad->address.name);
	slot = nw_name[h];
	previous_slot = -1;
	while (slot != -1 && slot != found_slot) {
	  previous_slot = slot;
	  slot = nw_address[slot].name_next;
	}
	if (slot == found_slot) {
	  if (previous_slot == -1)
	    nw_name[h] = ad->name_next;
	  else
	    nw_address[previous_slot].name_next = ad->name_next;
	}
      }
      rc = NW_SUCCESS;
    }
  } else {
    rc = NW_INVALID_ARGUMENT;
  }
  return rc;
}

nw_result nc_lookup(nw_lookup_type lt, int *look_info) {
  nw_result rc;
  nw_address_t addr;
  nw_alist_t ad;
  int h, slot;
  ip_address ip;
  nw_address_1 n1;
  nw_address_2 n2;

  if (lt == NW_HOST_ADDRESS_LOOKUP) {
    addr = (nw_address_t) look_info;
    if (addr->ip_addr != 0) {
      ip = addr->ip_addr;
      h = ip % MAX_HASH;
      slot = nw_ip[h];
      ad = &nw_address[slot];
      while (slot != -1 && ad->address.ip_addr != ip) {
	slot = ad->ip_next;
	ad = &nw_address[slot];
      }
      if (slot != -1) {
	strcpy(addr->name, ad->address.name);
	addr->nw_addr_1 = ad->address.nw_addr_1;
	addr->nw_addr_2 = ad->address.nw_addr_2;
	return NW_SUCCESS;
      }
    }
    if (addr->name[0] != '\0') {
      h = nc_hash_name(addr->name);
      slot = nw_name[h];
      ad = &nw_address[slot];
      while (slot != -1 && strcmp(ad->address.name, addr->name) != 0) {
	slot = ad->name_next;
	ad = &nw_address[slot];
      }
      if (slot != -1) {
	addr->ip_addr = ad->address.ip_addr;
	addr->nw_addr_1 = ad->address.nw_addr_1;
	addr->nw_addr_2 = ad->address.nw_addr_2;
	return NW_SUCCESS;
      }
    }
    if (addr->nw_addr_1 != 0 || addr->nw_addr_2 != 0) {
      n1 = addr->nw_addr_1;
      n2 = addr->nw_addr_2;
      h = (n1 % MAX_HASH + n2) % MAX_HASH;
      slot = nw_nw[h];
      ad = &nw_address[slot];
      while (slot != -1 && (ad->address.nw_addr_1 != n1 ||
			    ad->address.nw_addr_2 != n2)) {
	slot = ad->nw_next;
	ad = &nw_address[slot];
      }
      if (slot != -1) {
	strcpy(addr->name, ad->address.name);
	addr->ip_addr = ad->address.ip_addr;
	return NW_SUCCESS;
      }
    }
    rc = NW_BAD_ADDRESS;
  } else {
    rc = NW_INVALID_ARGUMENT;
  }
  return rc;
}

nw_result nc_line_update(nw_peer_t peer, nw_ep line) {
  nw_result rc;
  nw_alist_t ad;
  int h, slot;
  nw_address_1 n1;
  nw_address_2 n2;

  n1 = peer->rem_addr_1;
  n2 = peer->rem_addr_2;
  h = (n1 % MAX_HASH + n2) % MAX_HASH;
  slot = nw_nw[h];
  ad = &nw_address[slot];
  while (slot != -1 && (ad->address.nw_addr_1 != n1 ||
			ad->address.nw_addr_2 != n2)) {
    slot = ad->nw_next;
    ad = &nw_address[slot];
  }
  if (slot == -1) {
    rc = NW_FAILURE;
  } else {
    ad->line = line;
    rc = NW_SUCCESS;
  }
  return rc;
}

nw_ep nc_line_lookup(nw_peer_t peer) {
  nw_ep lep;
  nw_alist_t ad;
  int h, slot;
  nw_address_1 n1;
  nw_address_2 n2;

  n1 = peer->rem_addr_1;
  n2 = peer->rem_addr_2;
  h = (n1 % MAX_HASH + n2) % MAX_HASH;
  slot = nw_nw[h];
  ad = &nw_address[slot];
  while (slot != -1 && (ad->address.nw_addr_1 != n1 ||
			ad->address.nw_addr_2 != n2)) {
    slot = ad->nw_next;
    ad = &nw_address[slot];
  }
  if (slot == -1) {
    lep = -1;
  } else {
    lep = ad->line;
  }
  return lep;
}

nw_result nc_endpoint_allocate(nw_ep_t epp, nw_protocol protocol,
			       nw_acceptance accept,
			       char *buffer_address, u_int buffer_size) {
  nw_result rc;
  nw_ep ep;
  nw_ecb_t ecb;

  if (ect[(ep = *epp)].state != NW_INEXISTENT) {
    rc = NW_BAD_EP;
  } else if (nw_free_ep_first == 0) {
    *epp = nw_free_line_first;
    rc = NW_NO_EP;
  } else {
    if (ep == 0) {
      ecb = &ect[nw_free_ep_first];
      *epp = ep = ecb->id;
      nw_free_ep_first = ecb->next;
      if (nw_free_ep_first == 0)
	nw_free_ep_last = 0;
    } else {
      ecb = &ect[ep];
      if (ecb->previous == 0)
	nw_free_ep_first = ecb->next;
      else
	ect[ecb->previous].next = ecb->next;
      if (ecb->next == 0)
	nw_free_ep_last = ecb->previous;
      else
	ect[ecb->next].previous = ecb->previous;
    }
    if (protocol == NW_LINE) {
      if (nw_free_line_last == 0)
	nw_free_line_first = ep;
      else
	ect[nw_free_line_last].next = ep;
      ecb->previous = nw_free_line_last;
      ecb->next = 0;
      nw_free_line_last = ep;
    }
    ecb->protocol = protocol;
    ecb->accept = accept;
    ecb->state = NW_UNCONNECTED;
    ecb->conn = NULL;
    ecb->buf_start = buffer_address;
    ecb->buf_end = buffer_address + buffer_size;
    ecb->free_buffer = (nw_unused_buffer_t) buffer_address;
    ecb->free_buffer->buf_used = 0;
    ecb->free_buffer->buf_length = buffer_size;
    ecb->free_buffer->previous = NULL;
    ecb->free_buffer->next = NULL;
    ecb->overrun = 0;
    ecb->seqno = 0;
    ecb->tx_first = NULL;
    ecb->tx_last = NULL;
    ecb->tx_initial = NULL;
    ecb->tx_current = NULL;
    ecb->rx_first = NULL;
    ecb->rx_last = NULL;
    rc = NW_SUCCESS;
  }
  return rc;
}

nw_result nc_endpoint_deallocate(nw_ep ep) {
  nw_ecb_t ecb;
  nw_rx_header_t rx_header;

  ecb = &ect[ep];
  if (ecb->conn != NULL) 
    nc_peer_deallocate(ecb->conn);
  if (ecb->tx_first != NULL)
    nc_tx_header_deallocate(ecb->tx_first);
  if (ecb->tx_initial != NULL)
    nc_tx_header_deallocate(ecb->tx_initial);
  while (ecb->rx_first != NULL) {
    rx_header = ecb->rx_first;
    ecb->rx_first = rx_header->next;
    nc_rx_header_deallocate(rx_header);
  }
  if (ecb->protocol == NW_LINE) {
    if (ecb->previous == 0)
      nw_free_line_first = ecb->next;
    else
      ect[ecb->previous].next = ecb->next;
    if (ecb->next == 0)
      nw_free_line_last = ecb->previous;
    else
      ect[ecb->next].previous = ecb->previous;
  }
  ecb->next = 0;
  ecb->previous = nw_free_ep_last;
  if (nw_free_ep_last == 0)
    nw_free_ep_first = ep;
  else
    ect[nw_free_ep_last].next = ep;
  nw_free_ep_last = ep;
  ecb->id = ep;
  ecb->state = NW_INEXISTENT;
  return NW_SUCCESS;
}

void nc_buffer_coalesce(nw_ecb_t ecb) {
  nw_unused_buffer_t p, q, buf_free, buf_start, buf_end;

  buf_start = p = (nw_unused_buffer_t) ecb->buf_start;
  buf_end = (nw_unused_buffer_t) ecb->buf_end;
  buf_free = NULL;
  while (p >= buf_start && p < buf_end) {
    if (p->buf_length & 0x3)
      goto trash_area;
    if (p->buf_used) {
      p = (nw_unused_buffer_t) ((char *) p + p->buf_length);
    } else {
      q = (nw_unused_buffer_t) ((char *) p + p->buf_length);
      while (q >= buf_start && q < buf_end && !q->buf_used) {
	if (q->buf_length & 0x3)
	  goto trash_area;
	p->buf_length += q->buf_length;
	q = (nw_unused_buffer_t) ((char *) q + q->buf_length);
      }
      p->next = buf_free;
      p->previous = NULL;
      if (buf_free != NULL)
	buf_free->previous = p;
      buf_free = p;
      p = q;
    }
  }
  ecb->free_buffer = buf_free;
  return;

 trash_area:
  ecb->free_buffer = NULL;
  return;
}

  
nw_buffer_t nc_buffer_allocate(nw_ep ep, u_int size) {
  nw_ecb_t ecb;
  nw_unused_buffer_t buf, buf_start, buf_end;

  ecb = &ect[ep];
  buf_start = (nw_unused_buffer_t) ecb->buf_start;
  buf_end = (nw_unused_buffer_t) (ecb->buf_end - sizeof(nw_buffer_s));
  if (size < sizeof(nw_buffer_s))
    size = sizeof(nw_buffer_s);
  else
    size = ((size + 3) >> 2) << 2;
  buf = ecb->free_buffer;
  if (buf != NULL) {
    while (buf->buf_length < size) {
      buf = buf->next;
      if (buf < buf_start || buf > buf_end || ((int) buf & 0x3)) {
	buf = NULL;
	break;
      }
    }
  }
  if (buf == NULL) {
    nc_buffer_coalesce(ecb);
    buf = ecb->free_buffer;
    while (buf != NULL && buf->buf_length < size)
      buf = buf->next;
  }
  if (buf == NULL) {
    ecb->overrun = 1;
  } else {
    if (buf->buf_length < size + sizeof(nw_buffer_s)) {
      if (buf->previous == NULL)
	ecb->free_buffer = buf->next;
      else
	buf->previous->next = buf->next;
      if (buf->next != NULL)
	buf->next->previous = buf->previous;
    } else {
      buf->buf_length -= size;
      buf = (nw_unused_buffer_t) ((char *) buf + buf->buf_length);
      buf->buf_length = size;
    }
    buf->buf_used = 1;
  }
  return (nw_buffer_t) buf;
}

nw_result nc_buffer_deallocate(nw_ep ep, nw_buffer_t buffer) {
  nw_ecb_t ecb;
  nw_unused_buffer_t buf;

  ecb = &ect[ep];
  buf = (nw_unused_buffer_t) buffer;
  buf->buf_used = 0;
  buf->previous = NULL;
  buf->next = ecb->free_buffer;
  if (ecb->free_buffer != NULL)
    ecb->free_buffer->previous = buf;
  ecb->free_buffer = buf;
  return NW_SUCCESS;
}

nw_result nc_endpoint_status(nw_ep ep, nw_state_t state, nw_peer_t peer) {
  nw_result rc;
  nw_ecb_t ecb;

  ecb = &ect[ep];
  *state = ecb->state;
  if (ecb->conn)
    *peer = ecb->conn->peer;
  if (ecb->overrun) {
    ecb->overrun = 0;
    rc = NW_OVERRUN;
  } else if (ecb->rx_first != NULL) {
    rc = NW_QUEUED;
  } else {
    rc = NW_SUCCESS;
  }
  return rc;
}


nw_result nc_local_send(nw_ep ep, nw_tx_header_t header, nw_options options) {
  nw_result rc;
  nw_ep receiver;
  int length;
  nw_buffer_t buffer;
  nw_tx_header_t first_header;
  nw_rx_header_t rx_header;
  char *bufp;
  nw_ecb_t ecb;

  receiver = header->peer.remote_ep;
  length = header->msg_length;
  buffer = nc_buffer_allocate(receiver, sizeof(nw_buffer_s) + length);
  if (buffer == NULL) {
    rc = NW_OVERRUN;
  } else {
    buffer->buf_next = NULL;
    buffer->block_offset = sizeof(nw_buffer_s);
    buffer->block_length = length;
    buffer->peer.rem_addr_1 = NW_NULL << 28;
    buffer->peer.rem_addr_2 = 0;
    buffer->peer.remote_ep = ep;
    buffer->peer.local_ep = receiver;
    bufp = (char *) buffer + sizeof(nw_buffer_s);
    first_header = header;
    while (header != NULL) {
      length = header->block_length;
      bcopy(header->block, bufp, length);
      bufp += length;
      if (header->buffer != NULL) 
	nc_buffer_deallocate(ep, header->buffer);
      header = header->next;
    }
    nc_tx_header_deallocate(first_header);
    ecb = &ect[receiver];
    if (options == NW_URGENT) {
      buffer->msg_seqno = 0;
      if (nc_deliver_result(receiver, NW_RECEIVE_URGENT, (int) buffer))
	rc = NW_SUCCESS;
      else
	rc = NW_NO_RESOURCES;
    } else {
      if (ecb->seqno == 1023)
	buffer->msg_seqno = ecb->seqno = 1;
      else
	buffer->msg_seqno = ++ecb->seqno;
      if (nc_deliver_result(receiver, NW_RECEIVE, (int) buffer)) 
	rc = NW_SUCCESS;
      else
	rc = NW_NO_RESOURCES;
    }
  }
  return rc;
}

nw_buffer_t nc_local_rpc(nw_ep ep, nw_tx_header_t header, nw_options options) {
  nw_buffer_t buf;
  nw_ecb_t ecb;
  nw_rx_header_t rx_header;

  ecb = &ect[ep];
  rx_header = ecb->rx_first;
  if (nc_local_send(ep, header, options) != NW_SUCCESS) {
    buf = NW_BUFFER_ERROR;
  } else if (rx_header == NULL) {
    buf = NULL;
  } else {
    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;
}
			 

nw_result nc_succeed(int dev) {

  return NW_SUCCESS;
}
			      
void nc_null(int dev) {

}

nw_result nc_fail(int dev) {

  return NW_FAILURE;
}

int nc_null_poll(int dev) {
  
  return 1000000;
}

nw_result nc_null_send(nw_ep ep, nw_tx_header_t header, nw_options options) {

  return NW_FAILURE;
}

nw_buffer_t nc_null_rpc(nw_ep ep, nw_tx_header_t header, nw_options options) {

  return NW_BUFFER_ERROR;
}

void nc_null_signal(nw_buffer_t msg) {

}

nw_result nc_open_fail(nw_ep lep, nw_address_1 a1,
		       nw_address_2 a2, nw_ep rep) {

  return NW_FAILURE;
}

nw_result nc_close_fail(nw_ep ep) {

  return NW_FAILURE;
}

nw_result nc_accept_fail(nw_ep ep, nw_buffer_t msg, nw_ep_t epp) {

  return NW_FAILURE;
}