Author: hselasky
Date: Thu Feb 14 11:29:57 2013
New Revision: 246788
URL: http://svnweb.freebsd.org/changeset/base/246788

Log:
  MFC r246421 and r246454:
  Add support for buttons on USB audio devices,
  like Volume Up, Volume Down and Mute.
  
  Reviewed by:  mav @

Modified:
  stable/8/sys/dev/sound/pcm/mixer.c
  stable/8/sys/dev/sound/pcm/mixer.h
  stable/8/sys/dev/sound/usb/uaudio.c
  stable/8/sys/dev/usb/usb_transfer.c
  stable/8/sys/dev/usb/usbdi.h
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/dev/   (props changed)
  stable/8/sys/dev/sound/   (props changed)
  stable/8/sys/dev/sound/pcm/   (props changed)
  stable/8/sys/dev/sound/usb/   (props changed)

Modified: stable/8/sys/dev/sound/pcm/mixer.c
==============================================================================
--- stable/8/sys/dev/sound/pcm/mixer.c  Thu Feb 14 11:25:07 2013        
(r246787)
+++ stable/8/sys/dev/sound/pcm/mixer.c  Thu Feb 14 11:29:57 2013        
(r246788)
@@ -893,14 +893,8 @@ mixer_hwvol_init(device_t dev)
 }
 
 void
-mixer_hwvol_mute(device_t dev)
+mixer_hwvol_mute_locked(struct snd_mixer *m)
 {
-       struct snd_mixer *m;
-       struct cdev *pdev;
-
-       pdev = mixer_get_devt(dev);
-       m = pdev->si_drv1;
-       snd_mtxlock(m->lock);
        if (m->hwvol_muted) {
                m->hwvol_muted = 0;
                mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
@@ -909,19 +903,26 @@ mixer_hwvol_mute(device_t dev)
                m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
                mixer_set(m, m->hwvol_mixer, 0);
        }
-       snd_mtxunlock(m->lock);
 }
 
 void
-mixer_hwvol_step(device_t dev, int left_step, int right_step)
+mixer_hwvol_mute(device_t dev)
 {
        struct snd_mixer *m;
-       int level, left, right;
        struct cdev *pdev;
 
        pdev = mixer_get_devt(dev);
        m = pdev->si_drv1;
        snd_mtxlock(m->lock);
+       mixer_hwvol_mute_locked(m);
+       snd_mtxunlock(m->lock);
+}
+
+void
+mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step)
+{
+       int level, left, right;
+
        if (m->hwvol_muted) {
                m->hwvol_muted = 0;
                level = m->hwvol_mute_level;
@@ -929,15 +930,31 @@ mixer_hwvol_step(device_t dev, int left_
                level = mixer_get(m, m->hwvol_mixer);
        if (level != -1) {
                left = level & 0xff;
-               right = level >> 8;
+               right = (level >> 8) & 0xff;
                left += left_step * m->hwvol_step;
                if (left < 0)
                        left = 0;
+               else if (left > 100)
+                       left = 100;
                right += right_step * m->hwvol_step;
                if (right < 0)
                        right = 0;
+               else if (right > 100)
+                       right = 100;
                mixer_set(m, m->hwvol_mixer, left | right << 8);
        }
+}
+
+void
+mixer_hwvol_step(device_t dev, int left_step, int right_step)
+{
+       struct snd_mixer *m;
+       struct cdev *pdev;
+
+       pdev = mixer_get_devt(dev);
+       m = pdev->si_drv1;
+       snd_mtxlock(m->lock);
+       mixer_hwvol_step_locked(m, left_step, right_step);
        snd_mtxunlock(m->lock);
 }
 
@@ -1492,3 +1509,30 @@ mixer_get_lock(struct snd_mixer *m)
        }
        return (m->lock);
 }
