RCS_ID_C="$Id: if_sana.c,v 3.2 1994/02/03 19:12:08 ppessi Exp $";
/*
 * Copyright (c) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>,
 *                    Helsinki University of Technology, Finland.
 *                    All rights reserved.
 *
 * if_sana.c --- Generic Interface Routines for Sana II Drivers
 *
 * Created      : Thu Feb 11 13:41:25 1993 ppessi
 * Last modified: Thu Feb  3 17:47:18 1994 ppessi
 *
 * HISTORY
 * $Log: if_sana.c,v $
 * Revision 3.2  1994/02/03  19:12:08  ppessi
 * Changed ssconfig_make() arguments.
 *
 * Revision 3.1  1994/02/03  03:50:38  ppessi
 * Initially tested interface database
 *
 * Revision 1.31  1993/12/21  22:13:35  jraja
 * Changed sana2 tracking not to be done to an interface if configured so.
 * This is to get around a bug in CBM a2060.device.
 *
 * Revision 1.30  1993/11/14  21:15:19  jraja
 * Changed IPTOS_LOWDELAY check to bitwise and (was == including other fields).
 *
 * Revision 1.29  1993/11/06  23:39:15  ppessi
 * Automatically puts interface up when device returns to online state.
 * The LOWDELAY IP packets are given higher priority IO requests.
 *
 * Revision 1.28  1993/10/11  20:31:46  jraja
 * Added explicit prototype for the CheckIO(), which is prototyped
 * incorrectly in the <clib/exec_protos.h>.
 *
 */

#include <conf.h>

#include <sys/param.h>
#include <sys/cdefs.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/systm.h>
#include <sys/syslog.h>

#include <kern/amiga_includes.h>

#include <sys/synch.h>
#include <sys/time.h>

#include <net/if.h>
#include <net/netisr.h>

#define NDEBUG
#include <assert.h>

#if INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#endif

#if NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif

#include <net/if_sana.h>
#include <net/sana2arp.h>

#include <net/sana2config.h>
#include <net/sana2request.h>
#include <net/sana2errno.h>

#if __SASC
#include <proto/dos.h>
#elif __GNUC__
#include <inline/dos.h>
#else
#error Your compiler is not supported in this release.
#endif

/* Correct prototype for the CheckIO.
 * (The one in clib/exec_protos.h has wrong return value type: BOOL (16 bits) 
 * instead of a pointer (32 bits)!)
 */
struct IORequest * CheckIO(struct IORequest *req);

#define ARP_MTU (sizeof(struct s2_arppkt))

int debug_sana = 1;

/* Global port for all SANA-II network interfaces */
struct MsgPort *SanaPort = NULL;

/* queue for sana network interfaces */
struct sana_softc *ssq = NULL;

/* These are wire type dependant parameters of 
 * Sana-II Network Interface
 */
extern struct wiretype_parameters wiretype_table[];

/* 
 * Local prototypes
 */
static struct ifnet *iface_make(struct ssconfig *ifc);
static void sana_run(struct sana_softc *ssc, int requests, struct ifaddr *ifa);
static void sana_unrun(struct sana_softc *ssc);
static void sana_up(struct sana_softc *ssc);
static BOOL sana_down(struct sana_softc *ssc);
static struct mbuf *
sana_read(struct sana_softc *ssc, struct IOIPReq *req, 
	  UWORD  flags, UWORD *sent, const char *banner, size_t mtu);
static void sana_ip_read(struct sana_softc *ssc, struct IOIPReq *req);
static void sana_arp_read(struct sana_softc *ssc, struct IOIPReq *req);
static void sana_online(struct sana_softc *ssc, struct IOIPReq *req);
static void free_written_packet(struct sana_softc *ssc, struct IOIPReq *req);

/*
 * Initialize Sana-II interface
 *
 * This routine creates needed message port for Sana-II IO
 * It returns our signal mask, or 0L on an error.
 */
ULONG 
sana_init(void)
{
  assert(!SanaPort);

  SanaPort = CreateMsgPort();	/* V36 function, creates a PA_SIGNAL port */

  if (SanaPort) {
    SanaPort->mp_Node.ln_Name = (void *)"sana_if.port";
    loattach();
    return (ULONG) 1 << SanaPort->mp_SigBit;
  }

  return 0L;
}

