diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 73862c84004f..ba02f00bb3f5 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 b87a8663e3c0..72afd9366420 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 000000000000..1d10afe83b21 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef __DragonFly__ +#include +#include +#include "if_fwipvar.h" +#else +#include +#include +#include +#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 000000000000..554232894f4f --- /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 000000000000..86cd02383634 --- /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 6cc3ce9ab187..a9cdd42a3dfb 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 000000000000..72390e91322a --- /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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(INET) || defined(INET6) +#include +#include +#include +#include +#include +#endif +#ifdef INET6 +#include +#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 b24d562cfd47..b3921689b0aa 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);