Index: head/sys/dev/firewire/fwdev.c =================================================================== --- head/sys/dev/firewire/fwdev.c (revision 299350) +++ head/sys/dev/firewire/fwdev.c (revision 299351) @@ -1,952 +1,944 @@ /*- * Copyright (c) 2003 Hidetoshi Shimokawa * Copyright (c) 1998-2002 Katsushi Kobayashi and 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 acknowledgement as bellow: * * This product includes software developed by K. Kobayashi and H. Shimokawa * * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FWNODE_INVAL 0xffff static d_open_t fw_open; static d_close_t fw_close; static d_ioctl_t fw_ioctl; static d_poll_t fw_poll; static d_read_t fw_read; /* for Isochronous packet */ static d_write_t fw_write; static d_mmap_t fw_mmap; static d_strategy_t fw_strategy; struct cdevsw firewire_cdevsw = { .d_version = D_VERSION, .d_open = fw_open, .d_close = fw_close, .d_read = fw_read, .d_write = fw_write, .d_ioctl = fw_ioctl, .d_poll = fw_poll, .d_mmap = fw_mmap, .d_strategy = fw_strategy, .d_name = "fw", }; struct fw_drv1 { struct firewire_comm *fc; struct fw_xferq *ir; struct fw_xferq *it; struct fw_isobufreq bufreq; STAILQ_HEAD(, fw_bind) binds; STAILQ_HEAD(, fw_xfer) rq; }; static int fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q, struct fw_bufspec *b) { int i; if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF)) return (EBUSY); q->bulkxfer = malloc(sizeof(struct fw_bulkxfer) * b->nchunk, M_FW, M_WAITOK); - if (q->bulkxfer == NULL) - return (ENOMEM); b->psize = roundup2(b->psize, sizeof(uint32_t)); q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t), b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK); if (q->buf == NULL) { free(q->bulkxfer, M_FW); q->bulkxfer = NULL; return (ENOMEM); } q->bnchunk = b->nchunk; q->bnpacket = b->npacket; q->psize = (b->psize + 3) & ~3; q->queued = 0; STAILQ_INIT(&q->stvalid); STAILQ_INIT(&q->stfree); STAILQ_INIT(&q->stdma); q->stproc = NULL; for (i = 0; i < q->bnchunk; i++) { q->bulkxfer[i].poffset = i * q->bnpacket; q->bulkxfer[i].mbuf = NULL; STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link); } q->flag &= ~FWXFERQ_MODEMASK; q->flag |= FWXFERQ_STREAM; q->flag |= FWXFERQ_EXTBUF; return (0); } static int fwdev_freebuf(struct fw_xferq *q) { if (q->flag & FWXFERQ_EXTBUF) { if (q->buf != NULL) fwdma_free_multiseg(q->buf); q->buf = NULL; free(q->bulkxfer, M_FW); q->bulkxfer = NULL; q->flag &= ~FWXFERQ_EXTBUF; q->psize = 0; q->maxq = FWMAXQUEUE; } return (0); } static int fw_open(struct cdev *dev, int flags, int fmt, fw_proc *td) { int err = 0; int unit = DEV2UNIT(dev); struct fw_drv1 *d; struct firewire_softc *sc; if (DEV_FWMEM(dev)) return fwmem_open(dev, flags, fmt, td); sc = devclass_get_softc(firewire_devclass, unit); if (sc == NULL) return (ENXIO); FW_GLOCK(sc->fc); if (dev->si_drv1 != NULL) { FW_GUNLOCK(sc->fc); return (EBUSY); } /* set dummy value for allocation */ dev->si_drv1 = (void *)-1; FW_GUNLOCK(sc->fc); dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO); - if (dev->si_drv1 == NULL) - return (ENOMEM); if ((dev->si_flags & SI_NAMED) == 0) { int unit = DEV2UNIT(dev); int sub = DEV2SUB(dev); make_dev(&firewire_cdevsw, dev2unit(dev), UID_ROOT, GID_OPERATOR, 0660, "fw%d.%d", unit, sub); } d = dev->si_drv1; d->fc = sc->fc; STAILQ_INIT(&d->binds); STAILQ_INIT(&d->rq); return err; } static int fw_close(struct cdev *dev, int flags, int fmt, fw_proc *td) { struct firewire_comm *fc; struct fw_drv1 *d; struct fw_xfer *xfer; struct fw_bind *fwb; int err = 0; if (DEV_FWMEM(dev)) return fwmem_close(dev, flags, fmt, td); d = dev->si_drv1; fc = d->fc; /* remove binding */ for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL; fwb = STAILQ_FIRST(&d->binds)) { fw_bindremove(fc, fwb); STAILQ_REMOVE_HEAD(&d->binds, chlist); fw_xferlist_remove(&fwb->xferlist); free(fwb, M_FW); } if (d->ir != NULL) { struct fw_xferq *ir = d->ir; if ((ir->flag & FWXFERQ_OPEN) == 0) return (EINVAL); if (ir->flag & FWXFERQ_RUNNING) { ir->flag &= ~FWXFERQ_RUNNING; fc->irx_disable(fc, ir->dmach); } /* free extbuf */ fwdev_freebuf(ir); /* drain receiving buffer */ for (xfer = STAILQ_FIRST(&ir->q); xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) { ir->queued--; STAILQ_REMOVE_HEAD(&ir->q, link); xfer->resp = 0; fw_xfer_done(xfer); } ir->flag &= ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); d->ir = NULL; } if (d->it != NULL) { struct fw_xferq *it = d->it; if ((it->flag & FWXFERQ_OPEN) == 0) return (EINVAL); if (it->flag & FWXFERQ_RUNNING) { it->flag &= ~FWXFERQ_RUNNING; fc->itx_disable(fc, it->dmach); } /* free extbuf */ fwdev_freebuf(it); it->flag &= ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); d->it = NULL; } free(dev->si_drv1, M_FW); dev->si_drv1 = NULL; return err; } static int fw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag) { int err = 0, s; struct fw_xfer *xfer; struct fw_bind *fwb; struct fw_pkt *fp; struct tcode_info *tinfo; FW_GLOCK(d->fc); while ((xfer = STAILQ_FIRST(&d->rq)) == NULL && err == 0) err = msleep(&d->rq, FW_GMTX(d->fc), FWPRI, "fwra", 0); if (err != 0) { FW_GUNLOCK(d->fc); return (err); } s = splfw(); STAILQ_REMOVE_HEAD(&d->rq, link); FW_GUNLOCK(xfer->fc); splx(s); fp = &xfer->recv.hdr; #if 0 /* for GASP ?? */ if (fc->irx_post != NULL) fc->irx_post(fc, fp->mode.ld); #endif tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode]; err = uiomove(fp, tinfo->hdr_len, uio); if (err) goto out; err = uiomove(xfer->recv.payload, xfer->recv.pay_len, uio); out: /* recycle this xfer */ fwb = (struct fw_bind *)xfer->sc; fw_xfer_unload(xfer); xfer->recv.pay_len = PAGE_SIZE; FW_GLOCK(xfer->fc); STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link); FW_GUNLOCK(xfer->fc); return (err); } /* * read request. */ static int fw_read(struct cdev *dev, struct uio *uio, int ioflag) { struct fw_drv1 *d; struct fw_xferq *ir; struct firewire_comm *fc; int err = 0, s, slept = 0; struct fw_pkt *fp; if (DEV_FWMEM(dev)) return (physio(dev, uio, ioflag)); d = dev->si_drv1; fc = d->fc; ir = d->ir; if (ir == NULL) return (fw_read_async(d, uio, ioflag)); if (ir->buf == NULL) return (EIO); FW_GLOCK(fc); readloop: if (ir->stproc == NULL) { /* iso bulkxfer */ ir->stproc = STAILQ_FIRST(&ir->stvalid); if (ir->stproc != NULL) { s = splfw(); STAILQ_REMOVE_HEAD(&ir->stvalid, link); splx(s); ir->queued = 0; } } if (ir->stproc == NULL) { /* no data available */ if (slept == 0) { slept = 1; ir->flag |= FWXFERQ_WAKEUP; err = msleep(ir, FW_GMTX(fc), FWPRI, "fw_read", hz); ir->flag &= ~FWXFERQ_WAKEUP; if (err == 0) goto readloop; } else if (slept == 1) err = EIO; FW_GUNLOCK(fc); return err; } else if (ir->stproc != NULL) { /* iso bulkxfer */ FW_GUNLOCK(fc); fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, ir->stproc->poffset + ir->queued); if (fc->irx_post != NULL) fc->irx_post(fc, fp->mode.ld); if (fp->mode.stream.len == 0) { err = EIO; return err; } err = uiomove((caddr_t)fp, fp->mode.stream.len + sizeof(uint32_t), uio); ir->queued++; if (ir->queued >= ir->bnpacket) { s = splfw(); STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); splx(s); fc->irx_enable(fc, ir->dmach); ir->stproc = NULL; } if (uio->uio_resid >= ir->psize) { slept = -1; FW_GLOCK(fc); goto readloop; } } return err; } static int fw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag) { struct fw_xfer *xfer; struct fw_pkt pkt; struct tcode_info *tinfo; int err; bzero(&pkt, sizeof(struct fw_pkt)); if ((err = uiomove((caddr_t)&pkt, sizeof(uint32_t), uio))) return (err); tinfo = &d->fc->tcode[pkt.mode.hdr.tcode]; if ((err = uiomove((caddr_t)&pkt + sizeof(uint32_t), tinfo->hdr_len - sizeof(uint32_t), uio))) return (err); if ((xfer = fw_xfer_alloc_buf(M_FWXFER, uio->uio_resid, PAGE_SIZE/*XXX*/)) == NULL) return (ENOMEM); bcopy(&pkt, &xfer->send.hdr, sizeof(struct fw_pkt)); xfer->send.pay_len = uio->uio_resid; if (uio->uio_resid > 0) { if ((err = uiomove((caddr_t)&xfer->send.payload[0], uio->uio_resid, uio))) goto out; } xfer->fc = d->fc; xfer->sc = NULL; xfer->hand = fw_xferwake; xfer->send.spd = 2 /* XXX */; if ((err = fw_asyreq(xfer->fc, -1, xfer))) goto out; if ((err = fw_xferwait(xfer))) goto out; if (xfer->resp != 0) { err = xfer->resp; goto out; } if (xfer->flag & FWXF_RCVD) { FW_GLOCK(xfer->fc); STAILQ_INSERT_TAIL(&d->rq, xfer, link); FW_GUNLOCK(xfer->fc); return (0); } out: fw_xfer_free(xfer); return (err); } static int fw_write(struct cdev *dev, struct uio *uio, int ioflag) { int err = 0; int s, slept = 0; struct fw_drv1 *d; struct fw_pkt *fp; struct firewire_comm *fc; struct fw_xferq *it; if (DEV_FWMEM(dev)) return (physio(dev, uio, ioflag)); d = dev->si_drv1; fc = d->fc; it = d->it; if (it == NULL) return (fw_write_async(d, uio, ioflag)); if (it->buf == NULL) return (EIO); FW_GLOCK(fc); isoloop: if (it->stproc == NULL) { it->stproc = STAILQ_FIRST(&it->stfree); if (it->stproc != NULL) { s = splfw(); STAILQ_REMOVE_HEAD(&it->stfree, link); splx(s); it->queued = 0; } else if (slept == 0) { slept = 1; #if 0 /* XXX to avoid lock recursion */ err = fc->itx_enable(fc, it->dmach); if (err) goto out; #endif err = msleep(it, FW_GMTX(fc), FWPRI, "fw_write", hz); if (err) goto out; goto isoloop; } else { err = EIO; goto out; } } FW_GUNLOCK(fc); fp = (struct fw_pkt *)fwdma_v_addr(it->buf, it->stproc->poffset + it->queued); err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); err = uiomove((caddr_t)fp->mode.stream.payload, fp->mode.stream.len, uio); it->queued++; if (it->queued >= it->bnpacket) { s = splfw(); STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); splx(s); it->stproc = NULL; err = fc->itx_enable(fc, it->dmach); } if (uio->uio_resid >= sizeof(struct fw_isohdr)) { slept = 0; FW_GLOCK(fc); goto isoloop; } return err; out: FW_GUNLOCK(fc); return err; } static void fw_hand(struct fw_xfer *xfer) { struct fw_bind *fwb; struct fw_drv1 *d; fwb = (struct fw_bind *)xfer->sc; d = fwb->sc; FW_GLOCK(xfer->fc); STAILQ_INSERT_TAIL(&d->rq, xfer, link); FW_GUNLOCK(xfer->fc); wakeup(&d->rq); } /* * ioctl support. */ int fw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td) { struct firewire_comm *fc; struct fw_drv1 *d; int i, len, err = 0; struct fw_device *fwdev; struct fw_bind *fwb; struct fw_xferq *ir, *it; struct fw_xfer *xfer; struct fw_pkt *fp; struct fw_devinfo *devinfo; void *ptr; struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; struct fw_asyreq *asyreq = (struct fw_asyreq *)data; struct fw_isochreq *ichreq = (struct fw_isochreq *)data; struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; if (DEV_FWMEM(dev)) return fwmem_ioctl(dev, cmd, data, flag, td); if (!data) return (EINVAL); d = dev->si_drv1; fc = d->fc; ir = d->ir; it = d->it; switch (cmd) { case FW_STSTREAM: if (it == NULL) { i = fw_open_isodma(fc, /* tx */1); if (i < 0) { err = EBUSY; break; } it = fc->it[i]; err = fwdev_allocbuf(fc, it, &d->bufreq.tx); if (err) { it->flag &= ~FWXFERQ_OPEN; break; } } it->flag &= ~0xff; it->flag |= (0x3f & ichreq->ch); it->flag |= ((0x3 & ichreq->tag) << 6); d->it = it; break; case FW_GTSTREAM: if (it != NULL) { ichreq->ch = it->flag & 0x3f; ichreq->tag = it->flag >> 2 & 0x3; } else err = EINVAL; break; case FW_SRSTREAM: if (ir == NULL) { i = fw_open_isodma(fc, /* tx */0); if (i < 0) { err = EBUSY; break; } ir = fc->ir[i]; err = fwdev_allocbuf(fc, ir, &d->bufreq.rx); if (err) { ir->flag &= ~FWXFERQ_OPEN; break; } } ir->flag &= ~0xff; ir->flag |= (0x3f & ichreq->ch); ir->flag |= ((0x3 & ichreq->tag) << 6); d->ir = ir; err = fc->irx_enable(fc, ir->dmach); break; case FW_GRSTREAM: if (d->ir != NULL) { ichreq->ch = ir->flag & 0x3f; ichreq->tag = ir->flag >> 2 & 0x3; } else err = EINVAL; break; case FW_SSTBUF: bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq)); break; case FW_GSTBUF: bzero(&ibufreq->rx, sizeof(ibufreq->rx)); if (ir != NULL) { ibufreq->rx.nchunk = ir->bnchunk; ibufreq->rx.npacket = ir->bnpacket; ibufreq->rx.psize = ir->psize; } bzero(&ibufreq->tx, sizeof(ibufreq->tx)); if (it != NULL) { ibufreq->tx.nchunk = it->bnchunk; ibufreq->tx.npacket = it->bnpacket; ibufreq->tx.psize = it->psize; } break; case FW_ASYREQ: { struct tcode_info *tinfo; int pay_len = 0; fp = &asyreq->pkt; tinfo = &fc->tcode[fp->mode.hdr.tcode]; if ((tinfo->flag & FWTI_BLOCK_ASY) != 0) pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len); xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/); if (xfer == NULL) return (ENOMEM); switch (asyreq->req.type) { case FWASREQNODE: break; case FWASREQEUI: fwdev = fw_noderesolve_eui64(fc, &asyreq->req.dst.eui); if (fwdev == NULL) { device_printf(fc->bdev, "cannot find node\n"); err = EINVAL; goto out; } fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst; break; case FWASRESTL: /* XXX what's this? */ break; case FWASREQSTREAM: /* nothing to do */ break; } bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len); if (pay_len > 0) bcopy((char *)fp + tinfo->hdr_len, xfer->send.payload, pay_len); xfer->send.spd = asyreq->req.sped; xfer->hand = fw_xferwake; if ((err = fw_asyreq(fc, -1, xfer)) != 0) goto out; if ((err = fw_xferwait(xfer)) != 0) goto out; if (xfer->resp != 0) { err = EIO; goto out; } if ((tinfo->flag & FWTI_TLABEL) == 0) goto out; /* copy response */ tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode]; if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB || xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) { pay_len = xfer->recv.pay_len; if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) { asyreq->req.len = xfer->recv.pay_len + tinfo->hdr_len; } else { err = EINVAL; pay_len = 0; } } else { pay_len = 0; } bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len); bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, pay_len); out: fw_xfer_free_buf(xfer); break; } case FW_IBUSRST: fc->ibr(fc); break; case FW_CBINDADDR: fwb = fw_bindlookup(fc, bindreq->start.hi, bindreq->start.lo); if (fwb == NULL) { err = EINVAL; break; } fw_bindremove(fc, fwb); STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist); fw_xferlist_remove(&fwb->xferlist); free(fwb, M_FW); break; case FW_SBINDADDR: if (bindreq->len <= 0) { err = EINVAL; break; } if (bindreq->start.hi > 0xffff) { err = EINVAL; break; } fwb = malloc(sizeof(struct fw_bind), M_FW, M_WAITOK); - if (fwb == NULL) { - err = ENOMEM; - break; - } fwb->start = ((u_int64_t)bindreq->start.hi << 32) | bindreq->start.lo; fwb->end = fwb->start + bindreq->len; fwb->sc = d; STAILQ_INIT(&fwb->xferlist); err = fw_bindadd(fc, fwb); if (err == 0) { fw_xferlist_add(&fwb->xferlist, M_FWXFER, /* XXX */ PAGE_SIZE, PAGE_SIZE, 5, fc, fwb, fw_hand); STAILQ_INSERT_TAIL(&d->binds, fwb, chlist); } break; case FW_GDEVLST: i = len = 1; /* myself */ devinfo = &fwdevlst->dev[0]; devinfo->dst = fc->nodeid; devinfo->status = 0; /* XXX */ devinfo->eui.hi = fc->eui.hi; devinfo->eui.lo = fc->eui.lo; STAILQ_FOREACH(fwdev, &fc->devices, link) { if (len < FW_MAX_DEVLST) { devinfo = &fwdevlst->dev[len++]; devinfo->dst = fwdev->dst; devinfo->status = (fwdev->status == FWDEVINVAL) ? 0 : 1; devinfo->eui.hi = fwdev->eui.hi; devinfo->eui.lo = fwdev->eui.lo; } i++; } fwdevlst->n = i; fwdevlst->info_len = len; break; case FW_GTPMAP: bcopy(fc->topology_map, data, (fc->topology_map->crc_len + 1) * 4); break; case FW_GCROM: STAILQ_FOREACH(fwdev, &fc->devices, link) if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui)) break; if (fwdev == NULL) { if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) { err = FWNODE_INVAL; break; } /* myself */ ptr = malloc(CROMSIZE, M_FW, M_WAITOK); len = CROMSIZE; for (i = 0; i < CROMSIZE/4; i++) ((uint32_t *)ptr)[i] = ntohl(fc->config_rom[i]); } else { /* found */ ptr = (void *)&fwdev->csrrom[0]; if (fwdev->rommax < CSRROMOFF) len = 0; else len = fwdev->rommax - CSRROMOFF + 4; } if (crom_buf->len < len) len = crom_buf->len; else crom_buf->len = len; err = copyout(ptr, crom_buf->ptr, len); if (fwdev == NULL) /* myself */ free(ptr, M_FW); break; default: fc->ioctl(dev, cmd, data, flag, td); break; } return err; } int fw_poll(struct cdev *dev, int events, fw_proc *td) { struct fw_xferq *ir; int revents; int tmp; if (DEV_FWMEM(dev)) return fwmem_poll(dev, events, td); ir = ((struct fw_drv1 *)dev->si_drv1)->ir; revents = 0; tmp = POLLIN | POLLRDNORM; if (events & tmp) { if (STAILQ_FIRST(&ir->q) != NULL) revents |= tmp; else selrecord(td, &ir->rsel); } tmp = POLLOUT | POLLWRNORM; if (events & tmp) { /* XXX should be fixed */ revents |= tmp; } return revents; } static int fw_mmap (struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nproto, vm_memattr_t *memattr) { if (DEV_FWMEM(dev)) return fwmem_mmap(dev, offset, paddr, nproto, memattr); return EINVAL; } static void fw_strategy(struct bio *bp) { struct cdev *dev; dev = bp->bio_dev; if (DEV_FWMEM(dev)) { fwmem_strategy(bp); return; } bp->bio_error = EOPNOTSUPP; bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount; biodone(bp); } int fwdev_makedev(struct firewire_softc *sc) { int err = 0; struct cdev *d; int unit; unit = device_get_unit(sc->fc->bdev); sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0), UID_ROOT, GID_OPERATOR, 0660, "fw%d.%d", unit, 0); d = make_dev(&firewire_cdevsw, MAKEMINOR(FWMEM_FLAG, unit, 0), UID_ROOT, GID_OPERATOR, 0660, "fwmem%d.%d", unit, 0); dev_depends(sc->dev, d); make_dev_alias(sc->dev, "fw%d", unit); make_dev_alias(d, "fwmem%d", unit); return (err); } int fwdev_destroydev(struct firewire_softc *sc) { int err = 0; destroy_dev(sc->dev); return (err); } #define NDEVTYPE 2 void fwdev_clone(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **dev) { struct firewire_softc *sc; char *devnames[NDEVTYPE] = {"fw", "fwmem"}; char *subp = NULL; int devflag[NDEVTYPE] = {0, FWMEM_FLAG}; int i, unit = 0, sub = 0; if (*dev != NULL) return; for (i = 0; i < NDEVTYPE; i++) if (dev_stdclone(name, &subp, devnames[i], &unit) == 2) goto found; /* not match */ return; found: if (subp == NULL || *subp++ != '.') return; /* /dev/fwU.S */ while (isdigit(*subp)) { sub *= 10; sub += *subp++ - '0'; } if (*subp != '\0') return; sc = devclass_get_softc(firewire_devclass, unit); if (sc == NULL) return; *dev = make_dev_credf(MAKEDEV_REF, &firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub), cred, UID_ROOT, GID_OPERATOR, 0660, "%s%d.%d", devnames[i], unit, sub); dev_depends(sc->dev, *dev); return; } Index: head/sys/dev/firewire/fwdma.c =================================================================== --- head/sys/dev/firewire/fwdma.c (revision 299350) +++ head/sys/dev/firewire/fwdma.c (revision 299351) @@ -1,214 +1,210 @@ /*- * Copyright (c) 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. * */ #ifdef __FreeBSD__ #include __FBSDID("$FreeBSD$"); #endif #include #include #include #include #include #include #include #include #include #include #include #include static void fwdma_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *baddr; if (error) printf("fwdma_map_cb: error=%d\n", error); baddr = (bus_addr_t *)arg; *baddr = segs->ds_addr; } void * fwdma_malloc(struct firewire_comm *fc, int alignment, bus_size_t size, struct fwdma_alloc *dma, int flag) { int err; dma->v_addr = NULL; err = bus_dma_tag_create( /*parent*/ fc->dmat, /*alignment*/ alignment, /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/ size, /*nsegments*/ 1, /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, /*flags*/ BUS_DMA_ALLOCNOW, /*lockfunc*/busdma_lock_mutex, /*lockarg*/FW_GMTX(fc), &dma->dma_tag); if (err) { printf("fwdma_malloc: failed(1)\n"); return (NULL); } err = bus_dmamem_alloc(dma->dma_tag, &dma->v_addr, flag, &dma->dma_map); if (err) { printf("fwdma_malloc: failed(2)\n"); /* XXX destroy tag */ return (NULL); } bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->v_addr, size, fwdma_map_cb, &dma->bus_addr, /*flags*/0); return (dma->v_addr); } void fwdma_free(struct firewire_comm *fc, struct fwdma_alloc *dma) { bus_dmamap_unload(dma->dma_tag, dma->dma_map); bus_dmamem_free(dma->dma_tag, dma->v_addr, dma->dma_map); bus_dma_tag_destroy(dma->dma_tag); } void * fwdma_malloc_size(bus_dma_tag_t dmat, bus_dmamap_t *dmamap, bus_size_t size, bus_addr_t *bus_addr, int flag) { void *v_addr; if (bus_dmamem_alloc(dmat, &v_addr, flag, dmamap)) { printf("fwdma_malloc_size: failed(1)\n"); return (NULL); } bus_dmamap_load(dmat, *dmamap, v_addr, size, fwdma_map_cb, bus_addr, /*flags*/0); return (v_addr); } void fwdma_free_size(bus_dma_tag_t dmat, bus_dmamap_t dmamap, void *vaddr, bus_size_t size) { bus_dmamap_unload(dmat, dmamap); bus_dmamem_free(dmat, vaddr, dmamap); } /* * Allocate multisegment dma buffers * each segment size is eqaul to ssize except last segment. */ struct fwdma_alloc_multi * fwdma_malloc_multiseg(struct firewire_comm *fc, int alignment, int esize, int n, int flag) { struct fwdma_alloc_multi *am; struct fwdma_seg *seg; bus_size_t ssize; int nseg; if (esize > PAGE_SIZE) { /* round up to PAGE_SIZE */ esize = ssize = roundup2(esize, PAGE_SIZE); nseg = n; } else { /* allocate PAGE_SIZE segment for small elements */ ssize = rounddown(PAGE_SIZE, esize); nseg = howmany(n, ssize / esize); } am = (struct fwdma_alloc_multi *)malloc(sizeof(struct fwdma_alloc_multi) + sizeof(struct fwdma_seg)*nseg, M_FW, M_WAITOK); - if (am == NULL) { - printf("fwdma_malloc_multiseg: malloc failed\n"); - return (NULL); - } am->ssize = ssize; am->esize = esize; am->nseg = 0; if (bus_dma_tag_create( /*parent*/ fc->dmat, /*alignment*/ alignment, /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/ ssize, /*nsegments*/ 1, /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, /*flags*/ BUS_DMA_ALLOCNOW, /*lockfunc*/busdma_lock_mutex, /*lockarg*/FW_GMTX(fc), &am->dma_tag)) { printf("fwdma_malloc_multiseg: tag_create failed\n"); free(am, M_FW); return (NULL); } for (seg = &am->seg[0]; nseg--; seg++) { seg->v_addr = fwdma_malloc_size(am->dma_tag, &seg->dma_map, ssize, &seg->bus_addr, flag); if (seg->v_addr == NULL) { printf("fwdma_malloc_multi: malloc_size failed %d\n", am->nseg); fwdma_free_multiseg(am); return (NULL); } am->nseg++; } return (am); } void fwdma_free_multiseg(struct fwdma_alloc_multi *am) { struct fwdma_seg *seg; for (seg = &am->seg[0]; am->nseg--; seg++) { fwdma_free_size(am->dma_tag, seg->dma_map, seg->v_addr, am->ssize); } bus_dma_tag_destroy(am->dma_tag); free(am, M_FW); } Index: head/sys/dev/firewire/fwmem.c =================================================================== --- head/sys/dev/firewire/fwmem.c (revision 299350) +++ head/sys/dev/firewire/fwmem.c (revision 299351) @@ -1,438 +1,436 @@ /*- * 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. * */ #ifdef __FreeBSD__ #include __FBSDID("$FreeBSD$"); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int fwmem_speed = 2, fwmem_debug = 0; static struct fw_eui64 fwmem_eui64; SYSCTL_DECL(_hw_firewire); static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0, "FireWire Memory Access"); SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW, &fwmem_eui64.hi, 0, "Fwmem target EUI64 high"); SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW, &fwmem_eui64.lo, 0, "Fwmem target EUI64 low"); SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0, "Fwmem link speed"); SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0, "Fwmem driver debug flag"); static MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire"); #define MAXLEN (512 << fwmem_speed) struct fwmem_softc { struct fw_eui64 eui; struct firewire_softc *sc; int refcount; }; static struct fw_xfer * fwmem_xfer_req( struct fw_device *fwdev, caddr_t sc, int spd, int slen, int rlen, void *hand) { struct fw_xfer *xfer; xfer = fw_xfer_alloc(M_FWMEM); if (xfer == NULL) return NULL; xfer->fc = fwdev->fc; xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst; if (spd < 0) xfer->send.spd = fwdev->speed; else xfer->send.spd = min(spd, fwdev->speed); xfer->hand = hand; xfer->sc = sc; xfer->send.pay_len = slen; xfer->recv.pay_len = rlen; return xfer; } struct fw_xfer * fwmem_read_quad( struct fw_device *fwdev, caddr_t sc, uint8_t spd, uint16_t dst_hi, uint32_t dst_lo, void *data, void (*hand)(struct fw_xfer *)) { struct fw_xfer *xfer; struct fw_pkt *fp; xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 4, hand); if (xfer == NULL) { return NULL; } fp = &xfer->send.hdr; fp->mode.rreqq.tcode = FWTCODE_RREQQ; fp->mode.rreqq.dest_hi = dst_hi; fp->mode.rreqq.dest_lo = dst_lo; xfer->send.payload = NULL; xfer->recv.payload = (uint32_t *)data; if (fwmem_debug) printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst, dst_hi, dst_lo); if (fw_asyreq(xfer->fc, -1, xfer) == 0) return xfer; fw_xfer_free(xfer); return NULL; } struct fw_xfer * fwmem_write_quad( struct fw_device *fwdev, caddr_t sc, uint8_t spd, uint16_t dst_hi, uint32_t dst_lo, void *data, void (*hand)(struct fw_xfer *)) { struct fw_xfer *xfer; struct fw_pkt *fp; xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand); if (xfer == NULL) return NULL; fp = &xfer->send.hdr; fp->mode.wreqq.tcode = FWTCODE_WREQQ; fp->mode.wreqq.dest_hi = dst_hi; fp->mode.wreqq.dest_lo = dst_lo; fp->mode.wreqq.data = *(uint32_t *)data; xfer->send.payload = xfer->recv.payload = NULL; if (fwmem_debug) printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst, dst_hi, dst_lo, *(uint32_t *)data); if (fw_asyreq(xfer->fc, -1, xfer) == 0) return xfer; fw_xfer_free(xfer); return NULL; } struct fw_xfer * fwmem_read_block( struct fw_device *fwdev, caddr_t sc, uint8_t spd, uint16_t dst_hi, uint32_t dst_lo, int len, void *data, void (*hand)(struct fw_xfer *)) { struct fw_xfer *xfer; struct fw_pkt *fp; xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand); if (xfer == NULL) return NULL; fp = &xfer->send.hdr; fp->mode.rreqb.tcode = FWTCODE_RREQB; fp->mode.rreqb.dest_hi = dst_hi; fp->mode.rreqb.dest_lo = dst_lo; fp->mode.rreqb.len = len; fp->mode.rreqb.extcode = 0; xfer->send.payload = NULL; xfer->recv.payload = data; if (fwmem_debug) printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst, dst_hi, dst_lo, len); if (fw_asyreq(xfer->fc, -1, xfer) == 0) return xfer; fw_xfer_free(xfer); return NULL; } struct fw_xfer * fwmem_write_block( struct fw_device *fwdev, caddr_t sc, uint8_t spd, uint16_t dst_hi, uint32_t dst_lo, int len, void *data, void (*hand)(struct fw_xfer *)) { struct fw_xfer *xfer; struct fw_pkt *fp; xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand); if (xfer == NULL) return NULL; fp = &xfer->send.hdr; fp->mode.wreqb.tcode = FWTCODE_WREQB; fp->mode.wreqb.dest_hi = dst_hi; fp->mode.wreqb.dest_lo = dst_lo; fp->mode.wreqb.len = len; fp->mode.wreqb.extcode = 0; xfer->send.payload = data; xfer->recv.payload = NULL; if (fwmem_debug) printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst, dst_hi, dst_lo, len); if (fw_asyreq(xfer->fc, -1, xfer) == 0) return xfer; fw_xfer_free(xfer); return NULL; } int fwmem_open(struct cdev *dev, int flags, int fmt, fw_proc *td) { struct fwmem_softc *fms; struct firewire_softc *sc; int unit = DEV2UNIT(dev); sc = devclass_get_softc(firewire_devclass, unit); if (sc == NULL) return (ENXIO); FW_GLOCK(sc->fc); if (dev->si_drv1 != NULL) { if ((flags & FWRITE) != 0) { FW_GUNLOCK(sc->fc); return (EBUSY); } FW_GUNLOCK(sc->fc); fms = dev->si_drv1; fms->refcount++; } else { dev->si_drv1 = (void *)-1; FW_GUNLOCK(sc->fc); dev->si_drv1 = malloc(sizeof(struct fwmem_softc), M_FWMEM, M_WAITOK); - if (dev->si_drv1 == NULL) - return (ENOMEM); dev->si_iosize_max = DFLTPHYS; fms = dev->si_drv1; bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64)); fms->sc = sc; fms->refcount = 1; } if (fwmem_debug) printf("%s: refcount=%d\n", __func__, fms->refcount); return (0); } int fwmem_close (struct cdev *dev, int flags, int fmt, fw_proc *td) { struct fwmem_softc *fms; fms = dev->si_drv1; FW_GLOCK(fms->sc->fc); fms->refcount--; FW_GUNLOCK(fms->sc->fc); if (fwmem_debug) printf("%s: refcount=%d\n", __func__, fms->refcount); if (fms->refcount < 1) { free(dev->si_drv1, M_FWMEM); dev->si_drv1 = NULL; } return (0); } static void fwmem_biodone(struct fw_xfer *xfer) { struct bio *bp; bp = (struct bio *)xfer->sc; bp->bio_error = xfer->resp; if (bp->bio_error != 0) { if (fwmem_debug) printf("%s: err=%d\n", __func__, bp->bio_error); bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount; } fw_xfer_free(xfer); biodone(bp); } void fwmem_strategy(struct bio *bp) { struct fwmem_softc *fms; struct fw_device *fwdev; struct fw_xfer *xfer; struct cdev *dev; int err = 0, iolen; dev = bp->bio_dev; /* XXX check request length */ fms = dev->si_drv1; fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui); if (fwdev == NULL) { if (fwmem_debug) printf("fwmem: no such device ID:%08x%08x\n", fms->eui.hi, fms->eui.lo); err = EINVAL; goto error; } iolen = MIN(bp->bio_bcount, MAXLEN); if (bp->bio_cmd == BIO_READ) { if (iolen == 4 && (bp->bio_offset & 3) == 0) xfer = fwmem_read_quad(fwdev, (void *)bp, fwmem_speed, bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, bp->bio_data, fwmem_biodone); else xfer = fwmem_read_block(fwdev, (void *)bp, fwmem_speed, bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, iolen, bp->bio_data, fwmem_biodone); } else { if (iolen == 4 && (bp->bio_offset & 3) == 0) xfer = fwmem_write_quad(fwdev, (void *)bp, fwmem_speed, bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, bp->bio_data, fwmem_biodone); else xfer = fwmem_write_block(fwdev, (void *)bp, fwmem_speed, bp->bio_offset >> 32, bp->bio_offset & 0xffffffff, iolen, bp->bio_data, fwmem_biodone); } if (xfer == NULL) { err = EIO; goto error; } /* XXX */ bp->bio_resid = bp->bio_bcount - iolen; error: if (err != 0) { if (fwmem_debug) printf("%s: err=%d\n", __func__, err); bp->bio_error = err; bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount; biodone(bp); } } int fwmem_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td) { struct fwmem_softc *fms; int err = 0; fms = dev->si_drv1; switch (cmd) { case FW_SDEUI64: bcopy(data, &fms->eui, sizeof(struct fw_eui64)); break; case FW_GDEUI64: bcopy(&fms->eui, data, sizeof(struct fw_eui64)); break; default: err = EINVAL; } return (err); } int fwmem_poll(struct cdev *dev, int events, fw_proc *td) { return EINVAL; } int fwmem_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nproto, vm_memattr_t *memattr) { return EINVAL; } Index: head/sys/dev/firewire/fwohci.c =================================================================== --- head/sys/dev/firewire/fwohci.c (revision 299350) +++ head/sys/dev/firewire/fwohci.c (revision 299351) @@ -1,2971 +1,2967 @@ /*- * Copyright (c) 2003 Hidetoshi Shimokawa * Copyright (c) 1998-2002 Katsushi Kobayashi and 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 acknowledgement as bellow: * * This product includes software developed by K. Kobayashi and H. Shimokawa * * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef OHCI_DEBUG static int nocyclemaster; int firewire_phydma_enable = 1; SYSCTL_DECL(_hw_firewire); SYSCTL_INT(_hw_firewire, OID_AUTO, nocyclemaster, CTLFLAG_RWTUN, &nocyclemaster, 0, "Do not send cycle start packets"); SYSCTL_INT(_hw_firewire, OID_AUTO, phydma_enable, CTLFLAG_RWTUN, &firewire_phydma_enable, 0, "Allow physical request DMA from firewire"); static char dbcode[16][0x10] = {"OUTM", "OUTL", "INPM", "INPL", "STOR", "LOAD", "NOP ", "STOP",}; static char dbkey[8][0x10] = {"ST0", "ST1", "ST2", "ST3", "UNDEF", "REG", "SYS", "DEV"}; static char dbcond[4][0x10] = {"NEV", "C=1", "C=0", "ALL"}; char fwohcicode[32][0x20]= { "No stat", "Undef", "long", "miss Ack err", "FIFO underrun", "FIFO overrun", "desc err", "data read err", "data write err", "bus reset", "timeout", "tcode err", "Undef", "Undef", "unknown event", "flushed", "Undef" ,"ack complete", "ack pend", "Undef", "ack busy_X", "ack busy_A", "ack busy_B", "Undef", "Undef", "Undef", "Undef", "ack tardy", "Undef", "ack data_err", "ack type_err", ""}; #define MAX_SPEED 3 extern char *linkspeed[]; uint32_t tagbit[4] = {1 << 28, 1 << 29, 1 << 30, 1 << 31}; static struct tcode_info tinfo[] = { /* hdr_len block flag valid_response */ /* 0 WREQQ */ {16, FWTI_REQ | FWTI_TLABEL, FWTCODE_WRES}, /* 1 WREQB */ {16, FWTI_REQ | FWTI_TLABEL | FWTI_BLOCK_ASY, FWTCODE_WRES}, /* 2 WRES */ {12, FWTI_RES, 0xff}, /* 3 XXX */ { 0, 0, 0xff}, /* 4 RREQQ */ {12, FWTI_REQ | FWTI_TLABEL, FWTCODE_RRESQ}, /* 5 RREQB */ {16, FWTI_REQ | FWTI_TLABEL, FWTCODE_RRESB}, /* 6 RRESQ */ {16, FWTI_RES, 0xff}, /* 7 RRESB */ {16, FWTI_RES | FWTI_BLOCK_ASY, 0xff}, /* 8 CYCS */ { 0, 0, 0xff}, /* 9 LREQ */ {16, FWTI_REQ | FWTI_TLABEL | FWTI_BLOCK_ASY, FWTCODE_LRES}, /* a STREAM */ { 4, FWTI_REQ | FWTI_BLOCK_STR, 0xff}, /* b LRES */ {16, FWTI_RES | FWTI_BLOCK_ASY, 0xff}, /* c XXX */ { 0, 0, 0xff}, /* d XXX */ { 0, 0, 0xff}, /* e PHY */ {12, FWTI_REQ, 0xff}, /* f XXX */ { 0, 0, 0xff} }; #define ATRQ_CH 0 #define ATRS_CH 1 #define ARRQ_CH 2 #define ARRS_CH 3 #define ITX_CH 4 #define IRX_CH 0x24 #define OHCI_WRITE_SIGMASK 0xffff0000 #define OHCI_READ_SIGMASK 0xffff0000 #define OWRITE(sc, r, x) bus_space_write_4((sc)->bst, (sc)->bsh, (r), (x)) #define OREAD(sc, r) bus_space_read_4((sc)->bst, (sc)->bsh, (r)) static void fwohci_ibr (struct firewire_comm *); static void fwohci_db_init (struct fwohci_softc *, struct fwohci_dbch *); static void fwohci_db_free (struct fwohci_dbch *); static void fwohci_arcv (struct fwohci_softc *, struct fwohci_dbch *, int); static void fwohci_txd (struct fwohci_softc *, struct fwohci_dbch *); static void fwohci_start_atq (struct firewire_comm *); static void fwohci_start_ats (struct firewire_comm *); static void fwohci_start (struct fwohci_softc *, struct fwohci_dbch *); static uint32_t fwphy_wrdata (struct fwohci_softc *, uint32_t, uint32_t); static uint32_t fwphy_rddata (struct fwohci_softc *, uint32_t); static int fwohci_rx_enable (struct fwohci_softc *, struct fwohci_dbch *); static int fwohci_tx_enable (struct fwohci_softc *, struct fwohci_dbch *); static int fwohci_irx_enable (struct firewire_comm *, int); static int fwohci_irx_disable (struct firewire_comm *, int); #if BYTE_ORDER == BIG_ENDIAN static void fwohci_irx_post (struct firewire_comm *, uint32_t *); #endif static int fwohci_itxbuf_enable (struct firewire_comm *, int); static int fwohci_itx_disable (struct firewire_comm *, int); static void fwohci_timeout (void *); static void fwohci_set_intr (struct firewire_comm *, int); static int fwohci_add_rx_buf (struct fwohci_dbch *, struct fwohcidb_tr *, int, struct fwdma_alloc *); static int fwohci_add_tx_buf (struct fwohci_dbch *, struct fwohcidb_tr *, int); static void dump_db (struct fwohci_softc *, uint32_t); static void print_db (struct fwohcidb_tr *, struct fwohcidb *, uint32_t , uint32_t); static void dump_dma (struct fwohci_softc *, uint32_t); static uint32_t fwohci_cyctimer (struct firewire_comm *); static void fwohci_rbuf_update (struct fwohci_softc *, int); static void fwohci_tbuf_update (struct fwohci_softc *, int); void fwohci_txbufdb (struct fwohci_softc *, int , struct fw_bulkxfer *); static void fwohci_task_busreset(void *, int); static void fwohci_task_sid(void *, int); static void fwohci_task_dma(void *, int); /* * memory allocated for DMA programs */ #define DMA_PROG_ALLOC (8 * PAGE_SIZE) #define NDB FWMAXQUEUE #define OHCI_VERSION 0x00 #define OHCI_ATRETRY 0x08 #define OHCI_CROMHDR 0x18 #define OHCI_BUS_OPT 0x20 #define OHCI_BUSIRMC (1U << 31) #define OHCI_BUSCMC (1 << 30) #define OHCI_BUSISC (1 << 29) #define OHCI_BUSBMC (1 << 28) #define OHCI_BUSPMC (1 << 27) #define OHCI_BUSFNC OHCI_BUSIRMC | OHCI_BUSCMC | OHCI_BUSISC |\ OHCI_BUSBMC | OHCI_BUSPMC #define OHCI_EUID_HI 0x24 #define OHCI_EUID_LO 0x28 #define OHCI_CROMPTR 0x34 #define OHCI_HCCCTL 0x50 #define OHCI_HCCCTLCLR 0x54 #define OHCI_AREQHI 0x100 #define OHCI_AREQHICLR 0x104 #define OHCI_AREQLO 0x108 #define OHCI_AREQLOCLR 0x10c #define OHCI_PREQHI 0x110 #define OHCI_PREQHICLR 0x114 #define OHCI_PREQLO 0x118 #define OHCI_PREQLOCLR 0x11c #define OHCI_PREQUPPER 0x120 #define OHCI_PREQUPPER_MAX 0xffff0000 #define OHCI_SID_BUF 0x64 #define OHCI_SID_CNT 0x68 #define OHCI_SID_ERR (1U << 31) #define OHCI_SID_CNT_MASK 0xffc #define OHCI_IT_STAT 0x90 #define OHCI_IT_STATCLR 0x94 #define OHCI_IT_MASK 0x98 #define OHCI_IT_MASKCLR 0x9c #define OHCI_IR_STAT 0xa0 #define OHCI_IR_STATCLR 0xa4 #define OHCI_IR_MASK 0xa8 #define OHCI_IR_MASKCLR 0xac #define OHCI_LNKCTL 0xe0 #define OHCI_LNKCTLCLR 0xe4 #define OHCI_PHYACCESS 0xec #define OHCI_CYCLETIMER 0xf0 #define OHCI_DMACTL(off) (off) #define OHCI_DMACTLCLR(off) (off + 4) #define OHCI_DMACMD(off) (off + 0xc) #define OHCI_DMAMATCH(off) (off + 0x10) #define OHCI_ATQOFF 0x180 #define OHCI_ATQCTL OHCI_ATQOFF #define OHCI_ATQCTLCLR (OHCI_ATQOFF + 4) #define OHCI_ATQCMD (OHCI_ATQOFF + 0xc) #define OHCI_ATQMATCH (OHCI_ATQOFF + 0x10) #define OHCI_ATSOFF 0x1a0 #define OHCI_ATSCTL OHCI_ATSOFF #define OHCI_ATSCTLCLR (OHCI_ATSOFF + 4) #define OHCI_ATSCMD (OHCI_ATSOFF + 0xc) #define OHCI_ATSMATCH (OHCI_ATSOFF + 0x10) #define OHCI_ARQOFF 0x1c0 #define OHCI_ARQCTL OHCI_ARQOFF #define OHCI_ARQCTLCLR (OHCI_ARQOFF + 4) #define OHCI_ARQCMD (OHCI_ARQOFF + 0xc) #define OHCI_ARQMATCH (OHCI_ARQOFF + 0x10) #define OHCI_ARSOFF 0x1e0 #define OHCI_ARSCTL OHCI_ARSOFF #define OHCI_ARSCTLCLR (OHCI_ARSOFF + 4) #define OHCI_ARSCMD (OHCI_ARSOFF + 0xc) #define OHCI_ARSMATCH (OHCI_ARSOFF + 0x10) #define OHCI_ITOFF(CH) (0x200 + 0x10 * (CH)) #define OHCI_ITCTL(CH) (OHCI_ITOFF(CH)) #define OHCI_ITCTLCLR(CH) (OHCI_ITOFF(CH) + 4) #define OHCI_ITCMD(CH) (OHCI_ITOFF(CH) + 0xc) #define OHCI_IROFF(CH) (0x400 + 0x20 * (CH)) #define OHCI_IRCTL(CH) (OHCI_IROFF(CH)) #define OHCI_IRCTLCLR(CH) (OHCI_IROFF(CH) + 4) #define OHCI_IRCMD(CH) (OHCI_IROFF(CH) + 0xc) #define OHCI_IRMATCH(CH) (OHCI_IROFF(CH) + 0x10) d_ioctl_t fwohci_ioctl; /* * Communication with PHY device */ /* XXX need lock for phy access */ static uint32_t fwphy_wrdata(struct fwohci_softc *sc, uint32_t addr, uint32_t data) { uint32_t fun; addr &= 0xf; data &= 0xff; fun = (PHYDEV_WRCMD | (addr << PHYDEV_REGADDR) | (data << PHYDEV_WRDATA)); OWRITE(sc, OHCI_PHYACCESS, fun); DELAY(100); return (fwphy_rddata(sc, addr)); } static uint32_t fwohci_set_bus_manager(struct firewire_comm *fc, u_int node) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; int i; uint32_t bm; #define OHCI_CSR_DATA 0x0c #define OHCI_CSR_COMP 0x10 #define OHCI_CSR_CONT 0x14 #define OHCI_BUS_MANAGER_ID 0 OWRITE(sc, OHCI_CSR_DATA, node); OWRITE(sc, OHCI_CSR_COMP, 0x3f); OWRITE(sc, OHCI_CSR_CONT, OHCI_BUS_MANAGER_ID); for (i = 0; !(OREAD(sc, OHCI_CSR_CONT) & (1<<31)) && (i < 1000); i++) DELAY(10); bm = OREAD(sc, OHCI_CSR_DATA); if ((bm & 0x3f) == 0x3f) bm = node; if (firewire_debug) device_printf(sc->fc.dev, "%s: %d->%d (loop=%d)\n", __func__, bm, node, i); return (bm); } static uint32_t fwphy_rddata(struct fwohci_softc *sc, u_int addr) { uint32_t fun, stat; u_int i, retry = 0; addr &= 0xf; #define MAX_RETRY 100 again: OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_REG_FAIL); fun = PHYDEV_RDCMD | (addr << PHYDEV_REGADDR); OWRITE(sc, OHCI_PHYACCESS, fun); for (i = 0; i < MAX_RETRY; i++) { fun = OREAD(sc, OHCI_PHYACCESS); if ((fun & PHYDEV_RDCMD) == 0 && (fun & PHYDEV_RDDONE) != 0) break; DELAY(100); } if (i >= MAX_RETRY) { if (firewire_debug) device_printf(sc->fc.dev, "%s: failed(1).\n", __func__); if (++retry < MAX_RETRY) { DELAY(100); goto again; } } /* Make sure that SCLK is started */ stat = OREAD(sc, FWOHCI_INTSTAT); if ((stat & OHCI_INT_REG_FAIL) != 0 || ((fun >> PHYDEV_REGADDR) & 0xf) != addr) { if (firewire_debug) device_printf(sc->fc.dev, "%s: failed(2).\n", __func__); if (++retry < MAX_RETRY) { DELAY(100); goto again; } } if (firewire_debug > 1 || retry >= MAX_RETRY) device_printf(sc->fc.dev, "%s:: 0x%x loop=%d, retry=%d\n", __func__, addr, i, retry); #undef MAX_RETRY return ((fun >> PHYDEV_RDDATA) & 0xff); } /* Device specific ioctl. */ int fwohci_ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td) { struct firewire_softc *sc; struct fwohci_softc *fc; int unit = DEV2UNIT(dev); int err = 0; struct fw_reg_req_t *reg = (struct fw_reg_req_t *) data; uint32_t *dmach = (uint32_t *) data; sc = devclass_get_softc(firewire_devclass, unit); if (sc == NULL) return (EINVAL); fc = (struct fwohci_softc *)sc->fc; if (!data) return (EINVAL); switch (cmd) { case FWOHCI_WRREG: #define OHCI_MAX_REG 0x800 if (reg->addr <= OHCI_MAX_REG) { OWRITE(fc, reg->addr, reg->data); reg->data = OREAD(fc, reg->addr); } else { err = EINVAL; } break; case FWOHCI_RDREG: if (reg->addr <= OHCI_MAX_REG) { reg->data = OREAD(fc, reg->addr); } else { err = EINVAL; } break; /* Read DMA descriptors for debug */ case DUMPDMA: if (*dmach <= OHCI_MAX_DMA_CH) { dump_dma(fc, *dmach); dump_db(fc, *dmach); } else { err = EINVAL; } break; /* Read/Write Phy registers */ #define OHCI_MAX_PHY_REG 0xf case FWOHCI_RDPHYREG: if (reg->addr <= OHCI_MAX_PHY_REG) reg->data = fwphy_rddata(fc, reg->addr); else err = EINVAL; break; case FWOHCI_WRPHYREG: if (reg->addr <= OHCI_MAX_PHY_REG) reg->data = fwphy_wrdata(fc, reg->addr, reg->data); else err = EINVAL; break; default: err = EINVAL; break; } return err; } static int fwohci_probe_phy(struct fwohci_softc *sc, device_t dev) { uint32_t reg, reg2; int e1394a = 1; /* * probe PHY parameters * 0. to prove PHY version, whether compliance of 1394a. * 1. to probe maximum speed supported by the PHY and * number of port supported by core-logic. * It is not actually available port on your PC . */ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS); DELAY(500); reg = fwphy_rddata(sc, FW_PHY_SPD_REG); if ((reg >> 5) != 7) { sc->fc.mode &= ~FWPHYASYST; sc->fc.nport = reg & FW_PHY_NP; sc->fc.speed = reg & FW_PHY_SPD >> 6; if (sc->fc.speed > MAX_SPEED) { device_printf(dev, "invalid speed %d (fixed to %d).\n", sc->fc.speed, MAX_SPEED); sc->fc.speed = MAX_SPEED; } device_printf(dev, "Phy 1394 only %s, %d ports.\n", linkspeed[sc->fc.speed], sc->fc.nport); } else { reg2 = fwphy_rddata(sc, FW_PHY_ESPD_REG); sc->fc.mode |= FWPHYASYST; sc->fc.nport = reg & FW_PHY_NP; sc->fc.speed = (reg2 & FW_PHY_ESPD) >> 5; if (sc->fc.speed > MAX_SPEED) { device_printf(dev, "invalid speed %d (fixed to %d).\n", sc->fc.speed, MAX_SPEED); sc->fc.speed = MAX_SPEED; } device_printf(dev, "Phy 1394a available %s, %d ports.\n", linkspeed[sc->fc.speed], sc->fc.nport); /* check programPhyEnable */ reg2 = fwphy_rddata(sc, 5); #if 0 if (e1394a && (OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_PRPHY)) { #else /* XXX force to enable 1394a */ if (e1394a) { #endif if (firewire_debug) device_printf(dev, "Enable 1394a Enhancements\n"); /* enable EAA EMC */ reg2 |= 0x03; /* set aPhyEnhanceEnable */ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_PHYEN); OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_PRPHY); } else { /* for safe */ reg2 &= ~0x83; } reg2 = fwphy_wrdata(sc, 5, reg2); } reg = fwphy_rddata(sc, FW_PHY_SPD_REG); if ((reg >> 5) == 7) { reg = fwphy_rddata(sc, 4); reg |= 1 << 6; fwphy_wrdata(sc, 4, reg); reg = fwphy_rddata(sc, 4); } return 0; } void fwohci_reset(struct fwohci_softc *sc, device_t dev) { int i, max_rec, speed; uint32_t reg, reg2; struct fwohcidb_tr *db_tr; /* Disable interrupts */ OWRITE(sc, FWOHCI_INTMASKCLR, ~0); /* Now stopping all DMA channels */ OWRITE(sc, OHCI_ARQCTLCLR, OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_ARSCTLCLR, OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_IR_MASKCLR, ~0); for (i = 0; i < sc->fc.nisodma; i++) { OWRITE(sc, OHCI_IRCTLCLR(i), OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_ITCTLCLR(i), OHCI_CNTL_DMA_RUN); } /* FLUSH FIFO and reset Transmitter/Receiver */ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET); if (firewire_debug) device_printf(dev, "resetting OHCI..."); i = 0; while (OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_RESET) { if (i++ > 100) break; DELAY(1000); } if (firewire_debug) printf("done (loop=%d)\n", i); /* Probe phy */ fwohci_probe_phy(sc, dev); /* Probe link */ reg = OREAD(sc, OHCI_BUS_OPT); reg2 = reg | OHCI_BUSFNC; max_rec = (reg & 0x0000f000) >> 12; speed = (reg & 0x00000007); device_printf(dev, "Link %s, max_rec %d bytes.\n", linkspeed[speed], MAXREC(max_rec)); /* XXX fix max_rec */ sc->fc.maxrec = sc->fc.speed + 8; if (max_rec != sc->fc.maxrec) { reg2 = (reg2 & 0xffff0fff) | (sc->fc.maxrec << 12); device_printf(dev, "max_rec %d -> %d\n", MAXREC(max_rec), MAXREC(sc->fc.maxrec)); } if (firewire_debug) device_printf(dev, "BUS_OPT 0x%x -> 0x%x\n", reg, reg2); OWRITE(sc, OHCI_BUS_OPT, reg2); /* Initialize registers */ OWRITE(sc, OHCI_CROMHDR, sc->fc.config_rom[0]); OWRITE(sc, OHCI_CROMPTR, sc->crom_dma.bus_addr); OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_BIGEND); OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_POSTWR); OWRITE(sc, OHCI_SID_BUF, sc->sid_dma.bus_addr); OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_SID); /* Enable link */ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LINKEN); /* Force to start async RX DMA */ sc->arrq.xferq.flag &= ~FWXFERQ_RUNNING; sc->arrs.xferq.flag &= ~FWXFERQ_RUNNING; fwohci_rx_enable(sc, &sc->arrq); fwohci_rx_enable(sc, &sc->arrs); /* Initialize async TX */ OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD); OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD); /* AT Retries */ OWRITE(sc, FWOHCI_RETRY, /* CycleLimit PhyRespRetries ATRespRetries ATReqRetries */ (0xffff << 16) | (0x0f << 8) | (0x0f << 4) | 0x0f); sc->atrq.top = STAILQ_FIRST(&sc->atrq.db_trq); sc->atrs.top = STAILQ_FIRST(&sc->atrs.db_trq); sc->atrq.bottom = sc->atrq.top; sc->atrs.bottom = sc->atrs.top; for (i = 0, db_tr = sc->atrq.top; i < sc->atrq.ndb; i++, db_tr = STAILQ_NEXT(db_tr, link)) { db_tr->xfer = NULL; } for (i = 0, db_tr = sc->atrs.top; i < sc->atrs.ndb; i++, db_tr = STAILQ_NEXT(db_tr, link)) { db_tr->xfer = NULL; } /* Enable interrupts */ sc->intmask = (OHCI_INT_ERR | OHCI_INT_PHY_SID | OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS | OHCI_INT_DMA_PRRQ | OHCI_INT_DMA_PRRS | OHCI_INT_PHY_BUS_R | OHCI_INT_PW_ERR); sc->intmask |= OHCI_INT_DMA_IR | OHCI_INT_DMA_IT; sc->intmask |= OHCI_INT_CYC_LOST | OHCI_INT_PHY_INT; OWRITE(sc, FWOHCI_INTMASK, sc->intmask); fwohci_set_intr(&sc->fc, 1); } int fwohci_init(struct fwohci_softc *sc, device_t dev) { int i, mver; uint32_t reg; uint8_t ui[8]; /* OHCI version */ reg = OREAD(sc, OHCI_VERSION); mver = (reg >> 16) & 0xff; device_printf(dev, "OHCI version %x.%x (ROM=%d)\n", mver, reg & 0xff, (reg >> 24) & 1); if (mver < 1 || mver > 9) { device_printf(dev, "invalid OHCI version\n"); return (ENXIO); } /* Available Isochronous DMA channel probe */ OWRITE(sc, OHCI_IT_MASK, 0xffffffff); OWRITE(sc, OHCI_IR_MASK, 0xffffffff); reg = OREAD(sc, OHCI_IT_MASK) & OREAD(sc, OHCI_IR_MASK); OWRITE(sc, OHCI_IT_MASKCLR, 0xffffffff); OWRITE(sc, OHCI_IR_MASKCLR, 0xffffffff); for (i = 0; i < 0x20; i++) if ((reg & (1 << i)) == 0) break; sc->fc.nisodma = i; device_printf(dev, "No. of Isochronous channels is %d.\n", i); if (i == 0) return (ENXIO); sc->fc.arq = &sc->arrq.xferq; sc->fc.ars = &sc->arrs.xferq; sc->fc.atq = &sc->atrq.xferq; sc->fc.ats = &sc->atrs.xferq; sc->arrq.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE); sc->arrs.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE); sc->atrq.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE); sc->atrs.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE); sc->arrq.xferq.start = NULL; sc->arrs.xferq.start = NULL; sc->atrq.xferq.start = fwohci_start_atq; sc->atrs.xferq.start = fwohci_start_ats; sc->arrq.xferq.buf = NULL; sc->arrs.xferq.buf = NULL; sc->atrq.xferq.buf = NULL; sc->atrs.xferq.buf = NULL; sc->arrq.xferq.dmach = -1; sc->arrs.xferq.dmach = -1; sc->atrq.xferq.dmach = -1; sc->atrs.xferq.dmach = -1; sc->arrq.ndesc = 1; sc->arrs.ndesc = 1; sc->atrq.ndesc = 8; /* equal to maximum of mbuf chains */ sc->atrs.ndesc = 2; sc->arrq.ndb = NDB; sc->arrs.ndb = NDB / 2; sc->atrq.ndb = NDB; sc->atrs.ndb = NDB / 2; for (i = 0; i < sc->fc.nisodma; i++) { sc->fc.it[i] = &sc->it[i].xferq; sc->fc.ir[i] = &sc->ir[i].xferq; sc->it[i].xferq.dmach = i; sc->ir[i].xferq.dmach = i; sc->it[i].ndb = 0; sc->ir[i].ndb = 0; } sc->fc.tcode = tinfo; sc->fc.dev = dev; sc->fc.config_rom = fwdma_malloc(&sc->fc, CROMSIZE, CROMSIZE, &sc->crom_dma, BUS_DMA_WAITOK | BUS_DMA_COHERENT); if (sc->fc.config_rom == NULL) { device_printf(dev, "config_rom alloc failed."); return ENOMEM; } #if 0 bzero(&sc->fc.config_rom[0], CROMSIZE); sc->fc.config_rom[1] = 0x31333934; sc->fc.config_rom[2] = 0xf000a002; sc->fc.config_rom[3] = OREAD(sc, OHCI_EUID_HI); sc->fc.config_rom[4] = OREAD(sc, OHCI_EUID_LO); sc->fc.config_rom[5] = 0; sc->fc.config_rom[0] = (4 << 24) | (5 << 16); sc->fc.config_rom[0] |= fw_crc16(&sc->fc.config_rom[1], 5*4); #endif /* SID receive buffer must align 2^11 */ #define OHCI_SIDSIZE (1 << 11) sc->sid_buf = fwdma_malloc(&sc->fc, OHCI_SIDSIZE, OHCI_SIDSIZE, &sc->sid_dma, BUS_DMA_WAITOK | BUS_DMA_COHERENT); if (sc->sid_buf == NULL) { device_printf(dev, "sid_buf alloc failed."); return ENOMEM; } fwdma_malloc(&sc->fc, sizeof(uint32_t), sizeof(uint32_t), &sc->dummy_dma, BUS_DMA_WAITOK); if (sc->dummy_dma.v_addr == NULL) { device_printf(dev, "dummy_dma alloc failed."); return ENOMEM; } fwohci_db_init(sc, &sc->arrq); if ((sc->arrq.flags & FWOHCI_DBCH_INIT) == 0) return ENOMEM; fwohci_db_init(sc, &sc->arrs); if ((sc->arrs.flags & FWOHCI_DBCH_INIT) == 0) return ENOMEM; fwohci_db_init(sc, &sc->atrq); if ((sc->atrq.flags & FWOHCI_DBCH_INIT) == 0) return ENOMEM; fwohci_db_init(sc, &sc->atrs); if ((sc->atrs.flags & FWOHCI_DBCH_INIT) == 0) return ENOMEM; sc->fc.eui.hi = OREAD(sc, FWOHCIGUID_H); sc->fc.eui.lo = OREAD(sc, FWOHCIGUID_L); for (i = 0; i < 8; i++) ui[i] = FW_EUI64_BYTE(&sc->fc.eui,i); device_printf(dev, "EUI64 %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", ui[0], ui[1], ui[2], ui[3], ui[4], ui[5], ui[6], ui[7]); sc->fc.ioctl = fwohci_ioctl; sc->fc.cyctimer = fwohci_cyctimer; sc->fc.set_bmr = fwohci_set_bus_manager; sc->fc.ibr = fwohci_ibr; sc->fc.irx_enable = fwohci_irx_enable; sc->fc.irx_disable = fwohci_irx_disable; sc->fc.itx_enable = fwohci_itxbuf_enable; sc->fc.itx_disable = fwohci_itx_disable; #if BYTE_ORDER == BIG_ENDIAN sc->fc.irx_post = fwohci_irx_post; #else sc->fc.irx_post = NULL; #endif sc->fc.itx_post = NULL; sc->fc.timeout = fwohci_timeout; sc->fc.poll = fwohci_poll; sc->fc.set_intr = fwohci_set_intr; sc->intmask = sc->irstat = sc->itstat = 0; /* Init task queue */ sc->fc.taskqueue = taskqueue_create_fast("fw_taskq", M_WAITOK, taskqueue_thread_enqueue, &sc->fc.taskqueue); taskqueue_start_threads(&sc->fc.taskqueue, 1, PI_NET, "fw%d_taskq", device_get_unit(dev)); TASK_INIT(&sc->fwohci_task_busreset, 2, fwohci_task_busreset, sc); TASK_INIT(&sc->fwohci_task_sid, 1, fwohci_task_sid, sc); TASK_INIT(&sc->fwohci_task_dma, 0, fwohci_task_dma, sc); fw_init(&sc->fc); fwohci_reset(sc, dev); return 0; } void fwohci_timeout(void *arg) { struct fwohci_softc *sc; sc = (struct fwohci_softc *)arg; } uint32_t fwohci_cyctimer(struct firewire_comm *fc) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; return (OREAD(sc, OHCI_CYCLETIMER)); } int fwohci_detach(struct fwohci_softc *sc, device_t dev) { int i; if (sc->sid_buf != NULL) fwdma_free(&sc->fc, &sc->sid_dma); if (sc->fc.config_rom != NULL) fwdma_free(&sc->fc, &sc->crom_dma); fwohci_db_free(&sc->arrq); fwohci_db_free(&sc->arrs); fwohci_db_free(&sc->atrq); fwohci_db_free(&sc->atrs); for (i = 0; i < sc->fc.nisodma; i++) { fwohci_db_free(&sc->it[i]); fwohci_db_free(&sc->ir[i]); } if (sc->fc.taskqueue != NULL) { taskqueue_drain(sc->fc.taskqueue, &sc->fwohci_task_busreset); taskqueue_drain(sc->fc.taskqueue, &sc->fwohci_task_sid); taskqueue_drain(sc->fc.taskqueue, &sc->fwohci_task_dma); taskqueue_drain(sc->fc.taskqueue, &sc->fc.task_timeout); taskqueue_free(sc->fc.taskqueue); sc->fc.taskqueue = NULL; } return 0; } #define LAST_DB(dbtr, db) do { \ struct fwohcidb_tr *_dbtr = (dbtr); \ int _cnt = _dbtr->dbcnt; \ db = &_dbtr->db[ (_cnt > 2) ? (_cnt -1) : 0]; \ } while (0) static void fwohci_execute_db(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct fwohcidb_tr *db_tr; struct fwohcidb *db; bus_dma_segment_t *s; int i; db_tr = (struct fwohcidb_tr *)arg; db = &db_tr->db[db_tr->dbcnt]; if (error) { if (firewire_debug || error != EFBIG) printf("fwohci_execute_db: error=%d\n", error); return; } for (i = 0; i < nseg; i++) { s = &segs[i]; FWOHCI_DMA_WRITE(db->db.desc.addr, s->ds_addr); FWOHCI_DMA_WRITE(db->db.desc.cmd, s->ds_len); FWOHCI_DMA_WRITE(db->db.desc.res, 0); db++; db_tr->dbcnt++; } } static void fwohci_execute_db2(void *arg, bus_dma_segment_t *segs, int nseg, bus_size_t size, int error) { fwohci_execute_db(arg, segs, nseg, error); } static void fwohci_start(struct fwohci_softc *sc, struct fwohci_dbch *dbch) { int i; int tcode, hdr_len, pl_off; int fsegment = -1; uint32_t off; struct fw_xfer *xfer; struct fw_pkt *fp; struct fwohci_txpkthdr *ohcifp; struct fwohcidb_tr *db_tr; struct fwohcidb *db; uint32_t *ld; struct tcode_info *info; static int maxdesc=0; FW_GLOCK_ASSERT(&sc->fc); if (&sc->atrq == dbch) { off = OHCI_ATQOFF; } else if (&sc->atrs == dbch) { off = OHCI_ATSOFF; } else { return; } if (dbch->flags & FWOHCI_DBCH_FULL) return; db_tr = dbch->top; txloop: xfer = STAILQ_FIRST(&dbch->xferq.q); if (xfer == NULL) { goto kick; } #if 0 if (dbch->xferq.queued == 0) { device_printf(sc->fc.dev, "TX queue empty\n"); } #endif STAILQ_REMOVE_HEAD(&dbch->xferq.q, link); db_tr->xfer = xfer; xfer->flag = FWXF_START; fp = &xfer->send.hdr; tcode = fp->mode.common.tcode; ohcifp = (struct fwohci_txpkthdr *) db_tr->db[1].db.immed; info = &tinfo[tcode]; hdr_len = pl_off = info->hdr_len; ld = &ohcifp->mode.ld[0]; ld[0] = ld[1] = ld[2] = ld[3] = 0; for (i = 0; i < pl_off; i+= 4) ld[i/4] = fp->mode.ld[i/4]; ohcifp->mode.common.spd = xfer->send.spd & 0x7; if (tcode == FWTCODE_STREAM) { hdr_len = 8; ohcifp->mode.stream.len = fp->mode.stream.len; } else if (tcode == FWTCODE_PHY) { hdr_len = 12; ld[1] = fp->mode.ld[1]; ld[2] = fp->mode.ld[2]; ohcifp->mode.common.spd = 0; ohcifp->mode.common.tcode = FWOHCITCODE_PHY; } else { ohcifp->mode.asycomm.dst = fp->mode.hdr.dst; ohcifp->mode.asycomm.srcbus = OHCI_ASYSRCBUS; ohcifp->mode.asycomm.tlrt |= FWRETRY_X; } db = &db_tr->db[0]; FWOHCI_DMA_WRITE(db->db.desc.cmd, OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | hdr_len); FWOHCI_DMA_WRITE(db->db.desc.addr, 0); FWOHCI_DMA_WRITE(db->db.desc.res, 0); /* Specify bound timer of asy. response */ if (&sc->atrs == dbch) { FWOHCI_DMA_WRITE(db->db.desc.res, (OREAD(sc, OHCI_CYCLETIMER) >> 12) + (1 << 13)); } #if BYTE_ORDER == BIG_ENDIAN if (tcode == FWTCODE_WREQQ || tcode == FWTCODE_RRESQ) hdr_len = 12; for (i = 0; i < hdr_len/4; i++) FWOHCI_DMA_WRITE(ld[i], ld[i]); #endif again: db_tr->dbcnt = 2; db = &db_tr->db[db_tr->dbcnt]; if (xfer->send.pay_len > 0) { int err; /* handle payload */ if (xfer->mbuf == NULL) { err = bus_dmamap_load(dbch->dmat, db_tr->dma_map, &xfer->send.payload[0], xfer->send.pay_len, fwohci_execute_db, db_tr, /*flags*/0); } else { /* XXX we can handle only 6 (=8-2) mbuf chains */ err = bus_dmamap_load_mbuf(dbch->dmat, db_tr->dma_map, xfer->mbuf, fwohci_execute_db2, db_tr, /* flags */0); if (err == EFBIG) { struct mbuf *m0; if (firewire_debug) device_printf(sc->fc.dev, "EFBIG.\n"); m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m0 != NULL) { m_copydata(xfer->mbuf, 0, xfer->mbuf->m_pkthdr.len, mtod(m0, caddr_t)); m0->m_len = m0->m_pkthdr.len = xfer->mbuf->m_pkthdr.len; m_freem(xfer->mbuf); xfer->mbuf = m0; goto again; } device_printf(sc->fc.dev, "m_getcl failed.\n"); } } if (err) printf("dmamap_load: err=%d\n", err); bus_dmamap_sync(dbch->dmat, db_tr->dma_map, BUS_DMASYNC_PREWRITE); #if 0 /* OHCI_OUTPUT_MODE == 0 */ for (i = 2; i < db_tr->dbcnt; i++) FWOHCI_DMA_SET(db_tr->db[i].db.desc.cmd, OHCI_OUTPUT_MORE); #endif } if (maxdesc < db_tr->dbcnt) { maxdesc = db_tr->dbcnt; if (firewire_debug) device_printf(sc->fc.dev, "%s: maxdesc %d\n", __func__, maxdesc); } /* last db */ LAST_DB(db_tr, db); FWOHCI_DMA_SET(db->db.desc.cmd, OHCI_OUTPUT_LAST | OHCI_INTERRUPT_ALWAYS | OHCI_BRANCH_ALWAYS); FWOHCI_DMA_WRITE(db->db.desc.depend, STAILQ_NEXT(db_tr, link)->bus_addr); if (fsegment == -1) fsegment = db_tr->dbcnt; if (dbch->pdb_tr != NULL) { LAST_DB(dbch->pdb_tr, db); FWOHCI_DMA_SET(db->db.desc.depend, db_tr->dbcnt); } dbch->xferq.queued++; dbch->pdb_tr = db_tr; db_tr = STAILQ_NEXT(db_tr, link); if (db_tr != dbch->bottom) { goto txloop; } else { device_printf(sc->fc.dev, "fwohci_start: lack of db_trq\n"); dbch->flags |= FWOHCI_DBCH_FULL; } kick: /* kick asy q */ fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); if (dbch->xferq.flag & FWXFERQ_RUNNING) { OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE); } else { if (firewire_debug) device_printf(sc->fc.dev, "start AT DMA status=%x\n", OREAD(sc, OHCI_DMACTL(off))); OWRITE(sc, OHCI_DMACMD(off), dbch->top->bus_addr | fsegment); OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN); dbch->xferq.flag |= FWXFERQ_RUNNING; } dbch->top = db_tr; return; } static void fwohci_start_atq(struct firewire_comm *fc) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; FW_GLOCK(&sc->fc); fwohci_start(sc, &(sc->atrq)); FW_GUNLOCK(&sc->fc); return; } static void fwohci_start_ats(struct firewire_comm *fc) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; FW_GLOCK(&sc->fc); fwohci_start(sc, &(sc->atrs)); FW_GUNLOCK(&sc->fc); return; } void fwohci_txd(struct fwohci_softc *sc, struct fwohci_dbch *dbch) { int s, ch, err = 0; struct fwohcidb_tr *tr; struct fwohcidb *db; struct fw_xfer *xfer; uint32_t off; u_int stat, status; int packets; struct firewire_comm *fc = (struct firewire_comm *)sc; if (&sc->atrq == dbch) { off = OHCI_ATQOFF; ch = ATRQ_CH; } else if (&sc->atrs == dbch) { off = OHCI_ATSOFF; ch = ATRS_CH; } else { return; } s = splfw(); tr = dbch->bottom; packets = 0; fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTREAD); fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTWRITE); while (dbch->xferq.queued > 0) { LAST_DB(tr, db); status = FWOHCI_DMA_READ(db->db.desc.res) >> OHCI_STATUS_SHIFT; if (!(status & OHCI_CNTL_DMA_ACTIVE)) { if (fc->status != FWBUSINIT) /* maybe out of order?? */ goto out; } bus_dmamap_sync(dbch->dmat, tr->dma_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dbch->dmat, tr->dma_map); #if 1 if (firewire_debug > 1) dump_db(sc, ch); #endif if (status & OHCI_CNTL_DMA_DEAD) { /* Stop DMA */ OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN); device_printf(sc->fc.dev, "force reset AT FIFO\n"); OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_LINKEN); OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS | OHCI_HCC_LINKEN); OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN); } stat = status & FWOHCIEV_MASK; switch (stat) { case FWOHCIEV_ACKPEND: case FWOHCIEV_ACKCOMPL: err = 0; break; case FWOHCIEV_ACKBSA: case FWOHCIEV_ACKBSB: case FWOHCIEV_ACKBSX: err = EBUSY; break; case FWOHCIEV_FLUSHED: case FWOHCIEV_ACKTARD: err = EAGAIN; break; case FWOHCIEV_MISSACK: case FWOHCIEV_UNDRRUN: case FWOHCIEV_OVRRUN: case FWOHCIEV_DESCERR: case FWOHCIEV_DTRDERR: case FWOHCIEV_TIMEOUT: case FWOHCIEV_TCODERR: case FWOHCIEV_UNKNOWN: case FWOHCIEV_ACKDERR: case FWOHCIEV_ACKTERR: default: err = EINVAL; break; } if (tr->xfer != NULL) { xfer = tr->xfer; if (xfer->flag & FWXF_RCVD) { #if 0 if (firewire_debug) printf("already rcvd\n"); #endif fw_xfer_done(xfer); } else { microtime(&xfer->tv); xfer->flag = FWXF_SENT; if (err == EBUSY) { xfer->flag = FWXF_BUSY; xfer->resp = err; xfer->recv.pay_len = 0; fw_xfer_done(xfer); } else if (stat != FWOHCIEV_ACKPEND) { if (stat != FWOHCIEV_ACKCOMPL) xfer->flag = FWXF_SENTERR; xfer->resp = err; xfer->recv.pay_len = 0; fw_xfer_done(xfer); } } /* * The watchdog timer takes care of split * transaction timeout for ACKPEND case. */ } else { printf("this shouldn't happen\n"); } FW_GLOCK(fc); dbch->xferq.queued--; FW_GUNLOCK(fc); tr->xfer = NULL; packets++; tr = STAILQ_NEXT(tr, link); dbch->bottom = tr; if (dbch->bottom == dbch->top) { /* we reaches the end of context program */ if (firewire_debug && dbch->xferq.queued > 0) printf("queued > 0\n"); break; } } out: if ((dbch->flags & FWOHCI_DBCH_FULL) && packets > 0) { printf("make free slot\n"); dbch->flags &= ~FWOHCI_DBCH_FULL; FW_GLOCK(fc); fwohci_start(sc, dbch); FW_GUNLOCK(fc); } splx(s); } static void fwohci_db_free(struct fwohci_dbch *dbch) { struct fwohcidb_tr *db_tr; int idb; if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) return; for (db_tr = STAILQ_FIRST(&dbch->db_trq), idb = 0; idb < dbch->ndb; db_tr = STAILQ_NEXT(db_tr, link), idb++) { if ((dbch->xferq.flag & FWXFERQ_EXTBUF) == 0 && db_tr->buf != NULL) { fwdma_free_size(dbch->dmat, db_tr->dma_map, db_tr->buf, dbch->xferq.psize); db_tr->buf = NULL; } else if (db_tr->dma_map != NULL) bus_dmamap_destroy(dbch->dmat, db_tr->dma_map); } dbch->ndb = 0; db_tr = STAILQ_FIRST(&dbch->db_trq); fwdma_free_multiseg(dbch->am); free(db_tr, M_FW); STAILQ_INIT(&dbch->db_trq); dbch->flags &= ~FWOHCI_DBCH_INIT; } static void fwohci_db_init(struct fwohci_softc *sc, struct fwohci_dbch *dbch) { int idb; struct fwohcidb_tr *db_tr; if ((dbch->flags & FWOHCI_DBCH_INIT) != 0) goto out; /* create dma_tag for buffers */ #define MAX_REQCOUNT 0xffff if (bus_dma_tag_create(/*parent*/ sc->fc.dmat, /*alignment*/ 1, /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/ dbch->xferq.psize, /*nsegments*/ dbch->ndesc > 3 ? dbch->ndesc - 2 : 1, /*maxsegsz*/ MAX_REQCOUNT, /*flags*/ 0, /*lockfunc*/busdma_lock_mutex, /*lockarg*/FW_GMTX(&sc->fc), &dbch->dmat)) return; /* allocate DB entries and attach one to each DMA channels */ /* DB entry must start at 16 bytes bounary. */ STAILQ_INIT(&dbch->db_trq); db_tr = (struct fwohcidb_tr *) malloc(sizeof(struct fwohcidb_tr) * dbch->ndb, M_FW, M_WAITOK | M_ZERO); - if (db_tr == NULL) { - printf("fwohci_db_init: malloc(1) failed\n"); - return; - } #define DB_SIZE(x) (sizeof(struct fwohcidb) * (x)->ndesc) dbch->am = fwdma_malloc_multiseg(&sc->fc, sizeof(struct fwohcidb), DB_SIZE(dbch), dbch->ndb, BUS_DMA_WAITOK); if (dbch->am == NULL) { printf("fwohci_db_init: fwdma_malloc_multiseg failed\n"); free(db_tr, M_FW); return; } /* Attach DB to DMA ch. */ for (idb = 0; idb < dbch->ndb; idb++) { db_tr->dbcnt = 0; db_tr->db = (struct fwohcidb *)fwdma_v_addr(dbch->am, idb); db_tr->bus_addr = fwdma_bus_addr(dbch->am, idb); /* create dmamap for buffers */ /* XXX do we need 4bytes alignment tag? */ /* XXX don't alloc dma_map for AR */ if (bus_dmamap_create(dbch->dmat, 0, &db_tr->dma_map) != 0) { printf("bus_dmamap_create failed\n"); dbch->flags = FWOHCI_DBCH_INIT; /* XXX fake */ fwohci_db_free(dbch); return; } STAILQ_INSERT_TAIL(&dbch->db_trq, db_tr, link); if (dbch->xferq.flag & FWXFERQ_EXTBUF) { if (idb % dbch->xferq.bnpacket == 0) dbch->xferq.bulkxfer[idb / dbch->xferq.bnpacket ].start = (caddr_t)db_tr; if ((idb + 1) % dbch->xferq.bnpacket == 0) dbch->xferq.bulkxfer[idb / dbch->xferq.bnpacket ].end = (caddr_t)db_tr; } db_tr++; } STAILQ_LAST(&dbch->db_trq, fwohcidb_tr,link)->link.stqe_next = STAILQ_FIRST(&dbch->db_trq); out: dbch->xferq.queued = 0; dbch->pdb_tr = NULL; dbch->top = STAILQ_FIRST(&dbch->db_trq); dbch->bottom = dbch->top; dbch->flags = FWOHCI_DBCH_INIT; } static int fwohci_itx_disable(struct firewire_comm *fc, int dmach) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN | OHCI_CNTL_CYCMATCH_S); OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach); OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach); /* XXX we cannot free buffers until the DMA really stops */ pause("fwitxd", hz); fwohci_db_free(&sc->it[dmach]); sc->it[dmach].xferq.flag &= ~FWXFERQ_RUNNING; return 0; } static int fwohci_irx_disable(struct firewire_comm *fc, int dmach) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach); OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach); /* XXX we cannot free buffers until the DMA really stops */ pause("fwirxd", hz); fwohci_db_free(&sc->ir[dmach]); sc->ir[dmach].xferq.flag &= ~FWXFERQ_RUNNING; return 0; } #if BYTE_ORDER == BIG_ENDIAN static void fwohci_irx_post (struct firewire_comm *fc , uint32_t *qld) { qld[0] = FWOHCI_DMA_READ(qld[0]); return; } #endif static int fwohci_tx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch) { int err = 0; int idb, z, i, dmach = 0, ldesc; uint32_t off = 0; struct fwohcidb_tr *db_tr; struct fwohcidb *db; if (!(dbch->xferq.flag & FWXFERQ_EXTBUF)) { err = EINVAL; return err; } z = dbch->ndesc; for (dmach = 0; dmach < sc->fc.nisodma; dmach++) { if (&sc->it[dmach] == dbch) { off = OHCI_ITOFF(dmach); break; } } if (off == 0) { err = EINVAL; return err; } if (dbch->xferq.flag & FWXFERQ_RUNNING) return err; dbch->xferq.flag |= FWXFERQ_RUNNING; for (i = 0, dbch->bottom = dbch->top; i < (dbch->ndb - 1); i++) { dbch->bottom = STAILQ_NEXT(dbch->bottom, link); } db_tr = dbch->top; for (idb = 0; idb < dbch->ndb; idb++) { fwohci_add_tx_buf(dbch, db_tr, idb); if (STAILQ_NEXT(db_tr, link) == NULL) { break; } db = db_tr->db; ldesc = db_tr->dbcnt - 1; FWOHCI_DMA_WRITE(db[0].db.desc.depend, STAILQ_NEXT(db_tr, link)->bus_addr | z); db[ldesc].db.desc.depend = db[0].db.desc.depend; if (dbch->xferq.flag & FWXFERQ_EXTBUF) { if (((idb + 1) % dbch->xferq.bnpacket) == 0) { FWOHCI_DMA_SET( db[ldesc].db.desc.cmd, OHCI_INTERRUPT_ALWAYS); /* OHCI 1.1 and above */ FWOHCI_DMA_SET( db[0].db.desc.cmd, OHCI_INTERRUPT_ALWAYS); } } db_tr = STAILQ_NEXT(db_tr, link); } FWOHCI_DMA_CLEAR( dbch->bottom->db[dbch->bottom->dbcnt - 1].db.desc.depend, 0xf); return err; } static int fwohci_rx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch) { int err = 0; int idb, z, i, dmach = 0, ldesc; uint32_t off = 0; struct fwohcidb_tr *db_tr; struct fwohcidb *db; z = dbch->ndesc; if (&sc->arrq == dbch) { off = OHCI_ARQOFF; } else if (&sc->arrs == dbch) { off = OHCI_ARSOFF; } else { for (dmach = 0; dmach < sc->fc.nisodma; dmach++) { if (&sc->ir[dmach] == dbch) { off = OHCI_IROFF(dmach); break; } } } if (off == 0) { err = EINVAL; return err; } if (dbch->xferq.flag & FWXFERQ_STREAM) { if (dbch->xferq.flag & FWXFERQ_RUNNING) return err; } else { if (dbch->xferq.flag & FWXFERQ_RUNNING) { err = EBUSY; return err; } } dbch->xferq.flag |= FWXFERQ_RUNNING; dbch->top = STAILQ_FIRST(&dbch->db_trq); for (i = 0, dbch->bottom = dbch->top; i < (dbch->ndb - 1); i++) { dbch->bottom = STAILQ_NEXT(dbch->bottom, link); } db_tr = dbch->top; for (idb = 0; idb < dbch->ndb; idb++) { fwohci_add_rx_buf(dbch, db_tr, idb, &sc->dummy_dma); if (STAILQ_NEXT(db_tr, link) == NULL) break; db = db_tr->db; ldesc = db_tr->dbcnt - 1; FWOHCI_DMA_WRITE(db[ldesc].db.desc.depend, STAILQ_NEXT(db_tr, link)->bus_addr | z); if (dbch->xferq.flag & FWXFERQ_EXTBUF) { if (((idb + 1) % dbch->xferq.bnpacket) == 0) { FWOHCI_DMA_SET( db[ldesc].db.desc.cmd, OHCI_INTERRUPT_ALWAYS); FWOHCI_DMA_CLEAR( db[ldesc].db.desc.depend, 0xf); } } db_tr = STAILQ_NEXT(db_tr, link); } FWOHCI_DMA_CLEAR( dbch->bottom->db[db_tr->dbcnt - 1].db.desc.depend, 0xf); dbch->buf_offset = 0; fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); if (dbch->xferq.flag & FWXFERQ_STREAM) { return err; } else { OWRITE(sc, OHCI_DMACMD(off), dbch->top->bus_addr | z); } OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN); return err; } static int fwohci_next_cycle(struct firewire_comm *fc, int cycle_now) { int sec, cycle, cycle_match; cycle = cycle_now & 0x1fff; sec = cycle_now >> 13; #define CYCLE_MOD 0x10 #if 1 #define CYCLE_DELAY 8 /* min delay to start DMA */ #else #define CYCLE_DELAY 7000 /* min delay to start DMA */ #endif cycle = cycle + CYCLE_DELAY; if (cycle >= 8000) { sec++; cycle -= 8000; } cycle = roundup2(cycle, CYCLE_MOD); if (cycle >= 8000) { sec++; if (cycle == 8000) cycle = 0; else cycle = CYCLE_MOD; } cycle_match = ((sec << 13) | cycle) & 0x7ffff; return (cycle_match); } static int fwohci_itxbuf_enable(struct firewire_comm *fc, int dmach) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; int err = 0; unsigned short tag, ich; struct fwohci_dbch *dbch; int cycle_match, cycle_now, s, ldesc; uint32_t stat; struct fw_bulkxfer *first, *chunk, *prev; struct fw_xferq *it; dbch = &sc->it[dmach]; it = &dbch->xferq; tag = (it->flag >> 6) & 3; ich = it->flag & 0x3f; if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) { dbch->ndb = it->bnpacket * it->bnchunk; dbch->ndesc = 3; fwohci_db_init(sc, dbch); if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) return ENOMEM; err = fwohci_tx_enable(sc, dbch); } if (err) return err; ldesc = dbch->ndesc - 1; s = splfw(); FW_GLOCK(fc); prev = STAILQ_LAST(&it->stdma, fw_bulkxfer, link); while ((chunk = STAILQ_FIRST(&it->stvalid)) != NULL) { struct fwohcidb *db; fwdma_sync_multiseg(it->buf, chunk->poffset, it->bnpacket, BUS_DMASYNC_PREWRITE); fwohci_txbufdb(sc, dmach, chunk); if (prev != NULL) { db = ((struct fwohcidb_tr *)(prev->end))->db; #if 0 /* XXX necessary? */ FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, OHCI_BRANCH_ALWAYS); #endif #if 0 /* if bulkxfer->npacket changes */ db[ldesc].db.desc.depend = db[0].db.desc.depend = ((struct fwohcidb_tr *) (chunk->start))->bus_addr | dbch->ndesc; #else FWOHCI_DMA_SET(db[0].db.desc.depend, dbch->ndesc); FWOHCI_DMA_SET(db[ldesc].db.desc.depend, dbch->ndesc); #endif } STAILQ_REMOVE_HEAD(&it->stvalid, link); STAILQ_INSERT_TAIL(&it->stdma, chunk, link); prev = chunk; } FW_GUNLOCK(fc); fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); splx(s); stat = OREAD(sc, OHCI_ITCTL(dmach)); if (firewire_debug && (stat & OHCI_CNTL_CYCMATCH_S)) printf("stat 0x%x\n", stat); if (stat & (OHCI_CNTL_DMA_ACTIVE | OHCI_CNTL_CYCMATCH_S)) return 0; #if 0 OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN); #endif OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach); OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach); OWRITE(sc, OHCI_IT_MASK, 1 << dmach); OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IT); first = STAILQ_FIRST(&it->stdma); OWRITE(sc, OHCI_ITCMD(dmach), ((struct fwohcidb_tr *)(first->start))->bus_addr | dbch->ndesc); if (firewire_debug > 1) { printf("fwohci_itxbuf_enable: kick 0x%08x\n", stat); #if 1 dump_dma(sc, ITX_CH + dmach); #endif } if ((stat & OHCI_CNTL_DMA_RUN) == 0) { #if 1 /* Don't start until all chunks are buffered */ if (STAILQ_FIRST(&it->stfree) != NULL) goto out; #endif #if 1 /* Clear cycle match counter bits */ OWRITE(sc, OHCI_ITCTLCLR(dmach), 0xffff0000); /* 2bit second + 13bit cycle */ cycle_now = (fc->cyctimer(fc) >> 12) & 0x7fff; cycle_match = fwohci_next_cycle(fc, cycle_now); OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_CYCMATCH_S | (cycle_match << 16) | OHCI_CNTL_DMA_RUN); #else OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_RUN); #endif if (firewire_debug > 1) { printf("cycle_match: 0x%04x->0x%04x\n", cycle_now, cycle_match); dump_dma(sc, ITX_CH + dmach); dump_db(sc, ITX_CH + dmach); } } else if ((stat & OHCI_CNTL_CYCMATCH_S) == 0) { device_printf(sc->fc.dev, "IT DMA underrun (0x%08x)\n", stat); OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_WAKE); } out: return err; } static int fwohci_irx_enable(struct firewire_comm *fc, int dmach) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; int err = 0, s, ldesc; unsigned short tag, ich; uint32_t stat; struct fwohci_dbch *dbch; struct fwohcidb_tr *db_tr; struct fw_bulkxfer *first, *prev, *chunk; struct fw_xferq *ir; dbch = &sc->ir[dmach]; ir = &dbch->xferq; if ((ir->flag & FWXFERQ_RUNNING) == 0) { tag = (ir->flag >> 6) & 3; ich = ir->flag & 0x3f; OWRITE(sc, OHCI_IRMATCH(dmach), tagbit[tag] | ich); ir->queued = 0; dbch->ndb = ir->bnpacket * ir->bnchunk; dbch->ndesc = 2; fwohci_db_init(sc, dbch); if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) return ENOMEM; err = fwohci_rx_enable(sc, dbch); } if (err) return err; first = STAILQ_FIRST(&ir->stfree); if (first == NULL) { device_printf(fc->dev, "IR DMA no free chunk\n"); return 0; } ldesc = dbch->ndesc - 1; s = splfw(); if ((ir->flag & FWXFERQ_HANDLER) == 0) FW_GLOCK(fc); prev = STAILQ_LAST(&ir->stdma, fw_bulkxfer, link); while ((chunk = STAILQ_FIRST(&ir->stfree)) != NULL) { struct fwohcidb *db; #if 1 /* XXX for if_fwe */ if (chunk->mbuf != NULL) { db_tr = (struct fwohcidb_tr *)(chunk->start); db_tr->dbcnt = 1; err = bus_dmamap_load_mbuf(dbch->dmat, db_tr->dma_map, chunk->mbuf, fwohci_execute_db2, db_tr, /* flags */0); FWOHCI_DMA_SET(db_tr->db[1].db.desc.cmd, OHCI_UPDATE | OHCI_INPUT_LAST | OHCI_INTERRUPT_ALWAYS | OHCI_BRANCH_ALWAYS); } #endif db = ((struct fwohcidb_tr *)(chunk->end))->db; FWOHCI_DMA_WRITE(db[ldesc].db.desc.res, 0); FWOHCI_DMA_CLEAR(db[ldesc].db.desc.depend, 0xf); if (prev != NULL) { db = ((struct fwohcidb_tr *)(prev->end))->db; FWOHCI_DMA_SET(db[ldesc].db.desc.depend, dbch->ndesc); } STAILQ_REMOVE_HEAD(&ir->stfree, link); STAILQ_INSERT_TAIL(&ir->stdma, chunk, link); prev = chunk; } if ((ir->flag & FWXFERQ_HANDLER) == 0) FW_GUNLOCK(fc); fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); splx(s); stat = OREAD(sc, OHCI_IRCTL(dmach)); if (stat & OHCI_CNTL_DMA_ACTIVE) return 0; if (stat & OHCI_CNTL_DMA_RUN) { OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN); device_printf(sc->fc.dev, "IR DMA overrun (0x%08x)\n", stat); } if (firewire_debug) printf("start IR DMA 0x%x\n", stat); OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach); OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach); OWRITE(sc, OHCI_IR_MASK, 1 << dmach); OWRITE(sc, OHCI_IRCTLCLR(dmach), 0xf0000000); OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_ISOHDR); OWRITE(sc, OHCI_IRCMD(dmach), ((struct fwohcidb_tr *)(first->start))->bus_addr | dbch->ndesc); OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN); OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR); #if 0 dump_db(sc, IRX_CH + dmach); #endif return err; } int fwohci_stop(struct fwohci_softc *sc, device_t dev) { u_int i; fwohci_set_intr(&sc->fc, 0); /* Now stopping all DMA channel */ OWRITE(sc, OHCI_ARQCTLCLR, OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_ARSCTLCLR, OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN); for (i = 0; i < sc->fc.nisodma; i++) { OWRITE(sc, OHCI_IRCTLCLR(i), OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_ITCTLCLR(i), OHCI_CNTL_DMA_RUN); } #if 0 /* Let dcons(4) be accessed */ /* Stop interrupt */ OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_EN | OHCI_INT_ERR | OHCI_INT_PHY_SID | OHCI_INT_PHY_INT | OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS | OHCI_INT_DMA_PRRQ | OHCI_INT_DMA_PRRS | OHCI_INT_DMA_ARRQ | OHCI_INT_DMA_ARRS | OHCI_INT_PHY_BUS_R); /* FLUSH FIFO and reset Transmitter/Receiver */ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET); #endif /* XXX Link down? Bus reset? */ return 0; } int fwohci_resume(struct fwohci_softc *sc, device_t dev) { int i; struct fw_xferq *ir; struct fw_bulkxfer *chunk; fwohci_reset(sc, dev); /* XXX resume isochronous receive automatically. (how about TX?) */ for (i = 0; i < sc->fc.nisodma; i++) { ir = &sc->ir[i].xferq; if ((ir->flag & FWXFERQ_RUNNING) != 0) { device_printf(sc->fc.dev, "resume iso receive ch: %d\n", i); ir->flag &= ~FWXFERQ_RUNNING; /* requeue stdma to stfree */ while ((chunk = STAILQ_FIRST(&ir->stdma)) != NULL) { STAILQ_REMOVE_HEAD(&ir->stdma, link); STAILQ_INSERT_TAIL(&ir->stfree, chunk, link); } sc->fc.irx_enable(&sc->fc, i); } } bus_generic_resume(dev); sc->fc.ibr(&sc->fc); return 0; } #ifdef OHCI_DEBUG static void fwohci_dump_intr(struct fwohci_softc *sc, uint32_t stat) { if (stat & OREAD(sc, FWOHCI_INTMASK)) device_printf(fc->dev, "INTERRUPT < %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s> 0x%08x, 0x%08x\n", stat & OHCI_INT_EN ? "DMA_EN ":"", stat & OHCI_INT_PHY_REG ? "PHY_REG ":"", stat & OHCI_INT_CYC_LONG ? "CYC_LONG ":"", stat & OHCI_INT_ERR ? "INT_ERR ":"", stat & OHCI_INT_CYC_ERR ? "CYC_ERR ":"", stat & OHCI_INT_CYC_LOST ? "CYC_LOST ":"", stat & OHCI_INT_CYC_64SECOND ? "CYC_64SECOND ":"", stat & OHCI_INT_CYC_START ? "CYC_START ":"", stat & OHCI_INT_PHY_INT ? "PHY_INT ":"", stat & OHCI_INT_PHY_BUS_R ? "BUS_RESET ":"", stat & OHCI_INT_PHY_SID ? "SID ":"", stat & OHCI_INT_LR_ERR ? "DMA_LR_ERR ":"", stat & OHCI_INT_PW_ERR ? "DMA_PW_ERR ":"", stat & OHCI_INT_DMA_IR ? "DMA_IR ":"", stat & OHCI_INT_DMA_IT ? "DMA_IT " :"", stat & OHCI_INT_DMA_PRRS ? "DMA_PRRS " :"", stat & OHCI_INT_DMA_PRRQ ? "DMA_PRRQ " :"", stat & OHCI_INT_DMA_ARRS ? "DMA_ARRS " :"", stat & OHCI_INT_DMA_ARRQ ? "DMA_ARRQ " :"", stat & OHCI_INT_DMA_ATRS ? "DMA_ATRS " :"", stat & OHCI_INT_DMA_ATRQ ? "DMA_ATRQ " :"", stat, OREAD(sc, FWOHCI_INTMASK) ); } #endif static void fwohci_intr_core(struct fwohci_softc *sc, uint32_t stat, int count) { struct firewire_comm *fc = (struct firewire_comm *)sc; uintmax_t prequpper; uint32_t node_id, plen; FW_GLOCK_ASSERT(fc); if ((stat & OHCI_INT_PHY_BUS_R) && (fc->status != FWBUSRESET)) { fc->status = FWBUSRESET; /* Disable bus reset interrupt until sid recv. */ OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_PHY_BUS_R); device_printf(fc->dev, "%s: BUS reset\n", __func__); OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_CYC_LOST); OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCSRC); OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN); sc->atrq.xferq.flag &= ~FWXFERQ_RUNNING; OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN); sc->atrs.xferq.flag &= ~FWXFERQ_RUNNING; if (!kdb_active) taskqueue_enqueue(sc->fc.taskqueue, &sc->fwohci_task_busreset); } if (stat & OHCI_INT_PHY_SID) { /* Enable bus reset interrupt */ OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_BUS_R); OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_PHY_BUS_R); /* Allow async. request to us */ OWRITE(sc, OHCI_AREQHI, 1 << 31); if (firewire_phydma_enable) { /* allow from all nodes */ OWRITE(sc, OHCI_PREQHI, 0x7fffffff); OWRITE(sc, OHCI_PREQLO, 0xffffffff); prequpper = ((uintmax_t)Maxmem << PAGE_SHIFT) >> 16; if (prequpper > OHCI_PREQUPPER_MAX) { device_printf(fc->dev, "Physical memory size of 0x%jx exceeds " "fire wire address space. Limiting dma " "to memory below 0x%jx\n", (uintmax_t)Maxmem << PAGE_SHIFT, (uintmax_t)OHCI_PREQUPPER_MAX << 16); prequpper = OHCI_PREQUPPER_MAX; } OWRITE(sc, OHCI_PREQUPPER, prequpper & 0xffffffff); } /* Set ATRetries register */ OWRITE(sc, OHCI_ATRETRY, 1<<(13 + 16) | 0xfff); /* * Checking whether the node is root or not. If root, turn on * cycle master. */ node_id = OREAD(sc, FWOHCI_NODEID); plen = OREAD(sc, OHCI_SID_CNT); fc->nodeid = node_id & 0x3f; device_printf(fc->dev, "%s: node_id=0x%08x, SelfID Count=%d, ", __func__, fc->nodeid, (plen >> 16) & 0xff); if (!(node_id & OHCI_NODE_VALID)) { device_printf(fc->dev, "%s: Bus reset failure\n", __func__); goto sidout; } /* cycle timer */ sc->cycle_lost = 0; OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_CYC_LOST); if ((node_id & OHCI_NODE_ROOT) && !nocyclemaster) { printf("CYCLEMASTER mode\n"); OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_CYCMTR | OHCI_CNTL_CYCTIMER); } else { printf("non CYCLEMASTER mode\n"); OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCMTR); OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_CYCTIMER); } fc->status = FWBUSINIT; if (!kdb_active) taskqueue_enqueue(sc->fc.taskqueue, &sc->fwohci_task_sid); } sidout: if ((stat & ~(OHCI_INT_PHY_BUS_R | OHCI_INT_PHY_SID)) && (!kdb_active)) taskqueue_enqueue(sc->fc.taskqueue, &sc->fwohci_task_dma); } static void fwohci_intr_dma(struct fwohci_softc *sc, uint32_t stat, int count) { uint32_t irstat, itstat; u_int i; struct firewire_comm *fc = (struct firewire_comm *)sc; if (stat & OHCI_INT_DMA_IR) { irstat = atomic_readandclear_int(&sc->irstat); for (i = 0; i < fc->nisodma; i++) { struct fwohci_dbch *dbch; if ((irstat & (1 << i)) != 0) { dbch = &sc->ir[i]; if ((dbch->xferq.flag & FWXFERQ_OPEN) == 0) { device_printf(sc->fc.dev, "dma(%d) not active\n", i); continue; } fwohci_rbuf_update(sc, i); } } } if (stat & OHCI_INT_DMA_IT) { itstat = atomic_readandclear_int(&sc->itstat); for (i = 0; i < fc->nisodma; i++) { if ((itstat & (1 << i)) != 0) { fwohci_tbuf_update(sc, i); } } } if (stat & OHCI_INT_DMA_PRRS) { #if 0 dump_dma(sc, ARRS_CH); dump_db(sc, ARRS_CH); #endif fwohci_arcv(sc, &sc->arrs, count); } if (stat & OHCI_INT_DMA_PRRQ) { #if 0 dump_dma(sc, ARRQ_CH); dump_db(sc, ARRQ_CH); #endif fwohci_arcv(sc, &sc->arrq, count); } if (stat & OHCI_INT_CYC_LOST) { if (sc->cycle_lost >= 0) sc->cycle_lost++; if (sc->cycle_lost > 10) { sc->cycle_lost = -1; #if 0 OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCTIMER); #endif OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_CYC_LOST); device_printf(fc->dev, "too many cycles lost, " "no cycle master present?\n"); } } if (stat & OHCI_INT_DMA_ATRQ) { fwohci_txd(sc, &(sc->atrq)); } if (stat & OHCI_INT_DMA_ATRS) { fwohci_txd(sc, &(sc->atrs)); } if (stat & OHCI_INT_PW_ERR) { device_printf(fc->dev, "posted write error\n"); } if (stat & OHCI_INT_ERR) { device_printf(fc->dev, "unrecoverable error\n"); } if (stat & OHCI_INT_PHY_INT) { device_printf(fc->dev, "phy int\n"); } } static void fwohci_task_busreset(void *arg, int pending) { struct fwohci_softc *sc = (struct fwohci_softc *)arg; FW_GLOCK(&sc->fc); fw_busreset(&sc->fc, FWBUSRESET); OWRITE(sc, OHCI_CROMHDR, ntohl(sc->fc.config_rom[0])); OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->fc.config_rom[2])); FW_GUNLOCK(&sc->fc); } static void fwohci_task_sid(void *arg, int pending) { struct fwohci_softc *sc = (struct fwohci_softc *)arg; struct firewire_comm *fc = &sc->fc; uint32_t *buf; int i, plen; /* * We really should have locking * here. Not sure why it's not */ plen = OREAD(sc, OHCI_SID_CNT); if (plen & OHCI_SID_ERR) { device_printf(fc->dev, "SID Error\n"); return; } plen &= OHCI_SID_CNT_MASK; if (plen < 4 || plen > OHCI_SIDSIZE) { device_printf(fc->dev, "invalid SID len = %d\n", plen); return; } plen -= 4; /* chop control info */ buf = (uint32_t *)malloc(OHCI_SIDSIZE, M_FW, M_NOWAIT); if (buf == NULL) { device_printf(fc->dev, "malloc failed\n"); return; } for (i = 0; i < plen / 4; i++) buf[i] = FWOHCI_DMA_READ(sc->sid_buf[i + 1]); /* pending all pre-bus_reset packets */ fwohci_txd(sc, &sc->atrq); fwohci_txd(sc, &sc->atrs); fwohci_arcv(sc, &sc->arrs, -1); fwohci_arcv(sc, &sc->arrq, -1); fw_drain_txq(fc); fw_sidrcv(fc, buf, plen); free(buf, M_FW); } static void fwohci_task_dma(void *arg, int pending) { struct fwohci_softc *sc = (struct fwohci_softc *)arg; uint32_t stat; again: stat = atomic_readandclear_int(&sc->intstat); if (stat) fwohci_intr_dma(sc, stat, -1); else return; goto again; } static int fwohci_check_stat(struct fwohci_softc *sc) { uint32_t stat, irstat, itstat; FW_GLOCK_ASSERT(&sc->fc); stat = OREAD(sc, FWOHCI_INTSTAT); if (stat == 0xffffffff) { if (!bus_child_present(sc->fc.dev)) return (FILTER_HANDLED); device_printf(sc->fc.dev, "device physically ejected?\n"); return (FILTER_STRAY); } if (stat) OWRITE(sc, FWOHCI_INTSTATCLR, stat & ~OHCI_INT_PHY_BUS_R); stat &= sc->intmask; if (stat == 0) return (FILTER_STRAY); atomic_set_int(&sc->intstat, stat); if (stat & OHCI_INT_DMA_IR) { irstat = OREAD(sc, OHCI_IR_STAT); OWRITE(sc, OHCI_IR_STATCLR, irstat); atomic_set_int(&sc->irstat, irstat); } if (stat & OHCI_INT_DMA_IT) { itstat = OREAD(sc, OHCI_IT_STAT); OWRITE(sc, OHCI_IT_STATCLR, itstat); atomic_set_int(&sc->itstat, itstat); } fwohci_intr_core(sc, stat, -1); return (FILTER_HANDLED); } void fwohci_intr(void *arg) { struct fwohci_softc *sc = (struct fwohci_softc *)arg; FW_GLOCK(&sc->fc); fwohci_check_stat(sc); FW_GUNLOCK(&sc->fc); } void fwohci_poll(struct firewire_comm *fc, int quick, int count) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; FW_GLOCK(fc); fwohci_check_stat(sc); FW_GUNLOCK(fc); } static void fwohci_set_intr(struct firewire_comm *fc, int enable) { struct fwohci_softc *sc; sc = (struct fwohci_softc *)fc; if (firewire_debug) device_printf(sc->fc.dev, "fwohci_set_intr: %d\n", enable); if (enable) { sc->intmask |= OHCI_INT_EN; OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_EN); } else { sc->intmask &= ~OHCI_INT_EN; OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_EN); } } static void fwohci_tbuf_update(struct fwohci_softc *sc, int dmach) { struct firewire_comm *fc = &sc->fc; struct fwohcidb *db; struct fw_bulkxfer *chunk; struct fw_xferq *it; uint32_t stat, count; int s, w=0, ldesc; it = fc->it[dmach]; ldesc = sc->it[dmach].ndesc - 1; s = splfw(); /* unnecessary ? */ FW_GLOCK(fc); fwdma_sync_multiseg_all(sc->it[dmach].am, BUS_DMASYNC_POSTREAD); if (firewire_debug) dump_db(sc, ITX_CH + dmach); while ((chunk = STAILQ_FIRST(&it->stdma)) != NULL) { db = ((struct fwohcidb_tr *)(chunk->end))->db; stat = FWOHCI_DMA_READ(db[ldesc].db.desc.res) >> OHCI_STATUS_SHIFT; db = ((struct fwohcidb_tr *)(chunk->start))->db; /* timestamp */ count = FWOHCI_DMA_READ(db[ldesc].db.desc.res) & OHCI_COUNT_MASK; if (stat == 0) break; STAILQ_REMOVE_HEAD(&it->stdma, link); switch (stat & FWOHCIEV_MASK) { case FWOHCIEV_ACKCOMPL: #if 0 device_printf(fc->dev, "0x%08x\n", count); #endif break; default: device_printf(fc->dev, "Isochronous transmit err %02x(%s)\n", stat, fwohcicode[stat & 0x1f]); } STAILQ_INSERT_TAIL(&it->stfree, chunk, link); w++; } FW_GUNLOCK(fc); splx(s); if (w) wakeup(it); } static void fwohci_rbuf_update(struct fwohci_softc *sc, int dmach) { struct firewire_comm *fc = &sc->fc; struct fwohcidb_tr *db_tr; struct fw_bulkxfer *chunk; struct fw_xferq *ir; uint32_t stat; int w = 0, ldesc; ir = fc->ir[dmach]; ldesc = sc->ir[dmach].ndesc - 1; #if 0 dump_db(sc, dmach); #endif if ((ir->flag & FWXFERQ_HANDLER) == 0) FW_GLOCK(fc); fwdma_sync_multiseg_all(sc->ir[dmach].am, BUS_DMASYNC_POSTREAD); while ((chunk = STAILQ_FIRST(&ir->stdma)) != NULL) { db_tr = (struct fwohcidb_tr *)chunk->end; stat = FWOHCI_DMA_READ(db_tr->db[ldesc].db.desc.res) >> OHCI_STATUS_SHIFT; if (stat == 0) break; if (chunk->mbuf != NULL) { bus_dmamap_sync(sc->ir[dmach].dmat, db_tr->dma_map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->ir[dmach].dmat, db_tr->dma_map); } else if (ir->buf != NULL) { fwdma_sync_multiseg(ir->buf, chunk->poffset, ir->bnpacket, BUS_DMASYNC_POSTREAD); } else { /* XXX */ printf("fwohci_rbuf_update: this shouldn't happened\n"); } STAILQ_REMOVE_HEAD(&ir->stdma, link); STAILQ_INSERT_TAIL(&ir->stvalid, chunk, link); switch (stat & FWOHCIEV_MASK) { case FWOHCIEV_ACKCOMPL: chunk->resp = 0; break; default: chunk->resp = EINVAL; device_printf(fc->dev, "Isochronous receive err %02x(%s)\n", stat, fwohcicode[stat & 0x1f]); } w++; } if ((ir->flag & FWXFERQ_HANDLER) == 0) FW_GUNLOCK(fc); if (w == 0) return; if (ir->flag & FWXFERQ_HANDLER) ir->hand(ir); else wakeup(ir); } void dump_dma(struct fwohci_softc *sc, uint32_t ch) { uint32_t off, cntl, stat, cmd, match; if (ch == 0) { off = OHCI_ATQOFF; } else if (ch == 1) { off = OHCI_ATSOFF; } else if (ch == 2) { off = OHCI_ARQOFF; } else if (ch == 3) { off = OHCI_ARSOFF; } else if (ch < IRX_CH) { off = OHCI_ITCTL(ch - ITX_CH); } else { off = OHCI_IRCTL(ch - IRX_CH); } cntl = stat = OREAD(sc, off); cmd = OREAD(sc, off + 0xc); match = OREAD(sc, off + 0x10); device_printf(sc->fc.dev, "ch %1x cntl:0x%08x cmd:0x%08x match:0x%08x\n", ch, cntl, cmd, match); stat &= 0xffff; if (stat) { device_printf(sc->fc.dev, "dma %d ch:%s%s%s%s%s%s %s(%x)\n", ch, stat & OHCI_CNTL_DMA_RUN ? "RUN," : "", stat & OHCI_CNTL_DMA_WAKE ? "WAKE," : "", stat & OHCI_CNTL_DMA_DEAD ? "DEAD," : "", stat & OHCI_CNTL_DMA_ACTIVE ? "ACTIVE," : "", stat & OHCI_CNTL_DMA_BT ? "BRANCH," : "", stat & OHCI_CNTL_DMA_BAD ? "BADDMA," : "", fwohcicode[stat & 0x1f], stat & 0x1f ); } else { device_printf(sc->fc.dev, "dma %d ch: Nostat\n", ch); } } void dump_db(struct fwohci_softc *sc, uint32_t ch) { struct fwohci_dbch *dbch; struct fwohcidb_tr *cp = NULL, *pp, *np = NULL; struct fwohcidb *curr = NULL, *prev, *next = NULL; int idb, jdb; uint32_t cmd, off; if (ch == 0) { off = OHCI_ATQOFF; dbch = &sc->atrq; } else if (ch == 1) { off = OHCI_ATSOFF; dbch = &sc->atrs; } else if (ch == 2) { off = OHCI_ARQOFF; dbch = &sc->arrq; } else if (ch == 3) { off = OHCI_ARSOFF; dbch = &sc->arrs; } else if (ch < IRX_CH) { off = OHCI_ITCTL(ch - ITX_CH); dbch = &sc->it[ch - ITX_CH]; } else { off = OHCI_IRCTL(ch - IRX_CH); dbch = &sc->ir[ch - IRX_CH]; } cmd = OREAD(sc, off + 0xc); if (dbch->ndb == 0) { device_printf(sc->fc.dev, "No DB is attached ch=%d\n", ch); return; } pp = dbch->top; prev = pp->db; for (idb = 0; idb < dbch->ndb; idb++) { cp = STAILQ_NEXT(pp, link); if (cp == NULL) { curr = NULL; goto outdb; } np = STAILQ_NEXT(cp, link); for (jdb = 0; jdb < dbch->ndesc; jdb++) { if ((cmd & 0xfffffff0) == cp->bus_addr) { curr = cp->db; if (np != NULL) { next = np->db; } else { next = NULL; } goto outdb; } } pp = STAILQ_NEXT(pp, link); if (pp == NULL) { curr = NULL; goto outdb; } prev = pp->db; } outdb: if (curr != NULL) { #if 0 printf("Prev DB %d\n", ch); print_db(pp, prev, ch, dbch->ndesc); #endif printf("Current DB %d\n", ch); print_db(cp, curr, ch, dbch->ndesc); #if 0 printf("Next DB %d\n", ch); print_db(np, next, ch, dbch->ndesc); #endif } else { printf("dbdump err ch = %d cmd = 0x%08x\n", ch, cmd); } return; } void print_db(struct fwohcidb_tr *db_tr, struct fwohcidb *db, uint32_t ch, uint32_t max) { fwohcireg_t stat; int i, key; uint32_t cmd, res; if (db == NULL) { printf("No Descriptor is found\n"); return; } printf("ch = %d\n%8s %s %s %s %s %4s %8s %8s %4s:%4s\n", ch, "Current", "OP ", "KEY", "INT", "BR ", "len", "Addr", "Depend", "Stat", "Cnt"); for (i = 0; i <= max; i++) { cmd = FWOHCI_DMA_READ(db[i].db.desc.cmd); res = FWOHCI_DMA_READ(db[i].db.desc.res); key = cmd & OHCI_KEY_MASK; stat = res >> OHCI_STATUS_SHIFT; printf("%08jx %s %s %s %s %5d %08x %08x %04x:%04x", (uintmax_t)db_tr->bus_addr, dbcode[(cmd >> 28) & 0xf], dbkey[(cmd >> 24) & 0x7], dbcond[(cmd >> 20) & 0x3], dbcond[(cmd >> 18) & 0x3], cmd & OHCI_COUNT_MASK, FWOHCI_DMA_READ(db[i].db.desc.addr), FWOHCI_DMA_READ(db[i].db.desc.depend), stat, res & OHCI_COUNT_MASK); if (stat & 0xff00) { printf(" %s%s%s%s%s%s %s(%x)\n", stat & OHCI_CNTL_DMA_RUN ? "RUN," : "", stat & OHCI_CNTL_DMA_WAKE ? "WAKE," : "", stat & OHCI_CNTL_DMA_DEAD ? "DEAD," : "", stat & OHCI_CNTL_DMA_ACTIVE ? "ACTIVE," : "", stat & OHCI_CNTL_DMA_BT ? "BRANCH," : "", stat & OHCI_CNTL_DMA_BAD ? "BADDMA," : "", fwohcicode[stat & 0x1f], stat & 0x1f ); } else { printf(" Nostat\n"); } if (key == OHCI_KEY_ST2) { printf("0x%08x 0x%08x 0x%08x 0x%08x\n", FWOHCI_DMA_READ(db[i + 1].db.immed[0]), FWOHCI_DMA_READ(db[i + 1].db.immed[1]), FWOHCI_DMA_READ(db[i + 1].db.immed[2]), FWOHCI_DMA_READ(db[i + 1].db.immed[3])); } if (key == OHCI_KEY_DEVICE) { return; } if ((cmd & OHCI_BRANCH_MASK) == OHCI_BRANCH_ALWAYS) { return; } if ((cmd & OHCI_CMD_MASK) == OHCI_OUTPUT_LAST) { return; } if ((cmd & OHCI_CMD_MASK) == OHCI_INPUT_LAST) { return; } if (key == OHCI_KEY_ST2) { i++; } } return; } void fwohci_ibr(struct firewire_comm *fc) { struct fwohci_softc *sc; uint32_t fun; device_printf(fc->dev, "Initiate bus reset\n"); sc = (struct fwohci_softc *)fc; FW_GLOCK(fc); /* * Make sure our cached values from the config rom are * initialised. */ OWRITE(sc, OHCI_CROMHDR, ntohl(sc->fc.config_rom[0])); OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->fc.config_rom[2])); /* * Set root hold-off bit so that non cyclemaster capable node * shouldn't became the root node. */ #if 1 fun = fwphy_rddata(sc, FW_PHY_IBR_REG); fun |= FW_PHY_IBR | FW_PHY_RHB; fun = fwphy_wrdata(sc, FW_PHY_IBR_REG, fun); #else /* Short bus reset */ fun = fwphy_rddata(sc, FW_PHY_ISBR_REG); fun |= FW_PHY_ISBR | FW_PHY_RHB; fun = fwphy_wrdata(sc, FW_PHY_ISBR_REG, fun); #endif FW_GUNLOCK(fc); } void fwohci_txbufdb(struct fwohci_softc *sc, int dmach, struct fw_bulkxfer *bulkxfer) { struct fwohcidb_tr *db_tr, *fdb_tr; struct fwohci_dbch *dbch; struct fwohcidb *db; struct fw_pkt *fp; struct fwohci_txpkthdr *ohcifp; unsigned short chtag; int idb; FW_GLOCK_ASSERT(&sc->fc); dbch = &sc->it[dmach]; chtag = sc->it[dmach].xferq.flag & 0xff; db_tr = (struct fwohcidb_tr *)(bulkxfer->start); fdb_tr = (struct fwohcidb_tr *)(bulkxfer->end); /* device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, db_tr->bus_addr, fdb_tr->bus_addr); */ for (idb = 0; idb < dbch->xferq.bnpacket; idb++) { db = db_tr->db; fp = (struct fw_pkt *)db_tr->buf; ohcifp = (struct fwohci_txpkthdr *) db[1].db.immed; ohcifp->mode.ld[0] = fp->mode.ld[0]; ohcifp->mode.common.spd = 0 & 0x7; ohcifp->mode.stream.len = fp->mode.stream.len; ohcifp->mode.stream.chtag = chtag; ohcifp->mode.stream.tcode = 0xa; #if BYTE_ORDER == BIG_ENDIAN FWOHCI_DMA_WRITE(db[1].db.immed[0], db[1].db.immed[0]); FWOHCI_DMA_WRITE(db[1].db.immed[1], db[1].db.immed[1]); #endif FWOHCI_DMA_CLEAR(db[2].db.desc.cmd, OHCI_COUNT_MASK); FWOHCI_DMA_SET(db[2].db.desc.cmd, fp->mode.stream.len); FWOHCI_DMA_WRITE(db[2].db.desc.res, 0); #if 0 /* if bulkxfer->npackets changes */ db[2].db.desc.cmd = OHCI_OUTPUT_LAST | OHCI_UPDATE | OHCI_BRANCH_ALWAYS; db[0].db.desc.depend = = db[dbch->ndesc - 1].db.desc.depend = STAILQ_NEXT(db_tr, link)->bus_addr | dbch->ndesc; #else FWOHCI_DMA_SET(db[0].db.desc.depend, dbch->ndesc); FWOHCI_DMA_SET(db[dbch->ndesc - 1].db.desc.depend, dbch->ndesc); #endif bulkxfer->end = (caddr_t)db_tr; db_tr = STAILQ_NEXT(db_tr, link); } db = ((struct fwohcidb_tr *)bulkxfer->end)->db; FWOHCI_DMA_CLEAR(db[0].db.desc.depend, 0xf); FWOHCI_DMA_CLEAR(db[dbch->ndesc - 1].db.desc.depend, 0xf); #if 0 /* if bulkxfer->npackets changes */ db[dbch->ndesc - 1].db.desc.control |= OHCI_INTERRUPT_ALWAYS; /* OHCI 1.1 and above */ db[0].db.desc.control |= OHCI_INTERRUPT_ALWAYS; #endif /* db_tr = (struct fwohcidb_tr *)bulkxfer->start; fdb_tr = (struct fwohcidb_tr *)bulkxfer->end; device_printf(sc->fc.dev, "DB %08x %3d %08x %08x\n", bulkxfer, bulkxfer->npacket, db_tr->bus_addr, fdb_tr->bus_addr); */ return; } static int fwohci_add_tx_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr, int poffset) { struct fwohcidb *db = db_tr->db; struct fw_xferq *it; int err = 0; it = &dbch->xferq; if (it->buf == 0) { err = EINVAL; return err; } db_tr->buf = fwdma_v_addr(it->buf, poffset); db_tr->dbcnt = 3; FWOHCI_DMA_WRITE(db[0].db.desc.cmd, OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | 8); FWOHCI_DMA_WRITE(db[0].db.desc.addr, 0); bzero((void *)&db[1].db.immed[0], sizeof(db[1].db.immed)); FWOHCI_DMA_WRITE(db[2].db.desc.addr, fwdma_bus_addr(it->buf, poffset) + sizeof(uint32_t)); FWOHCI_DMA_WRITE(db[2].db.desc.cmd, OHCI_OUTPUT_LAST | OHCI_UPDATE | OHCI_BRANCH_ALWAYS); #if 1 FWOHCI_DMA_WRITE(db[0].db.desc.res, 0); FWOHCI_DMA_WRITE(db[2].db.desc.res, 0); #endif return 0; } int fwohci_add_rx_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr, int poffset, struct fwdma_alloc *dummy_dma) { struct fwohcidb *db = db_tr->db; struct fw_xferq *ir; int i, ldesc; bus_addr_t dbuf[2]; int dsiz[2]; ir = &dbch->xferq; if (ir->buf == NULL && (dbch->xferq.flag & FWXFERQ_EXTBUF) == 0) { if (db_tr->buf == NULL) { db_tr->buf = fwdma_malloc_size(dbch->dmat, &db_tr->dma_map, ir->psize, &dbuf[0], BUS_DMA_NOWAIT); if (db_tr->buf == NULL) return (ENOMEM); } db_tr->dbcnt = 1; dsiz[0] = ir->psize; bus_dmamap_sync(dbch->dmat, db_tr->dma_map, BUS_DMASYNC_PREREAD); } else { db_tr->dbcnt = 0; if (dummy_dma != NULL) { dsiz[db_tr->dbcnt] = sizeof(uint32_t); dbuf[db_tr->dbcnt++] = dummy_dma->bus_addr; } dsiz[db_tr->dbcnt] = ir->psize; if (ir->buf != NULL) { db_tr->buf = fwdma_v_addr(ir->buf, poffset); dbuf[db_tr->dbcnt] = fwdma_bus_addr(ir->buf, poffset); } db_tr->dbcnt++; } for (i = 0; i < db_tr->dbcnt; i++) { FWOHCI_DMA_WRITE(db[i].db.desc.addr, dbuf[i]); FWOHCI_DMA_WRITE(db[i].db.desc.cmd, OHCI_INPUT_MORE | dsiz[i]); if (ir->flag & FWXFERQ_STREAM) { FWOHCI_DMA_SET(db[i].db.desc.cmd, OHCI_UPDATE); } FWOHCI_DMA_WRITE(db[i].db.desc.res, dsiz[i]); } ldesc = db_tr->dbcnt - 1; if (ir->flag & FWXFERQ_STREAM) { FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, OHCI_INPUT_LAST); } FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, OHCI_BRANCH_ALWAYS); return 0; } static int fwohci_arcv_swap(struct fw_pkt *fp, int len) { struct fw_pkt *fp0; uint32_t ld0; int slen, hlen; #if BYTE_ORDER == BIG_ENDIAN int i; #endif ld0 = FWOHCI_DMA_READ(fp->mode.ld[0]); #if 0 printf("ld0: x%08x\n", ld0); #endif fp0 = (struct fw_pkt *)&ld0; /* determine length to swap */ switch (fp0->mode.common.tcode) { case FWTCODE_RREQQ: case FWTCODE_WRES: case FWTCODE_WREQQ: case FWTCODE_RRESQ: case FWOHCITCODE_PHY: slen = 12; break; case FWTCODE_RREQB: case FWTCODE_WREQB: case FWTCODE_LREQ: case FWTCODE_RRESB: case FWTCODE_LRES: slen = 16; break; default: printf("Unknown tcode %d\n", fp0->mode.common.tcode); return (0); } hlen = tinfo[fp0->mode.common.tcode].hdr_len; if (hlen > len) { if (firewire_debug) printf("splitted header\n"); return (-hlen); } #if BYTE_ORDER == BIG_ENDIAN for (i = 0; i < slen/4; i++) fp->mode.ld[i] = FWOHCI_DMA_READ(fp->mode.ld[i]); #endif return (hlen); } static int fwohci_get_plen(struct fwohci_softc *sc, struct fwohci_dbch *dbch, struct fw_pkt *fp) { struct tcode_info *info; int r; info = &tinfo[fp->mode.common.tcode]; r = info->hdr_len + sizeof(uint32_t); if ((info->flag & FWTI_BLOCK_ASY) != 0) r += roundup2(fp->mode.wreqb.len, sizeof(uint32_t)); if (r == sizeof(uint32_t)) { /* XXX */ device_printf(sc->fc.dev, "Unknown tcode %d\n", fp->mode.common.tcode); return (-1); } if (r > dbch->xferq.psize) { device_printf(sc->fc.dev, "Invalid packet length %d\n", r); return (-1); /* panic ? */ } return r; } static void fwohci_arcv_free_buf(struct fwohci_softc *sc, struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr, uint32_t off, int wake) { struct fwohcidb *db = &db_tr->db[0]; FWOHCI_DMA_CLEAR(db->db.desc.depend, 0xf); FWOHCI_DMA_WRITE(db->db.desc.res, dbch->xferq.psize); FWOHCI_DMA_SET(dbch->bottom->db[0].db.desc.depend, 1); fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); dbch->bottom = db_tr; if (wake) OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE); } static void fwohci_arcv(struct fwohci_softc *sc, struct fwohci_dbch *dbch, int count) { struct fwohcidb_tr *db_tr; struct iovec vec[2]; struct fw_pkt pktbuf; int nvec; struct fw_pkt *fp; uint8_t *ld; uint32_t stat, off, status, event; u_int spd; int len, plen, hlen, pcnt, offset; int s; caddr_t buf; int resCount; if (&sc->arrq == dbch) { off = OHCI_ARQOFF; } else if (&sc->arrs == dbch) { off = OHCI_ARSOFF; } else { return; } s = splfw(); db_tr = dbch->top; pcnt = 0; /* XXX we cannot handle a packet which lies in more than two buf */ fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTREAD); fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTWRITE); status = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) >> OHCI_STATUS_SHIFT; resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) & OHCI_COUNT_MASK; while (status & OHCI_CNTL_DMA_ACTIVE) { #if 0 if (off == OHCI_ARQOFF) printf("buf 0x%08x, status 0x%04x, resCount 0x%04x\n", db_tr->bus_addr, status, resCount); #endif len = dbch->xferq.psize - resCount; ld = (uint8_t *)db_tr->buf; if (dbch->pdb_tr == NULL) { len -= dbch->buf_offset; ld += dbch->buf_offset; } if (len > 0) bus_dmamap_sync(dbch->dmat, db_tr->dma_map, BUS_DMASYNC_POSTREAD); while (len > 0) { if (count >= 0 && count-- == 0) goto out; if (dbch->pdb_tr != NULL) { /* we have a fragment in previous buffer */ int rlen; offset = dbch->buf_offset; if (offset < 0) offset = - offset; buf = dbch->pdb_tr->buf + offset; rlen = dbch->xferq.psize - offset; if (firewire_debug) printf("rlen=%d, offset=%d\n", rlen, dbch->buf_offset); if (dbch->buf_offset < 0) { /* splitted in header, pull up */ char *p; p = (char *)&pktbuf; bcopy(buf, p, rlen); p += rlen; /* this must be too long but harmless */ rlen = sizeof(pktbuf) - rlen; if (rlen < 0) printf("why rlen < 0\n"); bcopy(db_tr->buf, p, rlen); ld += rlen; len -= rlen; hlen = fwohci_arcv_swap(&pktbuf, sizeof(pktbuf)); if (hlen <= 0) { printf("hlen should be positive."); goto err; } offset = sizeof(pktbuf); vec[0].iov_base = (char *)&pktbuf; vec[0].iov_len = offset; } else { /* splitted in payload */ offset = rlen; vec[0].iov_base = buf; vec[0].iov_len = rlen; } fp=(struct fw_pkt *)vec[0].iov_base; nvec = 1; } else { /* no fragment in previous buffer */ fp=(struct fw_pkt *)ld; hlen = fwohci_arcv_swap(fp, len); if (hlen == 0) goto err; if (hlen < 0) { dbch->pdb_tr = db_tr; dbch->buf_offset = - dbch->buf_offset; /* sanity check */ if (resCount != 0) { printf("resCount=%d hlen=%d\n", resCount, hlen); goto err; } goto out; } offset = 0; nvec = 0; } plen = fwohci_get_plen(sc, dbch, fp) - offset; if (plen < 0) { /* minimum header size + trailer = sizeof(fw_pkt) so this shouldn't happens */ printf("plen(%d) is negative! offset=%d\n", plen, offset); goto err; } if (plen > 0) { len -= plen; if (len < 0) { dbch->pdb_tr = db_tr; if (firewire_debug) printf("splitted payload\n"); /* sanity check */ if (resCount != 0) { printf("resCount=%d plen=%d" " len=%d\n", resCount, plen, len); goto err; } goto out; } vec[nvec].iov_base = ld; vec[nvec].iov_len = plen; nvec++; ld += plen; } dbch->buf_offset = ld - (uint8_t *)db_tr->buf; if (nvec == 0) printf("nvec == 0\n"); /* DMA result-code will be written at the tail of packet */ stat = FWOHCI_DMA_READ(*(uint32_t *)(ld - sizeof(struct fwohci_trailer))); #if 0 printf("plen: %d, stat %x\n", plen ,stat); #endif spd = (stat >> 21) & 0x3; event = (stat >> 16) & 0x1f; switch (event) { case FWOHCIEV_ACKPEND: #if 0 printf("fwohci_arcv: ack pending tcode=0x%x..\n", fp->mode.common.tcode); #endif /* fall through */ case FWOHCIEV_ACKCOMPL: { struct fw_rcv_buf rb; if ((vec[nvec-1].iov_len -= sizeof(struct fwohci_trailer)) == 0) nvec--; rb.fc = &sc->fc; rb.vec = vec; rb.nvec = nvec; rb.spd = spd; fw_rcv(&rb); break; } case FWOHCIEV_BUSRST: if ((sc->fc.status != FWBUSRESET) && (sc->fc.status != FWBUSINIT)) printf("got BUSRST packet!?\n"); break; default: device_printf(sc->fc.dev, "Async DMA Receive error err=%02x %s" " plen=%d offset=%d len=%d status=0x%08x" " tcode=0x%x, stat=0x%08x\n", event, fwohcicode[event], plen, dbch->buf_offset, len, OREAD(sc, OHCI_DMACTL(off)), fp->mode.common.tcode, stat); #if 1 /* XXX */ goto err; #endif break; } pcnt++; if (dbch->pdb_tr != NULL) { fwohci_arcv_free_buf(sc, dbch, dbch->pdb_tr, off, 1); dbch->pdb_tr = NULL; } } out: if (resCount == 0) { /* done on this buffer */ if (dbch->pdb_tr == NULL) { fwohci_arcv_free_buf(sc, dbch, db_tr, off, 1); dbch->buf_offset = 0; } else if (dbch->pdb_tr != db_tr) printf("pdb_tr != db_tr\n"); db_tr = STAILQ_NEXT(db_tr, link); status = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) >> OHCI_STATUS_SHIFT; resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) & OHCI_COUNT_MASK; /* XXX check buffer overrun */ dbch->top = db_tr; } else { dbch->buf_offset = dbch->xferq.psize - resCount; break; } /* XXX make sure DMA is not dead */ } #if 0 if (pcnt < 1) printf("fwohci_arcv: no packets\n"); #endif splx(s); return; err: device_printf(sc->fc.dev, "AR DMA status=%x, ", OREAD(sc, OHCI_DMACTL(off))); dbch->pdb_tr = NULL; /* skip until resCount != 0 */ printf(" skip buffer"); while (resCount == 0) { printf(" #"); fwohci_arcv_free_buf(sc, dbch, db_tr, off, 0); db_tr = STAILQ_NEXT(db_tr, link); resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) & OHCI_COUNT_MASK; } printf(" done\n"); dbch->top = db_tr; dbch->buf_offset = dbch->xferq.psize - resCount; OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE); splx(s); } Index: head/sys/dev/firewire/if_fwe.c =================================================================== --- head/sys/dev/firewire/if_fwe.c (revision 299350) +++ head/sys/dev/firewire/if_fwe.c (revision 299351) @@ -1,636 +1,632 @@ /*- * 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$ */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #include "opt_inet.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FWEDEBUG if (fwedebug) if_printf #define TX_MAX_QUEUE (FWMAXQUEUE - 1) /* network interface */ static void fwe_start (struct ifnet *); static int fwe_ioctl (struct ifnet *, u_long, caddr_t); static void fwe_init (void *); static void fwe_output_callback (struct fw_xfer *); static void fwe_as_output (struct fwe_softc *, struct ifnet *); static void fwe_as_input (struct fw_xferq *); static int fwedebug = 0; static int stream_ch = 1; static int tx_speed = 2; static int rx_queue_len = FWMAXQUEUE; static MALLOC_DEFINE(M_FWE, "if_fwe", "Ethernet over FireWire interface"); SYSCTL_INT(_debug, OID_AUTO, if_fwe_debug, CTLFLAG_RWTUN, &fwedebug, 0, ""); SYSCTL_DECL(_hw_firewire); static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwe, CTLFLAG_RD, 0, "Ethernet emulation subsystem"); SYSCTL_INT(_hw_firewire_fwe, OID_AUTO, stream_ch, CTLFLAG_RWTUN, &stream_ch, 0, "Stream channel to use"); SYSCTL_INT(_hw_firewire_fwe, OID_AUTO, tx_speed, CTLFLAG_RWTUN, &tx_speed, 0, "Transmission speed"); SYSCTL_INT(_hw_firewire_fwe, OID_AUTO, rx_queue_len, CTLFLAG_RWTUN, &rx_queue_len, 0, "Length of the receive queue"); #ifdef DEVICE_POLLING static poll_handler_t fwe_poll; static int fwe_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct fwe_softc *fwe; struct firewire_comm *fc; if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) return (0); fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe; fc = fwe->fd.fc; fc->poll(fc, (cmd == POLL_AND_CHECK_STATUS)?0:1, count); return (0); } #endif /* DEVICE_POLLING */ static void fwe_identify(driver_t *driver, device_t parent) { BUS_ADD_CHILD(parent, 0, "fwe", device_get_unit(parent)); } static int fwe_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, "Ethernet over FireWire"); return (0); } static int fwe_attach(device_t dev) { struct fwe_softc *fwe; struct ifnet *ifp; int unit, s; u_char eaddr[6]; struct fw_eui64 *eui; fwe = ((struct fwe_softc *)device_get_softc(dev)); unit = device_get_unit(dev); bzero(fwe, sizeof(struct fwe_softc)); mtx_init(&fwe->mtx, "fwe", NULL, MTX_DEF); /* XXX */ fwe->stream_ch = stream_ch; fwe->dma_ch = -1; fwe->fd.fc = device_get_ivars(dev); if (tx_speed < 0) tx_speed = fwe->fd.fc->speed; fwe->fd.dev = dev; fwe->fd.post_explore = NULL; fwe->eth_softc.fwe = fwe; fwe->pkt_hdr.mode.stream.tcode = FWTCODE_STREAM; fwe->pkt_hdr.mode.stream.sy = 0; fwe->pkt_hdr.mode.stream.chtag = fwe->stream_ch; /* generate fake MAC address: first and last 3bytes from eui64 */ #define LOCAL (0x02) #define GROUP (0x01) eui = &fwe->fd.fc->eui; eaddr[0] = (FW_EUI64_BYTE(eui, 0) | LOCAL) & ~GROUP; eaddr[1] = FW_EUI64_BYTE(eui, 1); eaddr[2] = FW_EUI64_BYTE(eui, 2); eaddr[3] = FW_EUI64_BYTE(eui, 5); eaddr[4] = FW_EUI64_BYTE(eui, 6); eaddr[5] = FW_EUI64_BYTE(eui, 7); printf("if_fwe%d: Fake Ethernet address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", unit, eaddr[0], eaddr[1], eaddr[2], eaddr[3], eaddr[4], eaddr[5]); /* fill the rest and attach interface */ ifp = fwe->eth_softc.ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); return (ENOSPC); } ifp->if_softc = &fwe->eth_softc; if_initname(ifp, device_get_name(dev), unit); ifp->if_init = fwe_init; ifp->if_start = fwe_start; ifp->if_ioctl = fwe_ioctl; ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST); ifp->if_snd.ifq_maxlen = TX_MAX_QUEUE; s = splimp(); ether_ifattach(ifp, eaddr); splx(s); /* Tell the upper layer(s) we support long frames. */ ifp->if_hdrlen = sizeof(struct ether_vlan_header); ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_POLLING; ifp->if_capenable |= IFCAP_VLAN_MTU; FWEDEBUG(ifp, "interface created\n"); return 0; } static void fwe_stop(struct fwe_softc *fwe) { struct firewire_comm *fc; struct fw_xferq *xferq; struct ifnet *ifp = fwe->eth_softc.ifp; struct fw_xfer *xfer, *next; int i; fc = fwe->fd.fc; if (fwe->dma_ch >= 0) { xferq = fc->ir[fwe->dma_ch]; if (xferq->flag & FWXFERQ_RUNNING) fc->irx_disable(fc, fwe->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_FWE); for (xfer = STAILQ_FIRST(&fwe->xferlist); xfer != NULL; xfer = next) { next = STAILQ_NEXT(xfer, link); fw_xfer_free(xfer); } STAILQ_INIT(&fwe->xferlist); xferq->bulkxfer = NULL; fwe->dma_ch = -1; } ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } static int fwe_detach(device_t dev) { struct fwe_softc *fwe; struct ifnet *ifp; int s; fwe = device_get_softc(dev); ifp = fwe->eth_softc.ifp; #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(ifp); #endif s = splimp(); fwe_stop(fwe); ether_ifdetach(ifp); if_free(ifp); splx(s); mtx_destroy(&fwe->mtx); return 0; } static void fwe_init(void *arg) { struct fwe_softc *fwe = ((struct fwe_eth_softc *)arg)->fwe; struct firewire_comm *fc; struct ifnet *ifp = fwe->eth_softc.ifp; struct fw_xferq *xferq; struct fw_xfer *xfer; struct mbuf *m; int i; FWEDEBUG(ifp, "initializing\n"); /* XXX keep promiscoud mode */ ifp->if_flags |= IFF_PROMISC; fc = fwe->fd.fc; if (fwe->dma_ch < 0) { fwe->dma_ch = fw_open_isodma(fc, /* tx */0); if (fwe->dma_ch < 0) return; xferq = fc->ir[fwe->dma_ch]; xferq->flag |= FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_STREAM; fwe->stream_ch = stream_ch; fwe->pkt_hdr.mode.stream.chtag = fwe->stream_ch; xferq->flag &= ~0xff; xferq->flag |= fwe->stream_ch & 0xff; /* register fwe_input handler */ xferq->sc = (caddr_t) fwe; xferq->hand = fwe_as_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_FWE, M_WAITOK); - if (xferq->bulkxfer == NULL) { - printf("if_fwe: 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 = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR); xferq->bulkxfer[i].mbuf = m; m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; STAILQ_INSERT_TAIL(&xferq->stfree, &xferq->bulkxfer[i], link); } STAILQ_INIT(&fwe->xferlist); for (i = 0; i < TX_MAX_QUEUE; i++) { xfer = fw_xfer_alloc(M_FWE); if (xfer == NULL) break; xfer->send.spd = tx_speed; xfer->fc = fwe->fd.fc; xfer->sc = (caddr_t)fwe; xfer->hand = fwe_output_callback; STAILQ_INSERT_TAIL(&fwe->xferlist, xfer, link); } } else xferq = fc->ir[fwe->dma_ch]; /* start dma */ if ((xferq->flag & FWXFERQ_RUNNING) == 0) fc->irx_enable(fc, fwe->dma_ch); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; #if 0 /* attempt to start output */ fwe_start(ifp); #endif } static int fwe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct fwe_softc *fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe; struct ifstat *ifs = NULL; int s, error; switch (cmd) { case SIOCSIFFLAGS: s = splimp(); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) fwe_init(&fwe->eth_softc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) fwe_stop(fwe); } /* XXX keep promiscoud mode */ ifp->if_flags |= IFF_PROMISC; splx(s); break; case SIOCADDMULTI: case SIOCDELMULTI: break; case SIOCGIFSTATUS: s = splimp(); ifs = (struct ifstat *)data; snprintf(ifs->ascii, sizeof(ifs->ascii), "\tch %d dma %d\n", fwe->stream_ch, fwe->dma_ch); splx(s); break; case SIOCSIFCAP: #ifdef DEVICE_POLLING { struct ifreq *ifr = (struct ifreq *) data; struct firewire_comm *fc = fwe->fd.fc; if (ifr->ifr_reqcap & IFCAP_POLLING && !(ifp->if_capenable & IFCAP_POLLING)) { error = ether_poll_register(fwe_poll, ifp); if (error) return (error); /* Disable interrupts */ fc->set_intr(fc, 0); ifp->if_capenable |= IFCAP_POLLING; ifp->if_capenable |= IFCAP_POLLING_NOCOUNT; return (error); } if (!(ifr->ifr_reqcap & IFCAP_POLLING) && ifp->if_capenable & IFCAP_POLLING) { error = ether_poll_deregister(ifp); /* Enable interrupts. */ fc->set_intr(fc, 1); ifp->if_capenable &= ~IFCAP_POLLING; ifp->if_capenable &= ~IFCAP_POLLING_NOCOUNT; return (error); } } #endif /* DEVICE_POLLING */ break; default: s = splimp(); error = ether_ioctl(ifp, cmd, data); splx(s); return (error); } return (0); } static void fwe_output_callback(struct fw_xfer *xfer) { struct fwe_softc *fwe; struct ifnet *ifp; int s; fwe = (struct fwe_softc *)xfer->sc; ifp = fwe->eth_softc.ifp; /* XXX error check */ FWEDEBUG(ifp, "resp = %d\n", xfer->resp); if (xfer->resp != 0) if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(xfer->mbuf); fw_xfer_unload(xfer); s = splimp(); FWE_LOCK(fwe); STAILQ_INSERT_TAIL(&fwe->xferlist, xfer, link); FWE_UNLOCK(fwe); splx(s); /* for queue full */ if (ifp->if_snd.ifq_head != NULL) fwe_start(ifp); } static void fwe_start(struct ifnet *ifp) { struct fwe_softc *fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe; int s; FWEDEBUG(ifp, "starting\n"); if (fwe->dma_ch < 0) { struct mbuf *m = NULL; FWEDEBUG(ifp, "not ready\n"); s = splimp(); do { IF_DEQUEUE(&ifp->if_snd, m); if (m != NULL) m_freem(m); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } while (m != NULL); splx(s); return; } s = splimp(); ifp->if_drv_flags |= IFF_DRV_OACTIVE; if (ifp->if_snd.ifq_len != 0) fwe_as_output(fwe, ifp); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; splx(s); } #define HDR_LEN 4 #ifndef ETHER_ALIGN #define ETHER_ALIGN 2 #endif /* Async. stream output */ static void fwe_as_output(struct fwe_softc *fwe, struct ifnet *ifp) { struct mbuf *m; struct fw_xfer *xfer; struct fw_xferq *xferq; struct fw_pkt *fp; int i = 0; xfer = NULL; xferq = fwe->fd.fc->atq; while ((xferq->queued < xferq->maxq - 1) && (ifp->if_snd.ifq_head != NULL)) { FWE_LOCK(fwe); xfer = STAILQ_FIRST(&fwe->xferlist); if (xfer == NULL) { #if 0 printf("if_fwe: lack of xfer\n"); #endif FWE_UNLOCK(fwe); break; } STAILQ_REMOVE_HEAD(&fwe->xferlist, link); FWE_UNLOCK(fwe); IF_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { FWE_LOCK(fwe); STAILQ_INSERT_HEAD(&fwe->xferlist, xfer, link); FWE_UNLOCK(fwe); break; } BPF_MTAP(ifp, m); /* keep ip packet alignment for alpha */ M_PREPEND(m, ETHER_ALIGN, M_NOWAIT); fp = &xfer->send.hdr; *(uint32_t *)&xfer->send.hdr = *(int32_t *)&fwe->pkt_hdr; fp->mode.stream.len = m->m_pkthdr.len; xfer->mbuf = m; xfer->send.pay_len = m->m_pkthdr.len; if (fw_asyreq(fwe->fd.fc, -1, xfer) != 0) { /* error */ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* XXX set error code */ fwe_output_callback(xfer); } else { if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); i++; } } #if 0 if (i > 1) printf("%d queued\n", i); #endif if (i > 0) xferq->start(fwe->fd.fc); } /* Async. stream output */ static void fwe_as_input(struct fw_xferq *xferq) { struct mbuf *m, *m0; struct ifnet *ifp; struct fwe_softc *fwe; struct fw_bulkxfer *sxfer; struct fw_pkt *fp; u_char *c; fwe = (struct fwe_softc *)xferq->sc; ifp = fwe->eth_softc.ifp; /* We do not need a lock here because the bottom half is serialized */ while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) { STAILQ_REMOVE_HEAD(&xferq->stvalid, link); fp = mtod(sxfer->mbuf, struct fw_pkt *); if (fwe->fd.fc->irx_post != NULL) fwe->fd.fc->irx_post(fwe->fd.fc, fp->mode.ld); m = sxfer->mbuf; /* insert new rbuf */ sxfer->mbuf = m0 = m_getcl(M_NOWAIT, 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("%s: m_getcl failed\n", __FUNCTION__); if (sxfer->resp != 0 || fp->mode.stream.len < ETHER_ALIGN + sizeof(struct ether_header)) { m_freem(m); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); continue; } m->m_data += HDR_LEN + ETHER_ALIGN; c = mtod(m, u_char *); m->m_len = m->m_pkthdr.len = fp->mode.stream.len - ETHER_ALIGN; m->m_pkthdr.rcvif = ifp; #if 0 FWEDEBUG(ifp, "%02x %02x %02x %02x %02x %02x\n" "%02x %02x %02x %02x %02x %02x\n" "%02x %02x %02x %02x\n" "%02x %02x %02x %02x\n" "%02x %02x %02x %02x\n" "%02x %02x %02x %02x\n", c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11], c[12], c[13], c[14], c[15], c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23], c[20], c[21], c[22], c[23] ); #endif (*ifp->if_input)(ifp, m); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); } if (STAILQ_FIRST(&xferq->stfree) != NULL) fwe->fd.fc->irx_enable(fwe->fd.fc, fwe->dma_ch); } static devclass_t fwe_devclass; static device_method_t fwe_methods[] = { /* device interface */ DEVMETHOD(device_identify, fwe_identify), DEVMETHOD(device_probe, fwe_probe), DEVMETHOD(device_attach, fwe_attach), DEVMETHOD(device_detach, fwe_detach), { 0, 0 } }; static driver_t fwe_driver = { "fwe", fwe_methods, sizeof(struct fwe_softc), }; DRIVER_MODULE(fwe, firewire, fwe_driver, fwe_devclass, 0, 0); MODULE_VERSION(fwe, 1); MODULE_DEPEND(fwe, firewire, 1, 1, 1);