/*
 * Clean up Sana-II Interfaces
 *
 * Note: main interface queue is SNAFU after deinitializing
 */
void 
sana_deinit(void)
{
  struct sana_softc *ssc; 
  struct IOSana2Req *req;

  assert(SanaPort);

  while (ssq) {
    sana_down(ssq);
    if (ssq->ss_if.if_flags & IFF_RUNNING) {
      sana_unrun(ssq);
    }
    ssc = ssq;
    ssq = ssc->ss_next;
    /* Close device */ 
    req = CreateIOSana2Req(ssc);
    if (req) {
      CloseDevice((struct IORequest*)req);
      DeleteIOSana2Req(req);
    } else {
      log(LOG_ERR, "sana_deinit(): Couldn't close device %s\n",
	  ssc->ss_name);
    }
  }

  if (SanaPort) {
    /* Clear possible pending signals */
    SetSignal(1<<SanaPort->mp_SigBit, 0L);
    DeleteMsgPort(SanaPort);
    SanaPort = NULL;
  }
}

/*
 * sana_poll()
 *  This routine polls SanaPort and processes replied
 *  requests appropriately
 */
BOOL
sana_poll(void)
{
  struct IOIPReq * io;
  spl_t s = splnet();

  while (io = (struct IOIPReq *)GetMsg(SanaPort)) {
    /* touch the network interface */
    get_time(&io->ioip_if->ss_if.if_lastchange);
    if (io->ioip_dispatch) {
      (*io->ioip_dispatch)(io->ioip_if, io);
     } else {
       log(LOG_ERR, "No dispatch function in request for %s\n",
	   io->ioip_if->ss_name);
     }
  }

  net_poll();

  splx(s);

  return FALSE;
}

#ifdef COMPAT_AMITCP2
/*
 * Name points to the full device name.
 * Device name is a legal DOS file name,
 * appended with a slash and a decimal unit number
 *
 * Some explanation on the device names:
 * There is a DOS wrapper around Exec OpenDevice() function.
 * The device is first searched from the Exec list, if that fails
 * DOS tries to load the segment file with the device name. 
 * If that fails too, the filename is catenated to string "DEVS:" and
 * DOS tries again. 
 *
 * AmiTCP uses internally only the Exec device name (ie. device name
 * without pathpart)
 */

/*
 * Map exec device name to
 * interface structure pointer.
 */
struct ifnet *aifunit(register char *name)
{
  register char *cp;
  register struct ifnet *ifp;
  long unit;
  unsigned len;
  char *ep, c;

  /* AmigaTCP/IP uses the slash as unit number separator 
   * because Exec device name may contain digits.
   */
  char *up;
  cp = ep = name - 1;
  /* Find pathpart */
  for (up = name; *up; up++) 
    if (*up == '/' || *up == ':') {
      cp = ep;
      ep = up;
    }
  /* Name is too long, or there is no unit number */
  if (up >= cp + IFNAMSIZ || cp == ep)	
    return ((struct ifnet *)0);
  cp++;

  /*
   * cp points first char in device name,
   * ep to unit number separator ('/')
   * and up to NUL ('\0') at the end of string
   */
  len = ep - cp;
  c = *ep;
  *ep = '\0';			/* sentinel */
  for (unit = 0, up--; *up >= '0' && *up <= '9'; up--) 
    unit = unit * 10 + *up - '0';
  if (up != ep) {
    *ep = c;
    return NULL;
  }

  /* Pathpart is not included in search */
  for (ifp = ifnet; ifp; ifp = ifp->if_next) {
    if (bcmp(ifp->if_name, cp, len))
      continue;
    if (unit == ifp->if_unit)
      break;
  }
  {
    extern struct ifnet *aiface_find(char *, long unit);
    *ep = '\0';			/* sentinel */
    if (ifp == 0)
      ifp = aiface_find(name, unit);
    *ep = c;
  }
  return (ifp);
}