+
+int
+mix_get_locked(struct snd_mixer *m, u_int dev, int *pleft, int *pright)
+{
+       int level;
+
+       level = mixer_get(m, dev);
+       if (level < 0) {
+               *pright = *pleft = -1;
+               return (-1);
+       }
+
+       *pleft = level & 0xFF;
+       *pright = (level >> 8) & 0xFF;
+
+       return (0);
+}
+
+int
+mix_set_locked(struct snd_mixer *m, u_int dev, int left, int right)
+{
+       int level;
+
+       level = (left & 0xFF) | ((right & 0xFF) << 8);
+
+       return (mixer_set(m, dev, level));
+}

Modified: stable/8/sys/dev/sound/pcm/mixer.h
==============================================================================
--- stable/8/sys/dev/sound/pcm/mixer.h  Thu Feb 14 11:25:07 2013        
(r246787)
+++ stable/8/sys/dev/sound/pcm/mixer.h  Thu Feb 14 11:29:57 2013        
(r246788)
@@ -37,11 +37,15 @@ int mixer_ioctl_cmd(struct cdev *i_dev, 
 int mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi);
 
 int mixer_hwvol_init(device_t dev);
+void mixer_hwvol_mute_locked(struct snd_mixer *m);
 void mixer_hwvol_mute(device_t dev);
+void mixer_hwvol_step_locked(struct snd_mixer *m, int l_step, int r_step);
 void mixer_hwvol_step(device_t dev, int left_step, int right_step);
 
 int mixer_busy(struct snd_mixer *m);
 
+int mix_get_locked(struct snd_mixer *m, u_int dev, int *pleft, int *pright);
+int mix_set_locked(struct snd_mixer *m, u_int dev, int left, int right);
 int mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right);
 int mix_get(struct snd_mixer *m, u_int dev);
 int mix_setrecsrc(struct snd_mixer *m, u_int32_t src);

Modified: stable/8/sys/dev/sound/usb/uaudio.c
==============================================================================
--- stable/8/sys/dev/sound/usb/uaudio.c Thu Feb 14 11:25:07 2013        
(r246787)
+++ stable/8/sys/dev/sound/usb/uaudio.c Thu Feb 14 11:29:57 2013        
(r246788)
@@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
 #include <dev/usb/usb_request.h>
 
 #define        USB_DEBUG_VAR uaudio_debug
@@ -277,16 +278,40 @@ struct uaudio_search_result {
        uint8_t is_input;
 };
 
+enum {
+       UAUDIO_HID_RX_TRANSFER,
+       UAUDIO_HID_N_TRANSFER,
+};
+
+struct uaudio_hid {
+       struct usb_xfer *xfer[UAUDIO_HID_N_TRANSFER];
+       struct hid_location volume_up_loc;
+       struct hid_location volume_down_loc;
+       struct hid_location mute_loc;
+       uint32_t flags;
+#define        UAUDIO_HID_VALID                0x0001
+#define        UAUDIO_HID_HAS_ID               0x0002
+#define        UAUDIO_HID_HAS_VOLUME_UP        0x0004
+#define        UAUDIO_HID_HAS_VOLUME_DOWN      0x0008
+#define        UAUDIO_HID_HAS_MUTE             0x0010
+       uint8_t iface_index;
+       uint8_t volume_up_id;
+       uint8_t volume_down_id;
+       uint8_t mute_id;
+};
+
 struct uaudio_softc {
        struct sbuf sc_sndstat;
        struct sndcard_func sc_sndcard_func;
        struct uaudio_chan sc_rec_chan;
        struct uaudio_chan sc_play_chan;
        struct umidi_chan sc_midi_chan;
+       struct uaudio_hid sc_hid;
        struct uaudio_search_result sc_mixer_clocks;
        struct uaudio_mixer_node sc_mixer_node;
 
        struct mtx *sc_mixer_lock;
+       struct snd_mixer *sc_mixer_dev;
        struct usb_device *sc_udev;
        struct usb_xfer *sc_mixer_xfer[1];
        struct uaudio_mixer_node *sc_mixer_root;
@@ -407,6 +432,7 @@ static usb_callback_t uaudio_chan_record
 static usb_callback_t uaudio_mixer_write_cfg_callback;
 static usb_callback_t umidi_bulk_read_callback;
 static usb_callback_t umidi_bulk_write_callback;
+static usb_callback_t uaudio_hid_rx_callback;
 
 /* ==== USB mixer ==== */
 
@@ -500,6 +526,9 @@ static void umidi_close(struct usb_fifo 
 static void    umidi_init(device_t dev);
 static int     umidi_probe(device_t dev);
 static int     umidi_detach(device_t dev);
+static int     uaudio_hid_probe(struct uaudio_softc *sc,
+                   struct usb_attach_arg *uaa);
+static void    uaudio_hid_detach(struct uaudio_softc *sc);
 
 #ifdef USB_DEBUG
 static void    uaudio_chan_dump_ep_desc(
@@ -624,6 +653,18 @@ static const struct usb_config
        },
 };
 
+static const struct usb_config
+       uaudio_hid_config[UAUDIO_HID_N_TRANSFER] = {
+       [UAUDIO_HID_RX_TRANSFER] = {
+               .type = UE_INTERRUPT,
+               .endpoint = UE_ADDR_ANY,
+               .direction = UE_DIR_IN,
+               .bufsize = 0,   /* use wMaxPacketSize */
+               .flags = {.short_xfer_ok = 1,},
+               .callback = &uaudio_hid_rx_callback,
+       },
+};
+
 static devclass_t uaudio_devclass;
 
 static device_method_t uaudio_methods[] = {
@@ -896,7 +937,7 @@ uaudio_attach(device_t dev)
                }
                device_printf(dev, "MIDI sequencer.\n");
        } else {
-               device_printf(dev, "No midi sequencer.\n");
+               device_printf(dev, "No MIDI sequencer.\n");
        }
 
        DPRINTF("doing child attach\n");
