Index: stable/4/sys/dev/sound/pcm/datatypes.h =================================================================== --- stable/4/sys/dev/sound/pcm/datatypes.h (revision 71955) +++ stable/4/sys/dev/sound/pcm/datatypes.h (revision 71956) @@ -1,166 +1,167 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ typedef struct _snd_mixer snd_mixer; typedef struct _snd_dbuf snd_dbuf; typedef struct _snddev_info snddev_info; typedef struct _pcmchan_caps pcmchan_caps; typedef struct _pcm_feeder pcm_feeder; typedef struct _pcm_channel pcm_channel; /*****************************************************************************/ struct _snd_mixer { KOBJ_FIELDS; const char *name; void *devinfo; int busy; int hwvol_muted; int hwvol_mixer; int hwvol_step; u_int32_t hwvol_mute_level; u_int32_t devs; u_int32_t recdevs; u_int32_t recsrc; u_int16_t level[32]; }; /*****************************************************************************/ /* * 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; 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 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; u_int32_t flags; bus_dmamap_t dmamap; bus_dma_tag_t dmatag; struct selinfo sel; }; #define SNDBUF_F_ISADMA 0x00000001 /*****************************************************************************/ struct pcm_feederdesc { u_int32_t type; u_int32_t in, out; u_int32_t flags; int idx; }; struct _pcm_feeder { KOBJ_FIELDS; int align; struct pcm_feederdesc *desc; void *data; pcm_feeder *source; }; /*****************************************************************************/ struct _pcmchan_caps { u_int32_t minspeed, maxspeed; u_int32_t *fmtlist; u_int32_t caps; }; struct _pcm_channel { kobj_t methods; pcm_feeder *feeder; u_int32_t align; int volume; u_int32_t speed; u_int32_t format; u_int32_t flags; u_int32_t feederflags; u_int32_t blocks; int direction; snd_dbuf buffer, buffer2nd; snddev_info *parent; void *devinfo; }; /*****************************************************************************/ #define SND_STATUSLEN 64 /* descriptor of audio device */ struct _snddev_info { pcm_channel *play, *rec, **aplay, **arec, fakechan; int *ref; unsigned playcount, reccount, chancount, maxchans; snd_mixer *mixer; u_long magic; unsigned flags; void *devinfo; device_t dev; char status[SND_STATUSLEN]; - /* sysctl stuff is only used in 5.x */ +#ifdef SND_DYNSYSCTL struct sysctl_ctx_list sysctl_tree; struct sysctl_oid *sysctl_tree_top; +#endif }; /*****************************************************************************/ /* mixer description structure and macros - these should go away, * only mss.[ch] use them */ struct mixer_def { u_int regno:7; u_int polarity:1; /* 1 means reversed */ u_int bitoffs:4; u_int nbits:4; }; typedef struct mixer_def mixer_ent; typedef struct mixer_def mixer_tab[32][2]; #define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \ {{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}} #define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \ {{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}} #define MIX_NONE(name) MIX_ENT(name, 0,0,0,0, 0,0,0,0) Index: stable/4/sys/dev/sound/pcm/mixer.c =================================================================== --- stable/4/sys/dev/sound/pcm/mixer.c (revision 71955) +++ stable/4/sys/dev/sound/pcm/mixer.c (revision 71956) @@ -1,386 +1,392 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include "mixer_if.h" MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_VOLUME] = 75, [SOUND_MIXER_BASS] = 50, [SOUND_MIXER_TREBLE] = 50, [SOUND_MIXER_SYNTH] = 75, [SOUND_MIXER_PCM] = 75, [SOUND_MIXER_SPEAKER] = 75, [SOUND_MIXER_LINE] = 75, [SOUND_MIXER_MIC] = 0, [SOUND_MIXER_CD] = 75, [SOUND_MIXER_LINE1] = 75, [SOUND_MIXER_VIDEO] = 75, [SOUND_MIXER_RECLEV] = 0, [SOUND_MIXER_OGAIN] = 50, }; static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; +#ifdef SND_DYNSYSCTL static int mixer_lookup(char *devname) { int i; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (strncmp(devname, snd_mixernames[i], strlen(snd_mixernames[i])) == 0) return i; return -1; } +#endif static int mixer_set(snd_mixer *mixer, unsigned dev, unsigned lev) { unsigned l, r; int v; if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev)))) return -1; l = min((lev & 0x00ff), 100); r = min(((lev & 0xff00) >> 8), 100); v = MIXER_SET(mixer, dev, l, r); if (v < 0) return -1; mixer->level[dev] = l | (r << 8); return 0; } static int mixer_get(snd_mixer *mixer, int dev) { if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) return mixer->level[dev]; else return -1; } static int mixer_setrecsrc(snd_mixer *mixer, u_int32_t src) { src &= mixer->recdevs; if (src == 0) src = SOUND_MASK_MIC; mixer->recsrc = MIXER_SETRECSRC(mixer, src); return 0; } static int mixer_getrecsrc(snd_mixer *mixer) { return mixer->recsrc; } void mix_setdevs(snd_mixer *m, u_int32_t v) { m->devs = v; } void mix_setrecdevs(snd_mixer *m, u_int32_t v) { m->recdevs = v; } u_int32_t mix_getdevs(snd_mixer *m) { return m->devs; } u_int32_t mix_getrecdevs(snd_mixer *m) { return m->recdevs; } void * mix_getdevinfo(snd_mixer *m) { return m->devinfo; } int mixer_busy(snd_mixer *m, int busy) { m->busy = busy; return 0; } int mixer_isbusy(snd_mixer *m) { return m->busy; } int mixer_init(device_t dev, kobj_class_t cls, void *devinfo) { snddev_info *d; snd_mixer *m; u_int16_t v; int i; d = device_get_softc(dev); m = (snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); m->name = cls->name; m->devinfo = devinfo; if (MIXER_INIT(m)) goto bad; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { v = snd_mixerdefaults[i]; mixer_set(m, i, v | (v << 8)); } mixer_setrecsrc(m, SOUND_MASK_MIC); d->mixer = m; return 0; bad: kobj_delete((kobj_t)m, M_MIXER); return -1; } int mixer_uninit(device_t dev) { int i; snddev_info *d; snd_mixer *m; d = device_get_softc(dev); m = d->mixer; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) mixer_set(m, i, 0); mixer_setrecsrc(m, SOUND_MASK_MIC); MIXER_UNINIT(m); kobj_delete((kobj_t)m, M_MIXER); d->mixer = NULL; return 0; } int mixer_reinit(device_t dev) { int i; snddev_info *d; snd_mixer *m; d = device_get_softc(dev); m = d->mixer; i = MIXER_REINIT(m); if (i) return i; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) mixer_set(m, i, m->level[i]); mixer_setrecsrc(m, m->recsrc); return 0; } int mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg) { int ret, *arg_i = (int *)arg; int v = -1, j = cmd & 0xff; snd_mixer *m; m = d->mixer; if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { if (j == SOUND_MIXER_RECSRC) ret = mixer_setrecsrc(m, *arg_i); else ret = mixer_set(m, j, *arg_i); return (ret == 0)? 0 : ENXIO; } if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { switch (j) { case SOUND_MIXER_DEVMASK: case SOUND_MIXER_CAPS: case SOUND_MIXER_STEREODEVS: v = mix_getdevs(m); break; case SOUND_MIXER_RECMASK: v = mix_getrecdevs(m); break; case SOUND_MIXER_RECSRC: v = mixer_getrecsrc(m); break; default: v = mixer_get(m, j); } *arg_i = v; return (v != -1)? 0 : ENXIO; } return ENXIO; } +#ifdef SND_DYNSYSCTL static int sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) { char devname[32]; int error, dev; snd_mixer *m; m = oidp->oid_arg1; strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); if (error == 0 && req->newptr != NULL) { dev = mixer_lookup(devname); if (dev == -1) return EINVAL; else if (dev != m->hwvol_mixer) { m->hwvol_mixer = dev; m->hwvol_muted = 0; } } return error; } +#endif int mixer_hwvol_init(device_t dev) { snddev_info *d; snd_mixer *m; d = device_get_softc(dev); m = d->mixer; m->hwvol_mixer = SOUND_MIXER_VOLUME; m->hwvol_step = 5; +#ifdef SND_DYNSYSCTL SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top), OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top), OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, sysctl_hw_snd_hwvol_mixer, "A", "") +#endif return 0; } void mixer_hwvol_mute(device_t dev) { snddev_info *d; snd_mixer *m; d = device_get_softc(dev); m = d->mixer; if (m->hwvol_muted) { m->hwvol_muted = 0; mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); } else { m->hwvol_muted++; m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); mixer_set(m, m->hwvol_mixer, 0); } } void mixer_hwvol_step(device_t dev, int left_step, int right_step) { snddev_info *d; snd_mixer *m; int level, left, right; d = device_get_softc(dev); m = d->mixer; if (m->hwvol_muted) { m->hwvol_muted = 0; level = m->hwvol_mute_level; } else level = mixer_get(m, m->hwvol_mixer); if (level != -1) { left = level & 0xff; right = level >> 8; left += left_step * m->hwvol_step; if (left < 0) left = 0; right += right_step * m->hwvol_step; if (right < 0) right = 0; mixer_set(m, m->hwvol_mixer, left | right << 8); } } /* * The various mixers use a variety of bitmasks etc. The Voxware * driver had a very nice technique to describe a mixer and interface * to it. A table defines, for each channel, which register, bits, * offset, polarity to use. This procedure creates the new value * using the table and the old value. */ void change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval) { u_char mask; int shift; DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x " "r %d p %d bit %d off %d\n", dev, chn, newval, *regval, (*t)[dev][chn].regno, (*t)[dev][chn].polarity, (*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) ); if ( (*t)[dev][chn].polarity == 1) /* reverse */ newval = 100 - newval ; mask = (1 << (*t)[dev][chn].nbits) - 1; newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/; *regval &= ~(mask << shift); /* Filter out the previous value */ *regval |= (newval & mask) << shift; /* Set the new value */ } Index: stable/4/sys/dev/sound/pcm/sound.c =================================================================== --- stable/4/sys/dev/sound/pcm/sound.c (revision 71955) +++ stable/4/sys/dev/sound/pcm/sound.c (revision 71956) @@ -1,684 +1,684 @@ /* * Copyright (c) 1999 Cameron Grant * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include static dev_t status_dev = 0; static int status_isopen = 0; static int status_init(char *buf, int size); static int status_read(struct uio *buf); static d_open_t sndopen; static d_close_t sndclose; static d_ioctl_t sndioctl; static d_read_t sndread; static d_write_t sndwrite; static d_mmap_t sndmmap; static d_poll_t sndpoll; #define CDEV_MAJOR 30 static struct cdevsw snd_cdevsw = { /* open */ sndopen, /* close */ sndclose, /* read */ sndread, /* write */ sndwrite, /* ioctl */ sndioctl, /* poll */ sndpoll, /* mmap */ sndmmap, /* strategy */ nostrategy, /* name */ "snd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; /* PROPOSAL: each unit needs: status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices dspW and audio are deprecated. dsp needs min 64 channels, will give it 256 minor = (unit << 20) + (dev << 16) + channel currently minor = (channel << 16) + (unit << 4) + dev nomenclature: /dev/pcmX/dsp.(0..255) /dev/pcmX/dspW /dev/pcmX/audio /dev/pcmX/status /dev/pcmX/mixer [etc.] */ #define PCMMINOR(x) (minor(x)) #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16) #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4) #define PCMDEV(x) (PCMMINOR(x) & 0x0000000f) #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) static devclass_t pcm_devclass; -#if __FreeBSD_version > 500000 -#define USING_DEVFS +#ifdef SND_DYNSYSCTL +SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); #endif #ifdef USING_DEVFS int snd_unit; TUNABLE_INT_DECL("hw.snd.unit", 0, snd_unit); static void pcm_makelinks(void *dummy) { int unit; dev_t pdev; static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; if (pcm_devclass == NULL || devfs_present == 0) return; if (dsp) { destroy_dev(dsp); dsp = 0; } if (dspW) { destroy_dev(dspW); dspW = 0; } if (audio) { destroy_dev(audio); audio = 0; } if (mixer) { destroy_dev(mixer); mixer = 0; } unit = snd_unit; if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) return; if (devclass_get_softc(pcm_devclass, unit) == NULL) return; pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0)); dsp = make_dev_alias(pdev, "dsp"); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0)); dspW = make_dev_alias(pdev, "dspW"); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); audio = make_dev_alias(pdev, "audio"); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); mixer = make_dev_alias(pdev, "mixer"); } -SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); - static int sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) { int error, unit; unit = snd_unit; error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); if (error == 0 && req->newptr != NULL) { snd_unit = unit; pcm_makelinks(NULL); } return (error); } SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_sndunit, "I", ""); #endif int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) { int unit = device_get_unit(dev), idx; snddev_info *d = device_get_softc(dev); pcm_channel *chns, *ch; char *dirs; int err; dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); idx = ((dir == PCMDIR_PLAY)? d->playcount++ : d->reccount++); if (chns == NULL) { device_printf(dev, "bad channel add (%s:%d)\n", dirs, idx); return 1; } ch = &chns[idx]; ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); ch->parent = d; err = chn_init(ch, devinfo, dir); if (err) { device_printf(dev, "chn_init() for (%s:%d) failed: err = %d\n", dirs, idx, err); return 1; } make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); /* XXX SND_DEV_NORESET? */ d->chancount++; #ifdef USING_DEVFS if (d->chancount == 1) pcm_makelinks(NULL); #endif return 0; } static int pcm_killchan(device_t dev, int dir) { int unit = device_get_unit(dev), idx; snddev_info *d = device_get_softc(dev); pcm_channel *chns, *ch; char *dirs; dev_t pdev; dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount); if (chns == NULL || idx < 0) { device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx); return 1; } ch = &chns[idx]; if (chn_kill(ch)) { device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx); return 1; } kobj_delete(ch->methods, M_DEVBUF); ch->methods = NULL; d->chancount--; pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); destroy_dev(pdev); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); destroy_dev(pdev); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); destroy_dev(pdev); return 0; } int pcm_setstatus(device_t dev, char *str) { snddev_info *d = device_get_softc(dev); strncpy(d->status, str, SND_STATUSLEN); return 0; } u_int32_t pcm_getflags(device_t dev) { snddev_info *d = device_get_softc(dev); return d->flags; } void pcm_setflags(device_t dev, u_int32_t val) { snddev_info *d = device_get_softc(dev); d->flags = val; } void * pcm_getdevinfo(device_t dev) { snddev_info *d = device_get_softc(dev); return d->devinfo; } /* This is the generic init routine */ int pcm_register(device_t dev, void *devinfo, int numplay, int numrec) { int sz, unit = device_get_unit(dev); snddev_info *d = device_get_softc(dev); if (!pcm_devclass) { pcm_devclass = device_get_devclass(dev); status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), UID_ROOT, GID_WHEEL, 0444, "sndstat"); } make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); d->dev = dev; d->devinfo = devinfo; d->chancount = d->playcount = d->reccount = 0; d->maxchans = numplay + numrec; sz = (numplay + numrec) * sizeof(pcm_channel *); if (sz > 0) { d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); if (!d->aplay) goto no; bzero(d->aplay, sz); d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); if (!d->arec) goto no; bzero(d->arec, sz); sz = (numplay + numrec) * sizeof(int); d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT); if (!d->ref) goto no; bzero(d->ref, sz); } if (numplay > 0) { d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel), M_DEVBUF, M_NOWAIT); if (!d->play) goto no; bzero(d->play, numplay * sizeof(pcm_channel)); } else d->play = NULL; if (numrec > 0) { d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel), M_DEVBUF, M_NOWAIT); if (!d->rec) goto no; bzero(d->rec, numrec * sizeof(pcm_channel)); } else d->rec = NULL; -#ifdef USING_DEVFS +#ifdef SND_DYNSYSCTL sysctl_ctx_init(&d->sysctl_tree); d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, device_get_nameunit(dev), CTLFLAG_RD, 0, ""); if (d->sysctl_tree_top == NULL) { sysctl_ctx_free(&d->sysctl_tree); goto no; } #endif if (numplay == 0 || numrec == 0) d->flags |= SD_F_SIMPLEX; fkchan_setup(&d->fakechan); chn_init(&d->fakechan, NULL, 0); d->magic = MAGIC(unit); /* debugging... */ return 0; no: if (d->aplay) free(d->aplay, M_DEVBUF); if (d->play) free(d->play, M_DEVBUF); if (d->arec) free(d->arec, M_DEVBUF); if (d->rec) free(d->rec, M_DEVBUF); if (d->ref) free(d->ref, M_DEVBUF); return ENXIO; } int pcm_unregister(device_t dev) { int r, i, unit = device_get_unit(dev); snddev_info *d = device_get_softc(dev); dev_t pdev; +#ifdef SND_DYNSYSCTL sysctl_remove_oid(d->sysctl_tree_top, 1, 1); d->sysctl_tree_top = NULL; sysctl_ctx_free(&d->sysctl_tree); +#endif r = 0; for (i = 0; i < d->chancount; i++) if (d->ref[i]) r = EBUSY; if (r) { device_printf(dev, "unregister: channel busy"); return r; } if (mixer_isbusy(d->mixer)) { device_printf(dev, "unregister: mixer busy"); return EBUSY; } pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); destroy_dev(pdev); mixer_uninit(dev); while (d->playcount > 0) pcm_killchan(dev, PCMDIR_PLAY); while (d->reccount > 0) pcm_killchan(dev, PCMDIR_REC); d->magic = 0; if (d->aplay) free(d->aplay, M_DEVBUF); if (d->play) free(d->play, M_DEVBUF); if (d->arec) free(d->arec, M_DEVBUF); if (d->rec) free(d->rec, M_DEVBUF); if (d->ref) free(d->ref, M_DEVBUF); fkchan_kill(&d->fakechan); #ifdef USING_DEVFS pcm_makelinks(NULL); #endif return 0; } /* * a small utility function which, given a device number, returns * a pointer to the associated snddev_info struct, and sets the unit * number. */ static snddev_info * get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) { snddev_info *sc; int u, d, c; u = PCMUNIT(i_dev); d = PCMDEV(i_dev); c = PCMCHAN(i_dev); if (u > devclass_get_maxunit(pcm_devclass)) u = -1; if (unit) *unit = u; if (dev) *dev = d; if (chan) *chan = c; if (u < 0) return NULL; sc = devclass_get_softc(pcm_devclass, u); if (sc == NULL || sc->magic == 0) return NULL; switch(d) { case SND_DEV_CTL: /* /dev/mixer handled by pcm */ case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return sc; case SND_DEV_SEQ: /* XXX when enabled... */ case SND_DEV_SEQ2: case SND_DEV_MIDIN: case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ default: printf("unsupported subdevice %d\n", d); return NULL; } } static int sndopen(dev_t i_dev, int flags, int mode, struct proc *p) { int dev, unit, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", unit, dev, flags, mode)); switch(dev) { case SND_DEV_STATUS: if (status_isopen) return EBUSY; status_isopen = 1; return 0; case SND_DEV_CTL: return d? mixer_busy(d->mixer, 1) : ENXIO; case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_NORESET: return d? dsp_open(d, chan, flags, dev) : ENXIO; default: return ENXIO; } } static int sndclose(dev_t i_dev, int flags, int mode, struct proc *p) { int dev, unit, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("close snd%d subdev %d\n", unit, dev)); switch(dev) { /* only those for which close makes sense */ case SND_DEV_STATUS: if (!status_isopen) return EBADF; status_isopen = 0; return 0; case SND_DEV_CTL: return d? mixer_busy(d->mixer, 0) : ENXIO; case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: return d? dsp_close(d, chan, dev) : ENXIO; default: return ENXIO; } } static int sndread(dev_t i_dev, struct uio *buf, int flag) { int dev, unit, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); switch(dev) { case SND_DEV_STATUS: return status_isopen? status_read(buf) : EBADF; case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: return d? dsp_read(d, chan, buf, flag) : EBADF; default: return ENXIO; } } static int sndwrite(dev_t i_dev, struct uio *buf, int flag) { int dev, unit, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); switch(dev) { /* only writeable devices */ case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return d? dsp_write(d, chan, buf, flag) : EBADF; default: return EPERM; /* for non-writeable devices ; */ } } static int sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) { int dev, chan; snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); if (d == NULL) return ENXIO; switch(dev) { case SND_DEV_CTL: return mixer_ioctl(d, cmd, arg); case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: if (IOCGROUP(cmd) == 'M') return mixer_ioctl(d, cmd, arg); else return dsp_ioctl(d, chan, cmd, arg); default: return ENXIO; } } static int sndpoll(dev_t i_dev, int events, struct proc *p) { int dev, chan; snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); if (d == NULL) return ENXIO; switch(dev) { case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: return dsp_poll(d, chan, events, p); default: return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; } } /* * The mmap interface allows access to the play and read buffer, * plus the device descriptor. * The various blocks are accessible at the following offsets: * * 0x00000000 ( 0 ) : write buffer ; * 0x01000000 (16 MB) : read buffer ; * 0x02000000 (32 MB) : device descriptor (dangerous!) * * WARNING: the mmap routines assume memory areas are aligned. This * is true (probably) for the dma buffers, but likely false for the * device descriptor. As a consequence, we do not know where it is * located in the requested area. */ static int sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) { int unit, dev, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", d, dev, offset, nprot)); if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ switch(dev) { case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: return dsp_mmap(d, chan, offset, nprot); default: return -1; } } static int status_init(char *buf, int size) { int i; device_t dev; snddev_info *d; snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n" "Installed devices:\n", __DATE__, __TIME__); for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); if (!d) continue; dev = devclass_get_device(pcm_devclass, i); if (1) { snprintf(buf + strlen(buf), size - strlen(buf), "pcm%d: <%s> %s", i, device_get_desc(dev), d->status); if (d->chancount > 0) snprintf(buf + strlen(buf), size - strlen(buf), " (%dp/%dr channels%s)\n", d->playcount, d->reccount, (!(d->flags & SD_F_SIMPLEX))? " duplex" : ""); else snprintf(buf + strlen(buf), size - strlen(buf), " (mixer only)\n"); } } return strlen(buf); } static int status_read(struct uio *buf) { static char status_buf[4096]; static int bufptr = 0, buflen = 0; int l; if (status_isopen == 1) { status_isopen++; bufptr = 0; buflen = status_init(status_buf, sizeof status_buf); } l = min(buf->uio_resid, buflen - bufptr); bufptr += l; return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0; } static int sndpcm_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: break; case MOD_UNLOAD: if (status_isopen) return EBUSY; if (status_dev) destroy_dev(status_dev); status_dev = 0; break; default: break; } return 0; } static moduledata_t sndpcm_mod = { "snd_pcm", sndpcm_modevent, NULL }; DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_VERSION(snd_pcm, PCM_MODVER); Index: stable/4/sys/dev/sound/pcm/sound.h =================================================================== --- stable/4/sys/dev/sound/pcm/sound.h (revision 71955) +++ stable/4/sys/dev/sound/pcm/sound.h (revision 71956) @@ -1,186 +1,191 @@ /* * Copyright (c) 1999 Cameron Grant * Copyright by Hannu Savolainen 1995 * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * first, include kernel header files. */ #ifndef _OS_H_ #define _OS_H_ #ifdef _KERNEL #include #include #include #include #include #include #include #include #include /* for DATA_SET */ #include #include #include #include #include #include #include #include #if __FreeBSD_version > 500000 #include #endif #include #include /* for DELAY */ #include #include #include #include #include #include #include #include #include #include #include +#if __FreeBSD_version > 500000 +#define USING_DEVFS +#define SND_DYNSYSCTL +#endif + #else struct isa_device { int dummy; }; #define d_open_t void #define d_close_t void #define d_read_t void #define d_write_t void #define d_ioctl_t void #define d_select_t void #endif /* _KERNEL */ #endif /* _OS_H_ */ #include #include #include #include #include #include #ifndef ISADMA_WRITE #define ISADMA_WRITE B_WRITE #define ISADMA_READ B_READ #define ISADMA_RAW B_RAW #endif #define PCM_MODVER 1 #define PCM_MINVER 1 #define PCM_PREFVER PCM_MODVER #define PCM_MAXVER 1 #define MAGIC(unit) (0xa4d10de0 + unit) #define SD_F_SIMPLEX 0x00000001 #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 #define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR) #define SD_F_DIR_SET 0x40000000 #define SD_F_TRANSIENT 0xf0000000 /* many variables should be reduced to a range. Here define a macro */ #define RANGE(var, low, high) (var) = \ (((var)<(low))? (low) : ((var)>(high))? (high) : (var)) #define DSP_BUFFSIZE (8192) /* make figuring out what a format is easier. got AFMT_STEREO already */ #define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE) #define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE) #define AFMT_8BIT (AFMT_U8 | AFMT_S8) #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_kill(pcm_channel *c); /* * Minor numbers for the sound driver. * * Unfortunately Creative called the codec chip of SB as a DSP. For this * reason the /dev/dsp is reserved for digitized audio use. There is a * device for true DSP processors but it will be called something else. * In v3.0 it's /dev/sndproc but this could be a temporary solution. */ #define SND_DEV_CTL 0 /* Control port /dev/mixer */ #define SND_DEV_SEQ 1 /* Sequencer /dev/sequencer */ #define SND_DEV_MIDIN 2 /* Raw midi access */ #define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ #define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ #define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ #define SND_DEV_STATUS 6 /* /dev/sndstat */ /* #7 not in use now. */ #define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ #define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ #define SND_DEV_PSS SND_DEV_SNDPROC /* ? */ #define SND_DEV_NORESET 10 #define DSP_DEFAULT_SPEED 8000 #define ON 1 #define OFF 0 #ifdef _KERNEL /* * some macros for debugging purposes * DDB/DEB to enable/disable debugging stuff * BVDDB to enable debugging when bootverbose */ #define DDB(x) x /* XXX */ #define BVDDB(x) if (bootverbose) x #ifndef DEB #define DEB(x) #endif SYSCTL_DECL(_hw_snd); int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo); int pcm_register(device_t dev, void *devinfo, int numplay, int numrec); int pcm_unregister(device_t dev); int pcm_setstatus(device_t dev, char *str); u_int32_t pcm_getflags(device_t dev); void pcm_setflags(device_t dev, u_int32_t val); void *pcm_getdevinfo(device_t dev); #endif /* _KERNEL */ /* usage of flags in device config entry (config file) */ #define DV_F_DRQ_MASK 0x00000007 /* mask for secondary drq */ #define DV_F_DUAL_DMA 0x00000010 /* set to use secondary dma channel */ /* ought to be made obsolete */ #define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */ #define DV_F_DEV_SHIFT 8 /* force device type/class */