struct ifnet *
aiface_find(char *name, long unit)
{
  struct  = sana2tag_find_exec(name, unit);

  /* No alias found, use defaults */
  if (sifp == NULL) {
    static short sana_units = 0;
    struct interface_parameters sifp[1];
    const static long tag_end = TAG_END;

    sifp->ifname = "sana";
    sifp->unit = sana_units++;
    sifp->execname = name;
    sifp->execunit = unit;
    sifp->tags = (struct TagItem *)&tag_end;
    return make_iface(sifp, sifp->unit);
  }
  return make_iface(sifp, sifp->unit);
}
#endif

/*
 * This function strategically plugs into ifunit(), and it is called
 * on a non-existant interface.  We try to look it up, and if successful
 * initialize a descriptor and call if_attach() with it.
 *
 * Name is Unix kernel device name,
 * we convert it to Exec device and unit.
 */
struct ifnet *
iface_find(char *name, short unit)
{
  struct ssconfig *ifc = ssconfig_make(SSC_ALIAS, name, unit);
  
  if (ifc) {
    struct ifnet *ifp = iface_make(ifc);
    ssconfig_free(ifc);
    return ifp;
  }
  return NULL;
}

static struct ifnet *
iface_make(struct ssconfig *ifc)
{
  register struct sana_softc *ssc = NULL;
  register struct IOSana2Req *req;
  struct Sana2DeviceQuery devicequery;

  /* Allocate the request for opening the device */
  if ((req = CreateIOSana2Req(NULL)) == NULL) 
    log(LOG_ERR, "iface_find(): CreateIOSana2Req failed\n");
  else {
    req->ios2_BufferManagement = buffermanagement;

    if (OpenDevice(ifc->args->a_dev, *ifc->args->a_unit, 
		   (struct IORequest *)req, 0L)) {
      sana2perror("OpenDevice", req);
    } else {
      /* Ask for our type, address length, MTU
       * Obl. bitch: nobody tells, WHO is supplying
       * DevQueryFormat and DeviceLevel
       */
      req->ios2_Req.io_Command   = S2_DEVICEQUERY;
      req->ios2_StatData         = &devicequery;
      devicequery.SizeAvailable  = sizeof(devicequery);
      devicequery.DevQueryFormat = 0L;
      
      DoIO((struct IORequest *)req);
      if (req->ios2_Req.io_Error)
	sana2perror("S2_DEVICEQUERY", req);
      else {
	/* Get Our Station address */
	req->ios2_StatData = NULL;
	req->ios2_Req.io_Command = S2_GETSTATIONADDRESS;
	DoIO((struct IORequest *)req);
	
	if (req->ios2_Req.io_Error)
	  sana2perror("S2_GETSTATIONADDRESS", req);
	else {
	  req->ios2_Req.io_Command = 0;
	  
	  /* Allocate the interface structure */
	  ssc = (struct sana_softc *)
	    bsd_malloc(sizeof(*ssc) + strlen(ifc->args->a_dev) + 1,
		       M_IFNET, M_WAITOK);
	  if (!ssc)
	    log(LOG_ERR, "iface_find: out of memory\n");
	  else {
	    aligned_bzero_const(ssc, sizeof(*ssc));
	    
	    /* Save request pointers */
	    ssc->ss_dev     = req->ios2_Req.io_Device;
	    ssc->ss_unit    = req->ios2_Req.io_Unit;
	    ssc->ss_bufmgnt = req->ios2_BufferManagement;
	    
	    /* Address must be full bytes */
	    ssc->ss_if.if_addrlen  = (devicequery.AddrFieldSize + 7) >> 3;
	    bcopy(req->ios2_DstAddr, ssc->ss_hwaddr, ssc->ss_if.if_addrlen);
	    ssc->ss_if.if_mtu      = devicequery.MTU;
	    ssc->ss_maxmtu         = devicequery.MTU;
	    ssc->ss_if.if_baudrate = devicequery.BPS;
	    ssc->ss_hwtype         = devicequery.HardwareType;	
	    
	    /* These might be different on different hwtypes */
	    ssc->ss_if.if_output = sana_output;
	    ssc->ss_if.if_ioctl  = sana_ioctl;

	    /* Initialize */ 
	    ssconfig(ssc, ifc);
	    
	    NewList((struct List*)&ssc->ss_freereq);

	    if_attach((struct ifnet*)ssc);
	    ifinit();
	    
	    ssc->ss_next = ssq;
	    ssq = ssc;
	  }
	}
      }
      if (!ssc)
	CloseDevice((struct IORequest *)req);
    }    
    DeleteIOSana2Req(req);
  }

  return (struct ifnet *)ssc;
}

