Driver for ADAX APC-PCX, an SGS Thompson MK5025 based HDLC board
authorKeith Sklower <sklower@ucbvax.Berkeley.EDU>
Sat, 10 Aug 1991 03:04:13 +0000 (19:04 -0800)
committerKeith Sklower <sklower@ucbvax.Berkeley.EDU>
Sat, 10 Aug 1991 03:04:13 +0000 (19:04 -0800)
SCCS-vsn: sys/i386/isa/if_apx.c 7.1
SCCS-vsn: sys/i386/isa/if_apxreg.h 7.1

usr/src/sys/i386/isa/if_apx.c [new file with mode: 0644]
usr/src/sys/i386/isa/if_apxreg.h [new file with mode: 0644]

diff --git a/usr/src/sys/i386/isa/if_apx.c b/usr/src/sys/i386/isa/if_apx.c
new file mode 100644 (file)
index 0000000..69e3a59
--- /dev/null
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 1982, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ *
+ *     @(#)if_apx.c    7.1 (Berkeley) %G%
+ */
+
+/*
+ * Driver for SGS-THOMSON MK5025 based Link level controller.
+ * The chip will do LAPB in hardware, although this driver only
+ * attempts to use it for HDLC framing.
+ *
+ * Driver written by Keith Sklower, based on lance AMD7990 
+ * driver by Van Jacobsen, and information graciously supplied
+ * by the ADAX corporation of Berkeley, CA.
+ */
+
+#include "apx.h"
+#if NAPX > 0
+
+#include "param.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "ioctl.h"
+#include "errno.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/netisr.h"
+#include "netccitt/x25.h"
+
+#include "apxreg.h"
+
+int    apxprobe(), apxattach(), apxstart(), apx_uprim(), apx_meminit();
+int    apxinit(), x25_ifoutput(), apxioctl(), apxreset();
+void   apx_ifattach(), apxinput(), apxintr(), apxtint(), apaxrint();
+
+struct apx_softc {
+       struct  ifnet apx_if;
+       caddr_t apx_device;             /* e.g. isa_device */
+       u_short apx_csr4;               /* byte gender, set in mach dep code */
+       struct  apc_reg *apx_reg;       /* control regs for both subunits */
+       struct  apc_mem *apx_hmem;      /* Host addr for shared memory */
+       struct  apc_mem *apx_dmem;      /* Device (chip) addr for shared mem */
+       struct  sgcp *apx_sgcp;         /* IO control port for this subunit */
+       struct  apc_modes apx_modes;    /* Parameters, as amended by ioctls */
+       int     apx_rxnum;              /* Last receiver dx we looked at */
+       int     apx_txnum;              /* Last tranmistter dx we stomped on */
+       int     apx_txcnt;              /* Number of packets queued for tx*/
+} apx_softc[2 * NAPX], *apx_lastsoftc = apx_softc;
+
+struct apxstat {
+       int     nulltx;
+       int     pint;
+};
+
+/* default operating paramters for devices */
+struct apc_modes apx_default_modes = {
+ { 1,          /* apm_sgob.lsaddr; */
+   3,          /* apm_sgob.rsaddr; */
+   -SGMTU,     /* apm_sgob.n1; */
+   ((-10)<<8), /* apm_sgob.n2_scale; */
+   -1250,      /* apm_sgob.t1; */
+   -10000,     /* apm_sgob.t3; */
+   -80,                /* apm_sgob.tp; */
+ },
+ 2,            /* apm_txwin; */
+ 5,            /* apm_apxmode; */
+ 0,            /* apm_apxaltmode; */
+ IFT_X25,      /* apm_iftype; */
+};
+
+/* Begin bus & endian dependence */
+
+#include "i386/isa/if_apxreg.h"
+#include "i386/isa/isa_device.h"
+
+struct isa_driver apxdriver = {
+       apxprobe, apxattach, "apx",
+};
+
+#define SG_RCSR(apx, csrnum) \
+        (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1),
+         inw(&(apx->apx_sgcp->sgcp_rdp))
+
+#define SG_WCSR(apx, csrnum, data) \
+        (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1),
+         outw(&(apx->apx_sgcp->sgcp_rdp), data))
+
+#define APX_RCSR(apx, csrname) inb(&(apx->apx_reg->csrname))
+#define APX_WCSR(apx, csrname, data) outb(&(apx->apx_reg->csrname), data)
+
+#define TIMO 10000 /* used in apx_uprim */
+
+apxprobe(id)
+       register struct isa_device *id;
+{
+       int     moffset, subunit, unit = id->id_unit << 1;
+       struct  apc_reg *reg = id->id_iobase;
+       register struct apx_softc *apx = apx_softc + unit;
+
+       /* Set and read DTR defeat in channel 0 to test presence of apc */
+       outb(&reg->axr_altmode, 4);
+       if (inb(&reg->axr_altmode) == 0)
+               return 0;                       /* No board present */
+
+       for (subunit = 0; subunit < 2; subunit++, apx++) {
+               /* Set and read DTR mode to test present of SGS thompson chip */
+               apx->apx_if.if_unit = unit++;
+               apx->apx_sgcp = reg->axr_sgcb + subunit;
+               SG_WCSR(apx, 5, 0x08);
+               if ((SG_RCSR(apx, 5) & 0xff08) != 0x08)) {
+                       apxerror(apx, "no mk5025 for channel", subunit);
+                       continue;
+               }
+               moffset = subunit ? id->id_msize >> 1 : 0;
+               apx->apx_hmem   = (struct apc_mem *) (id->id_maddr + moffset);
+               apx->apx_dmem   = (struct apc_mem *) (moffset);
+               apx->apx_modes  = apx_default_modes;
+               apx->apx_device = (caddr_t) id;
+               apx->apx_reg    = reg;
+               apx->apx_csr4   = 0x0110;       /* no byte swapping for PC-AT */
+       }
+       return 1;
+}
+
+apxattach(id)
+       register struct isa_device *id;
+{
+       int     unit = id->id_unit + id->id_unit;
+
+       apx_ifattach(unit);
+       apx_ifattach(unit + 1);
+       return (0);
+}
+
+/* End bus & endian dependence */
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record.  System will initialize the interface when it is ready
+ * to accept packets.
+ */
+apx_ifattach(unit)
+{
+       register struct ifnet *ifp = &(apx_softc[unit].apx_if);
+       /*
+        * Initialize ifnet structure
+        */
+       if (apx_softc[unit].apx_device == 0)
+               return;
+       ifp->if_name = "apc";
+       ifp->if_mtu = SGMTU;
+       ifp->if_init = apxinit;
+       ifp->if_output = x25_ifoutput;
+       ifp->if_start = apxstart;
+       ifp->if_ioctl = apxioctl;
+       ifp->if_reset = apxreset;
+       ifp->if_type = apx_default_modes.axp_iftype;
+       ifp->if_hdrlen = 5;
+       ifp->if_addrlen = 8;
+       if_attach(ifp);
+}
+/*
+ * Initialization of interface
+ */
+apxinit(unit)
+       int unit;
+{
+       struct ifnet *ifp = &apx_softc[unit].apx_if;
+       int s = splimp();
+
+       ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
+       if (apxreset(unit) && (ifp->if_flags & IFF_UP)) {
+               ifp->if_flags |= IFF_RUNNING;
+               (void)apxstart(ifp);
+       }
+       splx(s);
+       return 0;
+}
+
+apxreset(unit)
+       int     unit;
+{
+       register struct apx_softc *apx = &apx_softc[unit ^ 1];
+       u_char apm_apxmode = 0, apm_apxaltmode = 0;
+#define MODE(m) (m |= apx->apx_modes.m << ((apx->apx_if.if_unit & 1) ? 1 : 0))
+
+       MODE(apm_apxmode);
+       MODE(apm_apxaltmode);
+       apx = apx_softc + unit;
+       MODE(apm_apxmode);
+       MODE(apm_apxaltmode);
+       APX_WCSR(apx, axr_mode, apm_apxmode);
+       APX_WCSR(apx, axr_altmode, apm_apxaltmode);
+       apx->apx_txnum = apx->apx_rxnum = apx->apx_txcnt = apx->apx_rxnt = 0;
+
+       if (apx_uprim(apx, SG_STOP, "stop") ||
+           !(apx->apx_if.if_flags & IFF_UP))
+               return 0;
+       apx_meminit(apx->apc_mem, apx); /* also sets CSR2 */
+       SG_WCSR(apx, 3, (int)apx->apx_dmem);
+       SG_WCSR(apx, 4, apx->apx_csr4);
+       if (apx_uprim(apx, SG_INIT, "init request")) ||
+           apx_uprim(apx, SG_STAT, "status request") ||
+           apx_uprim(apx, SG_TRANS, "transparent mode"))
+               return 0;
+       SG_WCSR(apx, 0, SG_INEA);
+       return 1:
+}
+
+apx_uprim(apx, request, ident)
+       int request;
+       char *ident;
+       register struct apx_softc *apx;
+{
+       register int timo = 0;
+       int reply = SG_RCSR(apx, 1);
+
+       if (reply & x8040)
+               SG_WCRS(1, x8040); /* Magic! */
+       SG_WCSR(apx, 1, request | SG_UAV);
+       do {
+               reply = SG_RCRS(1);
+               if (timo >= TIMO | reply & 0x8000) {
+                       apxerror(apx, ident, reply);
+                       return 1;
+               }
+       } while (reply & SG_UAV);
+       return 0;
+}
+
+apx_meminit(apc, apx)
+       register struct apc_mem *apc;
+       struct apx_softc *apx;
+{
+       register struct apc_mem *apcbase = apx->apx_dmem;
+       register int i;
+#define LOWADDR(e) (((u_long)&(apcbase->(e))) & 0xffff)
+#define HIADDR(e) ((((u_long)&(apcbase->(e))) >> 16) & 0xff)
+#define SET_SGDX(dx, f, a, b, m) \
+       { (dx).sgdx_addr = LOWADDR(a); (dx).sgdx_bcnt = (b);\
+         (dx).sgdx_mcnt = (m); (dx).sgdx_flags = (f) | HIADDR(a); }
+
+       bzero((caddr_t)apc, LOWADDR(apc_rxmd[0]));
+       apc->apc_mode = 0x8040; /* 2 flag spacing, trans mode, 16bit FCS */
+       apc->apc_sgop = apx->apx_modes.apm_sgop;
+       apc->apc_rlen = SG_RLEN | HIADDR(apc_rxmd[0]);
+       apc->apc_rdra = LOWADDR(apc_rxmd[0]);
+       apc->apc_rlen = SG_TLEN | apx->apx_modes.apm_txwin |HIADDR(apc_txmd[0]);
+       apc->apc_tdra = LOWADDR(apc_txmd[0]);
+       SET_SGDX(apc->apc_rxtid, SG_OWN, apc_rxidbuf, -SGMTU, 0);
+       SET_SGDX(apc->apc_txtid, 0, apc_txidbuf, -SGMTU, 0);
+       apc->apc_stathi = HIADDR(apc_sgsb);
+       apc->apc_statlo = LOWADDR(apc_sgsb);
+       for (i = 0; i < SGRBUF; i++)
+                SET_SGDX(apc->apc_rxmd[i], SG_OWN, apc_rbuf[i][0], -SGMTU, 0)
+       for (i = 0; i < SGTBUF; i++)
+                SET_SGDX(apc->apc_txmd[i], SG_TUI, apc_tbuf[i][0], 0, 0)
+       SG_WCSR(apx, 2, SG_UIE | SG_PROM | HIADDR(apc_mode));
+}
+
+/*
+ * Start output on interface.  Get another datagram to send
+ * off of the interface queue, and copy it to the interface
+ * before starting the output.
+ */
+apxstart(ifp)
+       struct ifnet *ifp;
+{
+       register struct apx_softc *apx = &apx_softc[ifp->if_unit];
+       register struct sgdx *dx;
+       struct apc_mem *apc = apx->apx_mem;
+       struct mbuf *m;
+       int len;
+
+       if ((ifp->if_flags & IFF_RUNNING) == 0)
+               return (0);
+       do {
+               dx = apc->apc_txmd + apc->apc_txnum;
+               if (dx->sgdx_flags & SG_OWN)
+                       return (0);
+               IF_DEQUEUE(&ifp->if_snd, m);
+               if (m == 0)
+                       return (0);
+               len = min(m->m_pkthdr.len, SGMTU);
+               m_copydata(m, 0, len, apc->apc_txbuf[apx->apx_txnum]);
+               dx->sgdx_mcnt = -len;
+               dx->sgdx_flags = SG_OWN | SG_TUI | (0xff & dx->sgdx_flags);
+               SG_WCSR(apx, 0, SG_INEA | SG_TDMD);
+               if (++apx->apx_txnum >= SGTBUF)
+                       apx->apx_txnum = 0;
+       } while (++apx->apx_txcnt < SGTBUF);
+       apx->apx_txcnt = SGTBUF;
+       ifp->if_flags |= IFF_OACTIVE;
+       return (0);
+}
+
+apxintr()
+{
+       register struct apx_softc *apx = apx_lastsoftc;
+       struct apx_softc *apxlim = apx_softc + NAPX + NAPX;
+       int reply;
+
+       do {
+               if (apx->ap_if.if_flags & IFF_UP)
+                   /* Try to turn off interrupt cause */
+                   while ((reply = SG_RCSR(apx, 0)) & 0xff) {
+                       SG_WCSR(apx, 0, SG_INEA | 0xfe);
+                       if (reply & (SG_MERR|SG_TUR|SG_ROR)) {
+                               apxerror(apx, "mem, rx, or tx error", reply);
+                               apxinit(apx->apx_if.if_unit);
+                               break;
+                       }
+                       if (reply & SG_RINT)
+                               apxrint(apx);
+                       if (reply & SG_TINT)
+                               apxtint(apx);
+                       if (reply & SG_PINT)
+                               apxstat.pint++;
+               }
+               if (++apx >= apxlim)
+                       apx = apx_softc;
+       } while (apx != apx_lastsoftc);
+}
+
+apxtint(apx)
+       register struct apx_softc *apx;
+{
+       register struct apc_mem *apc = apx->apx_mem;
+       int i, loopcount = 0;
+
+       do {
+               if ((i = apx->apx_txnum - apx->apx_txcnt) < 0)
+                       i += SGTBUF;
+               if (apc->apc_txmd[i].sgdx_flags & SG_OWN) {
+                       if (loopcount)
+                               break;
+                       apxstat.nulltx++;
+                       return;
+               }
+               loopcount++;
+               apx->apx_if.if_flags &= ~IFF_OACTIVE;
+       } while (--apx->apx_txcnt > 0);
+       apxstart(&apx->apx_if);
+}
+
+apxrint(apx)
+       register struct apx_softc *apx;
+{
+       register struct apc_mem *apc = apx->apx_mem;
+       register struct sgdx *dx = apc->apc_rxmd + apx->apx_rxnum;
+#define SGNEXTRXMD \
+dx = ++apx->apx_rxnum == SGRBUF ? &apc->apc_rxmd[apx->apx_rxnum = 0] : dx + 1;
+
+       /*
+        * Out of sync with hardware, should never happen?
+        */
+       if (dx->sgdx_flags & SG_OWN) {
+               apxerror(apx, "out of sync");
+               return;
+       }
+       /*
+        * Process all buffers with valid data
+        */
+       while ((dx->sgdx_flags & SG_OWN) == 0) {
+               if ((dx->sgdx_flags & (SG_SLF|SG_ELF)) != (SG_SLF|SG_ELF)) {
+                       /*
+                        * Find the end of the packet so we can see how long
+                        * it was.  We still throw it away.
+                        */
+                       apxerror(apx, "chained buffer", ds->sgdx_flags);
+                       do {
+                               dx->sgdx_bcnt = 0;
+                               dx->sgdx_flags = SG_OWN | (0xff&dx->sgdx_flags);
+                               SGNEXTRXMD;
+                       } while (!(dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)));
+                       /*
+                        * If search terminated without successful completion
+                        * we reset the hardware (conservative).
+                        */
+                       if ((dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)) !=
+                           SG_ENP) {
+                               apxreset(apx->apx_if.if_unit);
+                               return;
+                       }
+               } else
+                       apxinput(&apx->apx_if, apc->apc_rbuf[apx->apx_rxnum],
+                                       -dx->sgdx_bcnt);
+               dx->sgdx_bcnt = 0;
+               dx->sgdx_flags = SG_OWN | (0xff & dx->sgdx_flags);
+               SGNEXTRXMD;
+       }
+}
+
+apxinput(ifp, buffer, len)
+register struct ifnet *ifp;
+caddr_t buffer;
+{
+       register struct ifqueue *inq;
+       struct mbuf *m, *apxget();
+       extern struct ifqueue hdintrq, ipintrq;
+       int isr;
+
+       ifp->if_ipackets++;
+    {
+       register u_char *cp = (u_char *)buffer;
+
+       if (cp[0] == 0xff && cp[1] == 0x3) {
+               /* This is a UI HDLC Packet, so we'll assume PPP
+                  protocol.  for now, IP only. */
+               buffer += 4;
+               len -= 4;
+               inq = &ipintrq;
+               isr = NETISR_IP;
+       } else {
+               inq = &hdintrq;
+               isr = NETISR_CCITT;
+       }
+    }
+       if (len <= 0)
+               return;
+
+       m = apxget(buffer, len , 0, ifp);
+       if (m == 0)
+               return;
+
+       if(IF_QFULL(inq)) {
+               IF_DROP(inq);
+               m_freem(m);
+       } else {
+               IF_ENQUEUE(inq, m);
+               schednetisr(isr);
+       }
+}
+
+/*
+ * Routine to copy from board local memory into mbufs.
+ */
+struct mbuf *
+apxget(buf, totlen, off0, ifp)
+       char *buf;
+       int totlen, off0;
+       struct ifnet *ifp;
+{
+       register struct mbuf *m;
+       struct mbuf *top = 0, **mp = &top;
+       register int off = off0, len;
+       register char *cp;
+       char *epkt;
+
+       cp = buf;
+       epkt = cp + totlen;
+       if (off) {
+               cp += off + 2 * sizeof(u_short);
+               totlen -= 2 * sizeof(u_short);
+       }
+
+       MGETHDR(m, M_DONTWAIT, MT_DATA);
+       if (m == 0)
+               return (0);
+       m->m_pkthdr.rcvif = ifp;
+       m->m_pkthdr.len = totlen;
+       m->m_len = MHLEN;
+
+       while (totlen > 0) {
+               if (top) {
+                       MGET(m, M_DONTWAIT, MT_DATA);
+                       if (m == 0) {
+                               m_freem(top);
+                               return (0);
+                       }
+                       m->m_len = MLEN;
+               }
+               len = min(totlen, epkt - cp);
+               if (len >= MINCLSIZE) {
+                       MCLGET(m, M_DONTWAIT);
+                       if (m->m_flags & M_EXT)
+                               m->m_len = len = min(len, MCLBYTES);
+                       else
+                               len = m->m_len;
+               } else {
+                       /*
+                        * Place initial small packet/header at end of mbuf.
+                        */
+                       if (len < m->m_len) {
+                               if (top == 0 && len + max_linkhdr <= m->m_len)
+                                       m->m_data += max_linkhdr;
+                               m->m_len = len;
+                       } else
+                               len = m->m_len;
+               }
+               bcopy(cp, mtod(m, caddr_t), (unsigned)len);
+               cp += len;
+               *mp = m;
+               mp = &m->m_next;
+               totlen -= len;
+               if (cp == epkt)
+                       cp = buf;
+       }
+       return (top);
+}
+
+/*
+ * Process an ioctl request.
+ */
+apxioctl(ifp, cmd, data)
+       register struct ifnet *ifp;
+       int cmd;
+       caddr_t data;
+{
+       register struct ifaddr *ifa = (struct ifaddr *)data;
+       int s = splimp(), error = 0;
+       struct apx_softc *apx = &apx_softc[ifp->if_unit];
+
+       switch (cmd) {
+       case SIOCSIFCONF_X25:
+               ifp->if_flags |= IFF_UP;
+               error = hd_ctlinput(PRC_IFUP, ifa->ifa_addr);
+               if (error == 0)
+                       apxinit(ifp->if_unit);
+               break;
+
+       case SIOCSIFADDR:
+               ifa->ifa_rtrequest = x25_rtrequest;
+               break;
+
+       case SIOCSIFFLAGS:
+               if (((ifp->if_flags & IFF_UP) == 0 &&
+                    (ifp->if_flags & IFF_RUNNING)) ||
+                   (ifp->if_flags & IFF_UP) &&
+                    (ifp->if_flags & IFF_RUNNING) == 0)
+                       apxinit(ifp->if_unit);
+               break;
+
+       case SIOCSIFMODE:
+               if ((ifp->if_flags & IFF_UP) == 0)
+                       apx->apx_modes = *(struct apx_modes *)data;
+               else
+       default:
+                       error = EINVAL;
+
+       }
+       splx(s);
+       return (error);
+}
+
+apxerror(apx, msg, data)
+       register struct apx_softc *apx; 
+       char    *msg;
+{
+       log(LOG_WARNING, "apc%d: %s, stat=0x%x\n",
+               apx->apx_if.if_unit, msg, data);
+}
+#endif /* NAPX */
diff --git a/usr/src/sys/i386/isa/if_apxreg.h b/usr/src/sys/i386/isa/if_apxreg.h
new file mode 100644 (file)
index 0000000..b80d4e9
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1982, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ *
+ *     @(#)if_apxreg.h 7.1 (Berkeley) %G%
+ */
+
+
+/* SGS Thompson MK5205 structures and registers */
+
+/* 
+ * Device Control Ports
+ * (Multiplexed CSR's)
+ */
+struct sgcp {
+       u_short sgcp_rdp;
+       u_short sgcp_rap;
+};
+/*
+ * Operating Parameters for timers etc.
+ * (Suitable for reseting by ioctl).
+ */ 
+struct sgop {
+       u_short lsaddr;
+       u_short rsaddr;
+       u_short n1;
+       u_short n2_scale;
+       u_short t1;
+       u_short t3;
+       u_short tp;
+};
+
+/*
+ * common format for tx/rx descriptors
+ */
+
+struct sgdx {
+       u_short sgdx_flags;
+       u_short sgdx_addr;
+       short   sgdx_bcnt;
+       u_short sgdx_mcnt;
+};
+
+/*
+ * interpretation of reported errors 
+ */
+struct sger {
+       u_short sger_bad;
+       u_short sger_t1timo;
+       u_short sger_frmr;
+       u_short sger_txrej;
+       u_short sger_rxrej;
+       u_short sger_short;
+};
+
+/*
+ * Status Buffer, paried bytes hi order low order
+ */
+struct sgsb {
+       u_short sgsb_vrvs;
+       u_short sgsb_lsrs;
+       u_short sgsb_phzva;
+       u_short sgsb_hirxcnt;
+       u_short sgsb_lorxcnt;
+       u_short sgsb_hitxcnt;
+       u_short sgsb_lotxcnt;
+};
+
+#define SGMTU 1032
+#define SGRBUF 16
+#define SGRBUFLOG2 4
+#define SG_RLEN (SGRBUF_LOG2 << 12)
+#define SGTBUF 8
+#define SGTBUFLOG2 3
+#define SG_TLEN (SGTBUF_LOG2 << 12)
+
+/*
+ * APC-PCX RAM
+ */
+
+struct apc_mem {
+       /* Initialization Block */
+       u_short apc_mode;
+       struct  sgop apc_sgop;
+       u_short apc_rlen;
+       u_short apc_rdra;
+       u_short apc_tlen;
+       u_short apc_tdra;
+       struct  sgdx apc_txtid;
+       struct  sgdx apc_rxtid;
+       u_short apc_stathi;
+       u_short apc_statlo;
+       struct  sger apc_sger;
+       struct  sgsb apc_sgsb;          /* Status Buffer */
+       struct  sgdx apc_rxmd[SGRBUF];  /* Receive Message Descriptors */
+       struct  sgdx apc_txmd[SGTBUF];  /* Transmit Message Descriptors */
+       char    apc_rbuf[SGRBUF][SGMTU]; /* Receive Message Buffers */
+       char    apc_tbuf[SGTBUF][SGMTU]; /* Transmit Message Buffers */
+       char    apc_rxidbuf[SGMTU];
+       char    apc_txidbuf[SGMTU];
+};
+/*
+ * APC-PCX registers
+ */
+struct apc_reg {
+       struct  sgcb axr_sgcb[2];
+       u_char  arx_pad0[4];
+       u_char  axr_cnt0;
+       u_char  axr_cnt1;
+       u_char  axr_cnt2;
+       u_char  axr_ccr;
+       u_char  axr_mode;
+       u_char  axr_pad1;
+       u_char  axr_status;
+       u_char  axr_pad2;
+       u_char  axr_altmode;
+       u_char  axr_pad3[11];
+};
+
+/*
+ * Hardware tweaking
+ */
+struct apc_modes {
+       struct  sgop apm_sgop;  /* tweak timers */
+       u_char  apm_txwin;      /* set parameter for transmit window */
+       u_char  apm_apxmode;
+       u_char  apm_apxaltmode;
+       u_char  apm_iftype;     /* someday indicate PPP vs X.25 */
+};
+
+#define        SIOCSIFMODE     _IOW('i', 127, struct apc_modes) /* set parameters */
+
+/* TX RX descriptor bits */
+#define SG_OWN 0x8000
+#define SG_SLF 0x2000
+#define SG_ELF 0x1000
+#define        SG_TUI 0x800
+
+/* CSR0 quantities */
+#define SG_TDMD        0x8000
+#define SG_STOPPED 0x4000
+#define SG_DTX 0x2000
+#define SG_DRX 0x1000
+#define SG_TXON 0x800
+#define SG_RXON 0x400
+#define SG_INEA 0x200
+#define SG_INTR 0x100
+#define SG_MERR 0x80
+#define SG_MISS 0x40
+#define SG_ROR 0x20
+#define SG_TUR 0x10
+#define SG_PINT 0x8
+#define SG_TINT 0x4
+#define SG_RINT 0x2
+
+/* CSR1 quantities */
+#define SG_UAV 0x4000
+#define SG_PAV 0x40
+#define SG_STOP        0
+#define        SG_INIT (2 << 8)
+#define SG_TRANS (3 << 8)
+#define SG_STAT        (4 << 8)
+
+#define SG_PROM        0x400
+#define SG_UIE 0x200
+#define SG_BSWAP 0x20