@@ -926,6 +967,12 @@ uaudio_attach(device_t dev)
                goto detach;
        }
 
+       if (uaudio_hid_probe(sc, uaa) == 0) {
+               device_printf(dev, "HID volume keys found.\n");
+       } else {
+               device_printf(dev, "No HID volume keys found.\n");
+       }
+
        /* reload all mixer settings */
        uaudio_mixer_reload_all(sc);
 
@@ -968,6 +1015,8 @@ uaudio_attach_sub(device_t dev, kobj_cla
                goto detach;
        sc->sc_mixer_init = 1;
 
+       mixer_hwvol_init(dev);
+
        snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
 
        if (pcm_register(dev, sc,
@@ -1034,6 +1083,8 @@ uaudio_detach(device_t dev)
        if (sc->sc_rec_chan.valid)
                usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 
1);
 
+       uaudio_hid_detach(sc);
+
        if (bus_generic_detach(dev) != 0) {
                DPRINTF("detach failed!\n");
        }
@@ -1213,6 +1264,18 @@ uaudio_chan_fill_info_sub(struct uaudio_
                                alt_index++;
                        }
 
+                       if ((!(sc->sc_hid.flags & UAUDIO_HID_VALID)) &&
+                           (id->bInterfaceClass == UICLASS_HID) &&
+                           (id->bInterfaceSubClass == 0) &&
+                           (id->bInterfaceProtocol == 0) &&
+                           (alt_index == 0) &&
+                           usbd_get_iface(udev, curidx) != NULL) {
+                               DPRINTF("Found HID interface at %d\n",
+                                   curidx);
+                               sc->sc_hid.flags |= UAUDIO_HID_VALID;
+                               sc->sc_hid.iface_index = curidx;
+                       }
+
                        uma_if_class =
                            ((id->bInterfaceClass == UICLASS_AUDIO) ||
                            ((id->bInterfaceClass == UICLASS_VENDOR) &&
@@ -2490,6 +2553,9 @@ uaudio_mixer_reload_all(struct uaudio_so
                        pmc->update[chan / 8] |= (1 << (chan % 8));
        }
        usbd_transfer_start(sc->sc_mixer_xfer[0]);
+
+       /* start HID volume keys, if any */
+       usbd_transfer_start(sc->sc_hid.xfer[0]);
        mtx_unlock(sc->sc_mixer_lock);
 }
 
@@ -4818,6 +4884,7 @@ uaudio_mixer_init_sub(struct uaudio_soft
        DPRINTF("\n");
 
        sc->sc_mixer_lock = mixer_get_lock(m);
+       sc->sc_mixer_dev = m;
 
        if (usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index,
            sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc,
@@ -5452,6 +5519,167 @@ umidi_detach(device_t dev)
        return (0);
 }
 
+static void
+uaudio_hid_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+       struct uaudio_softc *sc = usbd_xfer_softc(xfer);
+       const uint8_t *buffer = usbd_xfer_get_frame_buffer(xfer, 0);
+       struct snd_mixer *m;
+       uint8_t id;
+       int actlen;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               DPRINTF("actlen=%d\n", actlen);
+
+               if (actlen != 0 &&
+                   (sc->sc_hid.flags & UAUDIO_HID_HAS_ID)) {
+                       id = *buffer;
+                       buffer++;
+                       actlen--;
+               } else {
+                       id = 0;
+               }
+
+               m = sc->sc_mixer_dev;
+
+               if ((sc->sc_hid.flags & UAUDIO_HID_HAS_MUTE) &&
+                   (sc->sc_hid.mute_id == id) &&
+                   hid_get_data(buffer, actlen,
+                   &sc->sc_hid.mute_loc)) {
+
+                       DPRINTF("Mute toggle\n");
+
+                       mixer_hwvol_mute_locked(m);
+               }
+
+               if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_UP) &&
+                   (sc->sc_hid.volume_up_id == id) &&
+                   hid_get_data(buffer, actlen,
+                   &sc->sc_hid.volume_up_loc)) {
+
+                       DPRINTF("Volume Up\n");
+
+                       mixer_hwvol_step_locked(m, 1, 1);
+               }
+
+               if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_DOWN) &&
+                   (sc->sc_hid.volume_down_id == id) &&
+                   hid_get_data(buffer, actlen,
+                   &sc->sc_hid.volume_down_loc)) {
+
+                       DPRINTF("Volume Down\n");
+
+                       mixer_hwvol_step_locked(m, -1, -1);
+               }
+
+       case USB_ST_SETUP:
+tr_setup:
+               /* check if we can put more data into the FIFO */
+               usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+               usbd_transfer_submit(xfer);
+               break;
+
+       default:                        /* Error */
+               if (error != USB_ERR_CANCELLED) {
+                       /* try clear stall first */
+                       usbd_xfer_set_stall(xfer);
+                       goto tr_setup;
+               }
+               break;
+       }
+}
+
+static int
+uaudio_hid_probe(struct uaudio_softc *sc,
+    struct usb_attach_arg *uaa)
+{
+       void *d_ptr;
+       uint32_t flags;
+       uint16_t d_len;
+       uint8_t id;
+       int error;
+
+       if (!(sc->sc_hid.flags & UAUDIO_HID_VALID))
+               return (-1);
+
+       if (sc->sc_mixer_lock == NULL)
+               return (-1);
+
+       /* Get HID descriptor */
+       error = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr,
+           &d_len, M_TEMP, sc->sc_hid.iface_index);
+
+       if (error) {
+               DPRINTF("error reading report description\n");
+               return (-1);
+       }
+
+       /* check if there is an ID byte */
+       hid_report_size(d_ptr, d_len, hid_input, &id);
+
+       if (id != 0)
+               sc->sc_hid.flags |= UAUDIO_HID_HAS_ID;
+
+       if (hid_locate(d_ptr, d_len,
+           HID_USAGE2(HUP_CONSUMER, 0xE9 /* Volume Increment */),
+           hid_input, 0, &sc->sc_hid.volume_up_loc, &flags,
+           &sc->sc_hid.volume_up_id)) {
+               if (flags & HIO_VARIABLE)
+                       sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_UP;
+               DPRINTFN(1, "Found Volume Up key\n");
+       }
+
+       if (hid_locate(d_ptr, d_len,
+           HID_USAGE2(HUP_CONSUMER, 0xEA /* Volume Decrement */),
+           hid_input, 0, &sc->sc_hid.volume_down_loc, &flags,
+           &sc->sc_hid.volume_down_id)) {
+               if (flags & HIO_VARIABLE)
+                       sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_DOWN;
+               DPRINTFN(1, "Found Volume Down key\n");
+       }
+
+       if (hid_locate(d_ptr, d_len,
+           HID_USAGE2(HUP_CONSUMER, 0xE2 /* Mute */),
+           hid_input, 0, &sc->sc_hid.mute_loc, &flags,
+           &sc->sc_hid.mute_id)) {
+               if (flags & HIO_VARIABLE)
+                       sc->sc_hid.flags |= UAUDIO_HID_HAS_MUTE;
+               DPRINTFN(1, "Found Mute key\n");
+       }
+
+       free(d_ptr, M_TEMP);
+
+       if (!(sc->sc_hid.flags & (UAUDIO_HID_HAS_VOLUME_UP |
+           UAUDIO_HID_HAS_VOLUME_DOWN |
+           UAUDIO_HID_HAS_MUTE))) {
+               DPRINTFN(1, "Did not find any volume related keys\n");
+               return (-1);
+       }
+
+       /* prevent the uhid driver from attaching */
+       usbd_set_parent_iface(uaa->device, sc->sc_hid.iface_index,
+           sc->sc_mixer_iface_index);
+
+       /* allocate USB transfers */
+       error = usbd_transfer_setup(uaa->device, &sc->sc_hid.iface_index,
+           sc->sc_hid.xfer, uaudio_hid_config, UAUDIO_HID_N_TRANSFER,
+           sc, sc->sc_mixer_lock);
+       if (error) {
+               DPRINTF("error=%s\n", usbd_errstr(error));
+               return (-1);
+       }
+       return (0);
+}
+
+static void
+uaudio_hid_detach(struct uaudio_softc *sc)
+{
+       usbd_transfer_unsetup(sc->sc_hid.xfer, UAUDIO_HID_N_TRANSFER);
+}
+
 DRIVER_MODULE(uaudio, uhub, uaudio_driver, uaudio_devclass, NULL, 0);
 MODULE_DEPEND(uaudio, usb, 1, 1, 1);
 MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);