/*
 * Allocate Sana-II IORequests for TCP/IP process
 */
static void
sana_run(struct sana_softc *ssc, int requests, struct ifaddr *ifa)
{
  int i;
  spl_t s = splimp();
  struct IOIPReq *req, *next = ssc->ss_reqs;
  
  /*
   * Configure the Sana-II device driver
   * (now with factory address)
   */
  if ((ssc->ss_if.if_flags & IFF_RUNNING) == 0) {
    struct IOSana2Req *req;

    if (req = CreateIOSana2Req(ssc)) {
      req->ios2_Req.io_Command = S2_CONFIGINTERFACE;
      bcopy(ssc->ss_hwaddr, req->ios2_SrcAddr, ssc->ss_if.if_addrlen);

      DoIO((struct IORequest*)req);
      if (req->ios2_Req.io_Error)
	sana2perror("S2_CONFIGINTERFACE", req);

      if (req->ios2_Req.io_Error == 0 ||
	  req->ios2_WireError == S2WERR_IS_CONFIGURED) {
	/* Mark us as running */
	ssc->ss_if.if_flags |= IFF_RUNNING;
	if (ssc->ss_cflags & SSF_TRACK) {
#ifdef INET
	  /* Ask for packet type specific statistics */
	  req->ios2_Req.io_Command = S2_TRACKTYPE;
	  req->ios2_PacketType = ssc->ss_ip.type;
	  DoIO((struct IORequest*)req);
	  /* It is *not* safe to turn tracking off */
	  if (req->ios2_Req.io_Error && 
	      req->ios2_WireError != S2WERR_ALREADY_TRACKED) 
	    sana2perror("S2_TRACKTYPE for IP", req);
	  if (ssc->ss_arp.reqno) {
	    req->ios2_Req.io_Command = S2_TRACKTYPE;
	    req->ios2_PacketType = ssc->ss_arp.type;
	    DoIO((struct IORequest*)req);
	    if (req->ios2_Req.io_Error  && 
		req->ios2_WireError != S2WERR_ALREADY_TRACKED) 
	      sana2perror("S2_TRACKTYPE for ARP", req);
	  }
#endif	
	}
      }
      DeleteIOSana2Req(req);
    }
  }

  if ((ssc->ss_if.if_flags & IFF_RUNNING)) {
    /* Initialize ioRequests, add them into free queue */
    for (i = 0; i < requests ; i++) {
      if (!(req = CreateIORequest(SanaPort, sizeof(*req))))
	break;
      req->ioip_s2.ios2_Req.io_Device    = ssc->ss_dev;    
      req->ioip_s2.ios2_Req.io_Unit      = ssc->ss_unit;   
      req->ioip_s2.ios2_BufferManagement = ssc->ss_bufmgnt;
      aligned_bcopy(ssc->ss_hwaddr, req->ioip_s2.ios2_SrcAddr, ssc->ss_if.if_addrlen);
      req->ioip_s2.ios2_Req.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
      req->ioip_s2.ios2_Data = req;
      req->ioip_if = ssc;
      req->ioip_next = next;
      AddTail((struct List*)&ssc->ss_freereq, (struct Node*)req);
      next = req;
    }
    ssc->ss_reqs = next;
  }
  splx(s);
}

/*
 * Free Sana-II IO Requests 
 * Note: this is protected by splimp();
 */
static void
sana_unrun(struct sana_softc *ssc)
{
  struct IOIPReq *req, *next;
  
  for ( next = ssc->ss_reqs; req = next ;) {
    next = req -> ioip_next;
    WaitIO((struct IORequest *)req);
    DeleteIORequest(req);
  }
  ssc->ss_reqs = next;
  
  ssc->ss_if.if_flags &= ~IFF_RUNNING;
}

/*
 * Generic SANA-II interface ioctl
 *
 * Interface setup is thru IOCTL.
 */
