NetBSD/sys/arch/alpha/a12/if_xb.c
pooka 10fe49d72c Redefine bpf linkage through an always present op vector, i.e.
#if NBPFILTER is no longer required in the client.  This change
doesn't yet add support for loading bpf as a module, since drivers
can register before bpf is attached.  However, callers of bpf can
now be modularized.

Dynamically loadable bpf could probably be done fairly easily with
coordination from the stub driver and the real driver by registering
attachments in the stub before the real driver is loaded and doing
a handoff.  ... and I'm not going to ponder the depths of unload
here.

Tested with i386/MONOLITHIC, modified MONOLITHIC without bpf and rump.
2010-01-19 22:06:18 +00:00

751 lines
20 KiB
C

/* $NetBSD: if_xb.c,v 1.25 2010/01/19 22:06:18 pooka Exp $ */
/* [Notice revision 2.2]
* Copyright (c) 1997, 1998 Avalon Computer Systems, Inc.
* All rights reserved.
*
* Author: Ross Harvey
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright and
* author notice, this list of conditions, and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Avalon Computer Systems, Inc. nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. This copyright will be assigned to The NetBSD Foundation on
* 1/1/2000 unless these terms (including possibly the assignment
* date) are updated in writing by Avalon prior to the latest specified
* assignment date.
*
* THIS SOFTWARE IS PROVIDED BY AVALON COMPUTER SYSTEMS, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AVALON OR THE CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Notes.
*
* Since the NetBSD build rules force the use of function prototypes, even on
* functions that are defined before they are called, I've taken advantage of
* the opportunity and organized this module in top down fashion, with
* functions generally calling down the page rather than up. It's different.
* I think I'm getting to like it this way.
*
* The crossbar interface is not exactly a peripheral device, and it cannot
* appear on anything other than an alpha-based Avalon A12. The crossbar
* controller is built into the core logic.
*
* If this version of the driver supports MPS transport, it may have some
* large static data declarations. Don't worry about it, as Avalon a12
* support should not appear in a GENERIC or INSTALL kernel.
*
* (Every A12 ever shipped had 512 MB per CPU except one site, which had 256
* MB. Partly has a result of this, it is unlikely that a kernel configured
* for an A12 would be exactly the thing to use on most workstations, so we
* don't really need to worry that we might be configured in a generic or
* site-wide kernel image.)
*
* This preliminary crossbar driver supports IP transport using PIO. Although
* it would be nice to have a DMA driver, do note that the crossbar register
* port is 128 bits wide, so we have 128-bit PIO. (The 21164 write buffer
* will combine two 64-bit stores before they get off-chip.) Also, the rtmon
* driver wasn't DMA either, so at least the NetBSD driver is as good as any
* other that exists now.
*
* We'll do DMA and specialized transport ops later. Given the high speed of
* the PIO mode, no current applications require DMA bandwidth, but everyone
* benefits from low latency. The PIO mode is actually lower in latency
* anyway.
*/
#include "opt_avalon_a12.h" /* Config options headers */
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
__KERNEL_RCSID(0, "$NetBSD: if_xb.c,v 1.25 2010/01/19 22:06:18 pooka Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <sys/sockio.h>
#include <uvm/uvm_extern.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <machine/autoconf.h>
#include <machine/rpb.h>
#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <alpha/pci/a12creg.h>
#include <alpha/pci/a12cvar.h>
#include <alpha/pci/pci_a12.h>
#if 1
#define XB_DEBUG xb_debug
#else
#define XB_DEBUG 0
#endif
#undef Static
#if 1
#define Static
#else
#define Static static
#endif
#define IF_XB() /* Generate ctags(1) key */
#define XBAR_MTU (9*1024) /* Quite an arbitrary number */
#define XBAR_MAXFRAMEHDR 48 /* Used to compute if_mtu */
#define XB_DEFAULT_MTU() (XBAR_MTU - XBAR_MAXFRAMEHDR)
#define FIFO_WORDCOUNT 60
static int xb_put_blk(struct mbuf *);
static int xb_put(struct mbuf *);
static long xb_fifo_empty(void);
int xbmatch(struct device *, struct cfdata *, void *);
void xbattach(struct device *, struct device *, void *);
struct xb_softc {
struct device d;
} xb_softc;
CFATTACH_DECL(xb, sizeof(struct xb_softc),
xbmatch, xbattach, NULL, NULL);
extern struct cfdriver xb_cd;
long *xb_incoming;
int xb_incoming_max = XBAR_MTU;
typedef struct ccode_struct {
int64_t lo64, /* magic channel address s-word, high part*/
hi64; /* magic channel address s-word, low part */
} ccode_type;
/*
* Switch channel codes. Prepending one of these words will get you through
* the switch, which will eat the word, open the addressed channel, and
* forward the rest of the switch frame. Obviously, this helps if the second
* switch word in the frame is the address word for a cascaded switch. (This
* can be repeated for an arbitrary depth of MSN.) The words aren't quite as
* weird as they look: the switch is really lots of narrow switches in an
* array, and they don't switch an even number of hex digits. Also, there is
* a parity bit on most of the subunits.
*/
ccode_type channel[]={
{ 0x0000000000000000, 0x0000000000000000 },
{ 0x8882108421084210, 0x1104210842108421 },
{ 0x4441084210842108, 0x2208421084210842 },
{ 0xccc318c6318c6318, 0x330c6318c6318c63 },
{ 0x2220842108421084, 0x4410842108421084 },
{ 0xaaa294a5294a5294, 0x5514a5294a5294a5 },
{ 0x66618c6318c6318c, 0x6618c6318c6318c6 },
{ 0xeee39ce739ce739c, 0x771ce739ce739ce7 },
{ 0x1110421084210842, 0x8821084210842108 },
{ 0x99925294a5294a52, 0x9925294a5294a529 },
{ 0x55514a5294a5294a, 0xaa294a5294a5294a },
{ 0xddd35ad6b5ad6b5a, 0xbb2d6b5ad6b5ad6b },
{ 0x3330c6318c6318c6, 0xcc318c6318c6318c },
{ 0xbbb2d6b5ad6b5ad6, 0xdd35ad6b5ad6b5ad }
};
Static enum xb_intr_rcv_state_t {
XBIR_PKTHDR = 0, XBIR_TRANS
} xb_intr_rcv_state;
struct xb_config { int am_i_used; } xb_configuration;
Static struct ifnet xbi;
Static int frame_len;
static int xb_debug;
Static void xb_start(struct ifnet *);
Static void xb_mcrp_write(long *, int, int);
static inline void xb_onefree(void);
static long set_interrupt_on_fifo_empty(void);
static void xb_init(struct ifnet *);
static int xb_intr(void *);
static void xb_intr_rcv(void);
Static void quickload(volatile long *, long *);
static void xb_init_config(struct xb_config *, int);
static int xb_output(struct ifnet *, struct mbuf *,
const struct sockaddr *, struct rtentry *);
static int xb_ioctl(struct ifnet *, u_long, void *);
static void xb_stop(void);
static void a12_xbar_setup(void);
/* There Can Be Only One */
int xbfound;
int
xbmatch(struct device *parent, struct cfdata *match, void *aux)
{
return cputype == ST_AVALON_A12
&& !xbfound;
}
void
xbattach(struct device *parent, struct device *self, void *aux)
{
struct xb_config *ccp;
strcpy(xbi.if_xname, self->dv_xname);
xbfound = 1;
ccp = &xb_configuration;
xb_init_config(ccp, 1);
printf(": driver %s mtu %lu\n", "$Revision: 1.25 $", xbi.if_mtu);
}
static void
xb_init_config(struct xb_config *ccp, int mallocsafe)
{
/*
* The driver actually only needs about 64 bytes of buffer but with a
* nice contiguous frame we can call m_devget()
*/
if (mallocsafe && xb_incoming == NULL) {
xb_incoming = malloc(xb_incoming_max, M_DEVBUF, M_NOWAIT);
if (xb_incoming == NULL)
DIE();
}
a12_xbar_setup();
a12_intr_register_xb(xb_intr);
}
/*
* From The A12 Theory of Operation. Used with permission.
* --- --- ------ -- ---------
*
* Message Channel Status Register
*
* 31 0
* | |
* 10987654 32109876 54321098 76543210
*
* ........ ........ 0oiefaAr TR...... MCSR
*
* Field Type Name Function
*
* R R,W1C RBC Receive Block Complete
* T R,W1C TBC Transmit Block Complete
* r R IMP Incoming message pending
* A R IMFAE Incoming message fifo almost empty
* a R OMFAF Outgoing message fifo almost full
* f R OMFF Outgoing message fifo full
* e R OMFE Outgoing message fifo empty
* i R DMAin Incoming DMA channel armed
* o R DMAout Outgoing DMA channel armed
*
* Interrupts Generated from MCSR
*
* IMChInt <= (RBC or IMP) and not DMAin
* OMChInt <= ((TBC and not OMFAF) or (OMFE and OMR.E(6))
* ) and not DMAout
*
*/
static int
xb_intr(void *p)
{
int n;
long mcsrval;
/*
* The actual conditions under which this interrupt is generated are
* a bit complicated, and no status flag is available that reads out
* the final values of the interrupt inputs. But, it doesn't really
* matter. Simply check for receive data and transmitter IFF_OACTIVE.
*/
while ((mcsrval = REGVAL(A12_MCSR)) & A12_MCSR_IMP)
for(n = mcsrval & A12_MCSR_IMFAE ? 1 : 5; n; --n)
xb_intr_rcv();
if (xbi.if_flags & IFF_OACTIVE
&& mcsrval & A12_MCSR_OMFE) {
xbi.if_flags &= ~IFF_OACTIVE;
REGVAL(A12_OMR) &= ~A12_OMR_OMF_ENABLE;
alpha_wmb();
xb_start(&xbi);
}
return 0;
}
/*
* The interface logic will shoot us down with MCE (Missing Close Error) or
* ECE (Embedded Close Error) if we aren't in sync with the hardware w.r.t.
* frame boundaries. As those are panic-level errors: Don't Get Them.
*/
static void
xb_intr_rcv(void)
{
struct mbuf *m;
long frameword[2];
static long *xb_ibp;
int s = 0; /* XXX gcc */
switch (xb_intr_rcv_state) {
case XBIR_PKTHDR:
xb_ibp = xb_incoming;
quickload(REGADDR(A12_FIFO), frameword); /* frame_len >= 16 */
frame_len = frameword[0];
if (!(20 <= frame_len && frame_len+16 <= xb_incoming_max))
DIE();
/*
* The extra word when frames are of an aligned size is due
* to the way the output routines work. After the mbuf is
* sent xb_put_blk(NULL) is called. If there is a leftover
* 127-bit-or-less fragment then the close word rides on it,
* otherwise it gets an entire 128 bits of zeroes.
*/
if (frame_len & 0xf)
frame_len = (frame_len + 0xf) >> 4;
else frame_len = (frame_len >> 4) + 1;
--frame_len; /* we read the frame len + the first packet int64 */
*xb_ibp++ = frameword[1];
xb_intr_rcv_state = XBIR_TRANS;
break;
case XBIR_TRANS:
if (frame_len > 1)
quickload(REGADDR(A12_FIFO), frameword);
else if (frame_len == 1) {
quickload(REGADDR(A12_FIFO_LWE), frameword);
xb_intr_rcv_state = XBIR_PKTHDR;
} else if (XB_DEBUG)
DIE();
--frame_len;
xb_ibp[0] = frameword[0];
xb_ibp[1] = frameword[1];
xb_ibp += 2;
if (xb_intr_rcv_state == XBIR_PKTHDR) {
if (XB_DEBUG) {
s = splnet();
if (s != splnet())
DIE();
}
++xbi.if_ipackets;
if (IF_QFULL(&ipintrq)) {
IF_DROP(&ipintrq);
++xbi.if_iqdrops;
} else {
m = m_devget((void *)xb_incoming,
(char *)xb_ibp - (char *)xb_incoming,
0, &xbi, 0L);
if (m) {
xbi.if_ibytes += m->m_pkthdr.len;
IF_ENQUEUE(&ipintrq, m);
} else
++xbi.if_ierrors;
}
if (XB_DEBUG)
splx(s);
}
break;
default:
DIE();
}
}
/*
* Make it easy for gcc to load a[0..1] without interlocking between
* a[0] and a[1]. (If it did, that would be two external bus cycles.)
*/
Static void
quickload(volatile long *a, long *b)
{
long t1,t2;
t1 = a[0];
t2 = a[1];
b[0] = t1;
b[1] = t2;
}
/*
* Verify during debugging that we have not overflowed the FIFO
*/
static inline void
xb_onefree(void)
{
if (XB_DEBUG && REGVAL(A12_MCSR) & A12_MCSR_OMFF)
DIE();
}
static void
xb_init(struct ifnet *ifp)
{
ifp->if_flags |= IFF_RUNNING;
}
static void
xb_stop(void)
{
}
static int
xb_ioctl(struct ifnet *ifp, unsigned long cmd, void *data)
{
struct ifaddr *ifa = (struct ifaddr *)data;
int s, error = 0;
s = splnet();
switch (cmd) {
case SIOCINITIFADDR:
xbi.if_flags |= IFF_UP;
xb_init(ifp);
break;
case SIOCSIFFLAGS:
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
break;
if ((ifp->if_flags & IFF_UP) == 0 &&
(ifp->if_flags & IFF_RUNNING) != 0) {
xb_stop();
ifp->if_flags &= ~IFF_RUNNING;
} else if ((ifp->if_flags & IFF_UP) != 0 &&
(ifp->if_flags & IFF_RUNNING) == 0) {
xb_start(ifp);
} else
xb_init(ifp);
if (ifp->if_flags & IFF_DEBUG)
xb_debug = 1;
break;
default:
error = ifioctl_common(ifp, cmd, data);
break;
}
splx(s);
return error;
}
/*
* XXX - someday, keep a software copy of A12_OMR. We can execute up to
* 200 or 300 instructions in the time it takes to do the read part of an
* external bus cycle RMW op. (Or 10 - 20 cache cycles.)
*/
static inline long
xb_fifo_empty(void)
{
return REGVAL(A12_MCSR) & A12_MCSR_OMFE;
}
/*
* rtmon frames
*
* [ (... data) : commid : sourcepid : dstpid : ktype : length : frametype ]
*
* At the moment, NetBSD ip frames are not compatible with rtmon frames:
*
* [ ... data : length ]
*/
static int
xb_output(struct ifnet *ifp, struct mbuf *m0, const struct sockaddr *dst, struct rtentry *rt0)
{
int i,s;
struct mbuf *m = m0;
const char *lladdr;
char *xbh;
long xbo_framesize;
const struct sockaddr_dl *llsa;
int xbaddr;
#ifdef DIAGNOSTIC
if (ifp != &xbi)
DIE();
#endif
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
m_freem(m);
return ENETDOWN;
}
/*
* We want an IP packet with a link level route, on a silver platter.
*/
if (rt0 == NULL
|| (rt0->rt_flags & (RTF_GATEWAY | RTF_LLINFO))
|| (llsa = satocsdl(rt0->rt_gateway)) == NULL
|| llsa->sdl_family != AF_LINK
|| llsa->sdl_slen != 0) {
++ifp->if_oerrors;
m_freem(m);
return EHOSTUNREACH;
}
if (dst == NULL
|| dst->sa_family != AF_INET) {
/*
* This is because we give all received packets to ipintrq
* right now.
*/
What();
m_freem(m);
++ifp->if_noproto;
return EAFNOSUPPORT;
}
/*
* The a12MppSwitch is a wormhole routed MSN consisting of a number
* (usually n==1) of 14 channel crossbar switches. Each route through
* a switch requires a 128 bit address word that specifies the channel
* to emerge on. The address word is eaten by the switch and the
* rest of the packet is routed through.
*/
lladdr = CLLADDR(llsa);
if (llsa->sdl_alen != 1) /* XXX */
DIE(); /* OK someday, but totally unexpected right now */
/*
* Alternatively, we could lookup the address word and output
* it with PIO when the mbuf is dequeued
*/
xbo_framesize = m->m_pkthdr.len + 8;
M_PREPEND(m, 16 * llsa->sdl_alen + 8, M_DONTWAIT);
if (m == NULL)
return ENOBUFS;
xbh = mtod(m, char *);
for (i=0; i<llsa->sdl_alen; ++i) {
xbaddr = (lladdr[i] & 0xff) - 1;
if (!(0 <= xbaddr && xbaddr <= 11)) /* XXX */
DIE(); /* 12 or 13 will be OK later */
memcpy(xbh, &channel[xbaddr].lo64, 16);
xbh += 16;
}
memcpy(xbh, &xbo_framesize, 8);
s = splnet();
if (IF_QFULL(&ifp->if_snd)) {
IF_DROP(&ifp->if_snd);
++ifp->if_oerrors;
splx(s);
m_freem(m);
return ENOBUFS;
}
ifp->if_obytes += m->m_pkthdr.len;
++ifp->if_opackets;
IF_ENQUEUE(&ifp->if_snd, m);
if ((ifp->if_flags & IFF_OACTIVE) == 0)
xb_start(ifp);
splx(s);
if (m->m_flags & M_MCAST)
ifp->if_omcasts++;
return 0;
}
void
xb_start(struct ifnet *ifp)
{
struct mbuf *m;
if ((xbi.if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
return;
for (;;) {
IF_DEQUEUE(&xbi.if_snd, m);
if (m == 0)
return;
/*
* XXX The variable-length switch address words cause problems
* for bpf, for now, leave it out. XXX It's not too hard to
* fix, though, as there are lots of techniques that will
* identify the number of switch address words.
*/
if (!xb_put(m)) {
xbi.if_flags |= IFF_OACTIVE;
return;
}
}
}
static int
xb_put(struct mbuf *m)
{
struct mbuf *n;
int len;
if (XB_DEBUG && (alpha_pal_rdps() & 7) < 3)
DIE(); /* this "cannot happen", of course */
for (; m; m = n) {
len = m->m_len;
if (len == 0 || xb_put_blk(m))
MFREE(m, n);
else return 0;
}
xb_put_blk(NULL);
return 1;
}
/*
* Write a single mbuf to the transmit channel fifo. We can only write 128-bit
* words. Right now, we pad at the end. It is possible to pad at the
* beginning, especially since lots of games can be played at the receiver
* with the mbuf data pointer. Padding at the beginning requires a pad-count
* field in a header, but it means you can always DMA the data, regardless of
* alignment. Of course, we don't DMA at all, right now.
*/
static int
xb_put_blk(struct mbuf *m)
{
static long leftover[2]; /* 0-15 bytes from last xb_put_blk() */
static int leftover_len; /* non-aligned amount from last call */
long xfertmp[8]; /* aligned switch word buffer */
int frag_len, /* fifo stream unit */
fifo_len, /* space left in fifo */
fillin, /* amount needed to complete a switch word */
full, /* remember to restart on fifo full */
len; /* amount of mbuf left to do */
char *blk; /* location we are at in mbuf */
static int fifo_free; /* current # of switch words free in fifo */
#define XFERADJ() ((char *)xfertmp + leftover_len)
/* There is always room for the close word */
if (m == NULL) {
if (leftover_len)
leftover_len = 0;
else leftover[0] = leftover[1] = 0;
xb_mcrp_write(leftover, 1, 1);
--fifo_free;
return 1;
}
restart:
if (fifo_free < 2) {
if (!xb_fifo_empty()) {
if(!set_interrupt_on_fifo_empty()) {
/* still empty */
xbi.if_flags |= IFF_OACTIVE;
IF_PREPEND(&xbi.if_snd, m);
return 0;
}
}
fifo_free = FIFO_WORDCOUNT;
}
len = m->m_len;
if (len == 0)
return 1; /* clean finish, nothing left over */
blk = mtod(m, char *);
if (leftover_len) {
/* See function intro comment regarding padding */
if (leftover_len + len < sizeof leftover) {
/* Heh, not even enough to write out */
memcpy(XFERADJ(), blk, len);
leftover_len += len;
return 1;
}
xfertmp[0] = leftover[0];
xfertmp[1] = leftover[1];
fillin = sizeof leftover - leftover_len;
memcpy(XFERADJ(), blk, fillin);
blk += fillin;
len -= fillin;
xb_mcrp_write(xfertmp, 1, 0);
leftover_len = 0;
--fifo_free;
}
/* fifo_free is known to be >= 1 at this point */
while (len >= 16) {
full = 0;
frag_len = sizeof xfertmp;
if (frag_len > len)
frag_len = len;
fifo_len = fifo_free * 16;
if (frag_len > fifo_len) {
frag_len = fifo_len;
full = 1;
}
frag_len &= ~0xf;
memcpy(xfertmp, blk, frag_len);
frag_len >>= 4; /* Round down to switch word size */
xb_mcrp_write(xfertmp, frag_len, 0);
fifo_free -= frag_len;
frag_len <<= 4;
len -= frag_len;
blk += frag_len;
if (full) {
m_adj(m, blk - mtod(m, char *));
goto restart;
}
}
memcpy(leftover, blk, len);
leftover_len = len;
return 1;
}
static long
set_interrupt_on_fifo_empty(void)
{
REGVAL(A12_OMR) |= A12_OMR_OMF_ENABLE;
alpha_mb();
if(xb_fifo_empty()) {
REGVAL(A12_OMR) &= ~A12_OMR_OMF_ENABLE;
alpha_mb();
return 1;
}
return 0;
}
/*
* Write an aligned block of switch words to the FIFO
*/
Static void
xb_mcrp_write(long *d, int n, int islast)
{
volatile long *xb_fifo = islast ? REGADDR(A12_FIFO_LWE)
: REGADDR(A12_FIFO);
int i;
if (XB_DEBUG && islast && n != 1)
DIE();
n <<= 1;
for (i = 0; i < n; i += 2) {
xb_onefree();
xb_fifo[0] = d[i];
xb_fifo[1] = d[i+1];
alpha_wmb();
}
}
/*
const
int32_t xbar_bc_addr = XBAR_BROADCAST;
*/
static void
a12_xbar_setup()
{
xbi.if_softc = &xb_softc;
xbi.if_start = xb_start;
xbi.if_ioctl = xb_ioctl;
xbi.if_flags = IFF_BROADCAST /* ha ha */
| IFF_SIMPLEX;
xbi.if_type = IFT_A12MPPSWITCH;
xbi.if_addrlen = 32;
xbi.if_hdrlen = 32;
xbi.if_mtu = XB_DEFAULT_MTU();
xbi.if_output = xb_output;
/* xbi.if_broadcastaddr = (u_int8_t)&xbar_bc_addr; */
if_attach(&xbi);
if_alloc_sadl(&xbi);
bpf_ops->bpf_attach(&xbi, DLT_NULL, 0, &xbi.if_bpf);
}