Hello!
I need functionality similar to FreeBSD hw.snd.default_unit sysctl. It
defines which audio devices (/dev/dspN, /dev/mixerN) will be opened if
someone opens /dev/dsp or /dev/mixer.
I don't have a good idea how this can be done. I've made a quick patch for
dsp devices, it is attached.
The problem is that now the "default" node is opened all the time, no
matter if I open /dev/dsp or a specific /dev/dspN device.
My first thought was to compare ap->a_name to "dsp" to know if we just open
/dev/dsp or more specific /dev/dspN, but ap->a_name always has a form
"dspN", even if /dev/dsp is opened.
Any ideas how to implement this correctly?
Vasily
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 3206a42..7b1cf6d 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -1228,7 +1228,7 @@ dsp_mmap(struct dev_mmap_args *ap)
int
dsp_clone(struct dev_clone_args *ap)
{
- struct cdev *i_dev = ap->a_head.a_dev;
+ struct cdev *i_dev;
struct cdev *pdev;
struct snddev_info *pcm_dev;
struct snddev_channel *pcm_chan;
@@ -1236,7 +1236,8 @@ dsp_clone(struct dev_clone_args *ap)
int err = EBUSY;
int dir;
- pcm_dev = dsp_get_info(i_dev);
+ pcm_dev = devclass_get_softc(pcm_devclass, snd_default_unit);
+ i_dev = pcm_dev->dsp_clonedev;
if (pcm_dev == NULL)
return (ENODEV);
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 8f2a455..da42354 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -42,6 +42,10 @@ devclass_t pcm_devclass;
int pcm_veto_load = 1;
+int snd_default_unit = -1;
+TUNABLE_INT("hw.snd.default_unit", &snd_default_unit);
+static int get_suitable_unit(void);
+
int snd_maxautovchans = 4;
TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
@@ -354,14 +358,13 @@ pcm_setmaxautovchans(struct snddev_info *d, int num)
pcm_setvchans(d, 0);
}
-#ifdef USING_DEVFS
static int
sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
{
struct snddev_info *d;
int error, unit;
- unit = snd_unit;
+ unit = snd_default_unit;
error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
if (error == 0 && req->newptr != NULL) {
if (unit < 0 || (pcm_devclass != NULL &&
@@ -370,13 +373,12 @@ sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
d = devclass_get_softc(pcm_devclass, unit);
if (d == NULL || SLIST_EMPTY(&d->channels))
return EINVAL;
- snd_unit = unit;
+ snd_default_unit = unit;
}
return (error);
}
-SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(int), sysctl_hw_snd_unit, "I", "");
-#endif
static int
sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
@@ -744,6 +746,8 @@ pcm_setstatus(device_t dev, char *str)
snd_mtxunlock(d->lock);
if (snd_maxautovchans > 0)
pcm_setvchans(d, 1);
+ if (snd_default_unit < 0)
+ snd_default_unit = device_get_unit(dev);
return 0;
}
@@ -870,6 +874,20 @@ no:
return ENXIO;
}
+static int
+get_suitable_unit(void)
+{
+ struct snddev_info *d;
+ int i;
+ for (i = 0; pcm_devclass != NULL &&
+ i < devclass_get_maxunit(pcm_devclass); i++) {
+ d = devclass_get_softc(pcm_devclass, i);
+ if (d != NULL && (!SLIST_EMPTY(&d->channels)))
+ return i;
+ }
+ return -1;
+}
+
int
pcm_unregister(device_t dev)
{
@@ -963,6 +981,9 @@ pcm_unregister(device_t dev)
snd_mtxfree(d->lock);
sndstat_unregister(dev);
sndstat_release();
+
+ if (device_get_unit(dev) == snd_default_unit)
+ snd_default_unit = get_suitable_unit();
return 0;
}
@@ -999,12 +1020,7 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
}
sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
(d->flags & SD_F_SIMPLEX)? "" : " duplex",
-#ifdef USING_DEVFS
- (device_get_unit(dev) == snd_unit)? " default" : ""
-#else
- ""
-#endif
- );
+ (device_get_unit(dev) == snd_default_unit)? " default" : "");
if (verbose <= 1) {
snd_mtxunlock(d->lock);
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index 212db71..f5b3a94 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -174,6 +174,7 @@ int fkchan_kill(struct pcm_channel *c);
extern int pcm_veto_load;
extern int snd_maxautovchans;
extern devclass_t pcm_devclass;
+extern int snd_default_unit;
/*
* some macros for debugging purposes