Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 6 Feb 2013 17:43:06 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r246421 - in head/sys/dev/sound: pcm usb
Message-ID:  <201302061743.r16Hh6nA001213@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Wed Feb  6 17:43:05 2013
New Revision: 246421
URL: http://svnweb.freebsd.org/changeset/base/246421

Log:
  Add support for buttons on USB audio devices,
  like Volume Up and Volume Down.
  
  Reviewed by:	mav @
  MFC after:	1 week

Modified:
  head/sys/dev/sound/pcm/mixer.c
  head/sys/dev/sound/pcm/mixer.h
  head/sys/dev/sound/usb/uaudio.c

Modified: head/sys/dev/sound/pcm/mixer.c
==============================================================================
--- head/sys/dev/sound/pcm/mixer.c	Wed Feb  6 17:27:41 2013	(r246420)
+++ head/sys/dev/sound/pcm/mixer.c	Wed Feb  6 17:43:05 2013	(r246421)
@@ -1492,3 +1492,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: head/sys/dev/sound/pcm/mixer.h
==============================================================================
--- head/sys/dev/sound/pcm/mixer.h	Wed Feb  6 17:27:41 2013	(r246420)
+++ head/sys/dev/sound/pcm/mixer.h	Wed Feb  6 17:43:05 2013	(r246421)
@@ -45,6 +45,8 @@ void mixer_hwvol_step(device_t dev, int 
 
 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: head/sys/dev/sound/usb/uaudio.c
==============================================================================
--- head/sys/dev/sound/usb/uaudio.c	Wed Feb  6 17:27:41 2013	(r246420)
+++ head/sys/dev/sound/usb/uaudio.c	Wed Feb  6 17:43:05 2013	(r246421)
@@ -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,37 @@ 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;
+	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
+	uint8_t iface_index;
+	uint8_t volume_up_id;
+	uint8_t volume_down_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 +429,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 +523,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 +650,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 +934,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 +964,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);
 
@@ -1034,6 +1078,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 +1259,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 +2548,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 +4879,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 +5514,162 @@ 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;
+	int v;
+	int v_l;
+	int v_r;
+	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_VOLUME_UP) &&
+		    (sc->sc_hid.volume_up_id == id) &&
+		    hid_get_data(buffer, actlen,
+		    &sc->sc_hid.volume_up_loc)) {
+
+			DPRINTF("Volume Up\n");
+
+			v = mix_get_locked(m, SOUND_MIXER_PCM, &v_l, &v_r);
+			if (v == 0) {
+				v = ((v_l + v_r) / 2) + 5;
+				if (v > 100)
+					v = 100;
+				mix_set_locked(m, SOUND_MIXER_PCM, v, v);
+			}
+		}
+
+		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");
+
+			v = mix_get_locked(m, SOUND_MIXER_PCM, &v_l, &v_r);
+			if (v == 0) {
+				v = ((v_l + v_r) / 2) - 5;
+				if (v < 0)
+					v = 0;
+				mix_set_locked(m, SOUND_MIXER_PCM, v, v);
+			}
+		}
+
+	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");
+	}
+
+	free(d_ptr, M_TEMP);
+
+	if (!(sc->sc_hid.flags & (UAUDIO_HID_HAS_VOLUME_UP |
+	    UAUDIO_HID_HAS_VOLUME_DOWN))) {
+		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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201302061743.r16Hh6nA001213>