#include <sys/param.h>
#include <sys/endian.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/serialize.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <machine/md_var.h>
#include <machine/cothread.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/bpf.h>
#include <net/if_arp.h>
#include <net/if_media.h>
#include <net/ifq_var.h>
#include <net/vlan/if_vlan_ether.h>
#include <netinet/in_var.h>
#include <sys/stat.h>
#include <net/tap/if_tap.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define VKE_DEVNAME "vke"
#define VKE_CHUNK 8
#define NETFIFOINDEX(u, sc) ((u) & ((sc)->sc_ringsize - 1))
#define VKE_COTD_RUN 0
#define VKE_COTD_EXIT 1
#define VKE_COTD_DEAD 2
struct vke_fifo {
struct mbuf **array;
int rindex;
int windex;
};
typedef struct vke_fifo *fifo_t;
#define VKE_DEFAULT_RINGSIZE 256
static int vke_max_ringsize = 0;
TUNABLE_INT("hw.vke.max_ringsize", &vke_max_ringsize);
#define LOW_POW_2(n) (1 << (fls(n) - 1))
struct vke_softc {
struct arpcom arpcom;
int sc_fd;
int sc_unit;
cothread_t cotd_tx;
cothread_t cotd_rx;
int cotd_tx_exit;
int cotd_rx_exit;
void *sc_txbuf;
int sc_txbuf_len;
fifo_t sc_txfifo;
fifo_t sc_txfifo_done;
fifo_t sc_rxfifo;
int sc_ringsize;
long cotd_ipackets;
long cotd_oerrors;
long cotd_opackets;
struct sysctl_ctx_list sc_sysctl_ctx;
struct sysctl_oid *sc_sysctl_tree;
int sc_tap_unit;
in_addr_t sc_addr;
in_addr_t sc_mask;
struct ifmedia sc_media;
};
static void vke_start(struct ifnet *, struct ifaltq_subque *);
static void vke_init(void *);
static int vke_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
static int vke_media_change(struct ifnet *);
static void vke_media_status(struct ifnet *, struct ifmediareq *);
static int vke_attach(const struct vknetif_info *, int);
static int vke_stop(struct vke_softc *);
static int vke_init_addr(struct ifnet *, in_addr_t, in_addr_t);
static void vke_tx_intr(cothread_t cotd);
static void vke_tx_thread(cothread_t cotd);
static void vke_rx_intr(cothread_t cotd);
static void vke_rx_thread(cothread_t cotd);
static int vke_txfifo_enqueue(struct vke_softc *sc, struct mbuf *m);
static struct mbuf *vke_txfifo_dequeue(struct vke_softc *sc);
static int vke_txfifo_done_enqueue(struct vke_softc *sc, struct mbuf *m);
static struct mbuf * vke_txfifo_done_dequeue(struct vke_softc *sc, struct mbuf *nm);
static struct mbuf *vke_rxfifo_dequeue(struct vke_softc *sc, struct mbuf *nm);
static struct mbuf *vke_rxfifo_sniff(struct vke_softc *sc);
static void
vke_sysinit(void *arg __unused)
{
int i, unit;
KASSERT(NetifNum <= VKNETIF_MAX, ("too many netifs: %d", NetifNum));
unit = 0;
for (i = 0; i < NetifNum; ++i) {
if (vke_attach(&NetifInfo[i], unit) == 0)
++unit;
}
}
SYSINIT(vke, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, vke_sysinit, NULL);
static int
vke_txfifo_done_enqueue(struct vke_softc *sc, struct mbuf *m)
{
fifo_t fifo = sc->sc_txfifo_done;
while (NETFIFOINDEX(fifo->windex + 1, sc) ==
NETFIFOINDEX(fifo->rindex, sc)) {
usleep(20000);
}
fifo->array[NETFIFOINDEX(fifo->windex, sc)] = m;
cpu_sfence();
++fifo->windex;
return (0);
}
static struct mbuf *
vke_txfifo_done_dequeue(struct vke_softc *sc, struct mbuf *nm)
{
fifo_t fifo = sc->sc_txfifo_done;
struct mbuf *m;
if (NETFIFOINDEX(fifo->rindex, sc) == NETFIFOINDEX(fifo->windex, sc))
return (NULL);
cpu_lfence();
m = fifo->array[NETFIFOINDEX(fifo->rindex, sc)];
fifo->array[NETFIFOINDEX(fifo->rindex, sc)] = nm;
++fifo->rindex;
return (m);
}
static int
vke_txfifo_enqueue(struct vke_softc *sc, struct mbuf *m)
{
fifo_t fifo = sc->sc_txfifo;
if (NETFIFOINDEX(fifo->windex + 1, sc) ==
NETFIFOINDEX(fifo->rindex, sc)) {
return (-1);
}
fifo->array[NETFIFOINDEX(fifo->windex, sc)] = m;
cpu_sfence();
++fifo->windex;
return (0);
}
static struct mbuf *
vke_txfifo_dequeue(struct vke_softc *sc)
{
fifo_t fifo = sc->sc_txfifo;
struct mbuf *m;
if (NETFIFOINDEX(fifo->rindex, sc) == NETFIFOINDEX(fifo->windex, sc))
return (NULL);
cpu_lfence();
m = fifo->array[NETFIFOINDEX(fifo->rindex, sc)];
fifo->array[NETFIFOINDEX(fifo->rindex, sc)] = NULL;
cpu_sfence();
++fifo->rindex;
return (m);
}
static int
vke_txfifo_empty(struct vke_softc *sc)
{
fifo_t fifo = sc->sc_txfifo;
if (NETFIFOINDEX(fifo->rindex, sc) == NETFIFOINDEX(fifo->windex, sc))
return (1);
return(0);
}
static struct mbuf *
vke_rxfifo_dequeue(struct vke_softc *sc, struct mbuf *newm)
{
fifo_t fifo = sc->sc_rxfifo;
struct mbuf *m;
if (NETFIFOINDEX(fifo->rindex, sc) == NETFIFOINDEX(fifo->windex, sc))
return (NULL);
cpu_lfence();
m = fifo->array[NETFIFOINDEX(fifo->rindex, sc)];
fifo->array[NETFIFOINDEX(fifo->rindex, sc)] = newm;
cpu_sfence();
++fifo->rindex;
return (m);
}
static struct mbuf *
vke_rxfifo_sniff(struct vke_softc *sc)
{
fifo_t fifo = sc->sc_rxfifo;
struct mbuf *m;
if (NETFIFOINDEX(fifo->rindex, sc) == NETFIFOINDEX(fifo->windex, sc))
return (NULL);
cpu_lfence();
m = fifo->array[NETFIFOINDEX(fifo->rindex, sc)];
return (m);
}
static void
vke_init(void *xsc)
{
struct vke_softc *sc = xsc;
struct ifnet *ifp = &sc->arpcom.ac_if;
size_t ringsize = sc->sc_ringsize * sizeof(struct mbuf *);
int i;
ASSERT_SERIALIZED(ifp->if_serializer);
vke_stop(sc);
ifp->if_flags |= IFF_RUNNING;
ifsq_clr_oactive(ifq_get_subq_default(&ifp->if_snd));
sc->sc_txfifo = kmalloc(sizeof(*sc->sc_txfifo),
M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_txfifo_done = kmalloc(sizeof(*sc->sc_txfifo_done),
M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_rxfifo = kmalloc(sizeof(*sc->sc_rxfifo),
M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_txfifo->array = kmalloc(ringsize,
M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_txfifo_done->array = kmalloc(ringsize,
M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_rxfifo->array = kmalloc(ringsize,
M_DEVBUF, M_WAITOK | M_ZERO);
for (i = 0; i < sc->sc_ringsize; i++) {
sc->sc_rxfifo->array[i] = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
sc->sc_txfifo->array[i] = NULL;
sc->sc_txfifo_done->array[i] = NULL;
}
sc->cotd_tx_exit = sc->cotd_rx_exit = VKE_COTD_RUN;
sc->cotd_tx = cothread_create(vke_tx_thread, vke_tx_intr, sc, "vke_tx");
sc->cotd_rx = cothread_create(vke_rx_thread, vke_rx_intr, sc, "vke_rx");
if (sc->sc_addr != 0) {
in_addr_t addr, mask;
addr = sc->sc_addr;
mask = sc->sc_mask;
sc->sc_addr = 0;
sc->sc_mask = 0;
vke_init_addr(ifp, addr, mask);
}
}
static void
vke_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
{
struct vke_softc *sc = ifp->if_softc;
struct mbuf *m;
cothread_t cotd = sc->cotd_tx;
int count;
ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
ASSERT_SERIALIZED(ifp->if_serializer);
if ((ifp->if_flags & IFF_RUNNING) == 0 || ifsq_is_oactive(ifsq))
return;
count = 0;
while ((m = ifsq_dequeue(ifsq)) != NULL) {
if (vke_txfifo_enqueue(sc, m) != -1) {
ETHER_BPF_MTAP(ifp, m);
if (count++ == VKE_CHUNK) {
cothread_lock(cotd, 0);
cothread_signal(cotd);
cothread_unlock(cotd, 0);
count = 0;
}
} else {
m_freem(m);
}
}
if (count) {
cothread_lock(cotd, 0);
cothread_signal(cotd);
cothread_unlock(cotd, 0);
}
}
static int
vke_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
{
struct vke_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
ASSERT_SERIALIZED(ifp->if_serializer);
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
if ((ifp->if_flags & IFF_RUNNING) == 0)
vke_init(sc);
} else {
if (ifp->if_flags & IFF_RUNNING)
vke_stop(sc);
}
break;
case SIOCGIFMEDIA:
case SIOCGIFXMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
break;
case SIOCGIFSTATUS: {
struct ifstat *ifs = (struct ifstat *)data;
int len;
len = strlen(ifs->ascii);
if (len < sizeof(ifs->ascii)) {
if (sc->sc_tap_unit >= 0) {
ksnprintf(ifs->ascii + len,
sizeof(ifs->ascii) - len,
"\tBacked by tap%d\n",
sc->sc_tap_unit);
}
}
break;
}
case SIOCSIFADDR:
if (((struct ifaddr *)data)->ifa_addr->sa_family == AF_INET) {
sc->sc_addr = 0;
sc->sc_mask = 0;
}
default:
error = ether_ioctl(ifp, cmd, data);
break;
}
return error;
}
static int
vke_stop(struct vke_softc *sc)
{
struct ifnet *ifp = &sc->arpcom.ac_if;
int i;
ASSERT_SERIALIZED(ifp->if_serializer);
ifp->if_flags &= ~IFF_RUNNING;
ifsq_clr_oactive(ifq_get_subq_default(&ifp->if_snd));
if (sc) {
if (sc->cotd_tx) {
cothread_lock(sc->cotd_tx, 0);
if (sc->cotd_tx_exit == VKE_COTD_RUN)
sc->cotd_tx_exit = VKE_COTD_EXIT;
cothread_signal(sc->cotd_tx);
cothread_unlock(sc->cotd_tx, 0);
cothread_delete(&sc->cotd_tx);
}
if (sc->cotd_rx) {
cothread_lock(sc->cotd_rx, 0);
if (sc->cotd_rx_exit == VKE_COTD_RUN)
sc->cotd_rx_exit = VKE_COTD_EXIT;
cothread_signal(sc->cotd_rx);
cothread_unlock(sc->cotd_rx, 0);
cothread_delete(&sc->cotd_rx);
}
for (i = 0; i < sc->sc_ringsize; i++) {
if (sc->sc_rxfifo && sc->sc_rxfifo->array[i]) {
m_freem(sc->sc_rxfifo->array[i]);
sc->sc_rxfifo->array[i] = NULL;
}
if (sc->sc_txfifo && sc->sc_txfifo->array[i]) {
m_freem(sc->sc_txfifo->array[i]);
sc->sc_txfifo->array[i] = NULL;
}
if (sc->sc_txfifo_done && sc->sc_txfifo_done->array[i]) {
m_freem(sc->sc_txfifo_done->array[i]);
sc->sc_txfifo_done->array[i] = NULL;
}
}
if (sc->sc_txfifo) {
if (sc->sc_txfifo->array)
kfree(sc->sc_txfifo->array, M_DEVBUF);
kfree(sc->sc_txfifo, M_DEVBUF);
sc->sc_txfifo = NULL;
}
if (sc->sc_txfifo_done) {
if (sc->sc_txfifo_done->array)
kfree(sc->sc_txfifo_done->array, M_DEVBUF);
kfree(sc->sc_txfifo_done, M_DEVBUF);
sc->sc_txfifo_done = NULL;
}
if (sc->sc_rxfifo) {
if (sc->sc_rxfifo->array)
kfree(sc->sc_rxfifo->array, M_DEVBUF);
kfree(sc->sc_rxfifo, M_DEVBUF);
sc->sc_rxfifo = NULL;
}
}
return 0;
}
static void
vke_rx_intr(cothread_t cotd)
{
struct mbuf *m;
struct mbuf *nm;
struct vke_softc *sc = cotd->arg;
struct ifnet *ifp = &sc->arpcom.ac_if;
static int count = 0;
ifnet_serialize_all(ifp);
cothread_lock(cotd, 0);
if (sc->cotd_rx_exit != VKE_COTD_RUN) {
cothread_unlock(cotd, 0);
ifnet_deserialize_all(ifp);
return;
}
if (sc->cotd_ipackets) {
IFNET_STAT_INC(ifp, ipackets, 1);
sc->cotd_ipackets = 0;
}
cothread_unlock(cotd, 0);
while ((m = vke_rxfifo_sniff(sc)) != NULL) {
nm = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
if (nm) {
vke_rxfifo_dequeue(sc, nm);
ifp->if_input(ifp, m, NULL, -1);
if (count++ == VKE_CHUNK) {
cothread_lock(cotd, 0);
cothread_signal(cotd);
cothread_unlock(cotd, 0);
count = 0;
}
} else {
vke_rxfifo_dequeue(sc, m);
}
}
if (count) {
cothread_lock(cotd, 0);
cothread_signal(cotd);
cothread_unlock(cotd, 0);
}
ifnet_deserialize_all(ifp);
}
static void
vke_tx_intr(cothread_t cotd)
{
struct vke_softc *sc = cotd->arg;
struct ifnet *ifp = &sc->arpcom.ac_if;
struct mbuf *m;
ifnet_serialize_all(ifp);
cothread_lock(cotd, 0);
if (sc->cotd_tx_exit != VKE_COTD_RUN) {
cothread_unlock(cotd, 0);
ifnet_deserialize_all(ifp);
return;
}
if (sc->cotd_opackets) {
IFNET_STAT_INC(ifp, opackets, 1);
sc->cotd_opackets = 0;
}
if (sc->cotd_oerrors) {
IFNET_STAT_INC(ifp, oerrors, 1);
sc->cotd_oerrors = 0;
}
cothread_unlock(cotd, 0);
while ((m = vke_txfifo_done_dequeue(sc, NULL)) != NULL) {
m_freem(m);
}
if ((ifp->if_flags & IFF_RUNNING) == 0)
if_devstart(ifp);
ifnet_deserialize_all(ifp);
}
static void
vke_rx_thread(cothread_t cotd)
{
struct mbuf *m;
struct vke_softc *sc = cotd->arg;
struct ifnet *ifp = &sc->arpcom.ac_if;
fifo_t fifo = sc->sc_rxfifo;
fd_set fdset;
struct timeval tv;
int count;
int n;
int r;
tv.tv_sec = 0;
tv.tv_usec = 500000;
FD_ZERO(&fdset);
count = 0;
while (sc->cotd_rx_exit == VKE_COTD_RUN) {
if (NETFIFOINDEX(fifo->windex + 1, sc) ==
NETFIFOINDEX(fifo->rindex, sc)) {
usleep(20000);
continue;
}
cpu_lfence();
m = fifo->array[NETFIFOINDEX(fifo->windex, sc)];
if (m == NULL) {
fprintf(stderr,
VKE_DEVNAME "%d: NULL rxring mbuf\n",
sc->sc_unit);
*(volatile int *)0 = 1;
}
n = read(sc->sc_fd, mtod(m, void *), MCLBYTES);
if (n > 0) {
++sc->cotd_ipackets;
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = n;
cpu_sfence();
++fifo->windex;
if (count++ == VKE_CHUNK) {
cothread_intr(cotd);
count = 0;
}
} else {
if (count) {
cothread_intr(cotd);
count = 0;
}
FD_SET(sc->sc_fd, &fdset);
r = select(sc->sc_fd + 1, &fdset, NULL, NULL, &tv);
if (r == -1) {
fprintf(stderr,
VKE_DEVNAME "%d: select failed for "
"TAP device\n", sc->sc_unit);
usleep(1000000);
}
}
}
cpu_sfence();
sc->cotd_rx_exit = VKE_COTD_DEAD;
}
static void
vke_tx_thread(cothread_t cotd)
{
struct mbuf *m;
struct vke_softc *sc = cotd->arg;
int count = 0;
while (sc->cotd_tx_exit == VKE_COTD_RUN) {
m = vke_txfifo_dequeue(sc);
if (m) {
if (m->m_pkthdr.len <= MCLBYTES) {
m_copydata(m, 0, m->m_pkthdr.len, sc->sc_txbuf);
sc->sc_txbuf_len = m->m_pkthdr.len;
if (write(sc->sc_fd, sc->sc_txbuf,
sc->sc_txbuf_len) < 0) {
++sc->cotd_oerrors;
} else {
++sc->cotd_opackets;
}
}
if (count++ == VKE_CHUNK) {
cothread_intr(cotd);
count = 0;
}
vke_txfifo_done_enqueue(sc, m);
} else {
if (count) {
cothread_intr(cotd);
count = 0;
}
cothread_lock(cotd, 1);
if (vke_txfifo_empty(sc))
cothread_wait(cotd);
cothread_unlock(cotd, 1);
}
}
cpu_sfence();
sc->cotd_tx_exit = VKE_COTD_DEAD;
}
static void
vke_ifmedia_add(struct vke_softc *sc, int mword)
{
ifmedia_add(&sc->sc_media, IFM_ETHER | mword, 0, NULL);
}
static void
vke_ifmedia_addfdx(struct vke_softc *sc, int mword)
{
vke_ifmedia_add(sc, mword | IFM_FDX);
}
static int
vke_attach(const struct vknetif_info *info, int unit)
{
struct vke_softc *sc;
struct ifnet *ifp;
struct tapinfo tapinfo;
uint8_t enaddr[ETHER_ADDR_LEN];
int nmbufs;
int fd;
KKASSERT(info->tap_fd >= 0);
fd = info->tap_fd;
if (info->enaddr) {
bcopy(info->enaddr, enaddr, ETHER_ADDR_LEN);
} else {
bzero(enaddr, sizeof(enaddr));
if (info->tap_unit >= 0) {
if (ioctl(fd, TAPGIFINFO, &tapinfo) < 0) {
kprintf(VKE_DEVNAME "%d: ioctl(TAPGIFINFO) "
"failed: %s\n", unit, strerror(errno));
return ENXIO;
}
if (ioctl(fd, SIOCGIFADDR, enaddr) < 0) {
kprintf(VKE_DEVNAME "%d: ioctl(SIOCGIFADDR) "
"failed: %s\n", unit, strerror(errno));
return ENXIO;
}
} else {
int fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
read(fd, enaddr + 2, 4);
close(fd);
}
enaddr[4] = (int)getpid() >> 8;
enaddr[5] = (int)getpid() & 255;
}
enaddr[1] += 1;
}
if (ETHER_IS_MULTICAST(enaddr)) {
kprintf(VKE_DEVNAME "%d: illegal MULTICAST ether mac!\n", unit);
return ENXIO;
}
sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_txbuf = kmalloc(MCLBYTES, M_DEVBUF, M_WAITOK);
sc->sc_fd = fd;
sc->sc_unit = unit;
sc->sc_tap_unit = info->tap_unit;
sc->sc_addr = info->netif_addr;
sc->sc_mask = info->netif_mask;
if (vke_max_ringsize == 0) {
nmbufs = nmbclusters / (NetifNum * 2);
sc->sc_ringsize = LOW_POW_2(nmbufs);
if (sc->sc_ringsize > VKE_DEFAULT_RINGSIZE)
sc->sc_ringsize = VKE_DEFAULT_RINGSIZE;
} else if (vke_max_ringsize >= VKE_CHUNK) {
sc->sc_ringsize = LOW_POW_2(vke_max_ringsize);
} else {
sc->sc_ringsize = LOW_POW_2(VKE_CHUNK);
}
ifp = &sc->arpcom.ac_if;
if_initname(ifp, VKE_DEVNAME, sc->sc_unit);
sysctl_ctx_init(&sc->sc_sysctl_ctx);
sc->sc_sysctl_tree = SYSCTL_ADD_NODE(&sc->sc_sysctl_ctx,
SYSCTL_STATIC_CHILDREN(_hw),
OID_AUTO, ifp->if_xname,
CTLFLAG_RD, 0, "");
if (sc->sc_sysctl_tree == NULL) {
kprintf(VKE_DEVNAME "%d: can't add sysctl node\n", unit);
} else {
SYSCTL_ADD_INT(&sc->sc_sysctl_ctx,
SYSCTL_CHILDREN(sc->sc_sysctl_tree),
OID_AUTO, "tap_unit",
CTLFLAG_RD, &sc->sc_tap_unit, 0,
"Backend tap(4) unit");
}
ifp->if_softc = sc;
ifp->if_ioctl = vke_ioctl;
ifp->if_start = vke_start;
ifp->if_init = vke_init;
ifp->if_mtu = tapinfo.mtu;
ifp->if_baudrate = tapinfo.baudrate;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
ifq_set_ready(&ifp->if_snd);
ifmedia_init(&sc->sc_media, 0, vke_media_change, vke_media_status);
vke_ifmedia_add(sc, IFM_10_T);
vke_ifmedia_add(sc, IFM_10_T);
vke_ifmedia_add(sc, IFM_10_2);
vke_ifmedia_add(sc, IFM_10_5);
vke_ifmedia_add(sc, IFM_100_TX);
vke_ifmedia_addfdx(sc, IFM_100_TX);
vke_ifmedia_add(sc, IFM_100_FX);
vke_ifmedia_add(sc, IFM_100_T4);
vke_ifmedia_add(sc, IFM_100_VG);
vke_ifmedia_add(sc, IFM_100_T2);
vke_ifmedia_addfdx(sc, IFM_1000_SX);
vke_ifmedia_add(sc, IFM_10_STP);
vke_ifmedia_add(sc, IFM_10_FL);
vke_ifmedia_addfdx(sc, IFM_1000_LX);
vke_ifmedia_addfdx(sc, IFM_1000_CX);
vke_ifmedia_addfdx(sc, IFM_1000_T);
vke_ifmedia_add(sc, IFM_HPNA_1);
vke_ifmedia_addfdx(sc, IFM_10G_LR);
vke_ifmedia_addfdx(sc, IFM_10G_SR);
vke_ifmedia_addfdx(sc, IFM_10G_CX4);
vke_ifmedia_addfdx(sc, IFM_2500_SX);
vke_ifmedia_addfdx(sc, IFM_10G_TWINAX);
vke_ifmedia_addfdx(sc, IFM_10G_TWINAX_LONG);
vke_ifmedia_addfdx(sc, IFM_10G_LRM);
vke_ifmedia_addfdx(sc, IFM_10G_T);
vke_ifmedia_addfdx(sc, IFM_40G_CR4);
vke_ifmedia_addfdx(sc, IFM_40G_SR4);
vke_ifmedia_addfdx(sc, IFM_40G_LR4);
vke_ifmedia_addfdx(sc, IFM_1000_KX);
vke_ifmedia_addfdx(sc, IFM_10G_KX4);
vke_ifmedia_addfdx(sc, IFM_10G_KR);
vke_ifmedia_addfdx(sc, IFM_10G_CR1);
vke_ifmedia_addfdx(sc, IFM_20G_KR2);
vke_ifmedia_addfdx(sc, IFM_2500_KX);
vke_ifmedia_addfdx(sc, IFM_2500_T);
vke_ifmedia_addfdx(sc, IFM_5000_T);
vke_ifmedia_addfdx(sc, IFM_50G_PCIE);
vke_ifmedia_addfdx(sc, IFM_25G_PCIE);
vke_ifmedia_addfdx(sc, IFM_1000_SGMII);
vke_ifmedia_addfdx(sc, IFM_10G_SFI);
vke_ifmedia_addfdx(sc, IFM_40G_XLPPI);
vke_ifmedia_addfdx(sc, IFM_1000_CX_SGMII);
vke_ifmedia_addfdx(sc, IFM_40G_KR4);
vke_ifmedia_addfdx(sc, IFM_10G_ER);
vke_ifmedia_addfdx(sc, IFM_100G_CR4);
vke_ifmedia_addfdx(sc, IFM_100G_SR4);
vke_ifmedia_addfdx(sc, IFM_100G_KR4);
vke_ifmedia_addfdx(sc, IFM_100G_LR4);
vke_ifmedia_addfdx(sc, IFM_56G_R4);
vke_ifmedia_addfdx(sc, IFM_100_T);
vke_ifmedia_addfdx(sc, IFM_25G_CR);
vke_ifmedia_addfdx(sc, IFM_25G_KR);
vke_ifmedia_addfdx(sc, IFM_25G_SR);
vke_ifmedia_addfdx(sc, IFM_50G_CR2);
vke_ifmedia_addfdx(sc, IFM_50G_KR2);
vke_ifmedia_add(sc, IFM_AUTO);
ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
ifp->if_link_state = LINK_STATE_UP;
ether_ifattach(ifp, enaddr, NULL);
if (bootverbose && sc->sc_addr != 0) {
if_printf(ifp, "pre-configured "
"address 0x%08x, netmask 0x%08x, %d mbuf clusters\n",
ntohl(sc->sc_addr), ntohl(sc->sc_mask), sc->sc_ringsize);
}
return 0;
}
static int
vke_init_addr(struct ifnet *ifp, in_addr_t addr, in_addr_t mask)
{
struct ifaliasreq ifra;
struct sockaddr_in *sin;
int ret;
ASSERT_SERIALIZED(ifp->if_serializer);
if (bootverbose) {
if_printf(ifp, "add pre-configured "
"address 0x%08x, netmask 0x%08x\n",
ntohl(addr), ntohl(mask));
}
bzero(&ifra, sizeof(ifra));
sin = (struct sockaddr_in *)&ifra.ifra_addr;
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_addr.s_addr = addr;
if (mask != 0) {
sin = (struct sockaddr_in *)&ifra.ifra_mask;
sin->sin_len = sizeof(*sin);
sin->sin_addr.s_addr = mask;
}
ifnet_deserialize_all(ifp);
ret = in_control(SIOCAIFADDR, (caddr_t)&ifra, ifp, NULL);
ifnet_serialize_all(ifp);
return ret;
}
static int vke_media_change(struct ifnet *ifp)
{
return(0);
}
static void vke_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
struct vke_softc *sc = (struct vke_softc *)ifp->if_softc;
imr->ifm_status = IFM_AVALID;
imr->ifm_status |= IFM_ACTIVE;
if(sc->sc_media.ifm_cur) {
if(sc->sc_media.ifm_cur->ifm_media == IFM_ETHER) {
imr->ifm_active = IFM_ETHER | IFM_1000_T | IFM_FDX;
} else {
imr->ifm_active = sc->sc_media.ifm_cur->ifm_media;
}
} else {
imr->ifm_active = IFM_ETHER | IFM_1000_T | IFM_FDX;
}
}