int 
sana_ioctl(register struct ifnet *ifp, int cmd, caddr_t data)
{
  register struct sana_softc *ssc = (struct sana_softc*)ifp;
  register struct ifaddr *ifa = (struct ifaddr *)data;
  register struct ifreq *ifr = (struct ifreq *)data;
  
  spl_t s = splimp();
  int error = 0;

  switch (cmd) {

  case SIOCSIFFLAGS:
    if ((ifr->ifr_flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING))
      sana_up(ssc);
    /* Call sana_down() in every case */
    if ((ifr->ifr_flags & IFF_UP) == 0) 
      sana_down(ssc);
    if ((ifr->ifr_flags & IFF_NOARP) == 0)
      alloc_arptable(ssc, 0);
    break;

    /*
     * Set interface address (and mark interface up).
     */
  case SIOCSIFADDR:		/* Set Interface Address */
    if (!(ssc->ss_if.if_flags & IFF_RUNNING)) 
      sana_run(ssc, ssc->ss_reqno, ifa);
    if ((ssc->ss_if.if_flags & IFF_RUNNING) && !(ssc->ss_if.if_flags & IFF_UP))
      sana_up(ssc);
    if ((ifr->ifr_flags & IFF_NOARP) == 0)
      alloc_arptable(ssc, 0);
    
  case SIOCAIFADDR:		/* Alter Interface Address */
    switch (ifa->ifa_addr->sa_family) {
#if INET
    case AF_INET:
      ssc->ss_ipaddr = IA_SIN(ifa)->sin_addr;
      break;
#endif
    }
    break;

  case SIOCSIFDSTADDR:		/* Sets P-P-link destination address */
    break;

  default:
    error = EINVAL;
    break;
  }
  splx(s);
  return (error);
}

/*
 * sana_send_read(): 
 * send read requests with given types, dispatcher & c  
 * MUST be called at splimp()
 */
static inline WORD 
sana_send_read(struct sana_softc *ssc, WORD count, ULONG type, ULONG mtu,
	       void (*dispatch)(struct sana_softc *, struct IOIPReq *),
	       UWORD command, UBYTE flags)
{
  struct IOIPReq *req = NULL;
  WORD i;

  for (i = 0; i < count; i++) {
    if (!(req = (struct IOIPReq*)RemHead((struct List*)&ssc->ss_freereq)))
      return i;
    req->ioip_dispatch = dispatch;
    req->ioip_s2.ios2_PacketType = type;
    req->ioip_Command = command;
    req->ioip_s2.ios2_Req.io_Flags = flags;
    if (!ioip_alloc_mbuf(req, mtu))
      goto no_resources;
    BeginIO((struct IORequest*)req);
  }
  return i;

 no_resources:
  if (req)
    AddHead((struct List*)&ssc->ss_freereq, (struct Node*)req);
  log(LOG_ERR, "sana_up: could not queue enough read requests\n");
  return i;
}

/*
 * sana_up():
 * send read requests
 */
static void
sana_up(struct sana_softc *ssc)
{
  spl_t s = splimp();
  ssc->ss_if.if_flags |= IFF_UP;
  
  /* Send read requests to device driver */
#if	INET
  /* IP */
  ssc->ss_ip.sent += 
    sana_send_read(ssc, ssc->ss_ip.reqno - ssc->ss_ip.sent, ssc->ss_ip.type,
		   ssc->ss_if.if_mtu, sana_ip_read, CMD_READ, 0);

  ssc->ss_arp.sent += 
    sana_send_read(ssc, ssc->ss_arp.reqno - ssc->ss_arp.sent, ssc->ss_arp.type,
		   ARP_MTU, sana_arp_read, CMD_READ, 0);

#endif /* INET */
#if	ISO
#endif /* ISO */
#if	CCITT
#endif /* CCITT */
#if	NS
#endif /* NS */
#if 0
  ssc->ss_rawsent += 
    sana_send_read(ssc, ssc->ss_rawreqno - ssc->ss_rawsent, 0,
		   ssc->ss_if.if_mtu, sana_raw_read, 
		   S2_READORPHAN, SANA2_IOF_RAW);
#endif
  splx(s);
  return;
}

