diff --git a/lib/libmixer/mixer.3 b/lib/libmixer/mixer.3 --- a/lib/libmixer/mixer.3 +++ b/lib/libmixer/mixer.3 @@ -20,7 +20,7 @@ .\" THE SOFTWARE. .\" -.Dd January 19, 2023 +.Dd May 22, 2024 .Dt MIXER 3 .Os .Sh NAME @@ -395,7 +395,10 @@ .Pp The .Fn mixer_get_nmixers -function returns the total number of mixer devices in the system. +function returns the maximum mixer unit number. +Although this might sound as incorrect behavior, given that one would expect +"nmixers" to refer to the total number of active mixers, it is more intuitive +for applications that want to loop through all mixer devices. .Pp The .Fn MIX_ISDEV 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 @@ -2040,6 +2040,19 @@ return (NULL); } +static void +dsp_oss_audioinfo_unavail(oss_audioinfo *ai, int unit) +{ + bzero(ai, sizeof(*ai)); + ai->dev = unit; + snprintf(ai->name, sizeof(ai->name), "pcm%d (unavailable)", unit); + ai->pid = -1; + ai->card_number = unit; + ai->port_number = unit; + ai->mixer_dev = -1; + ai->legacy_device = unit; +} + /** * @brief Handler for SNDCTL_AUDIOINFO. * @@ -2084,8 +2097,14 @@ unit < devclass_get_maxunit(pcm_devclass); unit++) { d = devclass_get_softc(pcm_devclass, unit); if (!PCM_REGISTERED(d)) { - d = NULL; - continue; + if ((ai->dev == -1 && unit == snd_unit) || + ai->dev == unit) { + dsp_oss_audioinfo_unavail(ai, unit); + return (0); + } else { + d = NULL; + continue; + } } PCM_UNLOCKASSERT(d); diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h --- a/sys/dev/sound/pcm/mixer.h +++ b/sys/dev/sound/pcm/mixer.h @@ -69,8 +69,6 @@ void *mix_getdevinfo(struct snd_mixer *m); struct mtx *mixer_get_lock(struct snd_mixer *m); -extern int mixer_count; - #define MIXER_CMD_DIRECT 0 /* send command within driver */ #define MIXER_CMD_CDEV 1 /* send command from cdev/ioctl */ diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -105,11 +105,6 @@ .d_name = "mixer", }; -/** - * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl. - */ -int mixer_count = 0; - static eventhandler_tag mixer_ehtag = NULL; static struct cdev * @@ -701,22 +696,13 @@ snd_mtxfree(m->lock); kobj_delete((kobj_t)m, M_MIXER); - --mixer_count; - return (0); } struct snd_mixer * mixer_create(device_t dev, kobj_class_t cls, void *devinfo, const char *desc) { - struct snd_mixer *m; - - m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc); - - if (m != NULL) - ++mixer_count; - - return (m); + return (mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc)); } int @@ -769,8 +755,6 @@ pdev->si_drv1 = m; snddev->mixer_dev = pdev; - ++mixer_count; - if (bootverbose) { for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (!(m->devs & (1 << i))) @@ -839,8 +823,6 @@ d->mixer_dev = NULL; - --mixer_count; - return 0; } @@ -1411,6 +1393,17 @@ SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL); SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL); +static void +mixer_oss_mixerinfo_unavail(oss_mixerinfo *mi, int unit) +{ + bzero(mi, sizeof(*mi)); + mi->dev = unit; + snprintf(mi->id, sizeof(mi->id), "mixer%d (n/a)", unit); + snprintf(mi->name, sizeof(mi->name), "pcm%d:mixer (unavailable)", unit); + mi->card_number = unit; + mi->legacy_device = unit; +} + /** * @brief Handler for SNDCTL_MIXERINFO * @@ -1454,8 +1447,13 @@ for (i = 0; pcm_devclass != NULL && i < devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); - if (!PCM_REGISTERED(d) || PCM_DETACHING(d)) - continue; + if (!PCM_REGISTERED(d) || PCM_DETACHING(d)) { + if ((mi->dev == -1 && i == snd_unit) || mi->dev == i) { + mixer_oss_mixerinfo_unavail(mi, i); + return (0); + } else + continue; + } /* XXX Need Giant magic entry */ @@ -1463,89 +1461,96 @@ PCM_UNLOCKASSERT(d); PCM_LOCK(d); - if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL && - ((mi->dev == -1 && d->mixer_dev == i_dev) || + if (!((d->mixer_dev == i_dev && mi->dev == -1) || mi->dev == i)) { - m = d->mixer_dev->si_drv1; - mtx_lock(m->lock); - - /* - * At this point, the following synchronization stuff - * has happened: - * - a specific PCM device is locked. - * - a specific mixer device has been locked, so be - * sure to unlock when existing. - */ - bzero((void *)mi, sizeof(*mi)); - mi->dev = i; - snprintf(mi->id, sizeof(mi->id), "mixer%d", i); - strlcpy(mi->name, m->name, sizeof(mi->name)); - mi->modify_counter = m->modify_counter; - mi->card_number = i; - /* - * Currently, FreeBSD assumes 1:1 relationship between - * a pcm and mixer devices, so this is hardcoded to 0. - */ - mi->port_number = 0; - - /** - * @todo Fill in @sa oss_mixerinfo::mixerhandle. - * @note From 4Front: "mixerhandle is an arbitrary - * string that identifies the mixer better than - * the device number (mixerinfo.dev). Device - * numbers may change depending on the order the - * drivers are loaded. However the handle should - * remain the same provided that the sound card - * is not moved to another PCI slot." - */ - - /** - * @note - * @sa oss_mixerinfo::magic is a reserved field. - * - * @par - * From 4Front: "magic is usually 0. However some - * devices may have dedicated setup utilities and the - * magic field may contain an unique driver specific - * value (managed by [4Front])." - */ - - mi->enabled = device_is_attached(m->dev) ? 1 : 0; - /** - * The only flag for @sa oss_mixerinfo::caps is - * currently MIXER_CAP_VIRTUAL, which I'm not sure we - * really worry about. - */ - /** - * Mixer extensions currently aren't supported, so - * leave @sa oss_mixerinfo::nrext blank for now. - */ - - /** - * @todo Fill in @sa oss_mixerinfo::priority (requires - * touching drivers?) - * @note The priority field is for mixer applets to - * determine which mixer should be the default, with 0 - * being least preferred and 10 being most preferred. - * From 4Front: "OSS drivers like ICH use higher - * values (10) because such chips are known to be used - * only on motherboards. Drivers for high end pro - * devices use 0 because they will never be the - * default mixer. Other devices use values 1 to 9 - * depending on the estimated probability of being the - * default device. - */ - - snprintf(mi->devnode, sizeof(mi->devnode), "/dev/mixer%d", i); - mi->legacy_device = i; + PCM_UNLOCK(d); + continue; + } - mtx_unlock(m->lock); + if (d->mixer_dev->si_drv1 == NULL) { + mixer_oss_mixerinfo_unavail(mi, i); + PCM_UNLOCK(d); + return (0); } + m = d->mixer_dev->si_drv1; + mtx_lock(m->lock); + + /* + * At this point, the following synchronization stuff + * has happened: + * - a specific PCM device is locked. + * - a specific mixer device has been locked, so be + * sure to unlock when existing. + */ + bzero((void *)mi, sizeof(*mi)); + mi->dev = i; + snprintf(mi->id, sizeof(mi->id), "mixer%d", i); + strlcpy(mi->name, m->name, sizeof(mi->name)); + mi->modify_counter = m->modify_counter; + mi->card_number = i; + /* + * Currently, FreeBSD assumes 1:1 relationship between + * a pcm and mixer devices, so this is hardcoded to 0. + */ + mi->port_number = 0; + + /** + * @todo Fill in @sa oss_mixerinfo::mixerhandle. + * @note From 4Front: "mixerhandle is an arbitrary + * string that identifies the mixer better than + * the device number (mixerinfo.dev). Device + * numbers may change depending on the order the + * drivers are loaded. However the handle should + * remain the same provided that the sound card + * is not moved to another PCI slot." + */ + + /** + * @note + * @sa oss_mixerinfo::magic is a reserved field. + * + * @par + * From 4Front: "magic is usually 0. However some + * devices may have dedicated setup utilities and the + * magic field may contain an unique driver specific + * value (managed by [4Front])." + */ + + mi->enabled = device_is_attached(m->dev) ? 1 : 0; + /** + * The only flag for @sa oss_mixerinfo::caps is + * currently MIXER_CAP_VIRTUAL, which I'm not sure we + * really worry about. + */ + /** + * Mixer extensions currently aren't supported, so + * leave @sa oss_mixerinfo::nrext blank for now. + */ + + /** + * @todo Fill in @sa oss_mixerinfo::priority (requires + * touching drivers?) + * @note The priority field is for mixer applets to + * determine which mixer should be the default, with 0 + * being least preferred and 10 being most preferred. + * From 4Front: "OSS drivers like ICH use higher + * values (10) because such chips are known to be used + * only on motherboards. Drivers for high end pro + * devices use 0 because they will never be the + * default mixer. Other devices use values 1 to 9 + * depending on the estimated probability of being the + * default device. + */ + + snprintf(mi->devnode, sizeof(mi->devnode), "/dev/mixer%d", i); + mi->legacy_device = i; + + mtx_unlock(m->lock); + PCM_UNLOCK(d); - if (m != NULL) - return (0); + return (0); } return (EINVAL); diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c --- a/sys/dev/sound/pcm/sound.c +++ b/sys/dev/sound/pcm/sound.c @@ -766,7 +766,12 @@ */ si->nummidis = 0; si->numtimers = 0; - si->nummixers = mixer_count; + /* + * Set this to the maximum unit number so that applications will not + * break if they try to loop through all mixers and some of them are + * not available. + */ + si->nummixers = devclass_get_maxunit(pcm_devclass); si->numcards = devclass_get_maxunit(pcm_devclass); si->numaudios = devclass_get_maxunit(pcm_devclass); /* OSSv4 docs: Intended only for test apps; API doesn't @@ -797,23 +802,29 @@ for (i = 0; pcm_devclass != NULL && i < devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); - if (!PCM_REGISTERED(d)) - continue; - if (i != si->card) continue; - PCM_UNLOCKASSERT(d); - PCM_LOCK(d); - - strlcpy(si->shortname, device_get_nameunit(d->dev), - sizeof(si->shortname)); - strlcpy(si->longname, device_get_desc(d->dev), - sizeof(si->longname)); - strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); - si->intr_count = si->ack_count = 0; - - PCM_UNLOCK(d); + if (!PCM_REGISTERED(d)) { + snprintf(si->shortname, sizeof(si->shortname), + "pcm%d (n/a)", i); + strlcpy(si->longname, "Device unavailable", + sizeof(si->longname)); + si->hw_info[0] = '\0'; + si->intr_count = si->ack_count = 0; + } else { + PCM_UNLOCKASSERT(d); + PCM_LOCK(d); + + strlcpy(si->shortname, device_get_nameunit(d->dev), + sizeof(si->shortname)); + strlcpy(si->longname, device_get_desc(d->dev), + sizeof(si->longname)); + strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); + si->intr_count = si->ack_count = 0; + + PCM_UNLOCK(d); + } return (0); }