Page MenuHomeFreeBSD

D49216.id152010.diff
No OneTemporary

D49216.id152010.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 ,
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; /* open() flags */
+ int mode; /* open() mode */
+ int unit; /* device_get_unit(sc->dev) */
+ uint32_t rfmt;
+ uint32_t rspd;
+ uint32_t pfmt;
+ uint32_t pspd;
};
static int dsp_mmap_allow_prot_exec = 0;
@@ -67,6 +74,12 @@
#define DSP_REGISTERED(x) (PCM_REGISTERED(x) && (x)->dsp_dev != NULL)
+#define DSP_F_VALID(x) ((x) & (FREAD | FWRITE))
+#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
+#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x))
+#define DSP_F_READ(x) ((x) & FREAD)
+#define DSP_F_WRITE(x) ((x) & FWRITE)
+
#define OLDPCM_IOCTL
static d_open_t dsp_open;
@@ -89,8 +102,227 @@
.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_cdev;
static eventhandler_tag dsp_ehtag = NULL;
+static int
+vdsp_getdev(struct snddev_info **dev)
+{
+ struct dsp_cdevpriv *priv;
+ struct snddev_info *d;
+ int err = 0, flags, mode;
+ uint32_t pfmt, rfmt, pspd, rspd;
+
+ *dev = NULL;
+
+ /* No device attached at the moment. */
+ if (snd_unit < 0)
+ return (ENODEV);
+
+ /* Fetch the cdevpriv associated with the FD. */
+ if ((err = devfs_get_cdevpriv((void **)&priv)) != 0)
+ return (err);
+
+ /*
+ * The cdevpriv points to the current default device. Make sure it is
+ * available for use.
+ */
+ if (priv->unit == snd_unit) {
+ d = devclass_get_softc(pcm_devclass, snd_unit);
+ if (!DSP_REGISTERED(d)) {
+ /* The default device is not registered anymore. */
+ priv->sc = NULL;
+ return (EBADF);
+ } else if (d == priv->sc) {
+ /*
+ * The cdevpriv already points to the default device
+ * and is ready to use.
+ */
+ *dev = priv->sc;
+ return (0);
+ }
+ /*
+ * 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/dsp once, with certain
+ * parameters, and these will be carried over across any device
+ * sound(4) might use.
+ */
+
+ /* Save current configuration. */
+ 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();
+
+ /* Fetch the new default device. */
+ d = devclass_get_softc(pcm_devclass, snd_unit);
+ if (!DSP_REGISTERED(d))
+ return (EBADF);
+
+ /* Open it with the same open() flags and mode. */
+ if ((err = dsp_cdevsw.d_open(d->dsp_dev, flags, mode, curthread)) != 0)
+ return (err);
+
+ /* Reconfigure channels with the same format and rate. */
+ if ((err = devfs_get_cdevpriv((void **)&priv)) != 0)
+ return (err);
+ if (!DSP_REGISTERED(priv->sc))
+ return (EBADF);
+ if (priv->rdch != NULL) {
+ CHN_LOCK(priv->rdch);
+ err = chn_reset(priv->rdch, rfmt, rspd);
+ CHN_UNLOCK(priv->rdch);
+ if (err != 0)
+ return (err);
+ }
+ if (priv->wrch != NULL) {
+ CHN_LOCK(priv->wrch);
+ err = chn_reset(priv->wrch, pfmt, pspd);
+ CHN_UNLOCK(priv->wrch);
+ if (err != 0)
+ return (err);
+ }
+
+ *dev = d;
+ printf("%s(): hotswapped to %s\n", __func__, device_get_nameunit(d->dev));
+
+ return (0);
+}
+
+/*
+ * Because /dev/dsp is a router device, and we want to it to be persistent, the
+ * following functions ignore the cdev functions' return values.
+ */
+static int
+vdsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
+{
+ struct snddev_info *d;
+
+ d = devclass_get_softc(pcm_devclass, snd_unit);
+ if (!DSP_REGISTERED(d))
+ return (EBADF);
+
+ /*
+ * Only exception to the comment above, however, is open(), since we
+ * cannot do anything if the device cannot be opened in the first
+ * place.
+ */
+ return (dsp_cdevsw.d_open(d->dsp_dev, flags, mode, td));
+}
+
+static int
+vdsp_read(struct cdev *i_dev, struct uio *buf, int flag)
+{
+ struct snddev_info *d;
+
+ if (vdsp_getdev(&d) == 0)
+ (void)dsp_cdevsw.d_read(d->dsp_dev, buf, flag);
+
+ return (0);
+}
+
+static int
+vdsp_write(struct cdev *i_dev, struct uio *buf, int flag)
+{
+ struct snddev_info *d;
+
+ if (vdsp_getdev(&d) == 0)
+ (void)dsp_cdevsw.d_write(d->dsp_dev, buf, flag);
+
+ return (0);
+}
+
+static int
+vdsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
+ struct thread *td)
+{
+ struct snddev_info *d;
+
+ if (vdsp_getdev(&d) == 0)
+ (void)dsp_cdevsw.d_ioctl(d->dsp_dev, cmd, arg, mode, td);
+
+ return (0);
+}
+
+static int
+vdsp_poll(struct cdev *i_dev, int events, struct thread *td)
+{
+ struct snddev_info *d;
+
+ if (vdsp_getdev(&d) == 0)
+ (void)dsp_cdevsw.d_poll(d->dsp_dev, events, td);
+
+ return (0);
+}
+
+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;
+
+ if (vdsp_getdev(&d) == 0) {
+ (void)dsp_cdevsw.d_mmap(d->dsp_dev, offset, paddr,
+ nprot, memattr);
+ }
+
+ return (0);
+}
+
+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;
+
+ if (vdsp_getdev(&d) == 0) {
+ (void)dsp_cdevsw.d_mmap_single(d->dsp_dev, offset,
+ size, object, nprot);
+ }
+
+ return (0);
+}
+
static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group);
static int dsp_oss_syncstart(int sg_id);
static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy);
@@ -234,12 +466,6 @@
return (0);
}
-#define DSP_F_VALID(x) ((x) & (FREAD | FWRITE))
-#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
-#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x))
-#define DSP_F_READ(x) ((x) & FREAD)
-#define DSP_F_WRITE(x) ((x) & FWRITE)
-
static void
dsp_close(void *data)
{
@@ -448,6 +674,10 @@
PCM_UNLOCK(d);
}
+ priv->flags = flags;
+ priv->mode = mode;
+ priv->unit = device_get_unit(d->dev);
+
PCM_RELEASE_QUICK(d);
PCM_GIANT_LEAVE(d);
@@ -1768,6 +1998,23 @@
break;
}
+ /*
+ * Cache the play/rec formats and rates so that we can fetch them
+ * safely in vdsp_getdev() if the device goes away in the meantime.
+ */
+ if (priv->rdch != NULL) {
+ CHN_LOCK(priv->rdch);
+ priv->rfmt = sndbuf_getfmt(priv->rdch->bufsoft);
+ priv->rspd = sndbuf_getspd(priv->rdch->bufsoft);
+ CHN_UNLOCK(priv->rdch);
+ }
+ if (priv->wrch != NULL) {
+ CHN_LOCK(priv->wrch);
+ priv->pfmt = sndbuf_getfmt(priv->wrch->bufsoft);
+ priv->pspd = sndbuf_getspd(priv->wrch->bufsoft);
+ CHN_UNLOCK(priv->wrch);
+ }
+
PCM_GIANT_LEAVE(d);
return (ret);
@@ -1914,7 +2161,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)
@@ -1927,36 +2173,26 @@
}
return;
found:
- SND_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);
- }
- SND_UNLOCK();
+ *dev = vdsp_cdev;
+ 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_cdev = 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_cdev);
}
SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 12, 5:19 AM (12 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25194869
Default Alt Text
D49216.id152010.diff (10 KB)

Event Timeline