#if __SASC
/*
 * "Fix" for numerous sana2 drivers, which expect to get Unit * in the
 * register A3 when their AbortIO function is called.
 * Note that Exec AbortIO() does NOT put it there.
 */
extern VOID _AbortSanaIO(struct IORequest *, struct Unit *);
#pragma libcall DeviceBase _AbortSanaIO 24 B902

static inline __asm VOID 
AbortSanaIO(register __a1 struct IORequest *ioRequest)
{
#define DeviceBase ioRequest->io_Device
  _AbortSanaIO(ioRequest, ioRequest->io_Unit);
#undef DeviceBase
}
#else /* implement later for other compilers */
#define AbortSanaIO AbortIO
#endif

/*
 * sana_down(): Mark interface as down, abort all pending requests
 */
static BOOL
sana_down(struct sana_softc *ssc)
{
  spl_t s = splimp();
  struct IOIPReq *req = ssc->ss_reqs;

  /* Completed, Remove()'d requests are not aborted */
  while (req) {
    if (!CheckIO((struct IORequest*)req)) {
      AbortSanaIO((struct IORequest*)req);
    }
    req = req->ioip_next;
  }

  splx(s);

  return(TRUE);
}

/*
 * sana_read: deattach a packet from IORequest
 *            resend the IORequest
 */
static struct mbuf *
sana_read(struct sana_softc *ssc, struct IOIPReq *req, 
	  UWORD  flags, UWORD *sent, const char *banner, size_t mtu)
{
  register struct mbuf *m = req->ioip_packet;
  register spl_t s = splimp();

  req->ioip_packet = NULL;

  switch (req->ioip_Error) {
  case 0:
    if (req->ioip_s2.ios2_Req.io_Flags & SANA2IOF_BCAST) 
      m->m_flags |= M_BCAST;
    if (req->ioip_s2.ios2_Req.io_Flags & SANA2IOF_MCAST)
      m->m_flags |= M_MCAST;
    break;
  case S2ERR_OUTOFSERVICE:
    /*
     * Somebody put Sana-II driver offline.
     * We put down also the network interface 
     */
    if (ssc->ss_if.if_flags & IFF_UP) {
      /* Show a log message */
      sana2perror(ssc->ss_if.if_name, (struct IOSana2Req *)req);

      /* tell it to protocols */
      if_down((struct ifnet *)ssc); 

      /* Free mbufs allocated for packet */
      m_freem(req->ioip_reserved);
      req->ioip_reserved = NULL;

      /* Order an notify when driver is put back online */
      ssc->ss_eventsent++;
      req->ioip_s2.ios2_Req.io_Command = S2_ONEVENT;
      req->ioip_s2.ios2_WireError = S2EVENT_ONLINE;
      req->ioip_dispatch = sana_online;
      BeginIO(req);
      req = NULL;
    }
    ssc->ss_if.if_flags &= ~IFF_UP;
    m_freem(m);
    m = NULL;
    break;
  default:
    if (debug_sana && req->ioip_Error != IOERR_ABORTED) 
      sana2perror(banner, (struct IOSana2Req *)req);
    m_freem(m);
    m = NULL;
  }

  if (ssc->ss_if.if_flags & IFF_UP) {
    /* Return request to the Sana-II driver */
    if (ioip_alloc_mbuf(req, mtu)) {
      req->ioip_s2.ios2_Req.io_Flags = flags;
      BeginIO((struct IORequest*)req); 
      splx(s);
      return m;
    }
    log(LOG_ERR, "sana_read (%s): not enough mbufs\n", ssc->ss_name);
  } 

  /* do not resend, free used resources */
  (*sent)--;
  if (req) {
    m_freem(req->ioip_reserved);
    req->ioip_reserved = NULL;
    req->ioip_dispatch = NULL;
    AddHead((struct List*)&ssc->ss_freereq, (struct Node*)req);
  }

  if (m) {
    m_freem(m);
  }

  splx(s);
  return NULL;
}

/*
 * sana_ip_read(): feed a packet to the IP queue
 * (This routine is called from sana_poll)
 */
