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