diff options
author | simokawa <simokawa@FreeBSD.org> | 2003-01-26 15:39:04 +0000 |
---|---|---|
committer | simokawa <simokawa@FreeBSD.org> | 2003-01-26 15:39:04 +0000 |
commit | 376bd949e066a9aef4b2331a6b8e66e5eba8132f (patch) | |
tree | 07df55b90f7e481cf05714448e507f62ace570ba /sys | |
parent | 4865a58cd6760c2a3695e5f7b6b7bf42560ced68 (diff) | |
download | FreeBSD-src-376bd949e066a9aef4b2331a6b8e66e5eba8132f.zip FreeBSD-src-376bd949e066a9aef4b2331a6b8e66e5eba8132f.tar.gz |
- Improve IT/IR DMA queue management.
- Improve debug message for mbuf handling.
- Wait 1 sec for DMA stop in fwohci_i{t,r}x_disable() before freeing buffers.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/firewire/firewire.c | 178 | ||||
-rw-r--r-- | sys/dev/firewire/firewirereg.h | 10 | ||||
-rw-r--r-- | sys/dev/firewire/fwdev.c | 41 | ||||
-rw-r--r-- | sys/dev/firewire/fwohci.c | 434 |
4 files changed, 235 insertions, 428 deletions
diff --git a/sys/dev/firewire/firewire.c b/sys/dev/firewire/firewire.c index 7252cea..e555266 100644 --- a/sys/dev/firewire/firewire.c +++ b/sys/dev/firewire/firewire.c @@ -122,184 +122,6 @@ static driver_t firewire_driver = { }; /* - * transmitter buffer update. - */ -int -fw_tbuf_update(struct firewire_comm *fc, int sub, int flag){ - struct fw_bulkxfer *bulkxfer, *bulkxfer2 = NULL; - struct fw_xferq *it; - int s, err = 0; - - it = fc->it[sub]; - - s = splfw(); - if(it->stdma == NULL){ - bulkxfer = STAILQ_FIRST(&it->stvalid); - }else if(flag != 0){ - bulkxfer = STAILQ_FIRST(&it->stvalid); - if(bulkxfer == it->stdma){ - STAILQ_REMOVE_HEAD(&it->stvalid, link); - it->stdma->flag = 0; - STAILQ_INSERT_TAIL(&it->stfree, it->stdma, link); -#ifdef FWXFERQ_DV - if(!(it->flag & FWXFERQ_DV)) -#endif - wakeup(it); - } - bulkxfer = STAILQ_FIRST(&it->stvalid); - }else{ - bulkxfer = it->stdma; - } - if(bulkxfer != NULL){ - bulkxfer2 = STAILQ_NEXT(bulkxfer, link); -#if 0 - if(it->flag & FWXFERQ_DV && bulkxfer2 == NULL){ - bulkxfer2 = STAILQ_FIRST(&it->stfree); - STAILQ_REMOVE_HEAD(&it->stfree, link); - bcopy(bulkxfer->buf, bulkxfer2->buf, - it->psize * it->btpacket); - STAILQ_INSERT_TAIL(&it->stvalid, bulkxfer2, link); - } -#endif - } - it->stdma = bulkxfer; - it->stdma2 = bulkxfer2; - -#ifdef FWXFERQ_DV - if(it->flag & FWXFERQ_DV){ - struct fw_dvbuf *dvbuf = NULL; - int i, j, chtag; - struct fw_pkt *fp; - u_int64_t cycle, dvsync; - - chtag = it->flag & 0xff; -dvloop: - if(it->dvdma == NULL){ - dvbuf = STAILQ_FIRST(&it->dvvalid); - if(dvbuf != NULL){ - STAILQ_REMOVE_HEAD(&it->dvvalid, link); - it->dvdma = dvbuf; - it->queued = 0; - } - } - if(it->dvdma == NULL) - goto out; - - it->stproc = STAILQ_FIRST(&it->stfree); - if(it->stproc != NULL){ - STAILQ_REMOVE_HEAD(&it->stfree, link); - }else{ - goto out; - } -#if DV_PAL -#define DVSEC 3 -#define DVFRAC 75 /* PAL: 25 Hz (1875 = 25 * 3) */ -#define DVDIFF 5 /* 125 = (8000/300 - 25) * 3 */ -#else -#define DVSEC 100 -#define DVFRAC 2997 /* NTSC: 29.97 Hz (2997 = 29.97 * 100) */ -#define DVDIFF 203 /* 203 = (8000/250 - 29.97) * 100 */ -#endif -#define CYCLEFRAC 0xc00 - cycle = (u_int64_t) 8000 * DVSEC * it->dvsync; - /* least significant 12 bits */ - dvsync = (cycle * CYCLEFRAC / DVFRAC) % CYCLEFRAC; - /* most significat 4 bits */ - cycle = (cycle / DVFRAC + it->dvoffset) & 0xf; - fp = (struct fw_pkt *)(it->dvdma->buf); -#if 1 - fp->mode.ld[2] = htonl(0x80000000 | (cycle << 12) | dvsync); -#else - fp->mode.ld[2] = htonl(0x80000000 | dvsync); -#endif - it->dvsync ++; - it->dvsync %= 2997; - - for( i = 0, j = 0 ; i < it->dvpacket ; i++){ - bcopy(it->dvdma->buf + it->queued * it->psize, - it->stproc->buf + j * it->psize, it->psize); - fp = (struct fw_pkt *)(it->stproc->buf + j * it->psize); - fp->mode.stream.len = htons(488); - fp->mode.stream.chtag = chtag; - fp->mode.stream.tcode = FWTCODE_STREAM; - fp->mode.ld[1] = htonl((fc->nodeid << 24) | 0x00780000 | it->dvdbc); - it->dvdbc++; - it->dvdbc %= 256; - it->queued ++; - j++; - it->dvdiff += DVDIFF; - if(it->dvdiff >= DVFRAC){ - it->dvdiff %= DVFRAC; - fp = (struct fw_pkt *)(it->stproc->buf + j * it->psize); - - fp->mode.stream.len = htons(0x8); - fp->mode.stream.chtag = chtag; - fp->mode.stream.tcode = FWTCODE_STREAM; - fp->mode.ld[1] = htonl((fc->nodeid << 24) | - 0x00780000 | it->dvdbc); - j++; - } - } - it->stproc->npacket = j; - STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); - if(it->queued >= it->dvpacket){ - STAILQ_INSERT_TAIL(&it->dvfree, it->dvdma, link); - it->dvdma = NULL; - wakeup(it); - goto dvloop; - } - } -out: -#endif - splx(s); - return err; -} -/* - * receving buffer update. - */ -int -fw_rbuf_update(struct firewire_comm *fc, int sub, int flag){ - struct fw_bulkxfer *bulkxfer, *bulkxfer2 = NULL; - struct fw_xferq *ir; - int s, err = 0; - - ir = fc->ir[sub]; - s = splfw(); - if(ir->stdma != NULL){ - if(flag != 0){ - STAILQ_INSERT_TAIL(&ir->stvalid, ir->stdma, link); - }else{ - ir->stdma->flag = 0; - STAILQ_INSERT_TAIL(&ir->stfree, ir->stdma, link); - } - } - if(ir->stdma2 != NULL){ - bulkxfer = ir->stdma2; - bulkxfer2 = STAILQ_FIRST(&ir->stfree); - if(bulkxfer2 != NULL){ - STAILQ_REMOVE_HEAD(&ir->stfree, link); - } - }else{ - bulkxfer = STAILQ_FIRST(&ir->stfree); - if(bulkxfer != NULL){ - STAILQ_REMOVE_HEAD(&ir->stfree, link); - bulkxfer2 = STAILQ_FIRST(&ir->stfree); - if(bulkxfer2 != NULL){ - STAILQ_REMOVE_HEAD(&ir->stfree, link); - } - }else{ - device_printf(fc->bdev, "no free chunk available\n"); - bulkxfer = STAILQ_FIRST(&ir->stvalid); - STAILQ_REMOVE_HEAD(&ir->stvalid, link); - } - } - splx(s); - ir->stdma = bulkxfer; - ir->stdma2 = bulkxfer2; - return err; -} - -/* * To lookup node id. from EUI64. */ struct fw_device * diff --git a/sys/dev/firewire/firewirereg.h b/sys/dev/firewire/firewirereg.h index a2c994e..ffdd8b7 100644 --- a/sys/dev/firewire/firewirereg.h +++ b/sys/dev/firewire/firewirereg.h @@ -186,7 +186,7 @@ struct fw_xferq { #define FWXFERQ_PACKET (1 << 10) #define FWXFERQ_BULK (1 << 11) -#if 0 +#if 0 /* BROKEN */ #define FWXFERQ_DV (1 << 12) #endif #define FWXFERQ_MODEMASK (7 << 10) @@ -213,10 +213,9 @@ struct fw_xferq { struct fw_bulkxfer *bulkxfer; STAILQ_HEAD(, fw_bulkxfer) stvalid; STAILQ_HEAD(, fw_bulkxfer) stfree; - struct fw_bulkxfer *stdma; - struct fw_bulkxfer *stdma2; + STAILQ_HEAD(, fw_bulkxfer) stdma; struct fw_bulkxfer *stproc; - u_int procptr; +#ifdef FWXFERQ_DV int dvdbc, dvdiff, dvsync, dvoffset; struct fw_dvbuf *dvbuf; STAILQ_HEAD(, fw_dvbuf) dvvalid; @@ -225,14 +224,13 @@ struct fw_xferq { struct fw_dvbuf *dvproc; u_int dvptr; u_int dvpacket; - u_int need_wakeup; +#endif struct selinfo rsel; caddr_t sc; void (*hand) __P((struct fw_xferq *)); }; struct fw_bulkxfer{ - u_int32_t flag; caddr_t buf; STAILQ_ENTRY(fw_bulkxfer) link; caddr_t start; diff --git a/sys/dev/firewire/fwdev.c b/sys/dev/firewire/fwdev.c index 7233f35..460e8b8 100644 --- a/sys/dev/firewire/fwdev.c +++ b/sys/dev/firewire/fwdev.c @@ -173,7 +173,9 @@ fw_close (dev_t dev, int flags, int fmt, fw_proc *td) sc->fc->it[sub]->buf = NULL; free(sc->fc->it[sub]->bulkxfer, M_DEVBUF); sc->fc->it[sub]->bulkxfer = NULL; +#ifdef FWXFERQ_DV sc->fc->it[sub]->dvbuf = NULL; +#endif sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF; sc->fc->it[sub]->psize = 0; sc->fc->it[sub]->maxq = FWMAXQUEUE; @@ -284,24 +286,12 @@ readloop: ir->queued ++; if(ir->queued >= ir->bnpacket){ s = splfw(); - ir->stproc->flag = 0; STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); splx(s); + sc->fc->irx_enable(sc->fc, sub); ir->stproc = NULL; } } -#if 0 - if(STAILQ_FIRST(&ir->q) == NULL && - (ir->flag & FWXFERQ_RUNNING) && (ir->flag & FWXFERQ_PACKET)){ - err = sc->fc->irx_enable(sc->fc, sub); - } -#endif -#if 0 - if(STAILQ_FIRST(&ir->stvalid) == NULL && - (ir->flag & FWXFERQ_RUNNING) && !(ir->flag & FWXFERQ_PACKET)){ - err = sc->fc->irx_enable(sc->fc, sub); - } -#endif return err; } @@ -357,7 +347,6 @@ fw_write (dev_t dev, struct uio *uio, int ioflag) /* Discard unsent buffered stream packet, when sending Asyrequrst */ if(xferq != NULL && it->stproc != NULL){ s = splfw(); - it->stproc->flag = 0; STAILQ_INSERT_TAIL(&it->stfree, it->stproc, link); splx(s); it->stproc = NULL; @@ -368,14 +357,14 @@ fw_write (dev_t dev, struct uio *uio, int ioflag) if (xferq == NULL) { #endif isoloop: - if(it->stproc == NULL){ + if (it->stproc == NULL) { it->stproc = STAILQ_FIRST(&it->stfree); - if(it->stproc != NULL){ + if (it->stproc != NULL) { s = splfw(); STAILQ_REMOVE_HEAD(&it->stfree, link); splx(s); it->queued = 0; - }else if(slept == 0){ + } else if (slept == 0) { slept = 1; err = sc->fc->itx_enable(sc->fc, sub); if(err){ @@ -391,10 +380,6 @@ isoloop: return err; } } -#if 0 /* What's this for? (overwritten by the following uiomove)*/ - fp = (struct fw_pkt *)(it->stproc->buf + it->queued * it->psize); - fp->mode.stream.len = htons(uio->uio_resid - sizeof(u_int32_t)); -#endif err = uiomove(it->stproc->buf + it->queued * it->psize, uio->uio_resid, uio); it->queued ++; @@ -403,7 +388,6 @@ isoloop: STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); splx(s); it->stproc = NULL; - fw_tbuf_update(sc->fc, sub, 0); err = sc->fc->itx_enable(sc->fc, sub); } return err; @@ -684,22 +668,23 @@ fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) it->bnpacket = ibufreq->tx.npacket; it->btpacket = ibufreq->tx.npacket; it->psize = (ibufreq->tx.psize + 3) & ~3; - ir->queued = 0; + it->queued = 0; + +#ifdef FWXFERQ_DV it->dvdbc = 0; it->dvdiff = 0; it->dvsync = 0; it->dvoffset = 0; +#endif STAILQ_INIT(&ir->stvalid); STAILQ_INIT(&ir->stfree); - ir->stdma = NULL; - ir->stdma2 = NULL; + STAILQ_INIT(&ir->stdma); ir->stproc = NULL; STAILQ_INIT(&it->stvalid); STAILQ_INIT(&it->stfree); - it->stdma = NULL; - it->stdma2 = NULL; + STAILQ_INIT(&it->stdma); it->stproc = NULL; for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){ @@ -707,7 +692,6 @@ fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) ir->buf + i * sc->fc->ir[sub]->bnpacket * sc->fc->ir[sub]->psize; - ir->bulkxfer[i].flag = 0; STAILQ_INSERT_TAIL(&ir->stfree, &ir->bulkxfer[i], link); ir->bulkxfer[i].npacket = ir->bnpacket; @@ -717,7 +701,6 @@ fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) it->buf + i * sc->fc->it[sub]->bnpacket * sc->fc->it[sub]->psize; - it->bulkxfer[i].flag = 0; STAILQ_INSERT_TAIL(&it->stfree, &it->bulkxfer[i], link); it->bulkxfer[i].npacket = it->bnpacket; diff --git a/sys/dev/firewire/fwohci.c b/sys/dev/firewire/fwohci.c index ea6e3d2..78c238e 100644 --- a/sys/dev/firewire/fwohci.c +++ b/sys/dev/firewire/fwohci.c @@ -42,6 +42,7 @@ #define IRX_CH 0x24 #include <sys/param.h> +#include <sys/proc.h> #include <sys/systm.h> #include <sys/types.h> #include <sys/mbuf.h> @@ -847,17 +848,15 @@ txloop: db_tr->dbcnt++; } else { + int mchain=0; /* XXX we assume mbuf chain is shorter than ndesc */ for (m = xfer->mbuf; m != NULL; m = m->m_next) { if (m->m_len == 0) - /* unrecoverable error could ocurre. */ + /* unrecoverable error could occur. */ + continue; + mchain++; + if (db_tr->dbcnt >= dbch->ndesc) continue; - if (db_tr->dbcnt >= dbch->ndesc) { - device_printf(sc->fc.dev, - "dbch->ndesc is too small" - ", trancated.\n"); - break; - } db->db.desc.addr = vtophys(mtod(m, caddr_t)); db->db.desc.cmd = OHCI_OUTPUT_MORE | m->m_len; @@ -865,6 +864,11 @@ txloop: db++; db_tr->dbcnt++; } + if (mchain > dbch->ndesc - 2) + device_printf(sc->fc.dev, + "dbch->ndesc(%d) is too small for" + " mbuf chain(%d), trancated.\n", + dbch->ndesc, mchain); } } if (maxdesc < db_tr->dbcnt) { @@ -1201,9 +1205,13 @@ static int fwohci_itx_disable(struct firewire_comm *fc, int dmach) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int dummy; + OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach); OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach); + /* XXX we cannot free buffers until the DMA really stops */ + tsleep((void *)&dummy, FWPRI, "fwitxd", hz); fwohci_db_free(&sc->it[dmach]); sc->it[dmach].xferq.flag &= ~FWXFERQ_RUNNING; return 0; @@ -1213,10 +1221,13 @@ static int fwohci_irx_disable(struct firewire_comm *fc, int dmach) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int dummy; 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 */ + tsleep((void *)&dummy, FWPRI, "fwirxd", hz); if(sc->ir[dmach].dummy != NULL){ free(sc->ir[dmach].dummy, M_DEVBUF); } @@ -1416,21 +1427,51 @@ fwohci_rx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch) } static int +fwochi_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 +#define CYCLE_DELAY 8 /* min delay to start DMA */ + cycle = cycle + CYCLE_DELAY; + if (cycle >= 8000) { + sec ++; + cycle -= 8000; + } + cycle = ((cycle + CYCLE_MOD - 1) / CYCLE_MOD) * 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_now, sec, cycle, cycle_match; + int cycle_match, cycle_now, s, ldesc; u_int32_t stat; + struct fw_bulkxfer *first, *chunk, *prev; + struct fw_xferq *it; - tag = (sc->it[dmach].xferq.flag >> 6) & 3; - ich = sc->it[dmach].xferq.flag & 0x3f; dbch = &sc->it[dmach]; + it = &dbch->xferq; + + tag = (it->flag >> 6) & 3; + ich = it->flag & 0x3f; if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) { - dbch->xferq.queued = 0; - dbch->ndb = dbch->xferq.bnpacket * dbch->xferq.bnchunk; + dbch->ndb = it->bnpacket * it->bnchunk; dbch->ndesc = 3; fwohci_db_init(dbch); if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) @@ -1439,56 +1480,53 @@ fwohci_itxbuf_enable(struct firewire_comm *fc, int dmach) } if(err) return err; - stat = OREAD(sc, OHCI_ITCTL(dmach)); - if (stat & OHCI_CNTL_DMA_ACTIVE) { - if(dbch->xferq.stdma2 != NULL){ - fwohci_txbufdb(sc, dmach, dbch->xferq.stdma2); - ((struct fwohcidb_tr *) - (dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.cmd - |= OHCI_BRANCH_ALWAYS; - ((struct fwohcidb_tr *) - (dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.depend = - vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc; - ((struct fwohcidb_tr *)(dbch->xferq.stdma->end))->db[0].db.desc.depend = - vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc; - ((struct fwohcidb_tr *)(dbch->xferq.stdma2->end))->db[dbch->ndesc - 1].db.desc.depend &= ~0xf; - ((struct fwohcidb_tr *)(dbch->xferq.stdma2->end))->db[0].db.desc.depend &= ~0xf; - } else { - device_printf(fc->dev, - "fwohci_itxbuf_enable: queue underrun\n"); + + s = splfw(); + prev = STAILQ_LAST(&it->stdma, fw_bulkxfer, link); + while ((chunk = STAILQ_FIRST(&it->stvalid)) != NULL) { + volatile struct fwohcidb *db; + + fwohci_txbufdb(sc, dmach, chunk); + ldesc = dbch->ndesc - 1; + db = ((struct fwohcidb_tr *)(chunk->end))->db; + db[ldesc].db.desc.status = db[0].db.desc.status = 0; + db[ldesc].db.desc.count = db[0].db.desc.count = 0; + db[ldesc].db.desc.depend &= ~0xf; + db[0].db.desc.depend &= ~0xf; + if (prev != NULL) { + db = ((struct fwohcidb_tr *)(prev->end))->db; + db[ldesc].db.desc.cmd |= OHCI_BRANCH_ALWAYS; + db[ldesc].db.desc.depend = db[0].db.desc.depend = + vtophys(((struct fwohcidb_tr *) + (chunk->start))->db) | dbch->ndesc; } - return err; - } - if (firewire_debug) - printf("fwohci_itxbuf_enable: kick 0x%08x\n", stat); - fw_tbuf_update(&sc->fc, dmach, 0); - if(dbch->xferq.stdma == NULL){ - return err; - } - if(dbch->xferq.stdma2 == NULL){ - /* wait until 2 chunks buffered */ - return err; + STAILQ_REMOVE_HEAD(&it->stvalid, link); + STAILQ_INSERT_TAIL(&it->stdma, chunk, link); + prev = chunk; } + splx(s); + stat = OREAD(sc, OHCI_ITCTL(dmach)); + if (stat & (OHCI_CNTL_DMA_ACTIVE | OHCI_CNTL_CYCMATCH_S)) + return 0; + + OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN); OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach); OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach); OWRITE(sc, OHCI_IT_MASK, 1 << dmach); - fwohci_txbufdb(sc, dmach, dbch->xferq.stdma); - fwohci_txbufdb(sc, dmach, dbch->xferq.stdma2); - ((struct fwohcidb_tr *) - (dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.cmd - |= OHCI_BRANCH_ALWAYS; - ((struct fwohcidb_tr *)(dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.depend = - vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc; - ((struct fwohcidb_tr *)(dbch->xferq.stdma->end))->db[0].db.desc.depend = - vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc; - ((struct fwohcidb_tr *)(dbch->xferq.stdma2->end))->db[dbch->ndesc - 1].db.desc.depend &= ~0xf; - ((struct fwohcidb_tr *) (dbch->xferq.stdma2->end))->db[0].db.desc.depend &= ~0xf; - OWRITE(sc, OHCI_ITCMD(dmach), - vtophys(((struct fwohcidb_tr *) - (dbch->xferq.stdma->start))->db) | dbch->ndesc); -#define CYCLE_OFFSET 1 + + first = STAILQ_FIRST(&it->stdma); + OWRITE(sc, OHCI_ITCMD(dmach), vtophys(((struct fwohcidb_tr *) + (first->start))->db) | dbch->ndesc); + if (firewire_debug) + printf("fwohci_itxbuf_enable: kick 0x%08x\n", stat); 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 #ifdef FWXFERQ_DV +#define CYCLE_OFFSET 1 if(dbch->xferq.flag & FWXFERQ_DV){ struct fw_pkt *fp; struct fwohcidb_tr *db_tr; @@ -1499,41 +1537,26 @@ fwohci_itxbuf_enable(struct firewire_comm *fc, int dmach) fp->mode.ld[2] |= htonl(dbch->xferq.dvoffset << 12); } #endif - /* 2bit second + 13bit cycle */ - cycle_now = (fc->cyctimer(fc) >> 12) & 0x7fff; - cycle = cycle_now & 0x1fff; - sec = cycle_now >> 13; -#define CYCLE_MOD 0x10 -#define CYCLE_DELAY 8 /* min delay to start DMA */ - cycle = cycle + CYCLE_DELAY; - if (cycle >= 8000) { - sec ++; - cycle -= 8000; - } - cycle = ((cycle + CYCLE_MOD - 1) / CYCLE_MOD) * CYCLE_MOD; - if (cycle >= 8000) { - sec ++; - if (cycle == 8000) - cycle = 0; - else - cycle = CYCLE_MOD; - } - cycle_match = ((sec << 13) | cycle) & 0x7ffff; /* Clear cycle match counter bits */ OWRITE(sc, OHCI_ITCTLCLR(dmach), 0xffff0000); + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IT); + + /* 2bit second + 13bit cycle */ + cycle_now = (fc->cyctimer(fc) >> 12) & 0x7fff; + cycle_match = fwochi_next_cycle(fc, cycle_now); + OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_CYCMATCH_S | (cycle_match << 16) | OHCI_CNTL_DMA_RUN); - OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IT); if (firewire_debug) printf("cycle_match: 0x%04x->0x%04x\n", cycle_now, cycle_match); } else if ((stat & OHCI_CNTL_CYCMATCH_S) == 0) { - if (firewire_debug) - printf("fwohci_itxbuf_enable: restart 0x%08x\n", stat); - OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN); + device_printf(sc->fc.dev, + "IT DMA underrun (0x%08x)\n", stat); OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_RUN); } +out: return err; } @@ -1541,71 +1564,84 @@ static int fwohci_irxbuf_enable(struct firewire_comm *fc, int dmach) { struct fwohci_softc *sc = (struct fwohci_softc *)fc; - int err = 0; + int err = 0, s, ldesc; unsigned short tag, ich; u_int32_t stat; + struct fwohci_dbch *dbch; + struct fw_bulkxfer *first, *prev, *chunk; + struct fw_xferq *ir; - if(!(sc->ir[dmach].xferq.flag & FWXFERQ_RUNNING)){ - tag = (sc->ir[dmach].xferq.flag >> 6) & 3; - ich = sc->ir[dmach].xferq.flag & 0x3f; + dbch = &sc->ir[dmach]; + ir = &dbch->xferq; + ldesc = dbch->ndesc - 1; + + if ((ir->flag & FWXFERQ_RUNNING) == 0) { + tag = (ir->flag >> 6) & 3; + ich = ir->flag & 0x3f; OWRITE(sc, OHCI_IRMATCH(dmach), tagbit[tag] | ich); - sc->ir[dmach].xferq.queued = 0; - sc->ir[dmach].ndb = sc->ir[dmach].xferq.bnpacket * - sc->ir[dmach].xferq.bnchunk; - sc->ir[dmach].dummy = - malloc(sizeof(u_int32_t) * sc->ir[dmach].ndb, - M_DEVBUF, M_NOWAIT); - if(sc->ir[dmach].dummy == NULL){ + ir->queued = 0; + dbch->ndb = ir->bnpacket * ir->bnchunk; + dbch->dummy = malloc(sizeof(u_int32_t) * dbch->ndb, + M_DEVBUF, M_NOWAIT); + if (dbch->dummy == NULL) { err = ENOMEM; return err; } - sc->ir[dmach].ndesc = 2; - fwohci_db_init(&sc->ir[dmach]); - if ((sc->ir[dmach].flags & FWOHCI_DBCH_INIT) == 0) + dbch->ndesc = 2; + fwohci_db_init(dbch); + if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) return ENOMEM; - err = fwohci_rx_enable(sc, &sc->ir[dmach]); + err = fwohci_rx_enable(sc, dbch); } if(err) return err; - stat = OREAD(sc, OHCI_IRCTL(dmach)); - if (stat & OHCI_CNTL_DMA_ACTIVE) { - if(sc->ir[dmach].xferq.stdma2 != NULL){ - ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend = - vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db) | sc->ir[dmach].ndesc; - ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[0].db.desc.depend = - vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db); - ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend &= ~0xf; - ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->end))->db[0].db.desc.depend &= ~0xf; - } - } else if (!(stat & OHCI_CNTL_DMA_ACTIVE) - && !(sc->ir[dmach].xferq.flag & FWXFERQ_PACKET)) { - if (firewire_debug) - device_printf(sc->fc.dev, "IR DMA stat %x\n", stat); - fw_rbuf_update(&sc->fc, dmach, 0); + s = splfw(); - OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN); - 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); - if(sc->ir[dmach].xferq.stdma2 != NULL){ - ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend = - vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db) | sc->ir[dmach].ndesc; - ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[0].db.desc.depend = - vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db); - ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend &= ~0xf; - }else{ - ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend &= ~0xf; - ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[0].db.desc.depend &= ~0xf; + first = STAILQ_FIRST(&ir->stfree); + if (first == NULL) { + device_printf(fc->dev, "IR DMA no free chunk\n"); + splx(s); + return 0; + } + + prev = STAILQ_LAST(&ir->stdma, fw_bulkxfer, link); + while ((chunk = STAILQ_FIRST(&ir->stfree)) != NULL) { + volatile struct fwohcidb *db; + + db = ((struct fwohcidb_tr *)(chunk->end))->db; + db[ldesc].db.desc.status = db[ldesc].db.desc.count = 0; + db[ldesc].db.desc.depend &= ~0xf; + if (prev != NULL) { + db = ((struct fwohcidb_tr *)(prev->end))->db; + db[ldesc].db.desc.depend = + vtophys(((struct fwohcidb_tr *) + (chunk->start))->db) | dbch->ndesc; } - OWRITE(sc, OHCI_IRCMD(dmach), - vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->start))->db) | sc->ir[dmach].ndesc); - OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN); - OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR); + STAILQ_REMOVE_HEAD(&ir->stfree, link); + STAILQ_INSERT_TAIL(&ir->stdma, chunk, link); + prev = chunk; + } + 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); } + + 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), + vtophys(((struct fwohcidb_tr *)(first->start))->db) + | dbch->ndesc); + OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN); + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR); return err; } @@ -1959,92 +1995,72 @@ fwohci_set_intr(struct firewire_comm *fc, int enable) static void fwohci_tbuf_update(struct fwohci_softc *sc, int dmach) { - int stat; struct firewire_comm *fc = &sc->fc; - struct fwohci_dbch *dbch; - struct fwohcidb_tr *db_tr; - - dbch = &sc->it[dmach]; -#if 0 /* XXX OHCI interrupt before the last packet is really on the wire */ - if((dbch->xferq.flag & FWXFERQ_DV) && (dbch->xferq.stdma2 != NULL)){ - db_tr = (struct fwohcidb_tr *)dbch->xferq.stdma2->start; -/* - * Overwrite highest significant 4 bits timestamp information - */ - fp = (struct fw_pkt *)db_tr->buf; - fp->mode.ld[2] &= htonl(0xffff0fff); - fp->mode.ld[2] |= htonl((fc->cyctimer(fc) + 0x4000) & 0xf000); - } + volatile struct fwohcidb *db; + struct fw_bulkxfer *chunk; + struct fw_xferq *it; + u_int32_t stat, count; + int s, w=0; + + it = fc->it[dmach]; + s = splfw(); /* unnecessary ? */ + while ((chunk = STAILQ_FIRST(&it->stdma)) != NULL) { + db = ((struct fwohcidb_tr *)(chunk->end))->db; + stat = db[sc->it[dmach].ndesc - 1].db.desc.status; + db = ((struct fwohcidb_tr *)(chunk->start))->db; + count = db[sc->it[dmach].ndesc - 1].db.desc.count; + 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 - /* - * XXX interrupt could be missed. - * We have to check more than one buffer/chunk - */ - if (firewire_debug && dbch->xferq.stdma2 != NULL) { - db_tr = (struct fwohcidb_tr *)dbch->xferq.stdma2->end; - stat = db_tr->db[2].db.desc.status; - if (stat) + break; + default: device_printf(fc->dev, - "stdma2 already done stat:0x%x\n", stat); - } - - stat = OREAD(sc, OHCI_ITCTL(dmach)) & 0x1f; - switch(stat){ - case FWOHCIEV_ACKCOMPL: -#ifdef FWXFERQ_DV - if (dbch->xferq.flag & FWXFERQ_DV) { - struct ciphdr *ciph; - int timer, timestamp, cycl, diff; - static int last_timer=0; - struct fw_pkt *fp; - - timer = (fc->cyctimer(fc) >> 12) & 0xffff; - db_tr = (struct fwohcidb_tr *)dbch->xferq.stdma->start; - fp = (struct fw_pkt *)db_tr->buf; - ciph = (struct ciphdr *) &fp->mode.ld[1]; - timestamp = db_tr->db[2].db.desc.count & 0xffff; - cycl = ntohs(ciph->fdf.dv.cyc) >> 12; - diff = cycl - (timestamp & 0xf) - CYCLE_OFFSET; - if (diff < 0) - diff += 16; - if (diff > 8) - diff -= 16; - if (firewire_debug || diff != 0) - printf("dbc: %3d timer: 0x%04x packet: 0x%04x" - " cyc: 0x%x diff: %+1d\n", - ciph->dbc, last_timer, timestamp, cycl, diff); - last_timer = timer; - /* XXX adjust dbch->xferq.dvoffset if diff != 0 or 1 */ - } -#endif - fw_tbuf_update(fc, dmach, 1); - break; - default: - device_printf(fc->dev, "Isochronous transmit err %02x\n", stat); - fw_tbuf_update(fc, dmach, 0); - break; + "Isochronous transmit err %02x\n", stat); + } + STAILQ_INSERT_TAIL(&it->stfree, chunk, link); + w++; } - fwohci_itxbuf_enable(fc, dmach); + splx(s); + if (w) + wakeup(it); } static void fwohci_rbuf_update(struct fwohci_softc *sc, int dmach) { struct firewire_comm *fc = &sc->fc; - int stat; + volatile struct fwohcidb *db; + struct fw_bulkxfer *chunk; + struct fw_xferq *ir; + u_int32_t stat; + int s, w=0; - stat = OREAD(sc, OHCI_IRCTL(dmach)) & 0x1f; - switch(stat){ - case FWOHCIEV_ACKCOMPL: - fw_rbuf_update(fc, dmach, 1); - wakeup(fc->ir[dmach]); - fwohci_irx_enable(fc, dmach); - break; - default: - device_printf(fc->dev, "Isochronous receive err %02x\n", - stat); - break; + ir = fc->ir[dmach]; + s = splfw(); + while ((chunk = STAILQ_FIRST(&ir->stdma)) != NULL) { + db = ((struct fwohcidb_tr *)(chunk->end))->db; + stat = db[sc->ir[dmach].ndesc - 1].db.desc.status; + if (stat == 0) + break; + STAILQ_REMOVE_HEAD(&ir->stdma, link); + STAILQ_INSERT_TAIL(&ir->stvalid, chunk, link); + switch (stat & FWOHCIEV_MASK) { + case FWOHCIEV_ACKCOMPL: + break; + default: + device_printf(fc->dev, + "Isochronous receive err %02x\n", stat); + } + w++; } + splx(s); + if (w) + wakeup(ir); } void @@ -2294,10 +2310,6 @@ fwohci_txbufdb(struct fwohci_softc *sc, int dmach, struct fw_bulkxfer *bulkxfer) /* device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, vtophys(db_tr->db), vtophys(fdb_tr->db)); */ - if(bulkxfer->flag != 0){ - return; - } - bulkxfer->flag = 1; for( idb = 0 ; idb < bulkxfer->npacket ; idb ++){ db_tr->db[0].db.desc.cmd = OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | 8; @@ -2308,9 +2320,7 @@ device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, vtophys(db_tr->db), v ohcifp->mode.stream.len = ntohs(fp->mode.stream.len); ohcifp->mode.stream.chtag = chtag; ohcifp->mode.stream.tcode = 0xa; - ohcifp->mode.stream.spd = 4; - ohcifp->mode.ld[2] = ntohl(fp->mode.ld[1]); - ohcifp->mode.ld[3] = ntohl(fp->mode.ld[2]); + ohcifp->mode.stream.spd = 0; db_tr->db[2].db.desc.cmd = OHCI_OUTPUT_LAST @@ -2329,12 +2339,6 @@ device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, vtophys(db_tr->db), v db_tr = (struct fwohcidb_tr *)bulkxfer->end; db_tr->db[0].db.desc.depend &= ~0xf; db_tr->db[dbch->ndesc - 1].db.desc.depend &= ~0xf; -#if 0 -/**/ - db_tr->db[dbch->ndesc - 1].db.desc.cmd &= ~OHCI_BRANCH_ALWAYS; - db_tr->db[dbch->ndesc - 1].db.desc.cmd |= OHCI_BRANCH_NEVER; -/**/ -#endif db_tr->db[dbch->ndesc - 1].db.desc.cmd |= OHCI_INTERRUPT_ALWAYS; /* OHCI 1.1 and above */ db_tr->db[0].db.desc.cmd |= OHCI_INTERRUPT_ALWAYS; |