static void
sana_ip_read(struct sana_softc *ssc, struct IOIPReq *req)
{
  struct mbuf *m = sana_read(ssc, req, 0, &ssc->ss_ip.sent, "sana_ip_read",
			     ssc->ss_if.if_mtu);
  spl_t s;

  if (m) {
    s = splimp();
    if (IF_QFULL(&ipintrq)) {
      IF_DROP(&ipintrq);
      m_freem(m);
      /* m = NULL; */
    } else {
      /* Set interface pointer (needed for broadcasts) */
      m->m_pkthdr.rcvif = (struct ifnet *)ssc; 
      IF_ENQUEUE(&ipintrq, m);
      /* A signal might be needed if we use PA_EXCEPTION port */
      schednetisr_nosignal(NETISR_IP);
      /* m = NULL; */
    }
    splx(s);
  }
}

/*
 * sana_arp_read(): process an ARP packet
 * (This routine is called from sana_poll)
 */
static void
sana_arp_read(struct sana_softc *ssc, struct IOIPReq *req)
{
  struct mbuf *m; 
  UBYTE hwaddr[MAXADDRSANA];

  bcopy(req->ioip_s2.ios2_SrcAddr, hwaddr, ssc->ss_if.if_addrlen);

  m = sana_read(ssc, req, 0, &ssc->ss_arp.sent, "sana_arp_read", ARP_MTU);

  if (m)
    arpinput(ssc, m, hwaddr);
}

/*
 * sana_online(): process an ONLINE event
 */
static void
sana_online(struct sana_softc *ssc, struct IOIPReq *req)
{
  LONG events = req->ioip_s2.ios2_WireError;

  if (req->ioip_s2.ios2_Req.io_Error == 0 &&
      events & S2EVENT_ONLINE) {
    ssc->ss_eventsent--;
    req->ioip_dispatch = NULL;
    AddHead((struct List*)&ssc->ss_freereq, (struct Node*)req);
    log(LOG_NOTICE, "%s is online again.", ssc->ss_name);
    sana_up(ssc);
    return;
  }

  /* An error? */
  if (debug_sana && req->ioip_Error != IOERR_ABORTED) { 
    sana2perror("sana_online", (struct IOSana2Req *)req);
    req->ioip_s2.ios2_Req.io_Command = S2_ONEVENT;
    req->ioip_s2.ios2_WireError = S2EVENT_ONLINE;
    BeginIO(req);
  } else {
    /* Aborted -- probably because "ifconfig xxx/0 down" */
    ssc->ss_eventsent--;
    req->ioip_dispatch = NULL;
    AddHead((struct List*)&ssc->ss_freereq, (struct Node*)req);
  }
}

/*
 * sana_output: send a packet to Sana-II driver
 */
int
sana_output(struct ifnet *ifp, struct mbuf *m0, 
	    struct sockaddr *dst, struct rtentry *rt)
{
  register struct sana_softc *ssc = (struct sana_softc *)ifp;
  ULONG type;
  int error = 0;
  struct in_addr idst;

  /* If a broadcast, send a copy to ourself too */
  struct mbuf *mcopy = (struct mbuf *)NULL;
  struct IOIPReq *req = NULL;
  register struct mbuf *m = m0;

  int len = m->m_pkthdr.len;
  spl_t s = splimp();

  ifp->if_opackets++;		/* stats */

