On Sat, Feb 05, 2022 at 02:46:53PM +0100, Anton Lindqvist wrote:
> On Sat, Feb 05, 2022 at 12:28:08PM +0100, Mark Kettenis wrote:
> > > Date: Sat, 5 Feb 2022 09:29:42 +0100
> > > From: Anton Lindqvist <[email protected]>
> > > 
> > > Hi,
> > > I recently got a USB headset with physical volume buttons, handled by
> > > ucc(4). However, after enabling the device in sndiod the volume buttons
> > > does not cause the volume to change. Turns out wskbd_set_mixervolume()
> > > is only propagating volume changes to first attached audio device. Is
> > > their any good not to consider all attached audio devices?
> > 
> > I think this is tricky.  The mixer values of different audio devices
> > may start out differently and may have different granularity and
> > probably operate on a different scale.  This may lead to situations
> > where as you turn the volume up and down, the relative output volume
> > between devices changes considerably.  I also think that your
> > implementation will unmute all audio devices as soon as you touch the
> > volume control buttons, which is probably not desirable.
> > 
> > Thinking about other ways to do this, we could:
> > 
> > - Add a knob that allows the user to control which audio device is
> >   controlled by the volume control buttons.  The choice could include
> >   "none" and "all" as well as the individual devices.
> > 
> > - Add infrastructure to bind specific keyboards to specific audio
> >   devices, a bit like how we support binding specific wskbd devices to
> >   specific wsdisplay devices.
> > 
> > The first suggestion is probably relatively easy to achieve.  The
> > implementation of the latter would defenitely need more thought and
> > discussion.
> > 
> > The "none" choice above would (partly) solve another issue where
> > userland applications see the key presses and act upon them even
> > though the kernel already did the volume adjustment.
> > 
> > Cheers,
> > 
> > Mark
> 
> Thanks for the input. Another approach would be to correlate the audio
> and wskbd device. Here's a proof of concept diff that propagates a
> "cookie" from the common point of attachment, being uhub for my
> particular device. The same cookie is later used to find the matching
> audio device while handling volume keys. The found audio device could
> probably be cached inside `struct wskbd_softc' if it grabs a device
> reference. I'm taking quite a few shortcuts here to avoid changing the
> prototypes of commonly used functions.

Polished diff. I'm omitting a necessary refactoring commit making
audio_attach_mi() accept a new cookie argument.

diff --git sys/dev/audio.c sys/dev/audio.c
index 64696025a50..f2b40637771 100644
--- sys/dev/audio.c
+++ sys/dev/audio.c
@@ -28,6 +28,7 @@
 #include <sys/audioio.h>
 #include <dev/audio_if.h>
 #include <dev/mulaw.h>
+#include <dev/wscons/wskbdvar.h>       /* struct wskbd_audio */
 #include "audio.h"
 #include "wskbd.h"
 
@@ -96,6 +97,8 @@ struct wskbd_vol
 #define WSKBD_MUTE_DISABLE     2
 #define WSKBD_MUTE_ENABLE      3
 };
+
+int wskbd_set_mixervolume_unit(int, long, long);
 #endif
 
 /*
@@ -2455,10 +2458,6 @@ wskbd_mixer_init(struct audio_softc *sc)
        };
        int i;
 
-       if (sc->dev.dv_unit != 0) {
-               DPRINTF("%s: not configuring wskbd keys\n", DEVNAME(sc));
-               return;
-       }
        for (i = 0; i < sizeof(spkr_names) / sizeof(spkr_names[0]); i++) {
                if (wskbd_initvol(sc, &sc->spkr,
                        spkr_names[i].cn, spkr_names[i].dn))
@@ -2569,13 +2568,55 @@ wskbd_set_mixermute(long mute, long out)
        return 0;
 }
 
+int
+wskbd_set_mixervolume_sc(struct wskbd_audio *audio, long dir, long out)
+{
+       if (audio->dev != NULL) {
+               if ((audio->dev->dv_flags & DVF_ACTIVE) == 0) {
+                       /* Audio device gone, fallback to audio0. */
+                       device_unref(audio->dev);
+                       audio->dev = NULL;
+                       audio->unit = 0;
+               }
+       } else if (audio->cookie != NULL) {
+               void *cookie;
+               int i;
+
+               cookie = audio->cookie;
+               audio->cookie = NULL;
+               for (i = 0; i < audio_cd.cd_ndevs; i++) {
+                       struct audio_softc *sc;
+
+                       sc = (struct audio_softc *)device_lookup(&audio_cd, i);
+                       if (sc == NULL)
+                               continue;
+                       if (sc->cookie != cookie) {
+                               device_unref(&sc->dev);
+                               continue;
+                       }
+
+                       audio->dev = (struct device *)sc;
+                       audio->unit = i;
+                       break;
+               }
+       }
+
+       return wskbd_set_mixervolume_unit(audio->unit, dir, out);
+}
+
 int
 wskbd_set_mixervolume(long dir, long out)
+{
+       return wskbd_set_mixervolume_unit(0, dir, out);
+}
+
+int
+wskbd_set_mixervolume_unit(int unit, long dir, long out)
 {
        struct audio_softc *sc;
        struct wskbd_vol *vol;
 
-       sc = (struct audio_softc *)device_lookup(&audio_cd, 0);
+       sc = (struct audio_softc *)device_lookup(&audio_cd, unit);
        if (sc == NULL)
                return ENODEV;
        vol = out ? &sc->spkr : &sc->mic;
diff --git sys/dev/usb/uaudio.c sys/dev/usb/uaudio.c
index 0a19d512a5c..d86019311e0 100644
--- sys/dev/usb/uaudio.c
+++ sys/dev/usb/uaudio.c
@@ -3841,7 +3841,7 @@ uaudio_attach(struct device *parent, struct device *self, 
void *aux)
        /* print a nice uaudio attach line */
        uaudio_print(sc);
 
-       audio_attach_mi(&uaudio_hw_if, sc, NULL, &sc->dev);
+       audio_attach_mi(&uaudio_hw_if, sc, arg->cookie, &sc->dev);
 }
 
 int
diff --git sys/dev/usb/ucc.c sys/dev/usb/ucc.c
index f23f32990bb..705a31b327b 100644
--- sys/dev/usb/ucc.c
+++ sys/dev/usb/ucc.c
@@ -104,7 +104,7 @@ void        ucc_attach(struct device *, struct device *, 
void *);
 int    ucc_detach(struct device *, int);
 void   ucc_intr(struct uhidev *, void *, u_int);
 
-void   ucc_attach_wskbd(struct ucc_softc *);
+void   ucc_attach_wskbd(struct ucc_softc *, void *);
 int    ucc_enable(void *, int);
 void   ucc_set_leds(void *, int);
 int    ucc_ioctl(void *, u_long, caddr_t, int, struct proc *);
@@ -680,7 +680,7 @@ ucc_attach(struct device *parent, struct device *self, void 
*aux)
 
        /* Cannot load an empty map. */
        if (sc->sc_maplen > 0)
-               ucc_attach_wskbd(sc);
+               ucc_attach_wskbd(sc, uha->uaa->cookie);
 }
 
 int
@@ -772,7 +772,7 @@ unknown:
 }
 
 void
-ucc_attach_wskbd(struct ucc_softc *sc)
+ucc_attach_wskbd(struct ucc_softc *sc, void *cookie)
 {
        static const struct wskbd_accessops accessops = {
                .enable         = ucc_enable,
@@ -784,6 +784,7 @@ ucc_attach_wskbd(struct ucc_softc *sc)
                .keymap         = &sc->sc_keymap,
                .accessops      = &accessops,
                .accesscookie   = sc,
+               .audiocookie    = cookie,
        };
 
        sc->sc_keydesc[0].name = KB_US;
diff --git sys/dev/usb/usb_subr.c sys/dev/usb/usb_subr.c
index 7d8480f0f01..fc5808bfeb1 100644
--- sys/dev/usb/usb_subr.c
+++ sys/dev/usb/usb_subr.c
@@ -839,6 +839,7 @@ usbd_status
 usbd_probe_and_attach(struct device *parent, struct usbd_device *dev, int port,
     int addr)
 {
+       static char *cookie = 0;
        struct usb_attach_arg uaa;
        usb_device_descriptor_t *dd = &dev->ddesc;
        int i, confi, nifaces;
@@ -860,6 +861,7 @@ usbd_probe_and_attach(struct device *parent, struct 
usbd_device *dev, int port,
        uaa.vendor = UGETW(dd->idVendor);
        uaa.product = UGETW(dd->idProduct);
        uaa.release = UGETW(dd->bcdDevice);
+       uaa.cookie = ++cookie;
 
        /* First try with device specific drivers. */
        DPRINTF(("usbd_probe_and_attach trying device specific drivers\n"));
diff --git sys/dev/usb/usbdi.h sys/dev/usb/usbdi.h
index 05209bcb809..15404ffe9bd 100644
--- sys/dev/usb/usbdi.h
+++ sys/dev/usb/usbdi.h
@@ -227,6 +227,7 @@ struct usb_attach_arg {
        int                     usegeneric;
        struct usbd_interface   **ifaces;/* all interfaces */
        int                     nifaces; /* number of interfaces */
+       void                    *cookie;
 };
 
 /* Match codes. */
diff --git sys/dev/wscons/wskbd.c sys/dev/wscons/wskbd.c
index 5693a5b614e..e0a73830a2d 100644
--- sys/dev/wscons/wskbd.c
+++ sys/dev/wscons/wskbd.c
@@ -169,6 +169,10 @@ struct wskbd_softc {
 
        int     sc_refcnt;
        u_char  sc_dying;               /* device is being detached */
+
+#if NAUDIO > 0
+       struct wskbd_audio sc_audio;
+#endif
 };
 
 #define MOD_SHIFT_L            (1 << 0)
@@ -307,7 +311,7 @@ static struct wskbd_internal wskbd_console_data;
 void   wskbd_update_layout(struct wskbd_internal *, kbd_t);
 
 #if NAUDIO > 0
-extern int wskbd_set_mixervolume(long, long);
+extern int wskbd_set_mixervolume_sc(struct wskbd_audio *, long, long);
 #endif
 
 void
@@ -395,6 +399,10 @@ wskbd_attach(struct device *parent, struct device *self, 
void *aux)
        timeout_set(&sc->sc_repeat_ch, wskbd_repeat, sc);
 #endif
 
+#if NAUDIO > 0
+       sc->sc_audio.cookie = ap->audiocookie;
+#endif
+
        sc->id->t_sc = sc;
 
        sc->sc_accessops = ap->accessops;
@@ -608,6 +616,11 @@ wskbd_detach(struct device  *self, int flags)
        }
 #endif
 
+#if NAUDIO > 0
+       if (sc->sc_audio.dev != NULL)
+               device_unref(sc->sc_audio.dev);
+#endif
+
        if (sc->sc_isconsole) {
                KASSERT(wskbd_console_device == sc);
                wskbd_cndetach();
@@ -1766,13 +1779,13 @@ wskbd_translate(struct wskbd_internal *id, u_int type, 
int value)
                switch (ksym) {
 #if NAUDIO > 0
                case KS_AudioMute:
-                       wskbd_set_mixervolume(0, 1);
+                       wskbd_set_mixervolume_sc(&sc->sc_audio, 0, 1);
                        return (0);
                case KS_AudioLower:
-                       wskbd_set_mixervolume(-1, 1);
+                       wskbd_set_mixervolume_sc(&sc->sc_audio, -1, 1);
                        return (0);
                case KS_AudioRaise:
-                       wskbd_set_mixervolume(1, 1);
+                       wskbd_set_mixervolume_sc(&sc->sc_audio, 1, 1);
                        return (0);
 #endif
                default:
diff --git sys/dev/wscons/wskbdvar.h sys/dev/wscons/wskbdvar.h
index 91bca39e30f..ce0d88d6350 100644
--- sys/dev/wscons/wskbdvar.h
+++ sys/dev/wscons/wskbdvar.h
@@ -61,6 +61,15 @@ struct wskbd_consops {
        void    (*debugger)(void *);
 };
 
+/*
+ * Audio device associated with keyboard.
+ */
+struct wskbd_audio {
+       void            *cookie;
+       struct device   *dev;
+       int              unit;
+};
+
 /*
  * Attachment information provided by wskbddev devices when attaching
  * wskbd units.
@@ -71,6 +80,8 @@ struct wskbddev_attach_args {
 
        const struct wskbd_accessops *accessops;        /* access ops */
        void    *accesscookie;                          /* access cookie */
+
+       void    *audiocookie;
 };
 
 #define        WSKBDDEVCF_CONSOLE      0

Reply via email to