diff options
| author | Doug Rabson <dfr@FreeBSD.org> | 2004-06-13 10:54:36 +0000 |
|---|---|---|
| committer | Doug Rabson <dfr@FreeBSD.org> | 2004-06-13 10:54:36 +0000 |
| commit | b8b3323469c7fac150559f9acbc488cc2cd79f74 (patch) | |
| tree | f9ff797a413f188d7a3cfe59c9ce64b4963e6b1b /sys/dev/firewire | |
| parent | 1da2ceea66690211a03fc9e156b35113a24cc379 (diff) | |
Notes
Diffstat (limited to 'sys/dev/firewire')
| -rw-r--r-- | sys/dev/firewire/if_fwip.c | 917 | ||||
| -rw-r--r-- | sys/dev/firewire/if_fwipvar.h | 58 |
2 files changed, 975 insertions, 0 deletions
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 <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 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_ */ |
