summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/conf/NOTES1
-rw-r--r--sys/conf/files2
-rw-r--r--sys/dev/firewire/if_fwip.c917
-rw-r--r--sys/dev/firewire/if_fwipvar.h58
-rw-r--r--sys/net/firewire.h141
-rw-r--r--sys/net/if_arp.h1
-rw-r--r--sys/net/if_fwsubr.c799
-rw-r--r--sys/netinet/if_ether.c3
8 files changed, 1921 insertions, 1 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 73862c8..ba02f00 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2226,6 +2226,7 @@ device firewire # FireWire bus code
device sbp # SCSI over Firewire (Requires scbus and da)
device sbp_targ # SBP-2 Target mode (Requires scbus and targ)
device fwe # Ethernet over FireWire (non-standard!)
+device fwip # IP over FireWire (rfc2734 and rfc3146)
#####################################################################
# dcons support (Dumb Console Device)
diff --git a/sys/conf/files b/sys/conf/files
index b87a866..72afd93 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -447,6 +447,7 @@ dev/firewire/fwmem.c optional firewire
dev/firewire/fwohci.c optional firewire
dev/firewire/fwohci_pci.c optional firewire pci
dev/firewire/if_fwe.c optional fwe
+dev/firewire/if_fwip.c optional fwip
dev/firewire/sbp.c optional sbp
dev/firewire/sbp_targ.c optional sbp_targ
dev/fxp/if_fxp.c optional fxp
@@ -1216,6 +1217,7 @@ net/if_ef.c optional ef
net/if_ethersubr.c optional ether
net/if_faith.c optional faith
net/if_fddisubr.c optional fddi
+net/if_fwsubr.c optional firewire
net/if_gif.c optional gif
net/if_gre.c optional gre
net/if_iso88025subr.c optional token
diff --git a/sys/dev/firewire/if_fwip.c b/sys/dev/firewire/if_fwip.c
new file mode 100644
index 0000000..1d10afe
--- /dev/null
+++ b/sys/dev/firewire/if_fwip.c
@@ -0,0 +1,917 @@
+/*
+ * Copyright (c) 2004
+ * Doug Rabson
+ * Copyright (c) 2002-2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * 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
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/taskqueue.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/firewire.h>
+#include <net/if_arp.h>
+#ifdef __DragonFly__
+#include <bus/firewire/firewire.h>
+#include <bus/firewire/firewirereg.h>
+#include "if_fwipvar.h"
+#else
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/firewirereg.h>
+#include <dev/firewire/if_fwipvar.h>
+#endif
+
+/*
+ * We really need a mechanism for allocating regions in the FIFO
+ * address space. We pick a address in the OHCI controller's 'middle'
+ * address space. This means that the controller will automatically
+ * send responses for us, which is fine since we don't have any
+ * important information to put in the response anyway.
+ */
+#define INET_FIFO 0xfffe00000000LL
+
+#define FWIPDEBUG if (fwipdebug) if_printf
+#define TX_MAX_QUEUE (FWMAXQUEUE - 1)
+
+/* network interface */
+static void fwip_start (struct ifnet *);
+static int fwip_ioctl (struct ifnet *, u_long, caddr_t);
+static void fwip_init (void *);
+
+static void fwip_post_busreset (void *);
+static void fwip_output_callback (struct fw_xfer *);
+static void fwip_async_output (struct fwip_softc *, struct ifnet *);
+static void fwip_start_send (void *, int);
+static void fwip_stream_input (struct fw_xferq *);
+static void fwip_unicast_input(struct fw_xfer *);
+
+static int fwipdebug = 0;
+static int broadcast_channel = 31; /* XXX */
+static int tx_speed = 2;
+static int rx_queue_len = FWMAXQUEUE;
+
+MALLOC_DEFINE(M_FWIP, "if_fwip", "IP over FireWire interface");
+SYSCTL_INT(_debug, OID_AUTO, if_fwip_debug, CTLFLAG_RW, &fwipdebug, 0, "");
+SYSCTL_DECL(_hw_firewire);
+SYSCTL_NODE(_hw_firewire, OID_AUTO, fwip, CTLFLAG_RD, 0,
+ "Firewire ip subsystem");
+SYSCTL_INT(_hw_firewire_fwip, OID_AUTO, rx_queue_len, CTLFLAG_RW, &rx_queue_len,
+ 0, "Length of the receive queue");
+
+TUNABLE_INT("hw.firewire.fwip.rx_queue_len", &rx_queue_len);
+
+#ifdef DEVICE_POLLING
+#define FWIP_POLL_REGISTER(func, fwip, ifp) \
+ if (ether_poll_register(func, ifp)) { \
+ struct firewire_comm *fc = (fwip)->fd.fc; \
+ fc->set_intr(fc, 0); \
+ }
+
+#define FWIP_POLL_DEREGISTER(fwip, ifp) \
+ do { \
+ struct firewire_comm *fc = (fwip)->fd.fc; \
+ ether_poll_deregister(ifp); \
+ fc->set_intr(fc, 1); \
+ } while(0) \
+
+static poll_handler_t fwip_poll;
+
+static void
+fwip_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+ struct fwip_softc *fwip;
+ struct firewire_comm *fc;
+
+ fwip = ((struct fwip_eth_softc *)ifp->if_softc)->fwip;
+ fc = fwip->fd.fc;
+ if (cmd == POLL_DEREGISTER) {
+ /* enable interrupts */
+ fc->set_intr(fc, 1);
+ return;
+ }
+ fc->poll(fc, (cmd == POLL_AND_CHECK_STATUS)?0:1, count);
+}
+#else
+#define FWIP_POLL_REGISTER(func, fwip, ifp)
+#define FWIP_POLL_DEREGISTER(fwip, ifp)
+#endif
+static void
+fwip_identify(driver_t *driver, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "fwip", device_get_unit(parent));
+}
+
+static int
+fwip_probe(device_t dev)
+{
+ device_t pa;
+
+ pa = device_get_parent(dev);
+ if(device_get_unit(dev) != device_get_unit(pa)){
+ return(ENXIO);
+ }
+
+ device_set_desc(dev, "IP over FireWire");
+ return (0);
+}
+
+static int
+fwip_attach(device_t dev)
+{
+ struct fwip_softc *fwip;
+ struct ifnet *ifp;
+ int unit, s;
+ struct fw_hwaddr *hwaddr;
+
+ fwip = ((struct fwip_softc *)device_get_softc(dev));
+ unit = device_get_unit(dev);
+
+ bzero(fwip, sizeof(struct fwip_softc));
+ /* XXX */
+ fwip->dma_ch = -1;
+
+ fwip->fd.fc = device_get_ivars(dev);
+ if (tx_speed < 0)
+ tx_speed = fwip->fd.fc->speed;
+
+ fwip->fd.dev = dev;
+ fwip->fd.post_explore = NULL;
+ fwip->fd.post_busreset = fwip_post_busreset;
+ fwip->fw_softc.fwip = fwip;
+ TASK_INIT(&fwip->start_send, 0, fwip_start_send, fwip);
+
+ /*
+ * Encode our hardware the way that arp likes it.
+ */
+ hwaddr = &fwip->fw_softc.fwcom.fc_hwaddr;
+ hwaddr->sender_unique_ID_hi = htonl(fwip->fd.fc->eui.hi);
+ hwaddr->sender_unique_ID_lo = htonl(fwip->fd.fc->eui.lo);
+ hwaddr->sender_max_rec = fwip->fd.fc->maxrec;
+ hwaddr->sspd = fwip->fd.fc->speed;
+ hwaddr->sender_unicast_FIFO_hi = htons((uint16_t)(INET_FIFO >> 32));
+ hwaddr->sender_unicast_FIFO_lo = htonl((uint32_t)INET_FIFO);
+
+ /* fill the rest and attach interface */
+ ifp = &fwip->fwip_if;
+ ifp->if_softc = &fwip->fw_softc;
+
+#if __FreeBSD_version >= 501113 || defined(__DragonFly__)
+ if_initname(ifp, device_get_name(dev), unit);
+#else
+ ifp->if_unit = unit;
+ ifp->if_name = "fwip";
+#endif
+ ifp->if_init = fwip_init;
+ ifp->if_start = fwip_start;
+ ifp->if_ioctl = fwip_ioctl;
+ ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST);
+ ifp->if_snd.ifq_maxlen = TX_MAX_QUEUE;
+
+ s = splimp();
+ firewire_ifattach(ifp, hwaddr);
+ splx(s);
+
+ FWIPDEBUG(ifp, "interface created\n");
+ return 0;
+}
+
+static void
+fwip_stop(struct fwip_softc *fwip)
+{
+ struct firewire_comm *fc;
+ struct fw_xferq *xferq;
+ struct ifnet *ifp = &fwip->fwip_if;
+ struct fw_xfer *xfer, *next;
+ int i;
+
+ fc = fwip->fd.fc;
+
+ FWIP_POLL_DEREGISTER(fwip, ifp);
+
+ if (fwip->dma_ch >= 0) {
+ xferq = fc->ir[fwip->dma_ch];
+
+ if (xferq->flag & FWXFERQ_RUNNING)
+ fc->irx_disable(fc, fwip->dma_ch);
+ xferq->flag &=
+ ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM |
+ FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK);
+ xferq->hand = NULL;
+
+ for (i = 0; i < xferq->bnchunk; i ++)
+ m_freem(xferq->bulkxfer[i].mbuf);
+ free(xferq->bulkxfer, M_FWIP);
+
+ fw_bindremove(fc, &fwip->fwb);
+ for (xfer = STAILQ_FIRST(&fwip->fwb.xferlist); xfer != NULL;
+ xfer = next) {
+ next = STAILQ_NEXT(xfer, link);
+ fw_xfer_free(xfer);
+ }
+
+ for (xfer = STAILQ_FIRST(&fwip->xferlist); xfer != NULL;
+ xfer = next) {
+ next = STAILQ_NEXT(xfer, link);
+ fw_xfer_free(xfer);
+ }
+ STAILQ_INIT(&fwip->xferlist);
+
+ xferq->bulkxfer = NULL;
+ fwip->dma_ch = -1;
+ }
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+}
+
+static int
+fwip_detach(device_t dev)
+{
+ struct fwip_softc *fwip;
+ int s;
+
+ fwip = (struct fwip_softc *)device_get_softc(dev);
+ s = splimp();
+
+ fwip_stop(fwip);
+ firewire_ifdetach(&fwip->fwip_if);
+
+ splx(s);
+ return 0;
+}
+
+static void
+fwip_init(void *arg)
+{
+ struct fwip_softc *fwip = ((struct fwip_eth_softc *)arg)->fwip;
+ struct firewire_comm *fc;
+ struct ifnet *ifp = &fwip->fwip_if;
+ struct fw_xferq *xferq;
+ struct fw_xfer *xfer;
+ struct mbuf *m;
+ int i;
+
+ FWIPDEBUG(ifp, "initializing\n");
+
+ fc = fwip->fd.fc;
+#define START 0
+ if (fwip->dma_ch < 0) {
+ for (i = START; i < fc->nisodma; i ++) {
+ xferq = fc->ir[i];
+ if ((xferq->flag & FWXFERQ_OPEN) == 0)
+ goto found;
+ }
+ printf("no free dma channel\n");
+ return;
+found:
+ fwip->dma_ch = i;
+ /* allocate DMA channel and init packet mode */
+ xferq->flag |= FWXFERQ_OPEN | FWXFERQ_EXTBUF |
+ FWXFERQ_HANDLER | FWXFERQ_STREAM;
+ xferq->flag &= ~0xff;
+ xferq->flag |= broadcast_channel & 0xff;
+ /* register fwip_input handler */
+ xferq->sc = (caddr_t) fwip;
+ xferq->hand = fwip_stream_input;
+ xferq->bnchunk = rx_queue_len;
+ xferq->bnpacket = 1;
+ xferq->psize = MCLBYTES;
+ xferq->queued = 0;
+ xferq->buf = NULL;
+ xferq->bulkxfer = (struct fw_bulkxfer *) malloc(
+ sizeof(struct fw_bulkxfer) * xferq->bnchunk,
+ M_FWIP, M_WAITOK);
+ if (xferq->bulkxfer == NULL) {
+ printf("if_fwip: malloc failed\n");
+ return;
+ }
+ STAILQ_INIT(&xferq->stvalid);
+ STAILQ_INIT(&xferq->stfree);
+ STAILQ_INIT(&xferq->stdma);
+ xferq->stproc = NULL;
+ for (i = 0; i < xferq->bnchunk; i ++) {
+ m =
+#if defined(__DragonFly__) || __FreeBSD_version < 500000
+ m_getcl(M_WAIT, MT_DATA, M_PKTHDR);
+#else
+ m_getcl(M_TRYWAIT, MT_DATA, M_PKTHDR);
+#endif
+ xferq->bulkxfer[i].mbuf = m;
+ if (m != NULL) {
+ m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
+ STAILQ_INSERT_TAIL(&xferq->stfree,
+ &xferq->bulkxfer[i], link);
+ } else
+ printf("fwip_as_input: m_getcl failed\n");
+ }
+
+ fwip->fwb.start = INET_FIFO;
+ fwip->fwb.end = INET_FIFO + 16384; /* S3200 packet size */
+ fwip->fwb.act_type = FWACT_XFER;
+
+ /* pre-allocate xfer */
+ STAILQ_INIT(&fwip->fwb.xferlist);
+ for (i = 0; i < rx_queue_len; i ++) {
+ xfer = fw_xfer_alloc(M_FWIP);
+ if (xfer == NULL)
+ break;
+ m = m_getcl(M_TRYWAIT, MT_DATA, M_PKTHDR);
+ xfer->recv.payload = mtod(m, uint32_t *);
+ xfer->recv.pay_len = MCLBYTES;
+ xfer->act.hand = fwip_unicast_input;
+ xfer->fc = fc;
+ xfer->sc = (caddr_t)fwip;
+ xfer->mbuf = m;
+ STAILQ_INSERT_TAIL(&fwip->fwb.xferlist, xfer, link);
+ }
+ fw_bindadd(fc, &fwip->fwb);
+
+ STAILQ_INIT(&fwip->xferlist);
+ for (i = 0; i < TX_MAX_QUEUE; i++) {
+ xfer = fw_xfer_alloc(M_FWIP);
+ if (xfer == NULL)
+ break;
+ xfer->send.spd = tx_speed;
+ xfer->fc = fwip->fd.fc;
+ xfer->retry_req = fw_asybusy;
+ xfer->sc = (caddr_t)fwip;
+ xfer->act.hand = fwip_output_callback;
+ STAILQ_INSERT_TAIL(&fwip->xferlist, xfer, link);
+ }
+ } else
+ xferq = fc->ir[fwip->dma_ch];
+
+ fwip->last_dest.hi = 0;
+ fwip->last_dest.lo = 0;
+
+ /* start dma */
+ if ((xferq->flag & FWXFERQ_RUNNING) == 0)
+ fc->irx_enable(fc, fwip->dma_ch);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ FWIP_POLL_REGISTER(fwip_poll, fwip, ifp);
+#if 0
+ /* attempt to start output */
+ fwip_start(ifp);
+#endif
+}
+
+static int
+fwip_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct fwip_softc *fwip = ((struct fwip_eth_softc *)ifp->if_softc)->fwip;
+ int s, error;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ s = splimp();
+ if (ifp->if_flags & IFF_UP) {
+ if (!(ifp->if_flags & IFF_RUNNING))
+ fwip_init(&fwip->fw_softc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ fwip_stop(fwip);
+ }
+ splx(s);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ break;
+
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+ default:
+#else
+ case SIOCSIFADDR:
+ case SIOCGIFADDR:
+ case SIOCSIFMTU:
+#endif
+ s = splimp();
+ error = firewire_ioctl(ifp, cmd, data);
+ splx(s);
+ return (error);
+#if defined(__DragonFly__) || __FreeBSD_version < 500000
+ default:
+ return (EINVAL);
+#endif
+ }
+
+ return (0);
+}
+
+static void
+fwip_post_busreset(void *arg)
+{
+ struct fwip_softc *fwip = arg;
+
+ fwip->last_dest.hi = 0;
+ fwip->last_dest.lo = 0;
+ firewire_busreset(&fwip->fwip_if);
+}
+
+static void
+fwip_output_callback(struct fw_xfer *xfer)
+{
+ struct fwip_softc *fwip;
+ struct ifnet *ifp;
+ int s;
+
+ GIANT_REQUIRED;
+
+ fwip = (struct fwip_softc *)xfer->sc;
+ ifp = &fwip->fwip_if;
+ /* XXX error check */
+ FWIPDEBUG(ifp, "resp = %d\n", xfer->resp);
+ if (xfer->resp != 0)
+ ifp->if_oerrors ++;
+
+ m_freem(xfer->mbuf);
+ fw_xfer_unload(xfer);
+
+ s = splimp();
+ STAILQ_INSERT_TAIL(&fwip->xferlist, xfer, link);
+ splx(s);
+
+ /* for queue full */
+ if (ifp->if_snd.ifq_head != NULL)
+ fwip_start(ifp);
+}
+
+static void
+fwip_start(struct ifnet *ifp)
+{
+ struct fwip_softc *fwip = ((struct fwip_eth_softc *)ifp->if_softc)->fwip;
+ int s;
+
+ GIANT_REQUIRED;
+
+ FWIPDEBUG(ifp, "starting\n");
+
+ if (fwip->dma_ch < 0) {
+ struct mbuf *m = NULL;
+
+ FWIPDEBUG(ifp, "not ready\n");
+
+ s = splimp();
+ do {
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m != NULL)
+ m_freem(m);
+ ifp->if_oerrors ++;
+ } while (m != NULL);
+ splx(s);
+
+ return;
+ }
+
+ s = splimp();
+ ifp->if_flags |= IFF_OACTIVE;
+
+ if (ifp->if_snd.ifq_len != 0)
+ fwip_async_output(fwip, ifp);
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+ splx(s);
+}
+
+/* Async. stream output */
+static void
+fwip_async_output(struct fwip_softc *fwip, struct ifnet *ifp)
+{
+ struct firewire_comm *fc = fwip->fd.fc;
+ struct mbuf *m;
+ struct m_tag *mtag;
+ struct fw_hwaddr *destfw;
+ struct fw_xfer *xfer;
+ struct fw_xferq *xferq;
+ struct fw_pkt *fp;
+ uint16_t nodeid;
+ int i = 0;
+
+ GIANT_REQUIRED;
+
+ xfer = NULL;
+ xferq = fwip->fd.fc->atq;
+ while (xferq->queued < xferq->maxq - 1) {
+ xfer = STAILQ_FIRST(&fwip->xferlist);
+ if (xfer == NULL) {
+ printf("if_fwip: lack of xfer\n");
+ return;
+ }
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+
+ /*
+ * Dig out the link-level address which
+ * firewire_output got via arp or neighbour
+ * discovery. If we don't have a link-level address,
+ * just stick the thing on the broadcast channel.
+ */
+ mtag = m_tag_locate(m, MTAG_FIREWIRE, MTAG_FIREWIRE_HWADDR, 0);
+ if (mtag == NULL)
+ destfw = 0;
+ else
+ destfw = (struct fw_hwaddr *) (mtag + 1);
+
+ STAILQ_REMOVE_HEAD(&fwip->xferlist, link);
+
+ /*
+ * We don't do any bpf stuff here - the generic code
+ * in firewire_output gives the packet to bpf before
+ * it adds the link-level encapsulation.
+ */
+
+ /*
+ * Put the mbuf in the xfer early in case we hit an
+ * error case below - fwip_output_callback will free
+ * the mbuf.
+ */
+ xfer->mbuf = m;
+
+ /*
+ * We use the arp result (if any) to add a suitable firewire
+ * packet header before handing off to the bus.
+ */
+ fp = &xfer->send.hdr;
+ nodeid = FWLOCALBUS | fc->nodeid;
+ if ((m->m_flags & M_BCAST) || !destfw) {
+ /*
+ * Broadcast packets are sent as GASP packets with
+ * specifier ID 0x00005e, version 1 on the broadcast
+ * channel. To be conservative, we send at the
+ * slowest possible speed.
+ */
+ uint32_t *p;
+
+ M_PREPEND(m, 2*sizeof(uint32_t), M_DONTWAIT);
+ p = mtod(m, uint32_t *);
+ fp->mode.stream.len = m->m_pkthdr.len;
+ fp->mode.stream.chtag = broadcast_channel;
+ fp->mode.stream.tcode = FWTCODE_STREAM;
+ fp->mode.stream.sy = 0;
+ xfer->send.spd = 0;
+ p[0] = htonl(nodeid << 16);
+ p[1] = htonl((0x5e << 24) | 1);
+ } else {
+ /*
+ * Unicast packets are sent as block writes to the
+ * target's unicast fifo address. If we can't
+ * find the node address, we just give up. We
+ * could broadcast it but that might overflow
+ * the packet size limitations due to the
+ * extra GASP header. Note: the hardware
+ * address is stored in network byte order to
+ * make life easier for ARP.
+ */
+ struct fw_device *fd;
+ struct fw_eui64 eui;
+
+ eui.hi = ntohl(destfw->sender_unique_ID_hi);
+ eui.lo = ntohl(destfw->sender_unique_ID_lo);
+ if (fwip->last_dest.hi != eui.hi ||
+ fwip->last_dest.lo != eui.lo) {
+ fd = fw_noderesolve_eui64(fc, &eui);
+ if (!fd) {
+ /* error */
+ ifp->if_oerrors ++;
+ /* XXX set error code */
+ fwip_output_callback(xfer);
+ continue;
+
+ }
+ fwip->last_hdr.mode.wreqb.dst = FWLOCALBUS | fd->dst;
+ fwip->last_hdr.mode.wreqb.tlrt = 0;
+ fwip->last_hdr.mode.wreqb.tcode = FWTCODE_WREQB;
+ fwip->last_hdr.mode.wreqb.pri = 0;
+ fwip->last_hdr.mode.wreqb.src = nodeid;
+ fwip->last_hdr.mode.wreqb.dest_hi =
+ ntohs(destfw->sender_unicast_FIFO_hi);
+ fwip->last_hdr.mode.wreqb.dest_lo =
+ ntohl(destfw->sender_unicast_FIFO_lo);
+ fwip->last_hdr.mode.wreqb.extcode = 0;
+ fwip->last_dest = eui;
+ }
+
+ fp->mode.wreqb = fwip->last_hdr.mode.wreqb;
+ fp->mode.wreqb.len = m->m_pkthdr.len;
+ xfer->send.spd = min(destfw->sspd, fc->speed);
+ }
+
+ xfer->send.pay_len = m->m_pkthdr.len;
+
+ if (fw_asyreq(fc, -1, xfer) != 0) {
+ /* error */
+ ifp->if_oerrors ++;
+ /* XXX set error code */
+ fwip_output_callback(xfer);
+ continue;
+ } else {
+ ifp->if_opackets ++;
+ i++;
+ }
+ }
+#if 0
+ if (i > 1)
+ printf("%d queued\n", i);
+#endif
+ if (i > 0) {
+#if 1
+ xferq->start(fc);
+#else
+ taskqueue_enqueue(taskqueue_swi_giant, &fwip->start_send);
+#endif
+ }
+}
+
+static void
+fwip_start_send (void *arg, int count)
+{
+ struct fwip_softc *fwip = arg;
+
+ GIANT_REQUIRED;
+ fwip->fd.fc->atq->start(fwip->fd.fc);
+}
+
+/* Async. stream output */
+static void
+fwip_stream_input(struct fw_xferq *xferq)
+{
+ struct mbuf *m, *m0;
+ struct m_tag *mtag;
+ struct ifnet *ifp;
+ struct fwip_softc *fwip;
+ struct fw_bulkxfer *sxfer;
+ struct fw_pkt *fp;
+ uint16_t src;
+ uint32_t *p;
+
+ GIANT_REQUIRED;
+
+ fwip = (struct fwip_softc *)xferq->sc;
+ ifp = &fwip->fwip_if;
+#if 0
+ FWIP_POLL_REGISTER(fwip_poll, fwip, ifp);
+#endif
+ while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) {
+ STAILQ_REMOVE_HEAD(&xferq->stvalid, link);
+ fp = mtod(sxfer->mbuf, struct fw_pkt *);
+ if (fwip->fd.fc->irx_post != NULL)
+ fwip->fd.fc->irx_post(fwip->fd.fc, fp->mode.ld);
+ m = sxfer->mbuf;
+
+ /* insert new rbuf */
+ sxfer->mbuf = m0 = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m0 != NULL) {
+ m0->m_len = m0->m_pkthdr.len = m0->m_ext.ext_size;
+ STAILQ_INSERT_TAIL(&xferq->stfree, sxfer, link);
+ } else
+ printf("fwip_as_input: m_getcl failed\n");
+
+ /*
+ * We must have a GASP header - leave the
+ * encapsulation sanity checks to the generic
+ * code. Remeber that we also have the firewire async
+ * stream header even though that isn't accounted for
+ * in mode.stream.len.
+ */
+ if (sxfer->resp != 0 || fp->mode.stream.len <
+ 2*sizeof(uint32_t)) {
+ m_freem(m);
+ ifp->if_ierrors ++;
+ continue;
+ }
+ m->m_len = m->m_pkthdr.len = fp->mode.stream.len
+ + sizeof(fp->mode.stream);
+
+ /*
+ * If we received the packet on the broadcast channel,
+ * mark it as broadcast, otherwise we assume it must
+ * be multicast.
+ */
+ if (fp->mode.stream.chtag == broadcast_channel)
+ m->m_flags |= M_BCAST;
+ else
+ m->m_flags |= M_MCAST;
+
+ /*
+ * Make sure we recognise the GASP specifier and
+ * version.
+ */
+ p = mtod(m, uint32_t *);
+ if ((((ntohl(p[1]) & 0xffff) << 8) | ntohl(p[2]) >> 24) != 0x00005e
+ || (ntohl(p[2]) & 0xffffff) != 1) {
+ FWIPDEBUG(ifp, "Unrecognised GASP header %#08x %#08x\n",
+ ntohl(p[1]), ntohl(p[2]));
+ m_freem(m);
+ ifp->if_ierrors ++;
+ continue;
+ }
+
+ /*
+ * Record the sender ID for possible BPF usage.
+ */
+ src = ntohl(p[1]) >> 16;
+ if (ifp->if_bpf) {
+ mtag = m_tag_alloc(MTAG_FIREWIRE,
+ MTAG_FIREWIRE_SENDER_EUID,
+ 2*sizeof(uint32_t), M_NOWAIT);
+ if (mtag) {
+ /* bpf wants it in network byte order */
+ struct fw_device *fd;
+ uint32_t *p = (uint32_t *) (mtag + 1);
+ fd = fw_noderesolve_nodeid(fwip->fd.fc,
+ src & 0x3f);
+ if (fd) {
+ p[0] = htonl(fd->eui.hi);
+ p[1] = htonl(fd->eui.lo);
+ } else {
+ p[0] = 0;
+ p[1] = 0;
+ }
+ m_tag_prepend(m, mtag);
+ }
+ }
+
+ /*
+ * Trim off the GASP header
+ */
+ m_adj(m, 3*sizeof(uint32_t));
+ m->m_pkthdr.rcvif = ifp;
+ firewire_input(ifp, m, src);
+ ifp->if_ipackets ++;
+ }
+ if (STAILQ_FIRST(&xferq->stfree) != NULL)
+ fwip->fd.fc->irx_enable(fwip->fd.fc, fwip->dma_ch);
+}
+
+static __inline void
+fwip_unicast_input_recycle(struct fwip_softc *fwip, struct fw_xfer *xfer)
+{
+ struct mbuf *m;
+
+ GIANT_REQUIRED;
+
+ /*
+ * We have finished with a unicast xfer. Allocate a new
+ * cluster and stick it on the back of the input queue.
+ */
+ m = m_getcl(M_TRYWAIT, MT_DATA, M_PKTHDR);
+ xfer->mbuf = m;
+ xfer->recv.payload = mtod(m, uint32_t *);
+ xfer->recv.pay_len = MCLBYTES;
+ xfer->mbuf = m;
+ STAILQ_INSERT_TAIL(&fwip->fwb.xferlist, xfer, link);
+}
+
+static void
+fwip_unicast_input(struct fw_xfer *xfer)
+{
+ uint64_t address;
+ struct mbuf *m;
+ struct m_tag *mtag;
+ struct ifnet *ifp;
+ struct fwip_softc *fwip;
+ struct fw_pkt *fp;
+ //struct fw_pkt *sfp;
+ int rtcode;
+
+ GIANT_REQUIRED;
+
+ fwip = (struct fwip_softc *)xfer->sc;
+ ifp = &fwip->fwip_if;
+ m = xfer->mbuf;
+ xfer->mbuf = 0;
+ fp = &xfer->recv.hdr;
+
+ /*
+ * Check the fifo address - we only accept addresses of
+ * exactly INET_FIFO.
+ */
+ address = ((uint64_t)fp->mode.wreqb.dest_hi << 32)
+ | fp->mode.wreqb.dest_lo;
+ if (fp->mode.wreqb.tcode != FWTCODE_WREQB) {
+ rtcode = FWRCODE_ER_TYPE;
+ } else if (address != INET_FIFO) {
+ rtcode = FWRCODE_ER_ADDR;
+ } else {
+ rtcode = FWRCODE_COMPLETE;
+ }
+
+ /*
+ * Pick up a new mbuf and stick it on the back of the receive
+ * queue.
+ */
+ fwip_unicast_input_recycle(fwip, xfer);
+
+ /*
+ * If we've already rejected the packet, give up now.
+ */
+ if (rtcode != FWRCODE_COMPLETE) {
+ m_freem(m);
+ ifp->if_ierrors ++;
+ return;
+ }
+
+ if (ifp->if_bpf) {
+ /*
+ * Record the sender ID for possible BPF usage.
+ */
+ mtag = m_tag_alloc(MTAG_FIREWIRE, MTAG_FIREWIRE_SENDER_EUID,
+ 2*sizeof(uint32_t), M_NOWAIT);
+ if (mtag) {
+ /* bpf wants it in network byte order */
+ struct fw_device *fd;
+ uint32_t *p = (uint32_t *) (mtag + 1);
+ fd = fw_noderesolve_nodeid(fwip->fd.fc,
+ fp->mode.wreqb.src & 0x3f);
+ if (fd) {
+ p[0] = htonl(fd->eui.hi);
+ p[1] = htonl(fd->eui.lo);
+ } else {
+ p[0] = 0;
+ p[1] = 0;
+ }
+ m_tag_prepend(m, mtag);
+ }
+ }
+
+ /*
+ * Hand off to the generic encapsulation code. We don't use
+ * ifp->if_input so that we can pass the source nodeid as an
+ * argument to facilitate link-level fragment reassembly.
+ */
+ m->m_len = m->m_pkthdr.len = fp->mode.wreqb.len;
+ m->m_pkthdr.rcvif = ifp;
+ firewire_input(ifp, m, fp->mode.wreqb.src);
+ ifp->if_ipackets ++;
+}
+
+static devclass_t fwip_devclass;
+
+static device_method_t fwip_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, fwip_identify),
+ DEVMETHOD(device_probe, fwip_probe),
+ DEVMETHOD(device_attach, fwip_attach),
+ DEVMETHOD(device_detach, fwip_detach),
+ { 0, 0 }
+};
+
+static driver_t fwip_driver = {
+ "fwip",
+ fwip_methods,
+ sizeof(struct fwip_softc),
+};
+
+
+#ifdef __DragonFly__
+DECLARE_DUMMY_MODULE(fwip);
+#endif
+DRIVER_MODULE(fwip, firewire, fwip_driver, fwip_devclass, 0, 0);
+MODULE_VERSION(fwip, 1);
+MODULE_DEPEND(fwip, firewire, 1, 1, 1);
diff --git a/sys/dev/firewire/if_fwipvar.h b/sys/dev/firewire/if_fwipvar.h
new file mode 100644
index 0000000..5542328
--- /dev/null
+++ b/sys/dev/firewire/if_fwipvar.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2004
+ * Doug Rabson
+ * Copyright (c) 2002-2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * 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
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NET_IF_FWIPVAR_H_
+#define _NET_IF_FWIPVAR_H_
+
+struct fwip_softc {
+ /* XXX this must be first for fd.post_explore() */
+ struct firewire_dev_comm fd;
+ short dma_ch;
+ struct fw_bind fwb;
+ struct fw_eui64 last_dest;
+ struct fw_pkt last_hdr;
+ struct task start_send;
+ STAILQ_HEAD(, fw_xfer) xferlist;
+ struct fwip_eth_softc {
+ /* XXX this must be the first for if_fwsubr.c */
+ struct fw_com fwcom; /* firewire common data */
+ #define fwip_if fw_softc.fwcom.fc_if
+ struct fwip_softc *fwip;
+ } fw_softc;
+};
+#endif /* !_NET_IF_FWIPVAR_H_ */
diff --git a/sys/net/firewire.h b/sys/net/firewire.h
new file mode 100644
index 0000000..86cd023
--- /dev/null
+++ b/sys/net/firewire.h
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 2004 Doug Rabson
+ * All rights reserved.
+ *
+ * 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
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NET_FIREWIRE_H_
+#define _NET_FIREWIRE_H_
+
+#define FW_ENCAP_UNFRAG 0
+#define FW_ENCAP_FIRST 1
+#define FW_ENCAP_LAST 2
+#define FW_ENCAP_NEXT 3
+
+union fw_encap {
+ uint32_t ul[2];
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ uint32_t lf :2;
+ uint32_t reserved :14;
+ uint32_t ether_type :16;
+#else
+ uint32_t ether_type :16;
+ uint32_t reserved :14;
+ uint32_t lf :2;
+#endif
+ } unfrag;
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ uint32_t lf :2;
+ uint32_t reserved1 :2;
+ uint32_t datagram_size :12
+ uint32_t ether_type :16;
+ uint32_t dgl :16;
+ uint32_t reserved2 :16;
+#else
+ uint32_t ether_type :16;
+ uint32_t datagram_size :12;
+ uint32_t reserved1 :2;
+ uint32_t lf :2;
+ uint32_t reserved2 :16;
+ uint32_t dgl :16;
+#endif
+ } firstfrag;
+ struct {
+#if BYTE_ORDER == BIG_ENDIAN
+ uint32_t lf :2;
+ uint32_t reserved1 :2;
+ uint32_t datagram_size :12
+ uint32_t reserved2 :4;
+ uint32_t fragment_offset :12;
+ uint32_t dgl :16;
+ uint32_t reserved3 :16;
+#else
+ uint32_t fragment_offset :12;
+ uint32_t reserved2 :4;
+ uint32_t datagram_size :12;
+ uint32_t reserved1 :2;
+ uint32_t lf :2;
+ uint32_t reserved3 :16;
+ uint32_t dgl :16;
+#endif
+ } nextfrag;
+};
+
+#define MTAG_FIREWIRE 1394
+#define MTAG_FIREWIRE_HWADDR 0
+#define MTAG_FIREWIRE_SENDER_EUID 1
+
+struct fw_hwaddr {
+ uint32_t sender_unique_ID_hi;
+ uint32_t sender_unique_ID_lo;
+ uint8_t sender_max_rec;
+ uint8_t sspd;
+ uint16_t sender_unicast_FIFO_hi;
+ uint32_t sender_unicast_FIFO_lo;
+};
+
+/*
+ * BPF wants to see one of these.
+ */
+struct fw_bpfhdr {
+ uint8_t firewire_dhost[8];
+ uint8_t firewire_shost[8];
+ uint16_t firewire_type;
+};
+
+#ifdef _KERNEL
+
+/*
+ * A structure to track the reassembly of a link-level fragmented
+ * datagram.
+ */
+struct fw_reass {
+ STAILQ_ENTRY(fw_reass) fr_link;
+ uint32_t fr_id; /* host+dgl */
+ struct mbuf *fr_frags; /* chain of frags */
+};
+STAILQ_HEAD(fw_reass_list, fw_reass);
+
+struct fw_com {
+ struct ifnet fc_if;
+ struct fw_hwaddr fc_hwaddr;
+ struct firewire_comm *fc_fc;
+ uint8_t fc_broadcast_channel;
+ uint8_t fc_speed; /* our speed */
+ uint16_t fc_node; /* our nodeid */
+ struct fw_reass_list fc_frags; /* partial datagrams */
+};
+
+extern void firewire_input(struct ifnet *ifp, struct mbuf *m, uint16_t src);
+extern void firewire_ifattach(struct ifnet *, struct fw_hwaddr *);
+extern void firewire_ifdetach(struct ifnet *);
+extern void firewire_busreset(struct ifnet *);
+extern int firewire_ioctl(struct ifnet *, int, caddr_t);
+
+#endif /* !_KERNEL */
+
+#endif /* !_NET_FIREWIRE_H_ */
diff --git a/sys/net/if_arp.h b/sys/net/if_arp.h
index 6cc3ce9..a9cdd42 100644
--- a/sys/net/if_arp.h
+++ b/sys/net/if_arp.h
@@ -49,6 +49,7 @@ struct arphdr {
#define ARPHRD_IEEE802 6 /* token-ring hardware format */
#define ARPHRD_ARCNET 7 /* arcnet hardware format */
#define ARPHRD_FRELAY 15 /* frame relay hardware format */
+#define ARPHRD_IEEE1394 24 /* firewire hardware format */
u_short ar_pro; /* format of protocol address */
u_char ar_hln; /* length of hardware address */
u_char ar_pln; /* length of protocol address */
diff --git a/sys/net/if_fwsubr.c b/sys/net/if_fwsubr.c
new file mode 100644
index 0000000..72390e9
--- /dev/null
+++ b/sys/net/if_fwsubr.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 2004 Doug Rabson
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <net/if_llc.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/bpf.h>
+#include <net/firewire.h>
+
+#if defined(INET) || defined(INET6)
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip_fw.h>
+#include <netinet/ip_dummynet.h>
+#endif
+#ifdef INET6
+#include <netinet6/nd6.h>
+#endif
+
+#define IFP2FC(IFP) ((struct fw_com *)IFP)
+
+struct fw_hwaddr firewire_broadcastaddr = {
+ 0xffffffff,
+ 0xffffffff,
+ 0xff,
+ 0xff,
+ 0xffff,
+ 0xffffffff
+};
+
+static int
+firewire_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
+ struct rtentry *rt0)
+{
+ struct fw_com *fc = (struct fw_com *) ifp;
+ int error, type;
+ struct rtentry *rt;
+ struct m_tag *mtag;
+ union fw_encap *enc;
+ struct fw_hwaddr *destfw;
+ uint8_t speed;
+ uint16_t psize, fsize, dsize;
+ struct mbuf *mtail;
+ int unicast, dgl, foff;
+ static int next_dgl;
+
+ GIANT_REQUIRED;
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ error = ENETDOWN;
+ goto bad;
+ }
+
+ error = rt_check(&rt, &rt0, dst);
+ if (error)
+ goto bad;
+
+ /*
+ * For unicast, we make a tag to store the lladdr of the
+ * destination. This might not be the first time we have seen
+ * the packet (for instance, the arp code might be trying to
+ * re-send it after receiving an arp reply) so we only
+ * allocate a tag if there isn't one there already. For
+ * multicast, we will eventually use a different tag to store
+ * the channel number.
+ */
+ unicast = !(m->m_flags & (M_BCAST | M_MCAST));
+ if (unicast) {
+ mtag = m_tag_locate(m, MTAG_FIREWIRE, MTAG_FIREWIRE_HWADDR, NULL);
+ if (!mtag) {
+ mtag = m_tag_alloc(MTAG_FIREWIRE, MTAG_FIREWIRE_HWADDR,
+ sizeof (struct fw_hwaddr), M_NOWAIT);
+ if (!mtag) {
+ error = ENOMEM;
+ goto bad;
+ }
+ m_tag_prepend(m, mtag);
+ }
+ destfw = (struct fw_hwaddr *)(mtag + 1);
+ } else {
+ destfw = 0;
+ }
+
+ switch (dst->sa_family) {
+#ifdef AF_INET
+ case AF_INET:
+ /*
+ * Only bother with arp for unicast. Allocation of
+ * channels etc. for firewire is quite different and
+ * doesn't fit into the arp model.
+ */
+ if (unicast) {
+ error = arpresolve(ifp, rt, m, dst, (u_char *) destfw);
+ if (error)
+ return (error == EWOULDBLOCK ? 0 : error);
+ }
+ type = ETHERTYPE_IP;
+ break;
+
+ case AF_ARP:
+ {
+ struct arphdr *ah;
+ ah = mtod(m, struct arphdr *);
+ ah->ar_hrd = htons(ARPHRD_IEEE1394);
+ type = ETHERTYPE_ARP;
+ if (unicast)
+ *destfw = *(struct fw_hwaddr *) ar_tha(ah);
+
+ /*
+ * The standard arp code leaves a hole for the target
+ * hardware address which we need to close up.
+ */
+ bcopy(ar_tpa(ah), ar_tha(ah), ah->ar_pln);
+ m_adj(m, -ah->ar_hln);
+ break;
+ }
+#endif
+
+#ifdef INET6
+ case AF_INET6:
+ if (unicast) {
+ error = nd6_storelladdr(&fc->fc_if, rt, m, dst,
+ (u_char *) destfw);
+ if (error)
+ return (error);
+ }
+ type = ETHERTYPE_IPV6;
+ break;
+#endif
+
+ default:
+ if_printf(ifp, "can't handle af%d\n", dst->sa_family);
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+
+ /*
+ * Let BPF tap off a copy before we encapsulate.
+ */
+ if (ifp->if_bpf) {
+ struct fw_bpfhdr h;
+ if (unicast)
+ bcopy(destfw, h.firewire_dhost, 8);
+ else
+ bcopy(&firewire_broadcastaddr, h.firewire_dhost, 8);
+ bcopy(&fc->fc_hwaddr, h.firewire_shost, 8);
+ h.firewire_type = htons(type);
+ bpf_mtap2(ifp->if_bpf, &h, sizeof(h), m);
+ }
+
+ /*
+ * Punt on MCAP for now and send all multicast packets on the
+ * broadcast channel.
+ */
+ if (m->m_flags & M_MCAST)
+ m->m_flags |= M_BCAST;
+
+ /*
+ * Figure out what speed to use and what the largest supported
+ * packet size is. For unicast, this is the minimum of what we
+ * can speak and what they can hear. For broadcast, lets be
+ * conservative and use S100. We could possibly improve that
+ * by examining the bus manager's speed map or similar. We
+ * also reduce the packet size for broadcast to account for
+ * the GASP header.
+ */
+ if (unicast) {
+ speed = min(fc->fc_speed, destfw->sspd);
+ psize = min(512 << speed, 2 << destfw->sender_max_rec);
+ } else {
+ speed = 0;
+ psize = 512 - 2*sizeof(uint32_t);
+ }
+
+ /*
+ * Next, we encapsulate, possibly fragmenting the original
+ * datagram if it won't fit into a single packet.
+ */
+ if (m->m_pkthdr.len <= psize - sizeof(uint32_t)) {
+ /*
+ * No fragmentation is necessary.
+ */
+ M_PREPEND(m, sizeof(uint32_t), M_DONTWAIT);
+ if (!m) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ enc = mtod(m, union fw_encap *);
+ enc->unfrag.ether_type = type;
+ enc->unfrag.lf = FW_ENCAP_UNFRAG;
+
+ /*
+ * Byte swap the encapsulation header manually.
+ */
+ enc->ul[0] = htonl(enc->ul[0]);
+
+ return (IF_HANDOFF(&ifp->if_snd, m, ifp) ? 0 : ENOBUFS);
+ } else {
+ /*
+ * Fragment the datagram, making sure to leave enough
+ * space for the encapsulation header in each packet.
+ */
+ fsize = psize - 2*sizeof(uint32_t);
+ dgl = next_dgl++;
+ dsize = m->m_pkthdr.len;
+ foff = 0;
+ while (m) {
+ if (m->m_pkthdr.len > fsize) {
+ /*
+ * Split off the tail segment from the
+ * datagram, copying our tags over.
+ */
+ mtail = m_split(m, fsize, M_DONTWAIT);
+ m_tag_copy_chain(mtail, m, M_NOWAIT);
+ } else {
+ mtail = 0;
+ }
+
+ /*
+ * Add our encapsulation header to this
+ * fragment and hand it off to the link.
+ */
+ M_PREPEND(m, 2*sizeof(uint32_t), M_DONTWAIT);
+ if (!m) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ enc = mtod(m, union fw_encap *);
+ if (foff == 0) {
+ enc->firstfrag.lf = FW_ENCAP_FIRST;
+ enc->firstfrag.datagram_size = dsize - 1;
+ enc->firstfrag.ether_type = type;
+ enc->firstfrag.dgl = dgl;
+ } else {
+ if (mtail)
+ enc->nextfrag.lf = FW_ENCAP_NEXT;
+ else
+ enc->nextfrag.lf = FW_ENCAP_LAST;
+ enc->nextfrag.datagram_size = dsize - 1;
+ enc->nextfrag.fragment_offset = foff;
+ enc->nextfrag.dgl = dgl;
+ }
+ foff += m->m_pkthdr.len - 2*sizeof(uint32_t);
+
+ /*
+ * Byte swap the encapsulation header manually.
+ */
+ enc->ul[0] = htonl(enc->ul[0]);
+ enc->ul[1] = htonl(enc->ul[1]);
+
+ if (!IF_HANDOFF(&ifp->if_snd, m, ifp)) {
+ if (mtail)
+ m_freem(mtail);
+ return (ENOBUFS);
+ }
+
+ m = mtail;
+ }
+
+ return (0);
+ }
+
+bad:
+ if (m)
+ m_freem(m);
+ return (error);
+}
+
+static struct mbuf *
+firewire_input_fragment(struct fw_com *fc, struct mbuf *m, int src)
+{
+ union fw_encap *enc;
+ struct fw_reass *r;
+ struct mbuf *mf, *mprev;
+ int dsize;
+ int fstart, fend, start, end, islast;
+ uint32_t id;
+
+ GIANT_REQUIRED;
+
+ /*
+ * Find an existing reassembly buffer or create a new one.
+ */
+ enc = mtod(m, union fw_encap *);
+ id = enc->firstfrag.dgl | (src << 16);
+ STAILQ_FOREACH(r, &fc->fc_frags, fr_link)
+ if (r->fr_id == id)
+ break;
+ if (!r) {
+ r = malloc(sizeof(struct fw_reass), M_TEMP, M_NOWAIT);
+ if (!r) {
+ m_freem(m);
+ return 0;
+ }
+ r->fr_id = id;
+ r->fr_frags = 0;
+ STAILQ_INSERT_HEAD(&fc->fc_frags, r, fr_link);
+ }
+
+ /*
+ * If this fragment overlaps any other fragment, we must discard
+ * the partial reassembly and start again.
+ */
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ fstart = 0;
+ else
+ fstart = enc->nextfrag.fragment_offset;
+ fend = fstart + m->m_pkthdr.len - 2*sizeof(uint32_t);
+ dsize = enc->nextfrag.datagram_size;
+ islast = (enc->nextfrag.lf == FW_ENCAP_LAST);
+
+ for (mf = r->fr_frags; mf; mf = mf->m_nextpkt) {
+ enc = mtod(mf, union fw_encap *);
+ if (enc->nextfrag.datagram_size != dsize) {
+ /*
+ * This fragment must be from a different
+ * packet.
+ */
+ goto bad;
+ }
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ start = 0;
+ else
+ start = enc->nextfrag.fragment_offset;
+ end = start + mf->m_pkthdr.len - 2*sizeof(uint32_t);
+ if ((fstart < end && fend > start) ||
+ (islast && enc->nextfrag.lf == FW_ENCAP_LAST)) {
+ /*
+ * Overlap - discard reassembly buffer and start
+ * again with this fragment.
+ */
+ goto bad;
+ }
+ }
+
+ /*
+ * Find where to put this fragment in the list.
+ */
+ for (mf = r->fr_frags, mprev = NULL; mf;
+ mprev = mf, mf = mf->m_nextpkt) {
+ enc = mtod(mf, union fw_encap *);
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ start = 0;
+ else
+ start = enc->nextfrag.fragment_offset;
+ if (start >= fend)
+ break;
+ }
+
+ /*
+ * If this is a last fragment and we are not adding at the end
+ * of the list, discard the buffer.
+ */
+ if (islast && mprev && mprev->m_nextpkt)
+ goto bad;
+
+ if (mprev) {
+ m->m_nextpkt = mprev->m_nextpkt;
+ mprev->m_nextpkt = m;
+
+ /*
+ * Coalesce forwards and see if we can make a whole
+ * datagram.
+ */
+ enc = mtod(mprev, union fw_encap *);
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ start = 0;
+ else
+ start = enc->nextfrag.fragment_offset;
+ end = start + mprev->m_pkthdr.len - 2*sizeof(uint32_t);
+ while (end == fstart) {
+ /*
+ * Strip off the encap header from m and
+ * append it to mprev, freeing m.
+ */
+ m_adj(m, 2*sizeof(uint32_t));
+ mprev->m_nextpkt = m->m_nextpkt;
+ mprev->m_pkthdr.len += m->m_pkthdr.len;
+ m_cat(mprev, m);
+
+ if (mprev->m_pkthdr.len == dsize + 1 + 2*sizeof(uint32_t)) {
+ /*
+ * We have assembled a complete packet
+ * we must be finished. Make sure we have
+ * merged the whole chain.
+ */
+ STAILQ_REMOVE(&fc->fc_frags, r, fw_reass, fr_link);
+ free(r, M_TEMP);
+ m = mprev->m_nextpkt;
+ while (m) {
+ mf = m->m_nextpkt;
+ m_freem(m);
+ m = mf;
+ }
+ mprev->m_nextpkt = NULL;
+
+ return (mprev);
+ }
+
+ /*
+ * See if we can continue merging forwards.
+ */
+ end = fend;
+ m = mprev->m_nextpkt;
+ if (m) {
+ enc = mtod(m, union fw_encap *);
+ if (enc->firstfrag.lf == FW_ENCAP_FIRST)
+ fstart = 0;
+ else
+ fstart = enc->nextfrag.fragment_offset;
+ fend = fstart + m->m_pkthdr.len
+ - 2*sizeof(uint32_t);
+ } else {
+ break;
+ }
+ }
+ } else {
+ m->m_nextpkt = 0;
+ r->fr_frags = m;
+ }
+
+ return (0);
+
+bad:
+ while (r->fr_frags) {
+ mf = r->fr_frags;
+ r->fr_frags = mf->m_nextpkt;
+ m_freem(mf);
+ }
+ m->m_nextpkt = 0;
+ r->fr_frags = m;
+
+ return (0);
+}
+
+void
+firewire_input(struct ifnet *ifp, struct mbuf *m, uint16_t src)
+{
+ struct fw_com *fc = (struct fw_com *) ifp;
+ union fw_encap *enc;
+ int type, isr;
+
+ GIANT_REQUIRED;
+
+ /*
+ * The caller has already stripped off the packet header
+ * (stream or wreqb) and marked the mbuf's M_BCAST flag
+ * appropriately. We de-encapsulate the IP packet and pass it
+ * up the line after handling link-level fragmentation.
+ */
+ if (m->m_pkthdr.len < sizeof(uint32_t)) {
+ if_printf(ifp, "discarding frame without "
+ "encapsulation header (len %u pkt len %u)\n",
+ m->m_len, m->m_pkthdr.len);
+ }
+
+ m = m_pullup(m, sizeof(uint32_t));
+ enc = mtod(m, union fw_encap *);
+
+ /*
+ * Byte swap the encapsulation header manually.
+ */
+ enc->ul[0] = htonl(enc->ul[0]);
+
+ if (enc->unfrag.lf != 0) {
+ m = m_pullup(m, 2*sizeof(uint32_t));
+ if (!m)
+ return;
+ enc = mtod(m, union fw_encap *);
+ enc->ul[1] = htonl(enc->ul[1]);
+ m = firewire_input_fragment(fc, m, src);
+ if (!m)
+ return;
+ enc = mtod(m, union fw_encap *);
+ type = enc->firstfrag.ether_type;
+ m_adj(m, 2*sizeof(uint32_t));
+ } else {
+ type = enc->unfrag.ether_type;
+ m_adj(m, sizeof(uint32_t));
+ }
+
+ if (m->m_pkthdr.rcvif == NULL) {
+ if_printf(ifp, "discard frame w/o interface pointer\n");
+ ifp->if_ierrors++;
+ m_freem(m);
+ return;
+ }
+#ifdef DIAGNOSTIC
+ if (m->m_pkthdr.rcvif != ifp) {
+ if_printf(ifp, "Warning, frame marked as received on %s\n",
+ m->m_pkthdr.rcvif->if_xname);
+ }
+#endif
+
+#ifdef MAC
+ /*
+ * Tag the mbuf with an appropriate MAC label before any other
+ * consumers can get to it.
+ */
+ mac_create_mbuf_from_ifnet(ifp, m);
+#endif
+
+ /*
+ * Give bpf a chance at the packet. The link-level driver
+ * should have left us a tag with the EUID of the sender.
+ */
+ if (ifp->if_bpf) {
+ struct fw_bpfhdr h;
+ struct m_tag *mtag;
+
+ mtag = m_tag_locate(m, MTAG_FIREWIRE, MTAG_FIREWIRE_SENDER_EUID, 0);
+ if (mtag)
+ bcopy(mtag + 1, h.firewire_shost, 8);
+ else
+ bcopy(&firewire_broadcastaddr, h.firewire_dhost, 8);
+ bcopy(&fc->fc_hwaddr, h.firewire_dhost, 8);
+ h.firewire_type = htons(type);
+ bpf_mtap2(ifp->if_bpf, &h, sizeof(h), m);
+ }
+
+ if (ifp->if_flags & IFF_MONITOR) {
+ /*
+ * Interface marked for monitoring; discard packet.
+ */
+ m_freem(m);
+ return;
+ }
+
+ ifp->if_ibytes += m->m_pkthdr.len;
+
+ /* Discard packet if interface is not up */
+ if ((ifp->if_flags & IFF_UP) == 0) {
+ m_freem(m);
+ return;
+ }
+
+ if (m->m_flags & (M_BCAST|M_MCAST))
+ ifp->if_imcasts++;
+
+ switch (type) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ if (ip_fastforward(m))
+ return;
+ isr = NETISR_IP;
+ break;
+
+ case ETHERTYPE_ARP:
+ {
+ struct arphdr *ah;
+ ah = mtod(m, struct arphdr *);
+
+ /*
+ * Adjust the arp packet to insert an empty tha slot.
+ */
+ m->m_len += ah->ar_hln;
+ m->m_pkthdr.len += ah->ar_hln;
+ bcopy(ar_tha(ah), ar_tpa(ah), ah->ar_pln);
+ isr = NETISR_ARP;
+ break;
+ }
+#endif
+
+#ifdef INET6
+ case ETHERTYPE_IPV6:
+ isr = NETISR_IPV6;
+ break;
+#endif
+
+ default:
+ m_freem(m);
+ return;
+ }
+
+ netisr_dispatch(isr, m);
+}
+
+int
+firewire_ioctl(struct ifnet *ifp, int command, caddr_t data)
+{
+ struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0;
+
+ switch (command) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ifp->if_init(ifp->if_softc); /* before arpwhohas */
+ arp_ifinit(ifp, ifa);
+ break;
+#endif
+ default:
+ ifp->if_init(ifp->if_softc);
+ break;
+ }
+ break;
+
+ case SIOCGIFADDR:
+ {
+ struct sockaddr *sa;
+
+ sa = (struct sockaddr *) & ifr->ifr_data;
+ bcopy(&IFP2FC(ifp)->fc_hwaddr,
+ (caddr_t) sa->sa_data, sizeof(struct fw_hwaddr));
+ }
+ break;
+
+ case SIOCSIFMTU:
+ /*
+ * Set the interface MTU.
+ */
+ if (ifr->ifr_mtu > 1500) {
+ error = EINVAL;
+ } else {
+ ifp->if_mtu = ifr->ifr_mtu;
+ }
+ break;
+ default:
+ error = EINVAL; /* XXX netbsd has ENOTTY??? */
+ break;
+ }
+ return (error);
+}
+
+static int
+firewire_resolvemulti(struct ifnet *ifp, struct sockaddr **llsa,
+ struct sockaddr *sa)
+{
+#ifdef INET
+ struct sockaddr_in *sin;
+#endif
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch(sa->sa_family) {
+ case AF_LINK:
+ /*
+ * No mapping needed.
+ */
+ *llsa = 0;
+ return 0;
+
+#ifdef INET
+ case AF_INET:
+ sin = (struct sockaddr_in *)sa;
+ if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
+ return EADDRNOTAVAIL;
+ *llsa = 0;
+ return 0;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)sa;
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ /*
+ * An IP6 address of 0 means listen to all
+ * of the Ethernet multicast address used for IP6.
+ * (This is used for multicast routers.)
+ */
+ ifp->if_flags |= IFF_ALLMULTI;
+ *llsa = 0;
+ return 0;
+ }
+ if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ return EADDRNOTAVAIL;
+ *llsa = 0;
+ return 0;
+#endif
+
+ default:
+ /*
+ * Well, the text isn't quite right, but it's the name
+ * that counts...
+ */
+ return EAFNOSUPPORT;
+ }
+}
+
+void
+firewire_ifattach(struct ifnet *ifp, struct fw_hwaddr *llc)
+{
+ struct fw_com *fc = (struct fw_com *) ifp;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ static const char* speeds[] = {
+ "S100", "S200", "S400", "S800",
+ "S1600", "S3200"
+ };
+
+ fc->fc_speed = llc->sspd;
+ STAILQ_INIT(&fc->fc_frags);
+
+ ifp->if_type = IFT_IEEE1394;
+ ifp->if_addrlen = sizeof(struct fw_hwaddr);
+ ifp->if_hdrlen = 0;
+ if_attach(ifp);
+ ifp->if_mtu = 1500; /* XXX */
+ ifp->if_output = firewire_output;
+ ifp->if_resolvemulti = firewire_resolvemulti;
+ ifp->if_broadcastaddr = (u_char *) &firewire_broadcastaddr;
+
+ ifa = ifaddr_byindex(ifp->if_index);
+ KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_IEEE1394;
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy(llc, LLADDR(sdl), ifp->if_addrlen);
+
+ bpfattach(ifp, DLT_APPLE_IP_OVER_IEEE1394,
+ sizeof(struct fw_hwaddr));
+
+ if_printf(ifp, "Firewire address: %8D @ 0x%04x%08x, %s, maxrec %d\n",
+ (uint8_t *) &llc->sender_unique_ID_hi, ":",
+ ntohs(llc->sender_unicast_FIFO_hi),
+ ntohl(llc->sender_unicast_FIFO_lo),
+ speeds[llc->sspd],
+ (2 << llc->sender_max_rec));
+}
+
+void
+firewire_ifdetach(struct ifnet *ifp)
+{
+ bpfdetach(ifp);
+ if_detach(ifp);
+}
+
+void
+firewire_busreset(struct ifnet *ifp)
+{
+ struct fw_com *fc = (struct fw_com *) ifp;
+ struct fw_reass *r;
+ struct mbuf *m;
+
+ /*
+ * Discard any partial datagrams since the host ids may have changed.
+ */
+ while ((r = STAILQ_FIRST(&fc->fc_frags))) {
+ STAILQ_REMOVE_HEAD(&fc->fc_frags, fr_link);
+ while (r->fr_frags) {
+ m = r->fr_frags;
+ r->fr_frags = m->m_nextpkt;
+ m_freem(m);
+ }
+ free(r, M_TEMP);
+ }
+}
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
index b24d562..b392168 100644
--- a/sys/netinet/if_ether.c
+++ b/sys/netinet/if_ether.c
@@ -460,7 +460,8 @@ arpintr(struct mbuf *m)
if (ntohs(ar->ar_hrd) != ARPHRD_ETHER &&
ntohs(ar->ar_hrd) != ARPHRD_IEEE802 &&
- ntohs(ar->ar_hrd) != ARPHRD_ARCNET) {
+ ntohs(ar->ar_hrd) != ARPHRD_ARCNET &&
+ ntohs(ar->ar_hrd) != ARPHRD_IEEE1394) {
log(LOG_ERR, "arp: unknown hardware address format (0x%2D)\n",
(unsigned char *)&ar->ar_hrd, "");
m_freem(m);
OpenPOWER on IntegriCloud