  /* Check if we are up and running... */
  if ((ssc->ss_if.if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
    error = ENETDOWN;
    goto bad;
  }

  get_time(&ssc->ss_if.if_lastchange);

  /* Get a free Sana-II IO request */
  if (!(req = (struct IOIPReq*)RemHead((struct List*)&ssc->ss_freereq))) {
    error = ENOBUFS;
    goto bad;
  }

  req->ioip_s2.ios2_Req.io_Flags = 0;

  switch (dst->sa_family) {
#if INET
  case AF_INET:
    idst = ((struct sockaddr_in *)dst)->sin_addr;

    /* If the address is not resolved, arpresolve
     * stores the packet to its private queue for
     * later transmit and broadcasts the resolve
     * request packet to the (ether)net.
     * (Now ARP works only with IP and ethernet.)
     */
    if ((ssc->ss_if.if_flags & IFF_NOARP) != IFF_NOARP &&
	/* ssc = network interface 
	 * m = Packet to send 
	 * idst = destination IP address 
	 * ios2_DestAddr = destination hw address 
	 * error = error return
	 */
	!arpresolve(ssc, m, &idst, req->ioip_s2.ios2_DstAddr, &error)) {
      AddHead((struct List*)&ssc->ss_freereq, (struct Node*)req);
      splx(s);
      return (0);
    }
    type = ssc->ss_ip.type;

    /* Send to loopback if we do not hear our broadcasts */
    if ((ssc->ss_if.if_flags & IFF_SIMPLEX) && (m->m_flags & M_BCAST)) {
      mcopy = m_copy(m, 0, (int)M_COPYALL);
      (void) looutput(&ssc->ss_if, mcopy, dst, rt);
    }
    /* Set the message priority */
    req->ioip_s2.ios2_Req.io_Message.mn_Node.ln_Pri =
      (IPTOS_LOWDELAY & mtod(m, struct ip *)->ip_tos) ?
	1 : 0;
    break;
#endif
#if NS
#error NS unimplemented!!!
  case AF_NS:
    type = ssc->ss_nstype;
    /* There is hardware address straight in socket */
    /* Dunno how this works, if we have a P-to-P device */
    bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
	  (caddr_t)req->ioip_s2.ios2_DestAddr, ssc->ss_if.if_addrlen);
    /* Local send */
    if (!bcmp((caddr_t)req->ioip_s2.ios2_DestAddr,
	      (caddr_t)&ns_thishost, ssc->ss_if.if_addrlen)) {
      AddHead(&ssc->ss_freereq, req);
      return (looutput(ifp, m, dst, rt));
    }
    req->ioip_s2.ios2_Req.io_Message.mn_Node.ln_Pri = 0;
    break;
#endif
  case AF_UNSPEC:
    /* Raw packets. Sana-II address (a tuple of type and host)
     * specifies the destination 
     */
    if (type = ((struct sockaddr_sana2*)dst)->ss2_type) {
      bcopy(((struct sockaddr_sana2*)dst)->ss2_host, 
	    req->ioip_s2.ios2_DstAddr, 
	    ssc->ss_if.if_addrlen);
    } else {
      req->ioip_s2.ios2_Req.io_Flags = SANA2IOF_RAW;
      type = 0L;
    }
    req->ioip_s2.ios2_Req.io_Message.mn_Node.ln_Pri = 0;
    break;

#if	ISO
#endif /* ISO */
#if RMP
  case AF_RMP:
#endif

  default: 
    log(LOG_ERR, "%s%ld: can't handle af%ld\n",
	ssc->ss_if.if_name, ssc->ss_if.if_unit, dst->sa_family);
    error = EAFNOSUPPORT; 
    goto bad; 
  }

  /*
   * Queue packet to Sana-II driver
   */
  req->ioip_Command = (m->m_flags & M_BCAST) ? S2_BROADCAST : CMD_WRITE;
  req->ioip_dispatch = free_written_packet;
  req->ioip_packet = m;
  req->ioip_s2.ios2_PacketType = type;
  req->ioip_s2.ios2_DataLength = len;

  BeginIO((struct IORequest*)req);

  /* These statistics are somewhat redundant */
  ifp->if_obytes += len;
  if (m->m_flags & M_BCAST)
    ifp->if_omcasts++;

  splx(s);
  return 0;

 bad:
  if (req)
    AddHead((struct List*)&ssc->ss_freereq, (struct Node*)req);
  ifp->if_oerrors++;

  splx(s);
  if (m)
    m_freem(m);
  return error;
}

/*
 * free_written_packet(): free mbufs of written packet,
 *                        queue IOrequest for reuse
 * (This routine is called from sana_poll)
 */
static void
free_written_packet(struct sana_softc *ssc, struct IOIPReq *req)
{
  spl_t s = splimp();

  if (req->ioip_packet) {
    m_freem(req->ioip_packet);
    req->ioip_packet = NULL;
  }
  req->ioip_dispatch = NULL;
  if (debug_sana && req->ioip_Error)
    sana2perror("sana_output", (struct IOSana2Req *)req);
  AddHead((struct List*)&ssc->ss_freereq, (struct Node*)req);
  splx(s);
}