Modified: stable/8/sys/dev/usb/usb_transfer.c
==============================================================================
--- stable/8/sys/dev/usb/usb_transfer.c Thu Feb 14 11:25:07 2013        
(r246787)
+++ stable/8/sys/dev/usb/usb_transfer.c Thu Feb 14 11:29:57 2013        
(r246788)
@@ -1923,6 +1923,17 @@ usbd_xfer_get_frame(struct usb_xfer *xfe
        return (&xfer->frbuffers[frindex]);
 }
 
+void *
+usbd_xfer_get_frame_buffer(struct usb_xfer *xfer, usb_frcount_t frindex)
+{
+       struct usb_page_search page_info;
+
+       KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
+
+       usbd_get_page(&xfer->frbuffers[frindex], 0, &page_info);
+       return (page_info.buffer);
+}
+
 /*------------------------------------------------------------------------*
  *     usbd_xfer_get_fps_shift
  *

Modified: stable/8/sys/dev/usb/usbdi.h
==============================================================================
--- stable/8/sys/dev/usb/usbdi.h        Thu Feb 14 11:25:07 2013        
(r246787)
+++ stable/8/sys/dev/usb/usbdi.h        Thu Feb 14 11:29:57 2013        
(r246788)
@@ -522,6 +522,7 @@ void        usbd_xfer_status(struct usb_xfer *x
            int *aframes, int *nframes);
 struct usb_page_cache *usbd_xfer_get_frame(struct usb_xfer *xfer,
            usb_frcount_t frindex);
+void   *usbd_xfer_get_frame_buffer(struct usb_xfer *, usb_frcount_t);
 void   *usbd_xfer_softc(struct usb_xfer *xfer);
 void   *usbd_xfer_get_priv(struct usb_xfer *xfer);
 void   usbd_xfer_set_priv(struct usb_xfer *xfer, void *);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to