Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F135601817
D49216.id152010.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D49216.id152010.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D49216: sound: Implement /dev/dsp as a router device
Attached
Detach File
Event Timeline
Log In to Comment