Page MenuHomeFreeBSD

D49216.diff
No OneTemporary

D49216.diff

diff --git a/share/man/man4/pcm.4 b/share/man/man4/pcm.4
--- a/share/man/man4/pcm.4
+++ b/share/man/man4/pcm.4
@@ -23,7 +23,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd February 15, 2025
+.Dd March 7, 2025
.Dt SOUND 4
.Os
.Sh NAME
@@ -284,14 +284,6 @@
.El
.It Va hw.snd.default_unit
Default sound card for systems with multiple sound cards.
-When using
-.Xr devfs 4 ,
-the default device for
-.Pa /dev/dsp .
-Equivalent to a symlink from
-.Pa /dev/dsp
-to
-.Pa /dev/dsp Ns Va ${hw.snd.default_unit} .
.It Va hw.snd.feeder_eq_exact_rate
Only certain rates are allowed for precise processing.
The default behavior is however to allow sloppy processing for all rates,
@@ -533,16 +525,21 @@
.Nm
drivers may create the following device nodes:
.Pp
-.Bl -tag -width ".Pa /dev/sndstat" -compact
+.Bl -tag -width "/dev/sndstat" -compact
.It Pa /dev/dsp%d
Audio device.
The number represents the unit number of the device.
+.It Pa /dev/vdsp
+Virtual device which dispatches to
+.Pa /dev/dsp${hw.snd.default_unit} .
.It Pa /dev/dsp
Alias of
-.Pa /dev/dsp${hw.snd.default_unit} .
+.Pa /dev/vdsp .
Available only if
.Pa hw.snd.basename_clone
is set.
+Applications that want to open the default device should make use of this
+device.
.It Pa /dev/sndstat
Current
.Nm
@@ -592,7 +589,6 @@
A device node is not created properly.
.El
.Sh SEE ALSO
-.Xr devfs 4 ,
.Xr snd_ai2s 4 ,
.Xr snd_als4000 4 ,
.Xr snd_atiixp 4 ,
@@ -619,6 +615,7 @@
.Xr snd_via8233 4 ,
.Xr snd_via82c686 4 ,
.Xr snd_vibes 4 ,
+.Xr devfs 5 ,
.Xr device.hints 5 ,
.Xr loader.conf 5 ,
.Xr dmesg 8 ,
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -53,6 +53,13 @@
struct pcm_channel *rdch;
struct pcm_channel *wrch;
struct pcm_channel *volch;
+ int flags;
+ int mode;
+ int unit;
+ uint32_t rfmt;
+ uint32_t rspd;
+ uint32_t pfmt;
+ uint32_t pspd;
};
static int dsp_mmap_allow_prot_exec = 0;
@@ -89,6 +96,27 @@
.d_name = "dsp",
};
+static d_open_t vdsp_open;
+static d_read_t vdsp_read;
+static d_write_t vdsp_write;
+static d_ioctl_t vdsp_ioctl;
+static d_poll_t vdsp_poll;
+static d_mmap_t vdsp_mmap;
+static d_mmap_single_t vdsp_mmap_single;
+
+struct cdevsw vdsp_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = vdsp_open,
+ .d_read = vdsp_read,
+ .d_write = vdsp_write,
+ .d_ioctl = vdsp_ioctl,
+ .d_poll = vdsp_poll,
+ .d_mmap = vdsp_mmap,
+ .d_mmap_single = vdsp_mmap_single,
+ .d_name = "vdsp",
+};
+
+static struct cdev *vdsp_dev;
static eventhandler_tag dsp_ehtag = NULL;
static void dsp_close_chan(struct pcm_channel *c);
@@ -107,6 +135,282 @@
static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name);
#endif
+static int
+vdsp_open_dev(int flags, int mode, struct thread *td)
+{
+ struct snddev_info *d;
+ struct cdevsw *csw;
+ int ref, ret, unit;
+
+ unit = snd_unit;
+
+ bus_topo_lock();
+ d = devclass_get_softc(pcm_devclass, unit);
+ if (!DSP_REGISTERED(d)) {
+ bus_topo_unlock();
+ return (EBADF);
+ }
+ dev_ref(d->dsp_dev);
+ bus_topo_unlock();
+
+ csw = dev_refthread(d->dsp_dev, &ref);
+ if (csw == NULL) {
+ dev_rel(d->dsp_dev);
+ return (ENXIO);
+ }
+ ret = csw->d_open(d->dsp_dev, flags, mode, td);
+ dev_relthread(d->dsp_dev, ref);
+ if (ret != 0)
+ dev_rel(d->dsp_dev);
+
+ return (0);
+}
+
+static int
+vdsp_get_dev(struct snddev_info **dev)
+{
+ struct dsp_cdevpriv *priv;
+ struct snddev_info *d;
+ int ret = 0, unit, flags, mode;
+ uint32_t pfmt, rfmt, pspd, rspd;
+
+ *dev = NULL;
+ unit = snd_unit;
+
+ /* No device attached at the moment. */
+ if (unit < 0)
+ return (ENODEV);
+
+ /* Fetch the cdevpriv associated with the FD. */
+ if ((ret = devfs_get_cdevpriv((void **)&priv)) != 0)
+ return (ret);
+
+ /*
+ * The cdevpriv points to the current default device. Make sure it is
+ * available for use.
+ */
+ if (priv->unit == unit) {
+ bus_topo_lock();
+ d = devclass_get_softc(pcm_devclass, unit);
+ if (!DSP_REGISTERED(d)) {
+ /* The default device is not registered anymore. */
+ priv->sc = NULL;
+ bus_topo_unlock();
+ return (EBADF);
+ } else if (d == priv->sc) {
+ /*
+ * The cdevpriv already points to the default device
+ * and is ready to use.
+ */
+ *dev = priv->sc;
+ bus_topo_unlock();
+ return (0);
+ }
+ bus_topo_unlock();
+ /*
+ * The device is registered, but not associated with the
+ * cdevpriv yet. This can happen as a result of the device
+ * detaching, and re-attaching later at some point, which means
+ * that priv->unit might still point to it, but the device
+ * needs to be re-initialized.
+ */
+ }
+
+ /*
+ * At this point we are either hot-swapping, or re-opening a device.
+ * The main idea is that we close the previously used device, fetch the
+ * new one, and initialize it with the same parameters (open() flags,
+ * sample rate and format) as the previous one. This is so that
+ * userland applications can simply open /dev/vdsp once, with certain
+ * parameters, and these will be carried over across any device
+ * sound(4) might use.
+ */
+
+ /* Save current configuration. */
+ d = priv->sc;
+ flags = priv->flags;
+ mode = priv->mode;
+ rfmt = priv->rfmt;
+ rspd = priv->rspd;
+ pfmt = priv->pfmt;
+ pspd = priv->pspd;
+
+ /*
+ * Clear cdevpriv. Will also eventually call dsp_close() and shutdown
+ * current channels.
+ */
+ devfs_clear_cdevpriv();
+ if (DSP_REGISTERED(d))
+ dev_rel(d->dsp_dev);
+
+ /* Open the new default device. */
+ if ((ret = vdsp_open_dev(flags, mode, curthread)) != 0)
+ return (ret);
+ if ((ret = devfs_get_cdevpriv((void **)&priv)) != 0)
+ return (ret);
+ if (!DSP_REGISTERED(priv->sc))
+ return (EBADF);
+
+ /* Reconfigure channels with the same format and rate. */
+ if (priv->rdch != NULL) {
+ CHN_LOCK(priv->rdch);
+ ret = chn_reset(priv->rdch, rfmt, rspd);
+ CHN_UNLOCK(priv->rdch);
+ if (ret != 0) {
+ dev_rel(priv->rdch->parentsnddev->dsp_dev);
+ return (ret);
+ }
+ }
+ if (priv->wrch != NULL) {
+ CHN_LOCK(priv->wrch);
+ ret = chn_reset(priv->wrch, pfmt, pspd);
+ CHN_UNLOCK(priv->wrch);
+ if (ret != 0) {
+ dev_rel(priv->wrch->parentsnddev->dsp_dev);
+ return (ret);
+ }
+ }
+
+ /*
+ * Preserve original device (flags, mode) and channel (rates, formats)
+ * configuration across hot-swaps.
+ */
+ priv->flags = flags;
+ priv->mode = mode;
+ priv->rfmt = rfmt;
+ priv->rspd = rspd;
+ priv->pfmt = pfmt;
+ priv->pspd = pspd;
+
+ *dev = priv->sc;
+
+ return (0);
+}
+
+static int
+vdsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
+{
+ int ret = 0;
+
+ if ((ret = vdsp_open_dev(flags, mode, td)) != 0)
+ return (ret);
+
+ return (ret);
+}
+
+static int
+vdsp_read(struct cdev *i_dev, struct uio *buf, int flag)
+{
+ struct snddev_info *d;
+ struct cdevsw *csw;
+ int ref, ret = 0;
+
+ if (vdsp_get_dev(&d) == 0) {
+ csw = dev_refthread(d->dsp_dev, &ref);
+ if (csw == NULL)
+ return (ENXIO);
+ ret = csw->d_read(d->dsp_dev, buf, flag);
+ dev_relthread(d->dsp_dev, ref);
+ }
+
+ return (ret);
+}
+
+static int
+vdsp_write(struct cdev *i_dev, struct uio *buf, int flag)
+{
+ struct snddev_info *d;
+ struct cdevsw *csw;
+ int ref, ret = 0;
+
+ if (vdsp_get_dev(&d) == 0) {
+ csw = dev_refthread(d->dsp_dev, &ref);
+ if (csw == NULL)
+ return (ENXIO);
+ ret = csw->d_write(d->dsp_dev, buf, flag);
+ dev_relthread(d->dsp_dev, ref);
+ }
+
+ return (ret);
+}
+
+static int
+vdsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
+ struct thread *td)
+{
+ struct snddev_info *d;
+ struct cdevsw *csw;
+ int ref, ret = 0;
+
+ if (vdsp_get_dev(&d) == 0) {
+ csw = dev_refthread(d->dsp_dev, &ref);
+ if (csw == NULL)
+ return (ENXIO);
+ ret = csw->d_ioctl(d->dsp_dev, cmd, arg, mode, td);
+ dev_relthread(d->dsp_dev, ref);
+ }
+
+ return (ret);
+}
+
+static int
+vdsp_poll(struct cdev *i_dev, int events, struct thread *td)
+{
+ struct snddev_info *d;
+ struct cdevsw *csw;
+ int ref, ret = 0;
+
+ if (vdsp_get_dev(&d) == 0) {
+ csw = dev_refthread(d->dsp_dev, &ref);
+ if (csw == NULL)
+ return (ENXIO);
+ ret = csw->d_poll(d->dsp_dev, events, td);
+ dev_relthread(d->dsp_dev, ref);
+ }
+
+ return (ret);
+}
+
+static int
+vdsp_mmap(struct cdev *i_dev, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int nprot, vm_memattr_t *memattr)
+{
+ struct snddev_info *d;
+ struct cdevsw *csw;
+ int ref, ret = 0;
+
+ if (vdsp_get_dev(&d) == 0) {
+ csw = dev_refthread(d->dsp_dev, &ref);
+ if (csw == NULL)
+ return (ENXIO);
+ ret = csw->d_mmap(d->dsp_dev, offset, paddr,
+ nprot, memattr);
+ dev_relthread(d->dsp_dev, ref);
+ }
+
+ return (ret);
+}
+
+static int
+vdsp_mmap_single(struct cdev *i_dev, vm_ooffset_t *offset,
+ vm_size_t size, struct vm_object **object, int nprot)
+{
+ struct snddev_info *d;
+ struct cdevsw *csw;
+ int ref, ret = 0;
+
+ if (vdsp_get_dev(&d) == 0) {
+ csw = dev_refthread(d->dsp_dev, &ref);
+ if (csw == NULL)
+ return (ENXIO);
+ ret = csw->d_mmap_single(d->dsp_dev, offset,
+ size, object, nprot);
+ dev_relthread(d->dsp_dev, ref);
+ }
+
+ return (ret);
+}
+
int
dsp_make_dev(device_t dev)
{
@@ -329,12 +633,13 @@
struct dsp_cdevpriv *priv;
struct pcm_channel *ch;
struct snddev_info *d;
- int error, dir;
+ int error, dir, origflags;
/* Kind of impossible.. */
if (i_dev == NULL || td == NULL)
return (ENODEV);
+ origflags = flags;
d = i_dev->si_drv1;
if (!DSP_REGISTERED(d))
return (EBADF);
@@ -429,6 +734,14 @@
PCM_UNLOCK(d);
}
+ /*
+ * Because the flags can be modified in case of SIMPLEX, preserve the
+ * original ones as well.
+ */
+ priv->flags = origflags;
+ priv->mode = mode;
+ priv->unit = device_get_unit(d->dev);
+
PCM_RELEASE_QUICK(d);
PCM_GIANT_LEAVE(d);
@@ -1747,6 +2060,23 @@
break;
}
+ /*
+ * Cache the play/rec formats and rates so that we can fetch them in
+ * vdsp_getdev() if we need to hot-swap.
+ */
+ if (priv->rdch != NULL) {
+ CHN_LOCK(priv->rdch);
+ priv->rfmt = priv->rdch->format;
+ priv->rspd = priv->rdch->speed;
+ CHN_UNLOCK(priv->rdch);
+ }
+ if (priv->wrch != NULL) {
+ CHN_LOCK(priv->wrch);
+ priv->pfmt = priv->wrch->format;
+ priv->pspd = priv->wrch->speed;
+ CHN_UNLOCK(priv->wrch);
+ }
+
PCM_GIANT_LEAVE(d);
return (ret);
@@ -1893,7 +2223,6 @@
dsp_clone(void *arg, struct ucred *cred, char *name, int namelen,
struct cdev **dev)
{
- struct snddev_info *d;
size_t i;
if (*dev != NULL)
@@ -1906,36 +2235,26 @@
}
return;
found:
- bus_topo_lock();
- d = devclass_get_softc(pcm_devclass, snd_unit);
- /*
- * If we only have a single soundcard attached and we detach it right
- * before entering dsp_clone(), there is a chance pcm_unregister() will
- * have returned already, meaning it will have set snd_unit to -1, and
- * thus devclass_get_softc() will return NULL here.
- */
- if (DSP_REGISTERED(d)) {
- *dev = d->dsp_dev;
- dev_ref(*dev);
- }
- bus_topo_unlock();
+ *dev = vdsp_dev;
+ dev_ref(*dev);
}
static void
dsp_sysinit(void *p)
{
- if (dsp_ehtag != NULL)
- return;
- dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
+ vdsp_dev = make_dev(&vdsp_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "vdsp");
+ if (dsp_ehtag == NULL)
+ dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
}
static void
dsp_sysuninit(void *p)
{
- if (dsp_ehtag == NULL)
- return;
- EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
- dsp_ehtag = NULL;
+ if (dsp_ehtag != NULL) {
+ EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
+ dsp_ehtag = NULL;
+ }
+ destroy_dev(vdsp_dev);
}
SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 2, 8:22 PM (12 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29168925
Default Alt Text
D49216.diff (11 KB)

Event Timeline