Index: isa/ad1816.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/isa/ad1816.c,v retrieving revision 1.16 diff -u -r1.16 ad1816.c --- isa/ad1816.c 2000/12/23 03:16:11 1.16 +++ isa/ad1816.c 2001/02/19 14:31:25 @@ -586,7 +586,7 @@ ad1816_init(ad1816, dev); if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no; - bus_setup_intr(dev, ad1816->irq, INTR_TYPE_TTY, ad1816_intr, ad1816, &ad1816->ih); + PCM_SETUP_INTR(dev, ad1816->irq, ad1816_intr, ad1816, &ad1816->ih); if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, Index: isa/ess.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/isa/ess.c,v retrieving revision 1.15 diff -u -r1.15 ess.c --- isa/ess.c 2001/02/02 16:41:06 1.15 +++ isa/ess.c 2001/02/19 14:31:25 @@ -824,7 +824,7 @@ if (sc->newspeed) ess_setmixer(sc, 0x71, 0x22); - bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ess_intr, sc, &sc->ih); + PCM_SETUP_INTR(dev, sc->irq, ess_intr, sc, &sc->ih); if (!sc->duplex) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); Index: isa/mss.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/isa/mss.c,v retrieving revision 1.62 diff -u -r1.62 mss.c --- isa/mss.c 2000/12/23 03:16:11 1.62 +++ isa/mss.c 2001/02/19 14:31:25 @@ -1537,10 +1537,10 @@ mixer_init(dev, (mss->bd_id == MD_YM0020)? &ymmix_mixer_class : &mssmix_mixer_class, mss); switch (mss->bd_id) { case MD_OPTI931: - bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, opti931_intr, mss, &mss->ih); + PCM_SETUP_INTR(dev, mss->irq, opti931_intr, mss, &mss->ih); break; default: - bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, mss_intr, mss, &mss->ih); + PCM_SETUP_INTR(dev, mss->irq, mss_intr, mss, &mss->ih); } if (pdma == rdma) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); Index: isa/sb16.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/isa/sb16.c,v retrieving revision 1.64 diff -u -r1.64 sb16.c --- isa/sb16.c 2000/12/23 03:16:11 1.64 +++ isa/sb16.c 2001/02/19 16:08:38 @@ -36,6 +36,8 @@ #include #include +#include + #include "mixer_if.h" #define SB16_BUFFSIZE 4096 @@ -86,8 +88,11 @@ u_long bd_flags; /* board-specific flags */ int prio, prio16; struct sb_chinfo pch, rch; + struct mtx *lock; }; +static void sb_lock(struct sb_info *sb); +static void sb_unlock(struct sb_info *sb); static int sb_rd(struct sb_info *sb, int reg); static void sb_wr(struct sb_info *sb, int reg, u_int8_t val); static int sb_cmd(struct sb_info *sb, u_char val); @@ -195,12 +200,12 @@ { u_long flags; - flags = spltty(); + sb_lock(sb); sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff)); DELAY(10); - splx(flags); + sb_unlock(sb); } static int @@ -209,12 +214,12 @@ int val; u_long flags; - flags = spltty(); + sb_lock(sb); sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); val = sb_rd(sb, SB_MIX_DATA); DELAY(10); - splx(flags); + sb_unlock(sb); return val; } @@ -236,10 +241,15 @@ static int sb_reset_dsp(struct sb_info *sb) { + u_char b; + + sb_lock(sb); sb_wr(sb, SBDSP_RST, 3); DELAY(100); sb_wr(sb, SBDSP_RST, 0); - if (sb_get_byte(sb) != 0xAA) { + b = sb_get_byte(sb); + sb_unlock(sb); + if (b != 0xAA) { DEB(printf("sb_reset_dsp 0x%lx failed\n", rman_get_start(d->io_base))); return ENXIO; /* Sorry */ @@ -482,11 +492,17 @@ if ((reason & 2) && (sb->rch.run)) chn_intr(sb->rch.channel); - if (c & 1) + if (c & 1) { + sb_lock(sb); sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */ + sb_unlock(sb); + } - if (c & 2) + if (c & 2) { + sb_lock(sb); sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */ + sb_unlock(sb); + } } static int @@ -749,11 +765,12 @@ if (sb16_alloc_resources(sb, dev)) goto no; + sb->lock = (struct mtx *)snd_mtxcreate(device_get_nameunit(dev)); if (sb_reset_dsp(sb)) goto no; if (mixer_init(dev, &sb16mix_mixer_class, sb)) goto no; - if (bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &sb->ih)) + if (PCM_SETUP_INTR(dev, sb->irq, sb_intr, sb, &sb->ih)) goto no; if (sb->bd_flags & BD_F_SB16X) @@ -790,6 +807,8 @@ no: sb16_release_resources(sb, dev); + if (sb->lock != NULL) + snd_mtxfree((void *)sb->lock); return ENXIO; } @@ -805,7 +824,20 @@ sb = pcm_getdevinfo(dev); sb16_release_resources(sb, dev); + snd_mtxfree((void *)sb->lock); return 0; +} + +static void +sb_lock(struct sb_info *sb) { + + mtx_lock(sb->lock); +} + +static void +sb_unlock(struct sb_info *sb) { + + mtx_unlock(sb->lock); } static device_method_t sb16_methods[] = { Index: isa/sb8.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/isa/sb8.c,v retrieving revision 1.62 diff -u -r1.62 sb8.c --- isa/sb8.c 2000/12/23 03:16:11 1.62 +++ isa/sb8.c 2001/02/19 14:31:25 @@ -697,7 +697,7 @@ goto no; if (mixer_init(dev, (sb->bd_id < 0x300)? &sbmix_mixer_class : &sbpromix_mixer_class, sb)) goto no; - if (bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &sb->ih)) + if (PCM_SETUP_INTR(dev, sb->irq, sb_intr, sb, &sb->ih)) goto no; pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); Index: isa/sbc.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/isa/sbc.c,v retrieving revision 1.29 diff -u -r1.29 sbc.c --- isa/sbc.c 2001/01/04 17:12:57 1.29 +++ isa/sbc.c 2001/02/19 16:10:45 @@ -35,9 +35,12 @@ #define DRQ_MAX 2 #define INTR_MAX 2 +struct sbc_softc; + struct sbc_ihl { driver_intr_t *intr[INTR_MAX]; void *intr_arg[INTR_MAX]; + struct sbc_softc *parent; }; /* Here is the parameter structure per a device. */ @@ -60,6 +63,8 @@ void *ih[IRQ_MAX]; + void *lock; + u_int32_t bd_ver; }; @@ -103,8 +108,32 @@ static int sb_dspready(struct resource *io); static int sb_cmd(struct resource *io, u_char val); static u_int sb_get_byte(struct resource *io); -static void sb_setmixer(struct resource *io, u_int port, u_int value); +static void sb_setmixer(struct sbc_softc *scp, u_int port, u_int value); + +static void +sbc_lockinit(struct sbc_softc *scp) +{ + scp->lock = snd_mtxcreate(device_get_nameunit(scp->dev)); +} +static void +sbc_lockdestroy(struct sbc_softc *scp) +{ + snd_mtxfree(scp->lock); +} + +static void +sbc_lock(struct sbc_softc *scp) +{ + SND_MTXLOCK(scp->lock); +} + +static void +sbc_unlock(struct sbc_softc *scp) +{ + SND_MTXUNLOCK(scp->lock); +} + static int sb_rd(struct resource *io, int reg) { @@ -150,16 +179,16 @@ } static void -sb_setmixer(struct resource *io, u_int port, u_int value) +sb_setmixer(struct sbc_softc *scp, u_int port, u_int value) { u_long flags; - flags = spltty(); - sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + sbc_lock(scp); + sb_wr(scp->io[0], SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); - sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff)); + sb_wr(scp->io[0], SB_MIX_DATA, (u_char) (value & 0xff)); DELAY(10); - splx(flags); + sbc_unlock(scp); } static u_int @@ -311,6 +340,7 @@ scp = device_get_softc(dev); bzero(scp, sizeof(*scp)); scp->dev = dev; + sbc_lockinit(scp); err = "alloc_resource"; if (alloc_resource(scp)) goto bad; @@ -362,9 +392,9 @@ err = "bad irq (3/5/10/12 valid)"; goto bad; } - else sb_setmixer(scp->io[0], IRQ_NR, x); + else sb_setmixer(scp, IRQ_NR, x); /* SB16 in PC98 use different dma setting */ - sb_setmixer(scp->io[0], DMA_NR, dh == 0 ? 1 : 2); + sb_setmixer(scp, DMA_NR, dh == 0 ? 1 : 2); #else if (irq == 5) x = 2; else if (irq == 7) x = 4; @@ -374,8 +404,8 @@ err = "bad irq (5/7/9/10 valid)"; goto bad; } - else sb_setmixer(scp->io[0], IRQ_NR, x); - sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl)); + else sb_setmixer(scp, IRQ_NR, x); + sb_setmixer(scp, DMA_NR, (1 << dh) | (1 << dl)); #endif if (bootverbose) { device_printf(dev, "setting card to irq %d, drq %d", irq, dl); @@ -396,6 +426,7 @@ err = "setup_intr"; for (i = 0; i < IRQ_MAX; i++) { + scp->ihl[i].parent = scp; if (bus_setup_intr(dev, scp->irq[i], INTR_TYPE_TTY, sbc_intr, &scp->ihl[i], &scp->ih[i])) goto bad; } @@ -439,7 +470,9 @@ { struct sbc_softc *scp = device_get_softc(dev); + sbc_lock(scp); release_resource(scp); + sbc_lockdestroy(scp); return bus_generic_detach(dev); } @@ -449,11 +482,13 @@ struct sbc_ihl *ihl = p; int i; + sbc_lock(ihl->parent); i = 0; while (i < INTR_MAX) { if (ihl->intr[i] != NULL) ihl->intr[i](ihl->intr_arg[i]); i++; } + sbc_unlock(ihl->parent); } static int @@ -463,24 +498,27 @@ { struct sbc_softc *scp = device_get_softc(dev); struct sbc_ihl *ihl = NULL; - int i; + int i, ret; + sbc_lock(scp); i = 0; while (i < IRQ_MAX) { if (irq == scp->irq[i]) ihl = &scp->ihl[i]; i++; } - if (ihl == NULL) return (EINVAL); + ret = 0; + if (ihl == NULL) ret = EINVAL; i = 0; - while (i < INTR_MAX) { + while ((ret == 0) && (i < INTR_MAX)) { if (ihl->intr[i] == NULL) { ihl->intr[i] = intr; ihl->intr_arg[i] = arg; *cookiep = &ihl->intr[i]; - return 0; + ret = -1; } else i++; } - return (EINVAL); + sbc_unlock(scp); + return (ret > 0)? EINVAL : 0; } static int @@ -489,23 +527,26 @@ { struct sbc_softc *scp = device_get_softc(dev); struct sbc_ihl *ihl = NULL; - int i; + int i, ret; + sbc_lock(scp); i = 0; while (i < IRQ_MAX) { if (irq == scp->irq[i]) ihl = &scp->ihl[i]; i++; } - if (ihl == NULL) return (EINVAL); + ret = 0; + if (ihl == NULL) ret = EINVAL; i = 0; - while (i < INTR_MAX) { + while ((ret == 0) && (i < INTR_MAX)) { if (cookie == &ihl->intr[i]) { ihl->intr[i] = NULL; ihl->intr_arg[i] = NULL; return 0; } else i++; } - return (EINVAL); + sbc_unlock(scp); + return (ret > 0)? EINVAL : 0; } static struct resource * Index: pci/aureal.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/aureal.c,v retrieving revision 1.16 diff -u -r1.16 aureal.c --- pci/aureal.c 2000/12/23 03:16:12 1.16 +++ pci/aureal.c 2001/02/19 14:31:25 @@ -620,8 +620,7 @@ irqid = 0; irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!irq - || bus_setup_intr(dev, irq, INTR_TYPE_TTY, au_intr, au, &ih)) { + if (!irq || PCM_SETUP_INTR(dev, irq, au_intr, au, &ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } Index: pci/csapcm.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/csapcm.c,v retrieving revision 1.17 diff -u -r1.17 csapcm.c --- pci/csapcm.c 2000/12/23 03:16:12 1.17 +++ pci/csapcm.c 2001/02/19 14:31:25 @@ -854,7 +854,7 @@ snprintf(status, SND_STATUSLEN, "at irq %ld", rman_get_start(resp->irq)); /* Enable interrupt. */ - if (bus_setup_intr(dev, resp->irq, INTR_TYPE_TTY, csa_intr, csa, &csa->ih)) { + if (PCM_SETUP_INTR(dev, resp->irq, csa_intr, csa, &csa->ih)) { ac97_destroy(codec); csa_releaseres(csa, dev); return (ENXIO); Index: pci/ds1.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/ds1.c,v retrieving revision 1.22 diff -u -r1.22 ds1.c --- pci/ds1.c 2001/01/24 01:22:53 1.22 +++ pci/ds1.c 2001/02/19 14:31:25 @@ -973,8 +973,7 @@ sc->irqid = 0; sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || - bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ds_intr, sc, &sc->ih)) { + if (!sc->irq || PCM_SETUP_INTR(dev, sc->irq, ds_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } Index: pci/emu10k1.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/emu10k1.c,v retrieving revision 1.20 diff -u -r1.20 emu10k1.c --- pci/emu10k1.c 2000/12/23 03:16:12 1.20 +++ pci/emu10k1.c 2001/02/19 14:31:25 @@ -1482,8 +1482,7 @@ sc->irqid = 0; sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || - bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, emu_intr, sc, &sc->ih)) { + if (!sc->irq || PCM_SETUP_INTR(dev, sc->irq, emu_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } Index: pci/es137x.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/es137x.c,v retrieving revision 1.31 diff -u -r1.31 es137x.c --- pci/es137x.c 2001/02/16 13:29:41 1.31 +++ pci/es137x.c 2001/02/19 14:31:25 @@ -817,8 +817,7 @@ es->irqid = 0; es->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &es->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!es->irq - || bus_setup_intr(dev, es->irq, INTR_TYPE_TTY, es_intr, es, &es->ih)) { + if (!es->irq || PCM_SETUP_INTR(dev, es->irq, es_intr, es, &es->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } Index: pci/fm801.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/fm801.c,v retrieving revision 1.6 diff -u -r1.6 fm801.c --- pci/fm801.c 2001/01/24 01:36:15 1.6 +++ pci/fm801.c 2001/02/19 14:31:25 @@ -615,9 +615,7 @@ fm801->irqid = 0; fm801->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fm801->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!fm801->irq || - bus_setup_intr(dev, fm801->irq, INTR_TYPE_TTY, - fm801_intr, fm801, &fm801->ih)) { + if (!fm801->irq || PCM_SETUP_INTR(dev, fm801->irq, fm801_intr, fm801, &fm801->ih)) { device_printf(dev, "unable to map interrupt\n"); goto oops; } Index: pci/maestro.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/maestro.c,v retrieving revision 1.8 diff -u -r1.8 maestro.c --- pci/maestro.c 2001/01/11 23:26:38 1.8 +++ pci/maestro.c 2001/02/19 14:31:25 @@ -1020,8 +1020,7 @@ irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid, 0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE | RF_SHAREABLE); - if (irq == NULL - || bus_setup_intr(dev, irq, INTR_TYPE_TTY, agg_intr, ess, &ih)) { + if (irq == NULL || PCM_SETUP_INTR(dev, irq, agg_intr, ess, &ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } Index: pci/neomagic.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/neomagic.c,v retrieving revision 1.21 diff -u -r1.21 neomagic.c --- pci/neomagic.c 2000/12/23 03:16:12 1.21 +++ pci/neomagic.c 2001/02/19 14:31:25 @@ -623,8 +623,7 @@ sc->irqid = 0; sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || - bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, nm_intr, sc, &sc->ih)) { + if (!sc->irq || PCM_SETUP_INTR(dev, sc->irq, nm_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } Index: pci/solo.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/solo.c,v retrieving revision 1.18 diff -u -r1.18 solo.c --- pci/solo.c 2000/12/23 03:16:12 1.18 +++ pci/solo.c 2001/02/19 14:31:25 @@ -944,7 +944,7 @@ if (sc->newspeed) ess_setmixer(sc, 0x71, 0x2a); - bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ess_intr, sc, &sc->ih); + PCM_SETUP_INTR(dev, sc->irq, ess_intr, sc, &sc->ih); if (!sc->duplex) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); Index: pci/t4dwave.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/t4dwave.c,v retrieving revision 1.22 diff -u -r1.22 t4dwave.c --- pci/t4dwave.c 2001/01/24 01:20:04 1.22 +++ pci/t4dwave.c 2001/02/19 14:31:25 @@ -76,6 +76,8 @@ int regtype, regid, irqid; void *ih; + struct mtx *lock; + u_int32_t playchns; struct tr_chinfo chinfo[TR_MAXPLAYCH]; struct tr_rchinfo recchinfo; @@ -168,9 +170,11 @@ } regno &= 0x7f; + SND_MTXLOCK(tr->lock); tr_wr(tr, treg, regno | trw, 4); j=trw; for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4); + SND_MTXUNLOCK(tr->lock); if (i == 0) printf("codec timeout during read of register %x\n", regno); return (j >> TR_CDC_DATA) & 0xffff; } @@ -200,11 +204,13 @@ printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno)); #endif j=trw; + SND_MTXLOCK(tr->lock); for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4); tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4); #if 0 printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno)); #endif + SND_MTXUNLOCK(tr->lock); if (i==0) printf("codec timeout writing %x, data %x\n", regno, data); return (i > 0)? 0 : -1; } @@ -250,6 +256,7 @@ u_int32_t i, reg; int bank, chan; + SND_MTXLOCK(tr->lock); bank = (ch->index & 0x20) ? 1 : 0; chan = ch->index & 0x1f; reg = bank? TR_REG_INTENB : TR_REG_INTENA; @@ -260,6 +267,7 @@ tr_clrint(ch); tr_wr(tr, reg, i, 4); + SND_MTXUNLOCK(tr->lock); } /* playback channels */ @@ -336,9 +344,11 @@ cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14); break; } + SND_MTXLOCK(tr->lock); tr_selch(ch); for (i=0; ilock); } static void @@ -347,9 +357,11 @@ struct tr_info *tr = ch->parent; u_int32_t cr[5], i; + SND_MTXLOCK(tr->lock); tr_selch(ch); for (i=0; i<5; i++) cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4); + SND_MTXUNLOCK(tr->lock); ch->lba= (cr[1] & 0x3fffffff); @@ -646,9 +658,7 @@ if (ch->bufhalf != tmp) { chn_intr(ch->channel); ch->bufhalf = tmp; - } else - printf("same bufhalf\n"); - + } } } chnum++; @@ -716,6 +726,7 @@ bzero(tr, sizeof(*tr)); tr->type = pci_get_devid(dev); + tr->lock = snd_mtxcreate(device_get_nameunit(dev)); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); @@ -745,8 +756,7 @@ tr->irqid = 0; tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!tr->irq || - bus_setup_intr(dev, tr->irq, INTR_TYPE_TTY, tr_intr, tr, &tr->ih)) { + if (!tr->irq || PCM_SETUP_INTR(dev, tr->irq, tr_intr, tr, &tr->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -797,6 +807,7 @@ bus_teardown_intr(dev, tr->irq, tr->ih); bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); bus_dma_tag_destroy(tr->parent_dmat); + snd_mtxfree(tr->lock); free(tr, M_DEVBUF); return 0; Index: pci/via82c686.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pci/via82c686.c,v retrieving revision 1.8 diff -u -r1.8 via82c686.c --- pci/via82c686.c 2000/12/25 02:49:28 1.8 +++ pci/via82c686.c 2001/02/19 14:31:25 @@ -36,12 +36,12 @@ #include #define VIA_PCI_ID 0x30581106 -#define NSEGS 16 /* Number of segments in SGD table */ +#define NSEGS 4 /* Number of segments in SGD table */ #define SEGS_PER_CHAN (NSEGS/2) #define TIMEOUT 50 -#define VIA_BUFFSIZE 0x4000 +#define VIA_BUFFSIZE 0x1000 #undef DEB #define DEB(x) @@ -62,7 +62,9 @@ struct via_info *parent; pcm_channel *channel; snd_dbuf *buffer; - int dir; + struct via_dma_op *sgd_table; + int dir, blksz; + int base, count, mode, ctrl; }; struct via_info { @@ -70,39 +72,27 @@ bus_space_handle_t sh; bus_dma_tag_t parent_dmat; bus_dma_tag_t sgd_dmat; + bus_dmamap_t sgd_dmamap; struct resource *reg, *irq; int regid, irqid; void *ih; + struct ac97_info *codec; struct via_chinfo pch, rch; struct via_dma_op *sgd_table; u_int16_t codec_caps; }; - -static u_int32_t via_rd(struct via_info *via, int regno, int size); -static void via_wr(struct via_info *, int regno, u_int32_t data, int size); - -static void via_intr(void *); -bus_dmamap_callback_t dma_cb; - -static u_int32_t via_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - 0 -}; -static pcmchan_caps via_playcaps = {4000, 48000, via_playfmt, 0}; -static u_int32_t via_recfmt[] = { +static u_int32_t via_fmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; -static pcmchan_caps via_reccaps = {4000, 48000, via_recfmt, 0}; +static pcmchan_caps via_vracaps = {4000, 48000, via_fmt, 0}; +static pcmchan_caps via_caps = {48000, 48000, via_fmt, 0}; static u_int32_t via_rd(struct via_info *via, int regno, int size) @@ -184,8 +174,7 @@ if (via_waitready_codec(via)) return -1; - via_wr(via, VIA_CODEC_CTL, - VIA_CODEC_PRIVALID | VIA_CODEC_INDEX(reg) | val, 4); + via_wr(via, VIA_CODEC_CTL, VIA_CODEC_PRIVALID | VIA_CODEC_INDEX(reg) | val, 4); return 0; } @@ -197,16 +186,15 @@ struct via_info *via = addr; if (via_waitready_codec(via)) - return 1; + return -1; - via_wr(via, VIA_CODEC_CTL, - VIA_CODEC_PRIVALID | VIA_CODEC_READ | VIA_CODEC_INDEX(reg),4); + via_wr(via, VIA_CODEC_CTL, VIA_CODEC_PRIVALID | VIA_CODEC_READ | VIA_CODEC_INDEX(reg),4); if (via_waitready_codec(via)) - return 1; + return -1; if (via_waitvalid_codec(via)) - return 1; + return -1; return via_rd(via, VIA_CODEC_CTL, 2); } @@ -220,58 +208,60 @@ /* -------------------------------------------------------------------- */ -/* channel interface */ -static void * -viachan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) -{ - struct via_info *via = devinfo; - struct via_chinfo *ch = (dir == PCMDIR_PLAY) ? &via->pch : &via->rch; - - ch->parent = via; - ch->channel = c; - ch->buffer = b; - - if (sndbuf_alloc(ch->buffer, via->parent_dmat, VIA_BUFFSIZE) == -1) return NULL; - return ch; -} - static int -viachan_setdir(kobj_t obj, void *data, int dir) +via_buildsgdt(struct via_chinfo *ch) { - struct via_chinfo *ch = data; - struct via_info *via = ch->parent; - struct via_dma_op *ado; - int i, chunk_size; - int phys_addr, flag; + u_int32_t phys_addr, flag; + int i, segs, seg_size; - ch->dir = dir; /* * Build the scatter/gather DMA (SGD) table. * There are four slots in the table: two for play, two for record. * This creates two half-buffers, one of which is playing; the other * is feeding. */ - ado = via->sgd_table; - chunk_size = sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN; + seg_size = ch->blksz; + segs = sndbuf_getsize(ch->buffer) / seg_size; + phys_addr = vtophys(sndbuf_getbuf(ch->buffer)); - if (dir == PCMDIR_REC) { - ado += SEGS_PER_CHAN; + for (i = 0; i < segs; i++) { + flag = (i == segs - 1)? VIA_DMAOP_EOL : VIA_DMAOP_FLAG; + ch->sgd_table[i].ptr = phys_addr + (i * seg_size); + ch->sgd_table[i].flags = flag | seg_size; } - DEB(printf("SGD table located at va %p\n", ado)); - phys_addr = vtophys(sndbuf_getbuf(ch->buffer)); - for (i = 0; i < SEGS_PER_CHAN; i++) { - ado->ptr = phys_addr; - flag = (i == SEGS_PER_CHAN-1) ? - VIA_DMAOP_EOL : VIA_DMAOP_FLAG; - ado->flags = flag | chunk_size; - DEB(printf("ado->ptr/flags = %x/%x\n", phys_addr, flag)); - phys_addr += chunk_size; - ado++; - } return 0; } +/* channel interface */ +static void * +viachan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +{ + struct via_info *via = devinfo; + struct via_chinfo *ch = (dir == PCMDIR_PLAY)? &via->pch : &via->rch; + + ch->parent = via; + ch->channel = c; + ch->buffer = b; + ch->dir = dir; + ch->sgd_table = &via->sgd_table[(dir == PCMDIR_PLAY)? 0 : SEGS_PER_CHAN]; + if (ch->dir == PCMDIR_PLAY) { + ch->base = VIA_PLAY_DMAOPS_BASE; + ch->count = VIA_PLAY_DMAOPS_COUNT; + ch->ctrl = VIA_RECORD_CONTROL; + ch->mode = VIA_PLAY_MODE; + } else { + ch->base = VIA_RECORD_DMAOPS_BASE; + ch->count = VIA_RECORD_DMAOPS_COUNT; + ch->ctrl = VIA_PLAY_CONTROL; + ch->mode = VIA_RECORD_MODE; + } + + if (sndbuf_alloc(ch->buffer, via->parent_dmat, VIA_BUFFSIZE) == -1) + return NULL; + return ch; +} + static int viachan_setformat(kobj_t obj, void *data, u_int32_t format) { @@ -285,21 +275,11 @@ if (format & AFMT_S16_LE) mode_set |= VIA_RPMODE_16BIT; - /* Set up for output format */ - if (ch->dir == PCMDIR_PLAY) { - DEB(printf("set play format: %x\n", format)); - mode = via_rd(via, VIA_PLAY_MODE, 1); - mode &= ~(VIA_RPMODE_16BIT | VIA_RPMODE_STEREO); - mode |= mode_set; - via_wr(via, VIA_PLAY_MODE, mode, 1); - } - else { - DEB(printf("set record format: %x\n", format)); - mode = via_rd(via, VIA_RECORD_MODE, 1); + DEB(printf("set format: dir = %d, format=%x\n", ch->dir, format)); + mode = via_rd(via, ch->mode, 1); mode &= ~(VIA_RPMODE_16BIT | VIA_RPMODE_STEREO); mode |= mode_set; - via_wr(via, VIA_RECORD_MODE, mode, 1); - } + via_wr(via, ch->mode, mode, 1); return 0; } @@ -309,6 +289,7 @@ { struct via_chinfo *ch = data; struct via_info *via = ch->parent; + int reg; /* * Basic AC'97 defines a 48 kHz sample rate only. For other rates, @@ -319,39 +300,22 @@ * itself), then negotiate the rate with the codec. Otherwise, * return 48 kHz cuz that's all you got. */ - if (ch->dir == PCMDIR_PLAY) { - DEB(printf("requested play speed: %d\n", speed)); - if (via->codec_caps & AC97_CODEC_DOES_VRA) { - via_write_codec(NULL, via, AC97_REG_EXT_DAC_RATE, speed); - speed = via_read_codec(NULL, via, AC97_REG_EXT_DAC_RATE); - } - else { - DEB(printf("VRA not supported!\n")); - speed = 48000; - } - DEB(printf("obtained play speed: %d\n", speed)); - } - else { - DEB(printf("requested record speed: %d\n", speed)); - if (via->codec_caps & AC97_CODEC_DOES_VRA) { - via_write_codec(NULL, via, AC97_REG_EXT_ADC_RATE, speed); - speed = via_read_codec(NULL, via, AC97_REG_EXT_ADC_RATE); - } - else { - DEB(printf("VRA not supported!\n")); - speed = 48000; - } - DEB(printf("obtained record speed: %d\n", speed)); - } - return speed; + if (via->codec_caps & AC97_EXTCAP_VRA) { + reg = (ch->dir == PCMDIR_PLAY)? AC97_REGEXT_FDACRATE : AC97_REGEXT_LADCRATE; + return ac97_setrate(via->codec, reg, speed); + } else + return 48000; } static int viachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct via_chinfo *ch = data; + + ch->blksz = blocksize; + sndbuf_resize(ch->buffer, SEGS_PER_CHAN, ch->blksz); - return sndbuf_getsize(ch->buffer) / 2; + return ch->blksz; } static int @@ -361,37 +325,20 @@ struct via_info *via = ch->parent; struct via_dma_op *ado; - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; - if (ch->dir == PCMDIR_PLAY) { - if (go == PCMTRIG_START) { - ado = &via->sgd_table[0]; + if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + return 0; + + ado = ch->sgd_table; DEB(printf("ado located at va=%p pa=%x\n", ado, vtophys(ado))); - via_wr(via, VIA_PLAY_DMAOPS_BASE, vtophys(ado),4); - via_wr(via, VIA_PLAY_CONTROL, - VIA_RPCTRL_START, 1); - } - else { - /* Stop DMA */ - via_wr(via, VIA_PLAY_CONTROL, - VIA_RPCTRL_TERMINATE, 1); - } - } else { + if (go == PCMTRIG_START) { - ado = &via->sgd_table[SEGS_PER_CHAN]; - DEB(printf("ado located at va=%p pa=%x\n", ado, vtophys(ado))); - via_wr(via, VIA_RECORD_DMAOPS_BASE, - vtophys(ado),4); - via_wr(via, VIA_RECORD_CONTROL, - VIA_RPCTRL_START, 1); - } - else { - /* Stop DMA */ - via_wr(via, VIA_RECORD_CONTROL, - VIA_RPCTRL_TERMINATE, 1); - } - } + via_buildsgdt(ch); + via_wr(via, ch->base, vtophys(ado), 4); + via_wr(via, ch->ctrl, VIA_RPCTRL_START, 1); + } else + via_wr(via, ch->ctrl, VIA_RPCTRL_TERMINATE, 1); -DEB(printf("viachan_trigger: go=%d\n", go)); + DEB(printf("viachan_trigger: go=%d\n", go)); return 0; } @@ -401,68 +348,47 @@ struct via_chinfo *ch = data; struct via_info *via = ch->parent; struct via_dma_op *ado; - int ptr, base, len, seg; - int base1; + int ptr, base, base1, len, seg; - if (ch->dir == PCMDIR_PLAY) { - ado = &via->sgd_table[0]; - base1 = via_rd(via, VIA_PLAY_DMAOPS_BASE, 4); - len = via_rd(via, VIA_PLAY_DMAOPS_COUNT, 4); - base = via_rd(via, VIA_PLAY_DMAOPS_BASE, 4); - if (base != base1) { /* Avoid race hazzard */ - len = via_rd(via, VIA_PLAY_DMAOPS_COUNT, 4); - } - DEB(printf("viachan_getptr: len / base = %x / %x\n", len, base)); - - /* Base points to SGD segment to do, one past current */ - - /* Determine how many segments have been done */ - seg = (base - vtophys(ado)) / sizeof(struct via_dma_op); - if (seg == 0) seg = SEGS_PER_CHAN; + ado = ch->sgd_table; + base1 = via_rd(via, ch->base, 4); + len = via_rd(via, ch->count, 4); + base = via_rd(via, ch->base, 4); + if (base != base1) /* Avoid race hazard */ + len = via_rd(via, ch->count, 4); - /* Now work out offset: seg less count */ - ptr = seg * sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN - len; - DEB(printf("return ptr=%d\n", ptr)); - return ptr; - } - else { - base1 = via_rd(via, VIA_RECORD_DMAOPS_BASE, 4); - ado = &via->sgd_table[SEGS_PER_CHAN]; - len = via_rd(via, VIA_RECORD_DMAOPS_COUNT, 4); - base = via_rd(via, VIA_RECORD_DMAOPS_BASE, 4); - if (base != base1) { /* Avoid race hazzard */ - len = via_rd(via, VIA_RECORD_DMAOPS_COUNT, 4); - } DEB(printf("viachan_getptr: len / base = %x / %x\n", len, base)); - /* Base points to next block to do, one past current */ + /* Base points to SGD segment to do, one past current */ /* Determine how many segments have been done */ seg = (base - vtophys(ado)) / sizeof(struct via_dma_op); - if (seg == 0) seg = SEGS_PER_CHAN; + if (seg == 0) + seg = SEGS_PER_CHAN; /* Now work out offset: seg less count */ - ptr = seg * sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN - len; - + ptr = (seg * sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN) - len; + if (ch->dir == PCMDIR_REC) { /* DMA appears to operate on memory 'lines' of 32 bytes */ /* so don't return any part line - it isn't in RAM yet */ ptr = ptr & ~0x1f; + } + DEB(printf("return ptr=%d\n", ptr)); return ptr; - } - return 0; } static pcmchan_caps * viachan_getcaps(kobj_t obj, void *data) { struct via_chinfo *ch = data; - return (ch->dir == PCMDIR_PLAY) ? &via_playcaps : &via_reccaps; + struct via_info *via = ch->parent; + + return (via->codec_caps & AC97_EXTCAP_VRA)? &via_vracaps : &via_caps; } static kobj_method_t viachan_methods[] = { KOBJMETHOD(channel_init, viachan_init), - KOBJMETHOD(channel_setdir, viachan_setdir), KOBJMETHOD(channel_setformat, viachan_setformat), KOBJMETHOD(channel_setspeed, viachan_setspeed), KOBJMETHOD(channel_setblocksize, viachan_setblocksize), @@ -511,7 +437,8 @@ } -void dma_cb(void *p, bus_dma_segment_t *bds, int a, int b) +static void +dma_cb(void *p, bus_dma_segment_t *bds, int a, int b) { } @@ -520,14 +447,9 @@ via_attach(device_t dev) { struct via_info *via = 0; - struct ac97_info *codec = 0; char status[SND_STATUSLEN]; - u_int32_t data; - u_int16_t v; - bus_dmamap_t sgd_dma_map; - if ((via = malloc(sizeof *via, M_DEVBUF, M_NOWAIT)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; @@ -541,12 +463,10 @@ data = pci_read_config(dev, PCIR_COMMAND, 2); pci_write_config(dev, VIA_PCICONF_MISC, - VIA_PCICONF_ACLINKENAB | VIA_PCICONF_ACSGD | - VIA_PCICONF_ACNOTRST | VIA_PCICONF_ACVSR, 1); + VIA_PCICONF_ACLINKENAB | VIA_PCICONF_ACSGD | VIA_PCICONF_ACNOTRST | VIA_PCICONF_ACVSR, 1); via->regid = PCIR_MAPS; - via->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &via->regid, - 0, ~0, 1, RF_ACTIVE); + via->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &via->regid, 0, ~0, 1, RF_ACTIVE); if (!via->reg) { device_printf(dev, "cannot allocate bus resource."); goto bad; @@ -555,44 +475,24 @@ via->sh = rman_get_bushandle(via->reg); via->irqid = 0; - via->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &via->irqid, - 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!via->irq - || bus_setup_intr(dev, via->irq, INTR_TYPE_TTY, via_intr, via, &via->ih)){ + via->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &via->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (!via->irq || PCM_SETUP_INTR(dev, via->irq, via_intr, via, &via->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } - via_wr(via, VIA_PLAY_MODE, - VIA_RPMODE_AUTOSTART | - VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1); - via_wr(via, VIA_RECORD_MODE, - VIA_RPMODE_AUTOSTART | - VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1); + via_wr(via, VIA_PLAY_MODE, VIA_RPMODE_AUTOSTART | VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1); + via_wr(via, VIA_RECORD_MODE, VIA_RPMODE_AUTOSTART | VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1); - codec = AC97_CREATE(dev, via, via_ac97); - if (!codec) goto bad; + via->codec = AC97_CREATE(dev, via, via_ac97); + if (!via->codec) + goto bad; - mixer_init(dev, ac97_getmixerclass(), codec); + mixer_init(dev, ac97_getmixerclass(), via->codec); - /* - * The mixer init resets the codec. So enabling VRA must be done - * afterwards. - */ - v = via_read_codec(NULL, via, AC97_REG_EXT_AUDIO_ID); - v &= (AC97_ENAB_VRA | AC97_ENAB_MICVRA); - via_write_codec(NULL, via, AC97_REG_EXT_AUDIO_STAT, v); - via->codec_caps = v; - { - v = via_read_codec(NULL, via, AC97_REG_EXT_AUDIO_STAT); - DEB(printf("init: codec stat: %d\n", v)); - } - - if (!(v & AC97_CODEC_DOES_VRA)) { - /* no VRA => can do only 48 kbps */ - via_playcaps.minspeed = 48000; - via_reccaps.minspeed = 48000; - } + via->codec_caps = ac97_getextcaps(via->codec); + if (via->codec_caps & AC97_EXTCAP_VRA) + ac97_setextmode(via->codec, AC97_EXTCAP_VRA | AC97_EXTCAP_VRM); /* DMA tag for buffers */ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, @@ -621,13 +521,12 @@ goto bad; } - if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table, - BUS_DMA_NOWAIT, &sgd_dma_map) == -1) goto bad; - if (bus_dmamap_load(via->sgd_dmat, sgd_dma_map, via->sgd_table, - NSEGS * sizeof(struct via_dma_op), dma_cb, 0, 0)) goto bad; + if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table, BUS_DMA_NOWAIT, &via->sgd_dmamap) == -1) + goto bad; + if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table, NSEGS * sizeof(struct via_dma_op), dma_cb, 0, 0)) + goto bad; - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", - rman_get_start(via->reg), rman_get_start(via->irq)); + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", rman_get_start(via->reg), rman_get_start(via->irq)); /* Register */ if (pcm_register(dev, via, 1, 1)) goto bad; @@ -636,11 +535,12 @@ pcm_setstatus(dev, status); return 0; bad: - if (codec) ac97_destroy(codec); + if (via->codec) ac97_destroy(via->codec); if (via->reg) bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg); if (via->ih) bus_teardown_intr(dev, via->irq, via->ih); if (via->irq) bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat); + if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat); if (via) free(via, M_DEVBUF); return ENXIO; @@ -661,6 +561,7 @@ bus_teardown_intr(dev, via->irq, via->ih); bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); bus_dma_tag_destroy(via->parent_dmat); + bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); bus_dma_tag_destroy(via->sgd_dmat); free(via, M_DEVBUF); return 0; Index: pcm/buffer.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/buffer.c,v retrieving revision 1.1 diff -u -r1.1 buffer.c --- pcm/buffer.c 2000/12/23 03:16:13 1.1 +++ pcm/buffer.c 2001/02/19 14:31:25 @@ -28,6 +28,10 @@ #include +#include "feeder_if.h" + +#define MIN(x, y) (((x) < (y))? (x) : (y)) + static void sndbuf_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { @@ -41,11 +45,11 @@ } /* - * Allocate memory for DMA buffer. If the device do not perform DMA transfer, - * the driver can call malloc(9) by its own. + * Allocate memory for DMA buffer. If the device does not use DMA transfers, + * the driver can call malloc(9) and sndbuf_setup() itself. */ int -sndbuf_alloc(snd_dbuf *b, bus_dma_tag_t dmatag, int size) +sndbuf_alloc(snd_dbuf *b, bus_dma_tag_t dmatag, unsigned int size) { b->dmatag = dmatag; b->maxsize = size; @@ -58,7 +62,7 @@ } int -sndbuf_setup(snd_dbuf *b, void *buf, int size) +sndbuf_setup(snd_dbuf *b, void *buf, unsigned int size) { bzero(b, sizeof(*b)); b->buf = buf; @@ -70,13 +74,22 @@ void sndbuf_free(snd_dbuf *b) { + if (b->tmpbuf) + free(b->tmpbuf, M_DEVBUF); + b->tmpbuf = NULL; + + if (b->dmamap) bus_dmamap_unload(b->dmatag, b->dmamap); + + if (b->dmamap && b->buf) bus_dmamem_free(b->dmatag, b->buf, b->dmamap); } int -sndbuf_resize(snd_dbuf *b, int blkcnt, int blksz) +sndbuf_resize(snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) { + if (b->maxsize == 0) + return 0; if (blkcnt == 0) blkcnt = b->blkcnt; if (blksz == 0) @@ -86,12 +99,43 @@ b->blkcnt = blkcnt; b->blksz = blksz; b->bufsize = blkcnt * blksz; + if (b->tmpbuf) + free(b->tmpbuf, M_DEVBUF); + b->tmpbuf = malloc(b->bufsize, M_DEVBUF, M_WAITOK); sndbuf_reset(b); return 0; } +int +sndbuf_remalloc(snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) +{ + if (blkcnt < 2 || blksz < 16) + return EINVAL; + + b->blkcnt = blkcnt; + b->blksz = blksz; + + b->maxsize = blkcnt * blksz; + b->bufsize = b->maxsize; + + if (b->buf) + free(b->buf, M_DEVBUF); + b->buf = malloc(b->bufsize, M_DEVBUF, M_WAITOK); + if (b->buf == NULL) + return ENOMEM; + + if (b->tmpbuf) + free(b->tmpbuf, M_DEVBUF); + b->tmpbuf = malloc(b->bufsize, M_DEVBUF, M_WAITOK); + if (b->tmpbuf == NULL) + return ENOMEM; + + sndbuf_reset(b); + return 0; +} + void -sndbuf_clear(snd_dbuf *b, int length) +sndbuf_clear(snd_dbuf *b, unsigned int length) { int i; u_int16_t data, *p; @@ -112,8 +156,8 @@ if (b->fmt & AFMT_BIGENDIAN) data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00); - i = b->fp; - p = (u_int16_t *)(b->buf + b->fp); + i = sndbuf_getfreeptr(b); + p = (u_int16_t *)(b->buf + sndbuf_getfreeptr(b)); while (length > 1) { *p++ = data; length -= 2; @@ -130,12 +174,12 @@ void sndbuf_reset(snd_dbuf *b) { - b->rp = b->fp = 0; - b->dl = b->rl = 0; - b->fl = b->bufsize; - b->prev_total = b->total = 0; - b->prev_int_count = b->int_count = 0; - b->underflow = 0; + b->rp = 0; + b->rl = 0; + b->dl = 0; + b->prev_total = 0; + b->total = 0; + b->xrun = 0; if (b->buf && b->bufsize > 0) sndbuf_clear(b, b->bufsize); } @@ -151,7 +195,51 @@ return 0; } -int +unsigned int +sndbuf_getspd(snd_dbuf *b) +{ + return b->spd; +} + +void +sndbuf_setspd(snd_dbuf *b, unsigned int spd) +{ + b->spd = spd; +} + +unsigned int +sndbuf_getalign(snd_dbuf *b) +{ + static int align[] = {0, 1, 1, 2, 2, 2, 2, 3}; + + return align[b->bps - 1]; +} + +unsigned int +sndbuf_getblkcnt(snd_dbuf *b) +{ + return b->blkcnt; +} + +void +sndbuf_setblkcnt(snd_dbuf *b, unsigned int blkcnt) +{ + b->blkcnt = blkcnt; +} + +unsigned int +sndbuf_getblksz(snd_dbuf *b) +{ + return b->blksz; +} + +void +sndbuf_setblksz(snd_dbuf *b, unsigned int blksz) +{ + b->blksz = blksz; +} + +unsigned int sndbuf_getbps(snd_dbuf *b) { return b->bps; @@ -163,28 +251,345 @@ return b->buf; } -int +void * +sndbuf_getbufofs(snd_dbuf *b, unsigned int ofs) +{ + KASSERT((ofs >= 0) && (ofs <= b->bufsize), ("%s: ofs invalid %d", __FUNCTION__, ofs)); + + return b->buf + ofs; +} + +unsigned int sndbuf_getsize(snd_dbuf *b) { return b->bufsize; } -int +unsigned int +sndbuf_getmaxsize(snd_dbuf *b) +{ + return b->maxsize; +} + +unsigned int sndbuf_runsz(snd_dbuf *b) { return b->dl; } +void +sndbuf_setrun(snd_dbuf *b, int go) +{ + b->dl = go? b->blksz : 0; +} + +struct selinfo * +sndbuf_getsel(snd_dbuf *b) +{ + return &b->sel; +} + +/************************************************************/ +unsigned int +sndbuf_getxrun(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + + return b->xrun; +} + +void +sndbuf_setxrun(snd_dbuf *b, unsigned int cnt) +{ + SNDBUF_LOCKASSERT(b); + + b->xrun = cnt; +} + +unsigned int +sndbuf_gethwptr(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + + return b->hp; +} + +void +sndbuf_sethwptr(snd_dbuf *b, unsigned int ptr) +{ + SNDBUF_LOCKASSERT(b); + + b->hp = ptr; +} + +unsigned int +sndbuf_getready(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __FUNCTION__, b->rl)); + + return b->rl; +} + +unsigned int +sndbuf_getreadyptr(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __FUNCTION__, b->rp)); + + return b->rp; +} + +unsigned int +sndbuf_getfree(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __FUNCTION__, b->rl)); + + return b->bufsize - b->rl; +} + +unsigned int +sndbuf_getfreeptr(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __FUNCTION__, b->rp)); + KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __FUNCTION__, b->rl)); + + return (b->rp + b->rl) % b->bufsize; +} + +unsigned int +sndbuf_getblocks(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + + return b->total / b->blksz; +} + +unsigned int +sndbuf_getprevblocks(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + + return b->prev_total / b->blksz; +} + +unsigned int +sndbuf_gettotal(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + + return b->total; +} + +void +sndbuf_updateprevtotal(snd_dbuf *b) +{ + SNDBUF_LOCKASSERT(b); + + b->prev_total = b->total; +} + +/************************************************************/ + +int +sndbuf_acquire(snd_dbuf *b, u_int8_t *from, unsigned int count) +{ + int l; + + KASSERT(count <= sndbuf_getfree(b), ("%s: count %d > free %d", __FUNCTION__, count, sndbuf_getfree(b))); + KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __FUNCTION__, b->rl)); + b->total += count; + if (from != NULL) { + while (count > 0) { + l = MIN(count, sndbuf_getsize(b) - sndbuf_getfreeptr(b)); + bcopy(from, sndbuf_getbufofs(b, sndbuf_getfreeptr(b)), l); + from += l; + b->rl += l; + count -= l; + } + } else + b->rl += count; + KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __FUNCTION__, b->rl, count)); + + return 0; +} + +int +sndbuf_dispose(snd_dbuf *b, u_int8_t *to, unsigned int count) +{ + int l; + + KASSERT(count <= sndbuf_getready(b), ("%s: count %d > ready %d", __FUNCTION__, count, sndbuf_getready(b))); + KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __FUNCTION__, b->rl)); + if (to != NULL) { + while (count > 0) { + l = MIN(count, sndbuf_getsize(b) - sndbuf_getreadyptr(b)); + bcopy(sndbuf_getbufofs(b, sndbuf_getreadyptr(b)), to, l); + to += l; + b->rl -= l; + b->rp = (b->rp + l) % b->bufsize; + count -= l; + } + } else { + b->rl -= count; + b->rp = (b->rp + count) % b->bufsize; + } + KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __FUNCTION__, b->rl, count)); + + return 0; +} + +int +sndbuf_uiomove(snd_dbuf *b, struct uio *uio, unsigned int count) +{ + int x, c, p, rd, err; + + err = 0; + rd = (uio->uio_rw == UIO_READ)? 1 : 0; + if (count > uio->uio_resid) + return EINVAL; + + if (count > (rd? sndbuf_getready(b) : sndbuf_getfree(b))) { + return EINVAL; + } + + while (err == 0 && count > 0) { + p = rd? sndbuf_getreadyptr(b) : sndbuf_getfreeptr(b); + c = MIN(count, sndbuf_getsize(b) - p); + x = uio->uio_resid; + err = uiomove(sndbuf_getbufofs(b, p), c, uio); + x -= uio->uio_resid; + count -= x; + x = rd? sndbuf_dispose(b, NULL, x) : sndbuf_acquire(b, NULL, x); + } + + return 0; +} + +/* count is number of bytes we want added to destination buffer */ +int +sndbuf_feed(snd_dbuf *from, snd_dbuf *to, pcm_channel *channel, unsigned int count) +{ + if (sndbuf_getfree(to) < count) + return EINVAL; + + count = FEEDER_FEED(channel->feeder, channel, to->tmpbuf, count, from); + sndbuf_acquire(to, to->tmpbuf, count); + /* the root feeder has called sndbuf_dispose(from, , bytes fetched) */ + + return 0; +} + +/************************************************************/ + +#undef BUFMTX + int +sndbuf_init(snd_dbuf *b, char *drv, char *desc) +{ + bzero(b, sizeof(*b)); + snprintf(b->name, 48, "%s:%s", drv, desc); +#ifdef BUFVERBOSE + printf("init mtx %s @ 0x%p\n", b->name, &b->mutex); +#endif +#ifdef BUFMTX + mtx_init(&b->mutex, b->name, MTX_RECURSE); +#endif + + return 0; +} + +void +sndbuf_destroy(snd_dbuf *b) +{ +#ifdef BUFVERBOSE + printf("destroy mtx %s @ 0x%p\n", b->name, &b->mutex); +#endif +#ifdef BUFMTX + mtx_destroy(&b->mutex); +#endif +} + +#if 0 +void +_sndbuf_lock(snd_dbuf *b, const char *file, int line) +{ +#ifdef BUFVERBOSE + printf("lock mtx %s @ 0x%p\n", b->name, &b->mutex); +#endif +#ifdef BUFMTX + _mtx_enter(&b->mutex, MTX_DEF, file, line); +#endif +} + +void +_sndbuf_unlock(snd_dbuf *b, const char *file, int line) +{ +#ifdef BUFVERBOSE + printf("unlock mtx %s @ 0x%p\n", b->name, &b->mutex); +#endif +#ifdef BUFMTX + _mtx_exit(&b->mutex, MTX_DEF, file, line); +#endif +} + +int +sndbuf_sleep(snd_dbuf *b, char *str, int timeout) +{ + int ret; + + ret = msleep(b, NULL, PRIBIO | PCATCH, str, timeout); + + return ret; +} +#endif + +void +sndbuf_dump(snd_dbuf *b, char *s, u_int32_t what) +{ + printf("%s: [", s); + if (what & 0x01) + printf("bufsize: %d, maxsize: %d", b->bufsize, b->maxsize); + if (what & 0x02) + printf("dl: %d, rp: %d, rl: %d, hp: %d", b->dl, b->rp, b->rl, b->hp); + if (what & 0x04) + printf("total: %d, prev_total: %d, xrun: %d", b->total, b->prev_total, b->xrun); + if (what & 0x08) + printf("fmt: 0x%x, spd: %d", b->fmt, b->spd); + if (what & 0x10) + printf("blksz: %d, blkcnt: %d, flags: 0x%x", b->blksz, b->blkcnt, b->flags); + printf("]\n"); +} + +/************************************************************/ +u_int32_t +sndbuf_getflags(snd_dbuf *b) +{ + return b->flags; +} + +void +sndbuf_setflags(snd_dbuf *b, u_int32_t flags, int on) +{ + b->flags &= ~flags; + if (on) + b->flags |= flags; +} + +/************************************************************/ + +int sndbuf_isadmasetup(snd_dbuf *b, struct resource *drq) { /* should do isa_dma_acquire/isa_dma_release here */ if (drq == NULL) { - b->flags &= ~SNDBUF_F_ISADMA; - b->chan = -1; + sndbuf_setflags(b, SNDBUF_F_ISADMA, 0); + b->isadmachan = -1; } else { - b->flags &= ~SNDBUF_F_ISADMA; - b->chan = rman_get_start(drq); + sndbuf_setflags(b, SNDBUF_F_ISADMA, 1); + b->isadmachan = rman_get_start(drq); } return 0; } @@ -192,6 +597,9 @@ int sndbuf_isadmasetdir(snd_dbuf *b, int dir) { + KASSERT(b, ("sndbuf_isadmasetdir called with b == NULL")); + KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadmasetdir called on non-ISA buffer")); + b->dir = (dir == PCMDIR_PLAY)? ISADMA_WRITE : ISADMA_READ; return 0; } @@ -200,18 +608,18 @@ sndbuf_isadma(snd_dbuf *b, int go) { KASSERT(b, ("sndbuf_isadma called with b == NULL")); - KASSERT(ISA_DMA(b), ("sndbuf_isadma called on non-ISA channel")); + KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadma called on non-ISA buffer")); switch (go) { case PCMTRIG_START: /* isa_dmainit(b->chan, size); */ - isa_dmastart(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->chan); + isa_dmastart(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->isadmachan); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: - isa_dmastop(b->chan); - isa_dmadone(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->chan); + isa_dmastop(b->isadmachan); + isa_dmadone(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->isadmachan); break; } @@ -224,21 +632,24 @@ int sndbuf_isadmaptr(snd_dbuf *b) { - if (ISA_DMA(b)) { - int i = b->dl? isa_dmastatus(b->chan) : b->bufsize; - if (i < 0) - i = 0; + int i; + + KASSERT(b, ("sndbuf_isadmaptr called with b == NULL")); + KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadmaptr called on non-ISA buffer")); + + if (!sndbuf_runsz(b)) + return 0; + i = isa_dmastatus(b->isadmachan); + KASSERT(i >= 0, ("isa_dmastatus returned %d", i)); return b->bufsize - i; - } else KASSERT(1, ("sndbuf_isadmaptr called on invalid channel")); - return -1; } void sndbuf_isadmabounce(snd_dbuf *b) { - if (ISA_DMA(b)) { + KASSERT(b, ("sndbuf_isadmabounce called with b == NULL")); + KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadmabounce called on non-ISA buffer")); + /* tell isa_dma to bounce data in/out */ - } else - KASSERT(1, ("chn_isadmabounce called on invalid channel")); } Index: pcm/buffer.h =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/buffer.h,v retrieving revision 1.1 diff -u -r1.1 buffer.h --- pcm/buffer.h 2000/12/23 03:16:13 1.1 +++ pcm/buffer.h 2001/02/19 14:31:25 @@ -26,19 +26,68 @@ * $FreeBSD: src/sys/dev/sound/pcm/buffer.h,v 1.1 2000/12/23 03:16:13 cg Exp $ */ -#define ISA_DMA(b) (((b)->chan >= 0 && (b)->chan != 4 && (b)->chan < 8)) +#define ISA_DMA(b) ((b)->flags & SNDBUF_F_ISADMA) +#define SNDBUF_LOCKASSERT(b) -int sndbuf_alloc(snd_dbuf *b, bus_dma_tag_t dmatag, int size); -int sndbuf_setup(snd_dbuf *b, void *buf, int size); +int sndbuf_init(snd_dbuf *b, char *drv, char *desc); +void sndbuf_destroy(snd_dbuf *b); + +#if 0 +void _sndbuf_lock(snd_dbuf *b, const char *file, int line); +#define sndbuf_lock(b) _sndbuf_lock(b, __FILE__, __LINE__) +void _sndbuf_unlock(snd_dbuf *b, const char *file, int line); +#define sndbuf_unlock(b) _sndbuf_unlock(b, __FILE__, __LINE__) +int sndbuf_sleep(snd_dbuf *b, char *str, int timeout); +#endif + +void sndbuf_dump(snd_dbuf *b, char *s, u_int32_t what); + +int sndbuf_alloc(snd_dbuf *b, bus_dma_tag_t dmatag, unsigned int size); +int sndbuf_setup(snd_dbuf *b, void *buf, unsigned int size); void sndbuf_free(snd_dbuf *b); -int sndbuf_resize(snd_dbuf *b, int blkcnt, int blksz); +int sndbuf_resize(snd_dbuf *b, unsigned int blkcnt, unsigned int blksz); +int sndbuf_remalloc(snd_dbuf *b, unsigned int blkcnt, unsigned int blksz); void sndbuf_reset(snd_dbuf *b); -void sndbuf_clear(snd_dbuf *b, int length); +void sndbuf_clear(snd_dbuf *b, unsigned int length); + int sndbuf_setfmt(snd_dbuf *b, u_int32_t fmt); -int sndbuf_getbps(snd_dbuf *b); +unsigned int sndbuf_getspd(snd_dbuf *b); +void sndbuf_setspd(snd_dbuf *b, unsigned int spd); +unsigned int sndbuf_getbps(snd_dbuf *b); + void *sndbuf_getbuf(snd_dbuf *b); -int sndbuf_getsize(snd_dbuf *b); -int sndbuf_runsz(snd_dbuf *b); +void *sndbuf_getbufofs(snd_dbuf *b, unsigned int ofs); +unsigned int sndbuf_getsize(snd_dbuf *b); +unsigned int sndbuf_getmaxsize(snd_dbuf *b); +unsigned int sndbuf_getalign(snd_dbuf *b); +unsigned int sndbuf_getblkcnt(snd_dbuf *b); +void sndbuf_setblkcnt(snd_dbuf *b, unsigned int blkcnt); +unsigned int sndbuf_getblksz(snd_dbuf *b); +void sndbuf_setblksz(snd_dbuf *b, unsigned int blksz); +unsigned int sndbuf_runsz(snd_dbuf *b); +void sndbuf_setrun(snd_dbuf *b, int go); +struct selinfo *sndbuf_getsel(snd_dbuf *b); + +unsigned int sndbuf_getxrun(snd_dbuf *b); +void sndbuf_setxrun(snd_dbuf *b, unsigned int cnt); +unsigned int sndbuf_gethwptr(snd_dbuf *b); +void sndbuf_sethwptr(snd_dbuf *b, unsigned int ptr); +unsigned int sndbuf_getfree(snd_dbuf *b); +unsigned int sndbuf_getfreeptr(snd_dbuf *b); +unsigned int sndbuf_getready(snd_dbuf *b); +unsigned int sndbuf_getreadyptr(snd_dbuf *b); +unsigned int sndbuf_getblocks(snd_dbuf *b); +unsigned int sndbuf_getprevblocks(snd_dbuf *b); +unsigned int sndbuf_gettotal(snd_dbuf *b); +void sndbuf_updateprevtotal(snd_dbuf *b); + +int sndbuf_acquire(snd_dbuf *b, u_int8_t *from, unsigned int count); +int sndbuf_dispose(snd_dbuf *b, u_int8_t *to, unsigned int count); +int sndbuf_uiomove(snd_dbuf *b, struct uio *uio, unsigned int count); +int sndbuf_feed(snd_dbuf *from, snd_dbuf *to, pcm_channel *channel, unsigned int count); + +u_int32_t sndbuf_getflags(snd_dbuf *b); +void sndbuf_setflags(snd_dbuf *b, u_int32_t flags, int on); int sndbuf_isadmasetup(snd_dbuf *b, struct resource *drq); int sndbuf_isadmasetdir(snd_dbuf *b, int dir); Index: pcm/channel.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/channel.c,v retrieving revision 1.51 diff -u -r1.51 channel.c --- pcm/channel.c 2001/02/13 21:57:34 1.51 +++ pcm/channel.c 2001/02/19 16:52:01 @@ -31,95 +31,69 @@ #include "feeder_if.h" -MALLOC_DEFINE(M_CHANNEL, "channel", "pcm channel"); #define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ #define DMA_ALIGN_THRESHOLD 4 #define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1)) -#define CANCHANGE(c) (!(c)->buffer.dl) -#define ROUND(x) ((x) & DMA_ALIGN_MASK) +#define MIN(x, y) (((x) < (y))? (x) : (y)) +#define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED)) /* #define DEB(x) x */ -static void chn_dmaupdate(pcm_channel *c); -static void chn_wrintr(pcm_channel *c); -static void chn_rdintr(pcm_channel *c); static int chn_buildfeeder(pcm_channel *c); -/* - * SOUND OUTPUT -We use a circular buffer to store samples directed to the DAC. -The buffer is split into two variable-size regions, each identified -by an offset in the buffer (rp,fp) and a length (rl,fl): - - 0 rp,rl fp,fl bufsize - |__________>____________>________| - FREE d READY w FREE - - READY: data written from the process and ready to be sent to the DAC; - FREE: free part of the buffer. - -Both regions can wrap around the end of the buffer. At initialization, -READY is empty, FREE takes all the available space, and dma is -idle. dl contains the length of the current DMA transfer, dl=0 -means that the dma is idle. - -The two boundaries (rp,fp) in the buffers are advanced by DMA [d] -and write() [w] operations. The first portion of the READY region -is used for DMA transfers. The transfer is started at rp and with -chunks of length dl. During DMA operations, dsp_wr_dmaupdate() -updates rp, rl and fl tracking the ISA DMA engine as the transfer -makes progress. -When a new block is written, fp advances and rl,fl are updated -accordingly. - -The code works as follows: the user write routine dsp_write_body() -fills up the READY region with new data (reclaiming space from the -FREE region) and starts the write DMA engine if inactive. When a -DMA transfer is complete, an interrupt causes dsp_wrintr() to be -called which extends the FREE region and possibly starts the next -transfer. - -In some cases, the code tries to track the current status of DMA -operations by calling dsp_wr_dmaupdate() which changes rp, rl and fl. - -The system tries to make all DMA transfers use the same size, -play_blocksize or rec_blocksize. The size is either selected by -the user, or computed by the system to correspond to about .25s of -audio. The blocksize must be within a range which is currently: - - min(5ms, 40 bytes) ... 1/2 buffer size. - -When there aren't enough data (write) or space (read), a transfer -is started with a reduced size. - -To reduce problems in case of overruns, the routine which fills up -the buffer should initialize (e.g. by repeating the last value) a -reasonably long area after the last block so that no noise is -produced on overruns. - - * - */ - - -/* XXX this is broken: in the event a bounce buffer is used, data never - * gets copied in or out of the real buffer. fix requires mods to isa_dma.c - * and possibly fixes to other autodma mode clients - */ +static void +chn_lockinit(pcm_channel *c) +{ + mtx_init(&c->mutex, c->name, MTX_RECURSE); + mtx_lock(&c->mutex); +} + +static void +chn_lockdestroy(pcm_channel *c) +{ + mtx_destroy(&c->mutex); +} + +void +chn_lock(pcm_channel *c) +{ + mtx_lock(&c->mutex); +} + +void +chn_unlock(pcm_channel *c) +{ + mtx_unlock(&c->mutex); +} + +void +chn_lockassert(pcm_channel *c, const char *file, int line) +{ + _mtx_assert(&c->mutex, MA_OWNED, file, line); +} + static int chn_polltrigger(pcm_channel *c) { snd_dbuf *bs = &c->buffer2nd; - unsigned lim = (c->flags & CHN_F_HAS_SIZE)? bs->blksz : 0; - int trig = 0; + unsigned amt, lim; - if (c->flags & CHN_F_MAPPED) - trig = ((bs->int_count > bs->prev_int_count) || bs->prev_int_count == 0); + CHN_LOCKASSERT(c); + if (c->flags & CHN_F_MAPPED) { + if (sndbuf_getprevblocks(bs) == 0) + return 1; else - trig = (((c->direction == PCMDIR_PLAY)? bs->fl : bs->rl) > lim); - return trig; + return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0; + } else { + amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); + lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1; + lim = 0; + return (amt >= lim)? 1 : 0; + } + return 0; } static int @@ -127,454 +101,199 @@ { snd_dbuf *bs = &c->buffer2nd; - if (c->flags & CHN_F_MAPPED) - bs->prev_int_count = bs->int_count; + CHN_LOCKASSERT(c); + sndbuf_updateprevtotal(bs); return 1; } -/* - * chn_dmadone() updates pointers and wakes up any process waiting - * on a select(). Must be called at spltty(). - */ static void -chn_dmadone(pcm_channel *c) +chn_wakeup(pcm_channel *c) { - snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer2nd; - if (c->direction == PCMDIR_PLAY) - chn_checkunderflow(c); - else - chn_dmaupdate(c); - if (ISA_DMA(b)) - sndbuf_isadmabounce(b); /* sync bounce buffer */ - b->int_count++; + CHN_LOCKASSERT(c); + if (sndbuf_getsel(bs)->si_pid && chn_polltrigger(c)) + selwakeup(sndbuf_getsel(bs)); + wakeup(bs); } -/* - * chn_dmawakeup() wakes up any process sleeping. Separated from - * chn_dmadone() so that wakeup occurs only when feed from a - * secondary buffer to a DMA buffer takes place. Must be called - * at spltty(). - */ -static void -chn_dmawakeup(pcm_channel *c) +static int +chn_sleep(pcm_channel *c, char *str, int timeout) { - snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer2nd; + int ret; + + CHN_LOCKASSERT(c); + ret = msleep(bs, &c->mutex, PRIBIO | PCATCH, str, timeout); - wakeup(b); + return ret; } /* * chn_dmaupdate() tracks the status of a dma transfer, * updating pointers. It must be called at spltty(). - * - * NOTE: when we are using auto dma in the device, rl might become - * negative. */ -DEB (static int chn_updatecount=0); -static void +static unsigned int chn_dmaupdate(pcm_channel *c) { snd_dbuf *b = &c->buffer; - int delta, hwptr; - DEB (int b_rl=b->rl; int b_fl=b->fl; int b_rp=b->rp; int b_fp=b->fp); + unsigned int delta, old, hwptr, amt; + CHN_LOCKASSERT(c); + old = sndbuf_gethwptr(b); hwptr = chn_getptr(c); - delta = (b->bufsize + hwptr - b->hp) % b->bufsize; + delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b); + sndbuf_sethwptr(b, hwptr); DEB( if (delta >= ((b->bufsize * 15) / 16)) { if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING))) - device_printf(c->parent->dev, "hwptr went backwards %d -> %d\n", b->hp, hwptr); + device_printf(c->parent->dev, "hwptr went backwards %d -> %d\n", old, hwptr); } ) if (c->direction == PCMDIR_PLAY) { - delta = (b->bufsize + hwptr - b->rp) % b->bufsize; - b->rp = hwptr; - b->rl -= delta; - b->fl += delta; - - if (b->rl < 0) { - DEB(printf("OUCH!(%d) rl %d(%d) delta %d bufsize %d hwptr %d rp %d(%d)\n", chn_updatecount++, b->rl, b_rl, delta, b->bufsize, hwptr, b->rp, b_rp)); - } + amt = MIN(delta, sndbuf_getready(b)); + if (amt > 0) + sndbuf_dispose(b, NULL, amt); } else { - delta = (b->bufsize + hwptr - b->fp) % b->bufsize; - b->fp = hwptr; - b->rl += delta; - b->fl -= delta; - if (b->fl < 0) { - DEB(printf("OUCH!(%d) fl %d(%d) delta %d bufsize %d hwptr %d fp %d(%d)\n", chn_updatecount++, b->fl, b_fl, delta, b->bufsize, hwptr, b->fp, b_fp)); - } + amt = MIN(delta, sndbuf_getfree(b)); + if (amt > 0) + sndbuf_acquire(b, NULL, amt); } - b->hp = hwptr; - b->total += delta; + + return delta; } -/* - * Check channel for underflow occured. Reset DMA buffer in case of - * underflow, so that new data can go into the buffer. It must be - * called at spltty(). - */ void -chn_checkunderflow(pcm_channel *c) +chn_wrupdate(pcm_channel *c) { - snd_dbuf *b = &c->buffer; + int ret; - if (b->underflow) { - DEB(printf("Clear underflow condition\n")); - /* - * The DMA keeps running even after underflow occurs. - * Hence the value returned by chn_getptr() here soon - * gets a lag when we get back to chn_write(). Although - * there are no easy and precise methods to figure out - * the lag, a quarter of b->bufsize would be a fair - * choice, provided that a DMA interrupt generates upon - * each transfer of a half b->bufsize. - */ - b->rp = chn_getptr(c); - b->fp = (b->rp + b->bufsize / 4) % b->bufsize; - b->rl = b->bufsize / 4; - b->fl = b->bufsize - b->rl; - b->underflow = 0; - } else { - chn_dmaupdate(c); - } + CHN_LOCKASSERT(c); + KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); + if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED)) + return; + chn_dmaupdate(c); + ret = chn_wrfeed(c); + /* tell the driver we've updated the primary buffer */ + chn_trigger(c, PCMTRIG_EMLDMAWR); + if (ret) + printf("chn_wrfeed: %d\n", ret); } -/* - * Feeds new data to the write dma buffer. Can be called in the bottom half. - * Hence must be called at spltty. - */ int chn_wrfeed(pcm_channel *c) { snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; - int a, l, lacc; - - /* ensure we always have a whole number of samples */ - a = (1 << c->align) - 1; - lacc = 0; - if (c->flags & CHN_F_MAPPED) { - bs->rl = min(b->blksz, b->fl); - bs->fl = 0; - a = 0; - } - DEB(if (c->flags & CHN_F_CLOSING) - printf("b: [rl: %d, rp %d, fl %d, fp %d]; bs: [rl: %d, rp %d, fl %d, fp %d]\n", - b->rl, b->rp, b->fl, b->fp, bs->rl, bs->rp, bs->fl, bs->fp)); - /* Don't allow write unaligned data */ - while (bs->rl > a && b->fl > a) { - /* ensure we always have a whole number of samples */ - l = min(min(bs->rl, bs->bufsize - bs->rp), min(b->fl, b->bufsize - b->fp)) & ~a; - if (l == 0) - return lacc; - /* Move the samples, update the markers and pointers. */ - bcopy(bs->buf + bs->rp, b->buf + b->fp, l); - bs->fl += l; - bs->rl -= l; - bs->rp = (bs->rp + l) % bs->bufsize; - b->rl += l; - b->fl -= l; - b->fp = (b->fp + l) % b->bufsize; - /* Clear the new space in the secondary buffer. */ - sndbuf_clear(bs, l); - /* Accumulate the total bytes of the moved samples. */ - lacc += l; - /* A feed to the DMA buffer is equivalent to an interrupt. */ - bs->total += l; - if (c->flags & CHN_F_MAPPED) { - if (bs->total - bs->prev_total >= bs->blksz) { - bs->prev_total = bs->total; - bs->int_count++; - c->blocks++; - } - } else - bs->int_count++; - if (bs->sel.si_pid && chn_polltrigger(c)) - selwakeup(&bs->sel); - } - - return lacc; -} + unsigned int amt; -/* Feeds new data to the secondary write buffer. */ -static int -chn_wrfeed2nd(pcm_channel *c, struct uio *buf) -{ - snd_dbuf *bs = &c->buffer2nd; - int l, w, wacc, hl; - u_int8_t hackbuf[64]; - - /* The DMA buffer may have some space. */ - while (chn_wrfeed(c) > 0); - - /* ensure we always have a whole number of samples */ - wacc = 0; - hl = 0; - while (buf->uio_resid > 0 && bs->fl > 64) { - /* - * The size of the data to move here does not have to be - * aligned. We take care of it upon moving the data to a - * DMA buffer. - */ - l = min(bs->fl, bs->bufsize - bs->fp); - /* Move the samples, update the markers and pointers. */ - if (l < 64) { - w = FEEDER_FEED(c->feeder, c, hackbuf, 64, buf); - l = min(w, bs->bufsize - bs->fp); - bcopy(hackbuf, bs->buf + bs->fp, l); - if (w > l) - bcopy(hackbuf + l, bs->buf, w - l); - } else - w = FEEDER_FEED(c->feeder, c, bs->buf + bs->fp, l, buf); - if (w == 0) - panic("no feed"); - bs->rl += w; - bs->fl -= w; - bs->fp = (bs->fp + w) % bs->bufsize; - /* Accumulate the total bytes of the moved samples. */ - wacc += w; - - /* If any pcm data gets moved, push it to the DMA buffer. */ - if (w > 0) - while (chn_wrfeed(c) > 0); + CHN_LOCKASSERT(c); + DEB( + if (c->flags & CHN_F_CLOSING) { + sndbuf_dump(b, "b", 0x02); + sndbuf_dump(bs, "bs", 0x02); + }) + + amt = sndbuf_getfree(b); + if (amt > 0) { + sndbuf_feed(bs, b, c, amt); + chn_wakeup(c); + return 0; + } else { + return ENOSPC; } - - return wacc; } -/* - * Write interrupt routine. Can be called from other places (e.g. - * to start a paused transfer), but with interrupts disabled. - */ static void chn_wrintr(pcm_channel *c) { - snd_dbuf *b = &c->buffer; - - if (b->underflow && !(c->flags & CHN_F_MAPPED)) { -/* printf("underflow return\n"); -*/ return; /* nothing new happened */ - } - if (b->dl) - chn_dmadone(c); - - /* - * start another dma operation only if have ready data in the buffer, - * there is no pending abort, have a full-duplex device, or have a - * half duplex device and there is no pending op on the other side. - * - * Force transfers to be aligned to a boundary of 4, which is - * needed when doing stereo and 16-bit. - */ - - /* Check underflow and update the pointers. */ - chn_checkunderflow(c); + int ret; - /* - * Fill up the DMA buffer, followed by waking up the top half. - * If some of the pcm data in uio are still left, the top half - * goes to sleep by itself. - */ - if (c->flags & CHN_F_MAPPED) - chn_wrfeed(c); - else { - while (chn_wrfeed(c) > 0); - sndbuf_clear(b, b->fl); - } - chn_dmawakeup(c); - if (c->flags & CHN_F_TRIGGERED) { - chn_dmaupdate(c); - /* - * check if we need to reprogram the DMA on the sound card. - * This happens if the size has changed from zero - */ - if (b->dl == 0) { - /* Start DMA operation */ - b->dl = b->blksz; /* record new transfer size */ - chn_trigger(c, PCMTRIG_START); - } - /* - * Emulate writing by DMA, i.e. transfer the pcm data from - * the emulated-DMA buffer to the device itself. - */ - chn_trigger(c, PCMTRIG_EMLDMAWR); - if (b->rl < b->dl) { - DEB(printf("near underflow (%d < %d), %d\n", b->rl, b->dl, b->fl)); - /* - * we are near to underflow condition, so to prevent - * audio 'clicks' clear next b->fl bytes - */ - sndbuf_clear(b, b->fl); - if (b->rl < DMA_ALIGN_THRESHOLD) - b->underflow = 1; - } - } else { - /* cannot start a new dma transfer */ - DEB(printf("underflow, flags 0x%08x rp %d rl %d\n", c->flags, b->rp, b->rl)); - if (b->dl) { /* DMA was active */ - b->underflow = 1; /* set underflow flag */ - sndbuf_clear(b, b->bufsize); - } - } + CHN_LOCKASSERT(c); + /* update pointers in primary buffer */ + chn_dmaupdate(c); + /* ...and feed from secondary to primary */ + ret = chn_wrfeed(c); + /* tell the driver we've updated the primary buffer */ + chn_trigger(c, PCMTRIG_EMLDMAWR); + if (ret) + printf("chn_wrfeed: %d\n", ret); } /* - * user write routine - * - * advance the boundary between READY and FREE, fill the space with - * uiomove(), and possibly start DMA. Do the above until the transfer - * is complete. - * - * To minimize latency in case a pending DMA transfer is about to end, - * we do the transfer in pieces of increasing sizes, extending the - * READY area at every checkpoint. In the (necessary) assumption that - * memory bandwidth is larger than the rate at which the dma consumes - * data, we reduce the latency to something proportional to the length - * of the first piece, while keeping the overhead low and being able - * to feed the DMA with large blocks. + * user write routine - uiomove data into secondary buffer, trigger if necessary + * if blocking, sleep, rinse and repeat. + * called externally, so must handle locking */ - int chn_write(pcm_channel *c, struct uio *buf) { - int ret = 0, timeout, res, newsize, count; - long s; - snd_dbuf *b = &c->buffer; + int ret, timeout, newsize, count, sz; snd_dbuf *bs = &c->buffer2nd; - - if (c->flags & CHN_F_WRITING) { - /* This shouldn't happen and is actually silly - * - will never wake up, just timeout; why not sleep on b? - */ - tsleep(&s, PZERO, "pcmwrW", hz); - return EBUSY; - } - c->flags |= CHN_F_WRITING; - c->flags &= ~CHN_F_ABORTING; - s = spltty(); + CHN_LOCKASSERT(c); /* * XXX Certain applications attempt to write larger size * of pcm data than c->blocksize2nd without blocking, * resulting partial write. Expand the block size so that * the write operation avoids blocking. */ - if ((c->flags & CHN_F_NBIO) && buf->uio_resid > bs->blksz) { - DEB(printf("pcm warning: broken app, nbio and tried to write %d bytes with fragsz %d\n", - buf->uio_resid, bs->blksz)); + if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) { + DEB(device_printf(c->parent->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n", + buf->uio_resid, sndbuf_getblksz(bs))); newsize = 16; while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2)) newsize <<= 1; - chn_setblocksize(c, bs->blkcnt, newsize); - DEB(printf("pcm warning: frags reset to %d x %d\n", bs->blkcnt, bs->blksz)); + chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize); + DEB(device_printf(c->parent->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs))); } - /* - * Fill up the secondary and DMA buffer. - * chn_wrfeed*() takes care of the alignment. - */ - - /* Check for underflow before writing into the buffers. */ - chn_checkunderflow(c); - while (chn_wrfeed2nd(c, buf) > 0); - if ((c->flags & CHN_F_NBIO) && (buf->uio_resid > 0)) - ret = EAGAIN; - - /* Start playing if not yet. */ - if (!b->dl) - chn_start(c, 0); - - if (ret == 0) { - count = hz; - /* Wait until all samples are played in blocking mode. */ - while ((buf->uio_resid > 0) && (count > 0)) { - /* Check for underflow before writing into the buffers. */ - chn_checkunderflow(c); - /* Fill up the buffers with new pcm data. */ - res = buf->uio_resid; - while (chn_wrfeed2nd(c, buf) > 0); - if (buf->uio_resid < res) - count = hz; - else - count--; + ret = 0; + count = hz; + while (!ret && (buf->uio_resid > 0) && (count > 0)) { + sz = sndbuf_getfree(bs); + if (sz == 0) { + if (c->flags & CHN_F_NBIO) + ret = EWOULDBLOCK; + else { + timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); + if (timeout < 1) + timeout = 1; + ret = chn_sleep(c, "pcmwr", timeout); + if (ret == EWOULDBLOCK) { + count -= timeout; + ret = 0; + } else if (ret == 0) + count = hz; + } + } else { + sz = MIN(sz, buf->uio_resid); + KASSERT(sz > 0, ("confusion in chn_write")); + /* printf("sz: %d\n", sz); */ + ret = sndbuf_uiomove(bs, buf, sz); + if (ret == 0 && !(c->flags & CHN_F_TRIGGERED)) + chn_start(c, 0); + } + } + /* printf("ret: %d left: %d\n", ret, buf->uio_resid); */ + + if (count <= 0) { + c->flags |= CHN_F_DEAD; + device_printf(c->parent->dev, "play interrupt timeout, channel dead\n"); + } - /* Have we finished to feed the secondary buffer? */ - if (buf->uio_resid == 0) - break; - - /* Wait for new free space to write new pcm samples. */ - /* splx(s); */ - timeout = 1; /*(buf->uio_resid >= b->dl)? hz / 20 : 1; */ - ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout); - /* s = spltty(); */ - /* if (ret == EINTR) chn_abort(c); */ - if (ret == EINTR || ret == ERESTART) - break; - } - if (count == 0) { - c->flags |= CHN_F_DEAD; - device_printf(c->parent->dev, "play interrupt timeout, channel dead\n"); - } - } else - ret = 0; - c->flags &= ~CHN_F_WRITING; - splx(s); return ret; } -/* - * SOUND INPUT - * - -The input part is similar to the output one, with a circular buffer -split in two regions, and boundaries advancing because of read() calls -[r] or dma operation [d]. At initialization, as for the write -routine, READY is empty, and FREE takes all the space. - - 0 rp,rl fp,fl bufsize - |__________>____________>________| - FREE r READY d FREE - -Operation is as follows: upon user read (dsp_read_body()) a DMA read -is started if not already active (marked by b->dl > 0), -then as soon as data are available in the READY region they are -transferred to the user buffer, thus advancing the boundary between FREE -and READY. Upon interrupts, caused by a completion of a DMA transfer, -the READY region is extended and possibly a new transfer is started. - -When necessary, dsp_rd_dmaupdate() is called to advance fp (and update -rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are -updated accordingly. - -The rules to choose the size of the new DMA area are similar to -the other case, with a preferred constant transfer size equal to -rec_blocksize, and fallback to smaller sizes if no space is available. - - */ - static int -chn_rddump(pcm_channel *c, int cnt) +chn_rddump(pcm_channel *c, unsigned int cnt) { snd_dbuf *b = &c->buffer; - int maxover, ss; - ss = 1; - ss <<= (b->fmt & AFMT_STEREO)? 1 : 0; - ss <<= (b->fmt & AFMT_16BIT)? 1 : 0; - maxover = c->speed * ss; - - b->overrun += cnt; - if (b->overrun > maxover) { - device_printf(c->parent->dev, "record overrun, dumping %d bytes\n", - b->overrun); - b->overrun = 0; - } - b->rl -= cnt; - b->fl += cnt; - b->rp = (b->rp + cnt) % b->bufsize; - return cnt; + CHN_LOCKASSERT(c); + sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt); + return sndbuf_dispose(b, NULL, cnt); } /* @@ -586,68 +305,39 @@ { snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; - int l, lacc; + int ret; - /* - printf("b: [rl: %d, rp %d, fl %d, fp %d]; bs: [rl: %d, rp %d, fl %d, fp %d]\n", - b->rl, b->rp, b->fl, b->fp, bs->rl, bs->rp, bs->fl, bs->fp); - */ - /* ensure we always have a whole number of samples */ - lacc = 0; - while (bs->fl >= DMA_ALIGN_THRESHOLD && b->rl >= DMA_ALIGN_THRESHOLD) { - l = min(min(bs->fl, bs->bufsize - bs->fp), min(b->rl, b->bufsize - b->rp)) & DMA_ALIGN_MASK; - /* Move the samples, update the markers and pointers. */ - bcopy(b->buf + b->rp, bs->buf + bs->fp, l); - bs->fl -= l; - bs->rl += l; - bs->fp = (bs->fp + l) % bs->bufsize; - b->rl -= l; - b->fl += l; - b->rp = (b->rp + l) % b->bufsize; - /* Accumulate the total bytes of the moved samples. */ - lacc += l; - /* A feed from the DMA buffer is equivalent to an interrupt. */ - bs->int_count++; - if (bs->sel.si_pid && chn_polltrigger(c)) - selwakeup(&bs->sel); - } + CHN_LOCKASSERT(c); + DEB( + if (c->flags & CHN_F_CLOSING) { + sndbuf_dump(b, "b", 0x02); + sndbuf_dump(bs, "bs", 0x02); + }) + + ret = sndbuf_feed(b, bs, c, sndbuf_getblksz(b)); + + if (ret == 0) + chn_wakeup(c); - return lacc; + return ret; } -/* Feeds new data from the secondary read buffer. */ -static int -chn_rdfeed2nd(pcm_channel *c, struct uio *buf) +void +chn_rdupdate(pcm_channel *c) { - snd_dbuf *bs = &c->buffer2nd; - int l, w, wacc; + int ret; - /* ensure we always have a whole number of samples */ - wacc = 0; - while ((buf->uio_resid > 0) && (bs->rl > 0)) { - /* The DMA buffer may have pcm data. */ - /* while (chn_rdfeed(c) > 0); */ - /* - * The size of the data to move here does not have to be - * aligned. We take care of it upon moving the data to a - * DMA buffer. - */ - l = min(bs->rl, bs->bufsize - bs->rp); - /* Move the samples, update the markers and pointers. */ - w = FEEDER_FEED(c->feeder, c, bs->buf + bs->rp, l, buf); - if (w == 0) - panic("no feed"); - bs->fl += w; - bs->rl -= w; - bs->rp = (bs->rp + w) % bs->bufsize; - /* Clear the new space in the secondary buffer. */ - sndbuf_clear(bs, l); - /* Accumulate the total bytes of the moved samples. */ - bs->total += w; - wacc += w; - } + CHN_LOCKASSERT(c); + KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel")); + + if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED)) + return; + chn_trigger(c, PCMTRIG_EMLDMARD); + chn_dmaupdate(c); + ret = chn_rdfeed(c); + if (ret) + printf("chn_rdfeed: %d\n", ret); - return wacc; } /* read interrupt routine. Must be called with interrupts blocked. */ @@ -655,185 +345,103 @@ chn_rdintr(pcm_channel *c) { snd_dbuf *b = &c->buffer; - - if (b->dl) chn_dmadone(c); - - DEB(printf("rdintr: start dl %d, rp:rl %d:%d, fp:fl %d:%d\n", - b->dl, b->rp, b->rl, b->fp, b->fl)); - - /* Update the pointers. */ - chn_dmaupdate(c); - - /* - * Suck up the DMA buffer, followed by waking up the top half. - * If some of the pcm data in the secondary buffer are still left, - * the top half goes to sleep by itself. - */ - while(chn_rdfeed(c) > 0); - chn_dmawakeup(c); + int ret; - if (b->fl < b->dl) { - DEB(printf("near overflow (%d < %d), %d\n", b->fl, b->dl, b->rl)); - chn_rddump(c, b->blksz - b->fl); - } - - if (c->flags & CHN_F_TRIGGERED) { - /* - * check if we need to reprogram the DMA on the sound card. - * This happens if the size has changed from zero - */ - if (b->dl == 0) { - /* Start DMA operation */ - b->dl = b->blksz; /* record new transfer size */ - chn_trigger(c, PCMTRIG_START); - } - /* - * Emulate writing by DMA, i.e. transfer the pcm data from - * the emulated-DMA buffer to the device itself. - */ + CHN_LOCKASSERT(c); + /* tell the driver to update the primary buffer if non-dma */ chn_trigger(c, PCMTRIG_EMLDMARD); - } else { - if (b->dl) { /* was active */ - b->dl = 0; - chn_trigger(c, PCMTRIG_STOP); + /* update pointers in primary buffer */ chn_dmaupdate(c); - } - } + /* ...and feed from primary to secondary */ + ret = chn_rdfeed(c); + if (ret) + chn_rddump(c, sndbuf_getblksz(b)); } /* - * body of user-read routine + * user read routine - trigger if necessary, uiomove data from secondary buffer + * if blocking, sleep, rinse and repeat. * - * Start DMA if not active; wait for READY not empty. - * Transfer data from READY region using uiomove(), advance boundary - * between FREE and READY. Repeat until transfer is complete. - * - * To avoid excessive latency in freeing up space for the DMA - * engine, transfers are done in blocks of increasing size, so that - * the latency is proportional to the size of the smallest block, but - * we have a low overhead and are able to feed the dma engine with - * large blocks. - * - * NOTE: in the current version, read will not return more than - * blocksize bytes at once (unless more are already available), to - * avoid that requests using very large buffers block for too long. + * called externally, so must handle locking */ int chn_read(pcm_channel *c, struct uio *buf) { - int ret = 0, timeout, limit, res, count; - long s; - snd_dbuf *b = &c->buffer; + int ret, timeout, sz, count; snd_dbuf *bs = &c->buffer2nd; - - if (c->flags & CHN_F_READING) { - /* This shouldn't happen and is actually silly */ - tsleep(&s, PZERO, "pcmrdR", hz); - return (EBUSY); - } - - s = spltty(); - - /* Store the initial size in the uio. */ - res = buf->uio_resid; - - c->flags |= CHN_F_READING; - c->flags &= ~CHN_F_ABORTING; - /* suck up the DMA and secondary buffers. */ - while (chn_rdfeed2nd(c, buf) > 0); - - if (buf->uio_resid == 0) - goto skip; - - limit = res - b->blksz; - if (limit < 0) - limit = 0; - - /* Start capturing if not yet. */ - if ((!bs->rl || !b->rl) && !b->dl) + CHN_LOCKASSERT(c); + if (!(c->flags & CHN_F_TRIGGERED)) chn_start(c, 0); - if (!(c->flags & CHN_F_NBIO)) { - count = hz; - /* Wait until all samples are captured. */ - while ((buf->uio_resid > 0) && (count > 0)) { - /* Suck up the DMA and secondary buffers. */ - chn_dmaupdate(c); - res = buf->uio_resid; - while (chn_rdfeed(c) > 0); - while (chn_rdfeed2nd(c, buf) > 0); - if (buf->uio_resid < res) + ret = 0; count = hz; - else - count--; + while (!ret && (buf->uio_resid > 0) && (count > 0)) { + sz = MIN(buf->uio_resid, sndbuf_getblksz(bs)); - /* Have we finished to feed the uio? */ - if (buf->uio_resid == 0) - break; - - /* Wait for new pcm samples. */ - /* splx(s); */ - timeout = (buf->uio_resid - limit >= b->dl)? hz / 20 : 1; - ret = tsleep(b, PRIBIO | PCATCH, "pcmrd", 1); - /* s = spltty(); */ - /* if (ret == EINTR) chn_abort(c); */ - if (ret == EINTR || ret == ERESTART) - break; + if (sz <= sndbuf_getready(bs)) { + ret = sndbuf_uiomove(bs, buf, sz); + } else { + if (c->flags & CHN_F_NBIO) + ret = EWOULDBLOCK; + else { + timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); + if (timeout < 1) + timeout = 1; + CHN_UNLOCK(c); + ret = chn_sleep(c, "pcmrd", timeout); + CHN_LOCK(c); + if (ret == EWOULDBLOCK) { + count -= timeout; + ret = 0; + } + } } - if (count == 0) { + } + + if (count <= 0) { c->flags |= CHN_F_DEAD; device_printf(c->parent->dev, "record interrupt timeout, channel dead\n"); } - } else { - /* If no pcm data was read on nonblocking, return EAGAIN. */ - if (buf->uio_resid == res) - ret = EAGAIN; - } -skip: - c->flags &= ~CHN_F_READING; - splx(s); return ret; } void chn_intr(pcm_channel *c) { - if (c->flags & CHN_F_INIT) - chn_reinit(c); + CHN_LOCK(c); if (c->direction == PCMDIR_PLAY) chn_wrintr(c); else chn_rdintr(c); + CHN_UNLOCK(c); } u_int32_t chn_start(pcm_channel *c, int force) { - u_int32_t r, s; + u_int32_t i; snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer2nd; - r = 0; - s = spltty(); - if (b->dl == 0 && !(c->flags & CHN_F_NOTRIGGER)) { - if (c->direction == PCMDIR_PLAY) { - if (!(c->flags & CHN_F_MAPPED)) - while (chn_wrfeed(c) > 0); /* Fill up the DMA buffer. */ - if (force || (b->rl >= b->blksz)) - r = CHN_F_TRIGGERED; - } else { - if (!(c->flags & CHN_F_MAPPED)) - while (chn_rdfeed(c) > 0); /* Suck up the DMA buffer. */ - if (force || (b->fl >= b->blksz)) - r = CHN_F_TRIGGERED; - } - c->flags |= r; - chn_intr(c); + CHN_LOCKASSERT(c); + /* if we're running, or if we're prevented from triggering, bail */ + if ((c->flags & CHN_F_TRIGGERED) || (c->flags & CHN_F_NOTRIGGER)) + return EINVAL; + + i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs); + if (force || (i >= sndbuf_getblksz(b))) { + c->flags |= CHN_F_TRIGGERED; + if (c->direction == PCMDIR_PLAY) + chn_wrfeed(c); + sndbuf_setrun(b, 1); + chn_trigger(c, PCMTRIG_START); + return 0; } - splx(s); - return r; + + return 0; } void @@ -855,64 +463,49 @@ int chn_sync(pcm_channel *c, int threshold) { - u_long s, rdy; + u_long rdy; int ret; - snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; + CHN_LOCKASSERT(c); for (;;) { - s = spltty(); - chn_checkunderflow(c); - while (chn_wrfeed(c) > 0); - rdy = (c->direction == PCMDIR_PLAY)? bs->fl : bs->rl; + chn_wrupdate(c); + rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); if (rdy <= threshold) { - ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmsyn", 1); - splx(s); + ret = chn_sleep(c, "pcmsyn", 1); if (ret == ERESTART || ret == EINTR) { DEB(printf("chn_sync: tsleep returns %d\n", ret)); return -1; } - } else break; + } else + break; } - splx(s); return 0; } +/* called externally, handle locking */ int chn_poll(pcm_channel *c, int ev, struct proc *p) { - snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; - u_long s; int ret; - s = spltty(); - if (!(c->flags & CHN_F_MAPPED)) { - if (c->direction == PCMDIR_PLAY) { - /* Fill up the DMA buffer. */ - chn_checkunderflow(c); - while (chn_wrfeed(c) > 0); - } else { - /* Suck up the DMA buffer. */ - chn_dmaupdate(c); - while (chn_rdfeed(c) > 0); - } - if (!b->dl) + CHN_LOCKASSERT(c); + if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED)) chn_start(c, 1); - } ret = 0; if (chn_polltrigger(c) && chn_pollreset(c)) ret = ev; else - selrecord(p, &bs->sel); - splx(s); + selrecord(p, sndbuf_getsel(bs)); return ret; } /* - * chn_abort is a non-blocking function which aborts a pending - * DMA transfer and flushes the buffers. - * It returns the number of bytes that have not been transferred. + * chn_abort terminates a running dma transfer. it may sleep up to 200ms. + * it returns the number of bytes that have not been transferred. + * + * called from: dsp_close, dsp_ioctl, with both buffers locked */ int chn_abort(pcm_channel *c) @@ -921,17 +514,25 @@ snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; - if (!b->dl) + CHN_LOCKASSERT(c); + if (!(c->flags & CHN_F_TRIGGERED)) return 0; c->flags |= CHN_F_ABORTING; - c->flags &= ~CHN_F_TRIGGERED; + + /* wait up to 200ms for the secondary buffer to empty */ cnt = 10; - while (!b->underflow && (cnt-- > 0)) - tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmabr", hz / 50); + while ((sndbuf_getready(bs) > 0) && (cnt-- > 0)) { + chn_sleep(c, "pcmabr", hz / 50); + } + + c->flags &= ~CHN_F_TRIGGERED; + /* kill the channel */ chn_trigger(c, PCMTRIG_ABORT); - b->dl = 0; + sndbuf_setrun(b, 0); chn_dmaupdate(c); - missing = bs->rl + b->rl; + missing = sndbuf_getready(bs) + sndbuf_getready(b); + + c->flags &= ~CHN_F_ABORTING; return missing; } @@ -939,47 +540,48 @@ * this routine tries to flush the dma transfer. It is called * on a close. We immediately abort any read DMA * operation, and then wait for the play buffer to drain. + * + * called from: dsp_close */ int chn_flush(pcm_channel *c) { - int ret, count, s, resid, resid_p; + int ret, count, resid, resid_p; snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; + CHN_LOCKASSERT(c); + KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); DEB(printf("chn_flush c->flags 0x%08x\n", c->flags)); - if (!b->dl) + if (!(c->flags & CHN_F_TRIGGERED)) return 0; c->flags |= CHN_F_CLOSING; - if (c->direction == PCMDIR_REC) - chn_abort(c); - else { - resid = b->rl + bs->rl; + resid = sndbuf_getready(bs) + sndbuf_getready(b); resid_p = resid; count = 10; - while ((count > 0) && (resid > 0) && !b->underflow) { + ret = 0; + while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) { /* still pending output data. */ - ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmflu", hz / 10); - if (ret == EINTR || ret == ERESTART) { - DEB(printf("chn_flush: tsleep returns %d\n", ret)); - return ret; - } - s = spltty(); - chn_dmaupdate(c); - splx(s); - DEB(printf("chn_flush: now rl = %d, fl = %d, resid = %d\n", b->rl, b->fl, resid)); - resid = b->rl + bs->rl; + ret = chn_sleep(c, "pcmflu", hz / 10); + if (ret == EWOULDBLOCK) + ret = 0; + if (ret == 0) { + resid = sndbuf_getready(bs) + sndbuf_getready(b); if (resid >= resid_p) count--; resid_p = resid; } - if (count == 0) - DEB(printf("chn_flush: timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n", b->rl, c->flags)); - if (b->dl) - chn_abort(c); } + if (count == 0) + DEB(printf("chn_flush: timeout\n")); + + c->flags &= ~CHN_F_TRIGGERED; + /* kill the channel */ + chn_trigger(c, PCMTRIG_ABORT); + sndbuf_setrun(b, 0); + c->flags &= ~CHN_F_CLOSING; return 0; } @@ -1000,7 +602,7 @@ { int hwspd, r = 0; - chn_abort(c); + CHN_LOCKASSERT(c); c->flags &= CHN_F_RESET; CHANNEL_RESET(c->methods, c->devinfo); if (fmt) { @@ -1015,25 +617,11 @@ r = chn_setvolume(c, 100, 100); } r = chn_setblocksize(c, 0, 0); - if (r) - return r; + if (r == 0) { chn_resetbuf(c); CHANNEL_RESETDONE(c->methods, c->devinfo); - /* c->flags |= CHN_F_INIT; */ - return 0; -} - -int -chn_reinit(pcm_channel *c) -{ - if ((c->flags & CHN_F_INIT) && CANCHANGE(c)) { - chn_setformat(c, c->format); - chn_setspeed(c, c->speed); - chn_setvolume(c, (c->volume >> 8) & 0xff, c->volume & 0xff); - c->flags &= ~CHN_F_INIT; - return 1; } - return 0; + return r; } int @@ -1043,6 +631,7 @@ snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; + chn_lockinit(c); /* Initialize the hardware and DMA buffer first. */ c->feeder = NULL; fc = feeder_getclass(NULL); @@ -1051,59 +640,75 @@ if (chn_addfeeder(c, fc, NULL)) return EINVAL; + sndbuf_setup(bs, NULL, 0); + if (sndbuf_init(b, c->name, "primary")) + return ENOMEM; + if (sndbuf_init(bs, c->name, "secondary")) { + sndbuf_destroy(b); + return ENOMEM; + } c->flags = 0; c->feederflags = 0; - c->buffer.chan = -1; - c->devinfo = CHANNEL_INIT(c->methods, devinfo, &c->buffer, c, dir); - if (c->devinfo == NULL) + c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir); + if (c->devinfo == NULL) { + sndbuf_destroy(bs); + sndbuf_destroy(b); return ENODEV; - if (c->buffer.bufsize == 0) + } + if (sndbuf_getsize(b) == 0) { + sndbuf_destroy(bs); + sndbuf_destroy(b); return ENOMEM; + } chn_setdir(c, dir); /* And the secondary buffer. */ - bs->buf = NULL; sndbuf_setfmt(b, AFMT_U8); sndbuf_setfmt(bs, AFMT_U8); - bs->bufsize = 0; + CHN_UNLOCK(c); return 0; } int chn_kill(pcm_channel *c) { + snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer2nd; + + CHN_LOCK(c); if (c->flags & CHN_F_TRIGGERED) chn_trigger(c, PCMTRIG_ABORT); while (chn_removefeeder(c) == 0); if (CHANNEL_FREE(c->methods, c->devinfo)) sndbuf_free(&c->buffer); c->flags |= CHN_F_DEAD; + sndbuf_destroy(bs); + sndbuf_destroy(b); + chn_lockdestroy(c); return 0; } int chn_setdir(pcm_channel *c, int dir) { + snd_dbuf *b = &c->buffer; int r; + CHN_LOCKASSERT(c); c->direction = dir; r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); - if (!r && ISA_DMA(&c->buffer)) - c->buffer.dir = (dir == PCMDIR_PLAY)? ISADMA_WRITE : ISADMA_READ; + if (!r && ISA_DMA(b)) + sndbuf_isadmasetdir(b, c->direction); return r; } int chn_setvolume(pcm_channel *c, int left, int right) { + CHN_LOCKASSERT(c); /* could add a feeder for volume changing if channel returns -1 */ - if (CANCHANGE(c)) { c->volume = (left << 8) | right; return 0; - } - c->volume = (left << 8) | right; - c->flags |= CHN_F_INIT; - return 0; } static int @@ -1114,19 +719,21 @@ snd_dbuf *bs = &c->buffer2nd; int r, delta; + CHN_LOCKASSERT(c); DEB(printf("want speed %d, ", speed)); if (speed <= 0) return EINVAL; if (CANCHANGE(c)) { + r = 0; c->speed = speed; - b->spd = speed; - bs->spd = speed; - RANGE(b->spd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); - DEB(printf("try speed %d, ", b->spd)); - b->spd = CHANNEL_SETSPEED(c->methods, c->devinfo, b->spd); - DEB(printf("got speed %d, ", b->spd)); + sndbuf_setspd(bs, speed); + RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); + sndbuf_setspd(b, speed); + DEB(printf("try speed %d, ", sndbuf_getspd(b))); + sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, sndbuf_getspd(b))); + DEB(printf("got speed %d, ", sndbuf_getspd(b))); - delta = b->spd - bs->spd; + delta = sndbuf_getspd(b) - sndbuf_getspd(bs); if (delta < 0) delta = -delta; @@ -1134,40 +741,37 @@ if (delta > 500) c->feederflags |= 1 << FEEDER_RATE; else - bs->spd = b->spd; + sndbuf_setspd(bs, sndbuf_getspd(b)); r = chn_buildfeeder(c); DEB(printf("r = %d\n", r)); if (r) - return r; + goto out; r = chn_setblocksize(c, 0, 0); if (r) - return r; + goto out; if (!(c->feederflags & (1 << FEEDER_RATE))) - return 0; + goto out; + r = EINVAL; f = chn_findfeeder(c, FEEDER_RATE); DEB(printf("feedrate = %p\n", f)); if (f == NULL) - return EINVAL; + goto out; - r = FEEDER_SET(f, FEEDRATE_SRC, bs->spd); - DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", bs->spd, r)); + r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(bs)); + DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(bs), r)); if (r) - return r; + goto out; - r = FEEDER_SET(f, FEEDRATE_DST, b->spd); - DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", b->spd, r)); - if (r) + r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(b)); + DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(b), r)); +out: return r; - - return 0; - } - c->speed = speed; - c->flags |= CHN_F_INIT; - return 0; + } else + return EINVAL; } int @@ -1191,6 +795,7 @@ int r; u_int32_t hwfmt; + CHN_LOCKASSERT(c); if (CANCHANGE(c)) { DEB(printf("want format %d\n", fmt)); c->format = fmt; @@ -1199,18 +804,17 @@ if (!fmtvalid(hwfmt, chn_getcaps(c)->fmtlist)) c->feederflags |= 1 << FEEDER_FMT; r = chn_buildfeeder(c); - if (r) - return r; + if (r == 0) { hwfmt = c->feeder->desc->out; sndbuf_setfmt(b, hwfmt); - sndbuf_setfmt(bs, hwfmt); + sndbuf_setfmt(bs, fmt); chn_resetbuf(c); CHANNEL_SETFORMAT(c->methods, c->devinfo, hwfmt); - return chn_setspeed(c, c->speed); + r = chn_setspeed(c, c->speed); } - c->format = fmt; - c->flags |= CHN_F_INIT; - return 0; + return r; + } else + return EINVAL; } int @@ -1232,16 +836,19 @@ { snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; - int s, bufsz, irqhz, tmp; + int bufsz, irqhz, tmp, ret; + CHN_LOCKASSERT(c); if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) return EINVAL; + ret = 0; + DEB(printf("%s(%d, %d)\n", __FUNCTION__, blkcnt, blksz)); if (blksz == 0 || blksz == -1) { if (blksz == -1) c->flags &= ~CHN_F_HAS_SIZE; if (!(c->flags & CHN_F_HAS_SIZE)) { - blksz = (bs->bps * bs->spd) / CHN_DEFAULT_HZ; + blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / CHN_DEFAULT_HZ; tmp = 32; while (tmp <= blksz) tmp <<= 1; @@ -1251,60 +858,66 @@ RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz); + DEB(printf("%s: defaulting to (%d, %d)\n", __FUNCTION__, blkcnt, blksz)); } else { - blksz = bs->blksz; - blkcnt = bs->blkcnt; + blkcnt = sndbuf_getblkcnt(bs); + blksz = sndbuf_getblksz(bs); + DEB(printf("%s: updating (%d, %d)\n", __FUNCTION__, blkcnt, blksz)); } } else { + ret = EINVAL; if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE)) - return EINVAL; + goto out; + ret = 0; c->flags |= CHN_F_HAS_SIZE; } bufsz = blkcnt * blksz; - - s = spltty(); - if (bs->buf != NULL) - free(bs->buf, M_DEVBUF); - bs->buf = malloc(bufsz, M_DEVBUF, M_WAITOK); - if (bs->buf == NULL) { - splx(s); - DEB(printf("chn_setblocksize: out of memory\n")); - return ENOSPC; - } - - bs->bufsize = bufsz; - bs->blkcnt = blkcnt; - bs->blksz = blksz; + ret = ENOMEM; + if (sndbuf_remalloc(bs, blkcnt, blksz)) + goto out; + ret = 0; /* adjust for different hw format/speed */ - irqhz = (bs->bps * bs->spd) / bs->blksz; + irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs); + DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __FUNCTION__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz)); RANGE(irqhz, 16, 512); - b->blksz = (b->bps * b->spd) / irqhz; + sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz); /* round down to 2^x */ blksz = 32; - while (blksz <= b->blksz) + while (blksz <= sndbuf_getblksz(b)) blksz <<= 1; blksz >>= 1; /* round down to fit hw buffer size */ - RANGE(blksz, 16, b->maxsize / 2); + RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2); + DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __FUNCTION__, blksz, sndbuf_getmaxsize(b))); - b->blksz = CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz); + sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz)); - chn_resetbuf(c); - splx(s); + irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b); + DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz)); - return 0; + chn_resetbuf(c); +out: + return ret; } int chn_trigger(pcm_channel *c, int go) { - return CHANNEL_TRIGGER(c->methods, c->devinfo, go); + snd_dbuf *b = &c->buffer; + int ret; + + CHN_LOCKASSERT(c); + if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) + sndbuf_isadmabounce(b); + ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go); + + return ret; } int @@ -1312,9 +925,9 @@ { int hwptr; int a = (1 << c->align) - 1; - snd_dbuf *b = &c->buffer; - hwptr = b->dl? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; + CHN_LOCKASSERT(c); + hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; /* don't allow unaligned values in the hwa ptr */ hwptr &= ~a ; /* Apply channel align mask */ hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ @@ -1324,6 +937,7 @@ pcmchan_caps * chn_getcaps(pcm_channel *c) { + CHN_LOCKASSERT(c); return CHANNEL_GETCAPS(c->methods, c->devinfo); } @@ -1348,9 +962,10 @@ struct pcm_feederdesc desc; u_int32_t tmp[2], src, dst, type, flags; + CHN_LOCKASSERT(c); while (chn_removefeeder(c) == 0); KASSERT((c->feeder == NULL), ("feeder chain not empty")); - c->align = 0; + c->align = sndbuf_getalign(&c->buffer2nd); fc = feeder_getclass(NULL); if (fc == NULL) return EINVAL; @@ -1399,3 +1014,6 @@ } return 0; } + + + Index: pcm/channel.h =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/channel.h,v retrieving revision 1.16 diff -u -r1.16 channel.h --- pcm/channel.h 2000/12/23 03:16:13 1.16 +++ pcm/channel.h 2001/02/19 17:01:36 @@ -51,11 +51,20 @@ void chn_resetbuf(pcm_channel *c); void chn_intr(pcm_channel *c); -void chn_checkunderflow(pcm_channel *c); int chn_wrfeed(pcm_channel *c); int chn_rdfeed(pcm_channel *c); int chn_abort(pcm_channel *c); +void chn_wrupdate(pcm_channel *c); +void chn_rdupdate(pcm_channel *c); + +void chn_lock(pcm_channel *c); +void chn_unlock(pcm_channel *c); +void chn_lockassert(pcm_channel *c, const char *file, int line); +#define CHN_LOCK(c) chn_lock(c) +#define CHN_UNLOCK(c) chn_unlock(c) +#define CHN_LOCKASSERT(c) chn_lockassert(c, __FILE__, __LINE__) + int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist); #define PCMDIR_PLAY 1 @@ -67,11 +76,8 @@ #define PCMTRIG_STOP 0 #define PCMTRIG_ABORT -1 -#define CHN_F_READING 0x00000001 /* have a pending read */ -#define CHN_F_WRITING 0x00000002 /* have a pending write */ #define CHN_F_CLOSING 0x00000004 /* a pending close */ #define CHN_F_ABORTING 0x00000008 /* a pending abort */ -#define CHN_F_PENDING_IO (CHN_F_READING | CHN_F_WRITING) #define CHN_F_RUNNING 0x00000010 /* dma is running */ #define CHN_F_TRIGGERED 0x00000020 #define CHN_F_NOTRIGGER 0x00000040 @@ -79,7 +85,6 @@ #define CHN_F_BUSY 0x00001000 /* has been opened */ #define CHN_F_HAS_SIZE 0x00002000 /* user set block size */ #define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */ -#define CHN_F_INIT 0x00008000 /* changed parameters. need init */ #define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */ #define CHN_F_DEAD 0x00020000 #define CHN_F_BADSETTING 0x00040000 Index: pcm/datatypes.h =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/datatypes.h,v retrieving revision 1.22 diff -u -r1.22 datatypes.h --- pcm/datatypes.h 2001/01/11 23:26:16 1.22 +++ pcm/datatypes.h 2001/02/19 14:31:26 @@ -52,32 +52,28 @@ /*****************************************************************************/ -/* - * descriptor of a dma buffer. See dmabuf.c for documentation. - * (rp,rl) and (fp,fl) identify the READY and FREE regions of the - * buffer. dl contains the length used for dma transfer, dl>0 also - * means that the channel is busy and there is a DMA transfer in progress. - */ - struct _snd_dbuf { - u_int8_t *buf; - int bufsize, maxsize; + u_int8_t *buf, *tmpbuf; + unsigned int bufsize, maxsize; volatile int dl; /* transfer size */ - volatile int rp, fp; /* pointers to the ready and free area */ - volatile int rl, fl; /* lenght of ready and free areas. */ + volatile int rp; /* pointers to the ready area */ + volatile int rl; /* length of ready area */ volatile int hp; - volatile u_int32_t int_count, prev_int_count; volatile u_int32_t total, prev_total; - int chan, dir; /* dma channel */ - int fmt, spd, bps; - int blksz, blkcnt; - int underflow, overrun; + int isadmachan, dir; /* dma channel */ + u_int32_t fmt, spd, bps; + unsigned int blksz, blkcnt; + int xrun; u_int32_t flags; bus_dmamap_t dmamap; bus_dma_tag_t dmatag; struct selinfo sel; + char name[48]; + struct mtx mutex; }; #define SNDBUF_F_ISADMA 0x00000001 +#define SNDBUF_F_XRUN 0x00000002 +#define SNDBUF_F_RUNNING 0x00000004 /*****************************************************************************/ @@ -121,6 +117,8 @@ snd_dbuf buffer, buffer2nd; snddev_info *parent; void *devinfo; + char name[32]; + struct mtx mutex; }; /*****************************************************************************/ @@ -139,6 +137,7 @@ char status[SND_STATUSLEN]; struct sysctl_ctx_list sysctl_tree; struct sysctl_oid *sysctl_tree_top; + struct mtx mutex; }; /*****************************************************************************/ Index: pcm/dsp.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/dsp.c,v retrieving revision 1.33 diff -u -r1.33 dsp.c --- pcm/dsp.c 2000/12/23 03:16:13 1.33 +++ pcm/dsp.c 2001/02/19 14:31:26 @@ -100,9 +100,6 @@ } } else return EBUSY; } - d->aplay[chan] = wrch; - d->arec[chan] = rdch; - d->ref[chan]++; switch (devtype) { case SND_DEV_DSP16: fmt = AFMT_S16_LE; @@ -123,6 +120,13 @@ default: return ENXIO; } + if (rdch) + CHN_LOCK(rdch); + if (wrch) + CHN_LOCK(wrch); + d->aplay[chan] = wrch; + d->arec[chan] = rdch; + d->ref[chan]++; if (rdch && (oflags & FREAD)) { chn_reset(rdch, fmt); @@ -132,6 +136,10 @@ chn_reset(wrch, fmt); if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO; } + if (wrch) + CHN_UNLOCK(wrch); + if (rdch) + CHN_UNLOCK(rdch); return 0; } @@ -150,14 +158,18 @@ wrch = d->aplay[chan]; if (rdch) { + CHN_LOCK(rdch); chn_abort(rdch); rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(rdch, 0); + CHN_UNLOCK(rdch); } if (wrch) { + CHN_LOCK(wrch); chn_flush(wrch); wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(wrch, 0); + CHN_UNLOCK(wrch); } d->aplay[chan] = NULL; d->arec[chan] = NULL; @@ -168,32 +180,52 @@ dsp_read(snddev_info *d, int chan, struct uio *buf, int flag) { pcm_channel *rdch, *wrch; + int ret; if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD; if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan); + getchns(d, chan, &rdch, &wrch); + CHN_LOCK(rdch); KASSERT(rdch, ("dsp_read: nonexistant channel")); KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel")); - if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) return EINVAL; + + if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { + CHN_UNLOCK(rdch); + return EINVAL; + } if (!(rdch->flags & CHN_F_RUNNING)) rdch->flags |= CHN_F_RUNNING; - return chn_read(rdch, buf); + ret = chn_read(rdch, buf); + CHN_UNLOCK(rdch); + + return ret; } int dsp_write(snddev_info *d, int chan, struct uio *buf, int flag) { pcm_channel *rdch, *wrch; + int ret; if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR; if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan); + getchns(d, chan, &rdch, &wrch); + CHN_LOCK(wrch); KASSERT(wrch, ("dsp_write: nonexistant channel")); KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel")); - if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) return EINVAL; + + if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { + CHN_UNLOCK(wrch); + return EINVAL; + } if (!(wrch->flags & CHN_F_RUNNING)) wrch->flags |= CHN_F_RUNNING; - return chn_write(wrch, buf); + ret = chn_write(wrch, buf); + CHN_UNLOCK(wrch); + + return ret; } int @@ -212,6 +244,11 @@ wrch = NULL; if (!(rdch || wrch)) return EINVAL; + + if (wrch) + CHN_LOCK(wrch); + if (rdch) + CHN_LOCK(rdch); /* * all routines are called with int. blocked. Make sure that * ints are re-enabled when calling slow or blocking functions! @@ -223,9 +260,11 @@ * we start with the new ioctl interface. */ case AIONWRITE: /* how many bytes can write ? */ +/* if (wrch && wrch->buffer.dl) - while (chn_wrfeed(wrch) > 0); - *arg_i = wrch? wrch->buffer2nd.fl : 0; + while (chn_wrfeed(wrch) == 0); +*/ + *arg_i = wrch? sndbuf_getfree(&wrch->buffer2nd) : 0; break; case AIOSSIZE: /* set the current blocksize */ @@ -241,9 +280,9 @@ { struct snd_size *p = (struct snd_size *)arg; if (wrch) - p->play_size = wrch->buffer2nd.blksz; + p->play_size = sndbuf_getblksz(&wrch->buffer2nd); if (rdch) - p->rec_size = rdch->buffer2nd.blksz; + p->rec_size = sndbuf_getblksz(&rdch->buffer2nd); } break; @@ -281,8 +320,8 @@ pcaps? pcaps->minspeed : 0); p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, pcaps? pcaps->maxspeed : 1000000); - p->bufsize = min(rdch? rdch->buffer2nd.bufsize : 1000000, - wrch? wrch->buffer2nd.bufsize : 1000000); + p->bufsize = min(rdch? sndbuf_getsize(&rdch->buffer2nd) : 1000000, + wrch? sndbuf_getsize(&wrch->buffer2nd) : 1000000); /* XXX bad on sb16 */ p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) & (wrch? chn_getformats(wrch) : 0xffffffff); @@ -314,9 +353,9 @@ * here follow the standard ioctls (filio.h etc.) */ case FIONREAD: /* get # bytes to read */ - if (rdch && rdch->buffer.dl) - while (chn_rdfeed(rdch) > 0); - *arg_i = rdch? rdch->buffer2nd.rl : 0; +/* if (rdch && rdch->buffer.dl) + while (chn_rdfeed(rdch) == 0); +*/ *arg_i = rdch? rdch->buffer2nd.rl : 0; break; case FIOASYNC: /*set/clear async i/o */ @@ -340,9 +379,9 @@ case THE_REAL_SNDCTL_DSP_GETBLKSIZE: case SNDCTL_DSP_GETBLKSIZE: if (wrch) - *arg_i = wrch->buffer2nd.blksz; + *arg_i = sndbuf_getblksz(&wrch->buffer2nd); else if (rdch) - *arg_i = rdch->buffer2nd.blksz; + *arg_i = sndbuf_getblksz(&rdch->buffer2nd); else *arg_i = 0; break ; @@ -355,19 +394,18 @@ case SNDCTL_DSP_RESET: DEB(printf("dsp reset\n")); - splx(s); - if (wrch) chn_abort(wrch); - if (rdch) chn_abort(rdch); + if (wrch) + chn_abort(wrch); + if (rdch) + chn_abort(rdch); break; case SNDCTL_DSP_SYNC: DEB(printf("dsp sync\n")); - splx(s); - if (wrch) chn_sync(wrch, wrch->buffer2nd.bufsize - 4); + if (wrch) chn_sync(wrch, sndbuf_getsize(&wrch->buffer2nd) - 4); break; case SNDCTL_DSP_SPEED: - splx(s); if (wrch) ret = chn_setspeed(wrch, *arg_i); if (rdch && ret == 0) @@ -379,7 +417,6 @@ break; case SNDCTL_DSP_STEREO: - splx(s); if (wrch) ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | ((*arg_i)? AFMT_STEREO : 0)); @@ -391,7 +428,6 @@ case SOUND_PCM_WRITE_CHANNELS: /* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ - splx(s); if (*arg_i == 1 || *arg_i == 2) { if (wrch) ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | @@ -423,11 +459,6 @@ *arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO; break; - case SNDCTL_DSP_SUBDIVIDE: - /* XXX watch out, this is RW! */ - printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n"); - break; - case SNDCTL_DSP_SETFRAGMENT: DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg)); { @@ -454,13 +485,13 @@ if (wrch && ret == 0) ret = chn_setblocksize(wrch, maxfrags, fragsz); - fragsz = c->buffer2nd.blksz; + fragsz = sndbuf_getblksz(&c->buffer2nd); fragln = 0; while (fragsz > 1) { fragln++; fragsz >>= 1; } - *arg_i = (c->buffer2nd.blkcnt << 16) | fragln; + *arg_i = (sndbuf_getblkcnt(&c->buffer2nd) << 16) | fragln; } break; @@ -469,18 +500,13 @@ { audio_buf_info *a = (audio_buf_info *)arg; if (rdch) { - snd_dbuf *b = &rdch->buffer; snd_dbuf *bs = &rdch->buffer2nd; - if (b->dl && !(rdch->flags & CHN_F_MAPPED)) - /* - * Suck up the secondary and DMA buffer. - * chn_rdfeed*() takes care of the alignment. - */ - while (chn_rdfeed(rdch) > 0); - a->bytes = bs->rl; - a->fragments = a->bytes / rdch->buffer2nd.blksz; - a->fragstotal = rdch->buffer2nd.blkcnt; - a->fragsize = rdch->buffer2nd.blksz; + + chn_rdupdate(rdch); + a->bytes = sndbuf_getfree(bs); + a->fragments = a->bytes / sndbuf_getblksz(bs); + a->fragstotal = sndbuf_getblkcnt(bs); + a->fragsize = sndbuf_getblksz(bs); } } break; @@ -490,21 +516,13 @@ { audio_buf_info *a = (audio_buf_info *)arg; if (wrch) { - snd_dbuf *b = &wrch->buffer; snd_dbuf *bs = &wrch->buffer2nd; - if (b->dl && !(wrch->flags & CHN_F_MAPPED)) { - /* - * Fill up the secondary and DMA buffer. - * chn_wrfeed*() takes care of the alignment. - * Check for underflow before writing into the buffers. - */ - chn_checkunderflow(wrch); - while (chn_wrfeed(wrch) > 0); - } - a->bytes = bs->fl; - a->fragments = a->bytes / wrch->buffer2nd.blksz; - a->fragstotal = wrch->buffer2nd.blkcnt; - a->fragsize = wrch->buffer2nd.blksz; + + chn_wrupdate(wrch); + a->bytes = sndbuf_getfree(bs); + a->fragments = a->bytes / sndbuf_getblksz(bs); + a->fragstotal = sndbuf_getblkcnt(bs); + a->fragsize = sndbuf_getblksz(bs); } } break; @@ -513,18 +531,13 @@ { count_info *a = (count_info *)arg; if (rdch) { - snd_dbuf *b = &rdch->buffer; snd_dbuf *bs = &rdch->buffer2nd; - if (b->dl && !(rdch->flags & CHN_F_MAPPED)) - /* - * Suck up the secondary and DMA buffer. - * chn_rdfeed*() takes care of the alignment. - */ - while (chn_rdfeed(rdch) > 0); - a->bytes = bs->total; - a->blocks = rdch->blocks; - a->ptr = bs->rp; - rdch->blocks = 0; + + chn_rdupdate(rdch); + a->bytes = sndbuf_gettotal(bs); + a->blocks = sndbuf_getblocks(bs) - rdch->blocks; + a->ptr = sndbuf_getreadyptr(bs); + rdch->blocks = sndbuf_getblocks(bs); } else ret = EINVAL; } break; @@ -533,21 +546,13 @@ { count_info *a = (count_info *)arg; if (wrch) { - snd_dbuf *b = &wrch->buffer; snd_dbuf *bs = &wrch->buffer2nd; - if (b->dl && !(wrch->flags & CHN_F_MAPPED)) { - /* - * Fill up the secondary and DMA buffer. - * chn_wrfeed*() takes care of the alignment. - * Check for underflow before writing into the buffers. - */ - chn_checkunderflow(wrch); - while (chn_wrfeed(wrch) > 0); - } - a->bytes = bs->total; - a->blocks = wrch->blocks; - a->ptr = bs->rp; - wrch->blocks = 0; + + chn_wrupdate(wrch); + a->bytes = sndbuf_gettotal(bs); + a->blocks = sndbuf_getblocks(bs) - wrch->blocks; + a->ptr = sndbuf_getreadyptr(bs); + wrch->blocks = sndbuf_getblocks(bs); } else ret = EINVAL; } break; @@ -565,19 +570,19 @@ case SNDCTL_DSP_SETTRIGGER: if (rdch) { rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); - if (*arg_i & PCM_ENABLE_INPUT) + if (*arg_i & PCM_ENABLE_INPUT) { rdch->flags |= CHN_F_TRIGGERED; - else + chn_start(rdch, 1); + } else rdch->flags |= CHN_F_NOTRIGGER; - chn_intr(rdch); } if (wrch) { wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); - if (*arg_i & PCM_ENABLE_OUTPUT) + if (*arg_i & PCM_ENABLE_OUTPUT) { wrch->flags |= CHN_F_TRIGGERED; - else + chn_start(wrch, 1); + } else wrch->flags |= CHN_F_NOTRIGGER; - chn_intr(wrch); } break; @@ -593,12 +598,9 @@ if (wrch) { snd_dbuf *b = &wrch->buffer; snd_dbuf *bs = &wrch->buffer2nd; - if (b->dl) { - chn_checkunderflow(wrch); - if (!(wrch->flags & CHN_F_MAPPED)) - while (chn_wrfeed(wrch) > 0); - } - *arg_i = b->rl + bs->rl; + + chn_wrupdate(wrch); + *arg_i = sndbuf_getready(b) + sndbuf_getready(bs); } else ret = EINVAL; break; @@ -615,6 +617,7 @@ case SNDCTL_DSP_SETSYNCRO: /* undocumented */ + case SNDCTL_DSP_SUBDIVIDE: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: /* dunno what these do, don't sound important */ @@ -623,7 +626,10 @@ ret = EINVAL; break; } - splx(s); + if (rdch) + CHN_UNLOCK(rdch); + if (wrch) + CHN_UNLOCK(wrch); return ret; } @@ -634,10 +640,23 @@ pcm_channel *wrch = NULL, *rdch = NULL; getchns(d, chan, &rdch, &wrch); + + if (wrch) + CHN_LOCK(wrch); + if (rdch) + CHN_LOCK(rdch); + e = events & (POLLOUT | POLLWRNORM); if (wrch && e) ret |= chn_poll(wrch, e, p); + e = events & (POLLIN | POLLRDNORM); if (rdch && e) ret |= chn_poll(rdch, e, p); + + if (rdch) + CHN_UNLOCK(rdch); + if (wrch) + CHN_UNLOCK(wrch); + return ret; } @@ -645,17 +664,29 @@ dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot) { pcm_channel *wrch = NULL, *rdch = NULL, *c = NULL; + int ret; getchns(d, chan, &rdch, &wrch); - /* XXX this is broken by line 204 of vm/device_pager.c, so force write buffer */ +#if 0 + /* + * XXX the linux api uses the nprot to select read/write buffer + * our vm system doesn't allow this, so force write buffer + */ + if (1 || (wrch && (nprot & PROT_WRITE))) c = wrch; else if (rdch && (nprot & PROT_READ)) c = rdch; - if (c && (c->format == c->buffer.fmt)) { - c->flags |= CHN_F_MAPPED; - return atop(vtophys(c->buffer2nd.buf + offset)); - } else +#else + c = wrch; +#endif + + if (c == NULL) return -1; + CHN_LOCK(c); + c->flags |= CHN_F_MAPPED; + ret = atop(vtophys(c->buffer2nd.buf + offset)); + CHN_UNLOCK(c); + return ret; } Index: pcm/fake.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/fake.c,v retrieving revision 1.6 diff -u -r1.6 fake.c --- pcm/fake.c 2000/12/18 01:36:37 1.6 +++ pcm/fake.c 2001/02/19 14:31:26 @@ -45,12 +45,14 @@ }; static pcmchan_caps fk_caps = {0, 1000000, fk_fmt, 0}; +#define FKBUFSZ 4096 +static char fakebuf[FKBUFSZ]; + /* channel interface */ static void * fkchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { - b->bufsize = 16384; - b->buf = malloc(b->bufsize, M_DEVBUF, M_NOWAIT); + sndbuf_setup(b, fakebuf, FKBUFSZ); return (void *)0xbabef00d; } @@ -103,9 +105,14 @@ CHANNEL_DECLARE(fkchan); int -fkchan_setup(pcm_channel *c) +fkchan_setup(device_t dev, pcm_channel *c) { + snddev_info *d = device_get_softc(dev); + c->methods = kobj_create(&fkchan_class, M_DEVBUF, M_WAITOK); + c->parent = d; + snprintf(c->name, 32, "%s:fake", device_get_nameunit(dev)); + return 0; } Index: pcm/feeder.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/feeder.c,v retrieving revision 1.13 diff -u -r1.13 feeder.c --- pcm/feeder.c 2000/12/18 01:36:37 1.13 +++ pcm/feeder.c 2001/02/19 14:31:26 @@ -294,21 +294,27 @@ /*****************************************************************************/ static int -feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream) +feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source) { - int ret, s; + snd_dbuf *src = source; + int l; + u_int8_t x; - KASSERT(count, ("feed_root: count == 0")); - count &= ~((1 << ch->align) - 1); - KASSERT(count, ("feed_root: aligned count == 0 (align = %d)", ch->align)); + KASSERT(count > 0, ("feed_root: count == 0")); + /* count &= ~((1 << ch->align) - 1); */ + KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); - s = spltty(); - count = min(count, stream->uio_resid); - if (count) { - ret = uiomove(buffer, count, stream); - KASSERT(ret == 0, ("feed_root: uiomove failed (%d)", ret)); - } - splx(s); + l = min(count, sndbuf_getready(src)); + sndbuf_dispose(src, buffer, l); + +/* + if (l < count) + printf("appending %d bytes", count - l); +*/ + + x = (src->fmt & AFMT_SIGNED)? 0 : 0x80; + while (l < count) + buffer[l++] = x; return count; } Index: pcm/feeder_fmt.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/feeder_fmt.c,v retrieving revision 1.3 diff -u -r1.3 feeder_fmt.c --- pcm/feeder_fmt.c 2001/02/13 22:00:57 1.3 +++ pcm/feeder_fmt.c 2001/02/19 14:31:26 @@ -177,11 +177,11 @@ /*****************************************************************************/ static int -feed_8to16le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) +feed_8to16le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) { int i, j, k; - k = FEEDER_FEED(f->source, c, b, count / 2, stream); + k = FEEDER_FEED(f->source, c, b, count / 2, source); j = k - 1; i = j * 2 + 1; while (i > 0 && j >= 0) { @@ -223,12 +223,12 @@ } static int -feed_16leto8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) +feed_16leto8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) { u_int32_t i = 0, toget = count * 2; int j = 1, k; - k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); + k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source); while (j < k) { b[i++] = ((u_int8_t *)f->data)[j]; j += 2; @@ -254,9 +254,9 @@ /*****************************************************************************/ static int -feed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) +feed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) { - int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, stream); + int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, source); j = k - 1; i = j * 2 + 1; @@ -282,9 +282,9 @@ /*****************************************************************************/ static int -feed_monotostereo16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) +feed_monotostereo16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) { - int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, stream); + int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, source); u_int8_t x, y; j = k - 1; @@ -332,12 +332,12 @@ } static int -feed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) +feed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) { u_int32_t i = 0, toget = count * 2; int j = 0, k; - k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); + k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source); while (j < k) { b[i++] = ((u_int8_t *)f->data)[j]; j += 2; @@ -377,12 +377,12 @@ } static int -feed_stereotomono16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) +feed_stereotomono16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) { u_int32_t i = 0, toget = count * 2; int j = 0, k; - k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); + k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source); while (j < k) { b[i++] = ((u_int8_t *)f->data)[j]; b[i++] = ((u_int8_t *)f->data)[j + 1]; @@ -409,10 +409,10 @@ /*****************************************************************************/ static int -feed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) +feed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) { u_int8_t t; - int i = 0, j = FEEDER_FEED(f->source, c, b, count, stream); + int i = 0, j = FEEDER_FEED(f->source, c, b, count, source); while (i < j) { t = b[i]; @@ -443,9 +443,9 @@ /*****************************************************************************/ static int -feed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) +feed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) { - int i = 0, j = FEEDER_FEED(f->source, c, b, count, stream); + int i = 0, j = FEEDER_FEED(f->source, c, b, count, source); int ssz = (int)f->data, ofs = ssz - 1; while (i < j) { @@ -484,9 +484,9 @@ /*****************************************************************************/ static int -feed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) +feed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) { - int i = 0, j = FEEDER_FEED(f->source, c, b, count, stream); + int i = 0, j = FEEDER_FEED(f->source, c, b, count, source); while (i < j) { b[i] = ((u_int8_t *)f->data)[b[i]]; Index: pcm/feeder_if.m =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/feeder_if.m,v retrieving revision 1.1 diff -u -r1.1 feeder_if.m --- pcm/feeder_if.m 2000/12/18 01:36:37 1.1 +++ pcm/feeder_if.m 2001/02/19 14:31:26 @@ -65,7 +65,7 @@ pcm_channel* c; u_int8_t* buffer; u_int32_t count; - struct uio* stream; + void* source; }; Index: pcm/sound.c =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/sound.c,v retrieving revision 1.35 diff -u -r1.35 sound.c --- pcm/sound.c 2001/01/11 23:22:33 1.35 +++ pcm/sound.c 2001/02/19 16:41:14 @@ -90,6 +90,38 @@ int snd_unit; TUNABLE_INT_DECL("hw.snd.unit", 0, snd_unit); +struct mtx * +snd_mtxcreate(const char *desc) +{ + struct mtx *m; + + m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK); + if (m == NULL) + return NULL; + mtx_init(m, desc, MTX_RECURSE); + return m; +} + +void +snd_mtxfree(struct mtx *m) +{ + mtx_lock(m); + mtx_destroy(m); + free(m, M_DEVBUF); +} + +void +snd_mtxlock(struct mtx *m) +{ + mtx_lock(m); +} + +void +snd_mtxunlock(struct mtx *m) +{ + mtx_unlock(m); +} + static void pcm_makelinks(void *dummy) { @@ -170,6 +202,7 @@ ch = &chns[idx]; ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); ch->parent = d; + snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(dev), dirs, idx); err = chn_init(ch, devinfo, dir); if (err) { device_printf(dev, "chn_init() for (%s:%d) failed: err = %d\n", dirs, idx, err); @@ -314,7 +347,7 @@ if (numplay == 0 || numrec == 0) d->flags |= SD_F_SIMPLEX; - fkchan_setup(&d->fakechan); + fkchan_setup(dev, &d->fakechan); chn_init(&d->fakechan, NULL, 0); d->magic = MAGIC(unit); /* debugging... */ Index: pcm/sound.h =================================================================== RCS file: /usr2/ncvs/src/sys/dev/sound/pcm/sound.h,v retrieving revision 1.24 diff -u -r1.24 sound.h --- pcm/sound.h 2001/01/03 01:25:26 1.24 +++ pcm/sound.h 2001/02/19 16:40:29 @@ -120,9 +120,12 @@ #define AFMT_SIGNED (AFMT_S16_LE | AFMT_S16_BE | AFMT_S8) #define AFMT_BIGENDIAN (AFMT_S16_BE | AFMT_U16_BE) -int fkchan_setup(pcm_channel *c); +int fkchan_setup(device_t dev, pcm_channel *c); int fkchan_kill(pcm_channel *c); +#define PCM_SETUP_INTR(dev, res, hand, param, cookie) \ + bus_setup_intr(dev, res, INTR_TYPE_AV | INTR_MPSAFE, hand, param, cookie) + /* * Minor numbers for the sound driver. * @@ -173,6 +176,13 @@ u_int32_t pcm_getflags(device_t dev); void pcm_setflags(device_t dev, u_int32_t val); void *pcm_getdevinfo(device_t dev); + +struct mtx *snd_mtxcreate(const char *desc); +void snd_mtxfree(struct mtx *m); +void snd_mtxlock(struct mtx *m); +void snd_mtxunlock(struct mtx *m); +#define SND_MTXLOCK(m) snd_mtxlock(m) +#define SND_MTXUNLOCK(m) snd_mtxunlock(m) #endif /* _KERNEL */