svn commit: r359446 - head/sys/dev/sound/usb

Hans Petter Selasky hselasky at FreeBSD.org
Mon Mar 30 17:01:41 UTC 2020


Author: hselasky
Date: Mon Mar 30 16:50:32 2020
New Revision: 359446
URL: https://svnweb.freebsd.org/changeset/base/359446

Log:
  Add support for multiple playback and recording devices per physical USB audio
  device. This requires some structural refactoring inside the driver, mostly
  about converting existing audio channel structures into arrays.
  
  The main audio mixer is provided by the first PCM instance.
  The non-first audio instances may only have a software mixer for PCM playback.
  
  Tested by:	Horse Ma <Shichun.Ma at dell.com>
  MFC after:	1 week
  Sponsored by:	Mellanox Technologies

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

Modified: head/sys/dev/sound/usb/uaudio.c
==============================================================================
--- head/sys/dev/sound/usb/uaudio.c	Mon Mar 30 16:32:55 2020	(r359445)
+++ head/sys/dev/sound/usb/uaudio.c	Mon Mar 30 16:50:32 2020	(r359446)
@@ -262,6 +262,8 @@ struct uaudio_chan {
 #define	CHAN_OP_START 1
 #define	CHAN_OP_STOP 2
 #define	CHAN_OP_DRAIN 3
+
+	uint8_t iface_index;
 };
 
 #define	UMIDI_EMB_JACK_MAX   16		/* units */
@@ -347,37 +349,45 @@ struct uaudio_hid {
 #define	UAUDIO_SPDIF_OUT_96K	0x04	/* Out sample rate = 96K */
 #define	UAUDIO_SPDIF_IN_MIX	0x10	/* Input mix enable */
 
+#define	UAUDIO_MAX_CHILD 2
+
+struct uaudio_softc_child {
+	device_t pcm_device;
+	struct mtx *mixer_lock;
+	struct snd_mixer *mixer_dev;
+
+	uint32_t mix_info;
+	uint32_t recsrc_info;
+
+	uint8_t	pcm_registered:1;
+	uint8_t	mixer_init:1;
+};
+
 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 uaudio_chan sc_rec_chan[UAUDIO_MAX_CHILD];
+	struct uaudio_chan sc_play_chan[UAUDIO_MAX_CHILD];
 	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 uaudio_configure_msg sc_config_msg[2];
+	struct uaudio_softc_child sc_child[UAUDIO_MAX_CHILD];
 
-	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;
 	struct uaudio_mixer_node *sc_mixer_curr;
 	int     (*sc_set_spdif_fn) (struct uaudio_softc *, int);
 
-	uint32_t sc_mix_info;
-	uint32_t sc_recsrc_info;
-
 	uint16_t sc_audio_rev;
 	uint16_t sc_mixer_count;
 
-	uint8_t	sc_sndstat_valid;
 	uint8_t	sc_mixer_iface_index;
 	uint8_t	sc_mixer_iface_no;
 	uint8_t	sc_mixer_chan;
-	uint8_t	sc_pcm_registered:1;
-	uint8_t	sc_mixer_init:1;
+	uint8_t	sc_sndstat_valid:1;
 	uint8_t	sc_uq_audio_swap_lr:1;
 	uint8_t	sc_uq_au_inp_async:1;
 	uint8_t	sc_uq_au_no_xu:1;
@@ -477,7 +487,7 @@ static usb_proc_callback_t uaudio_configure_msg;
 
 static int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS);
 static void uaudio_mixer_ctl_free(struct uaudio_softc *);
-static void uaudio_mixer_register_sysctl(struct uaudio_softc *, device_t);
+static void uaudio_mixer_register_sysctl(struct uaudio_softc *, device_t, unsigned);
 static void uaudio_mixer_reload_all(struct uaudio_softc *);
 static void uaudio_mixer_controls_create_ftu(struct uaudio_softc *);
 
@@ -537,7 +547,7 @@ static void	uaudio_mixer_add_ctl(struct uaudio_softc *
 static void	uaudio_mixer_fill_info(struct uaudio_softc *,
 		    struct usb_device *, void *);
 static int	uaudio_mixer_signext(uint8_t, int);
-static void	uaudio_mixer_init(struct uaudio_softc *);
+static void	uaudio_mixer_init(struct uaudio_softc *, unsigned);
 static uint8_t	umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t);
 static struct	umidi_sub_chan *umidi_sub_by_fifo(struct usb_fifo *);
 static void	umidi_start_read(struct usb_fifo *);
@@ -827,6 +837,33 @@ static const STRUCT_USB_HOST_ID __used uaudio_devs[] =
 	 USB_IFACE_SUBCLASS(UISUBCLASS_MIDISTREAM),},
 };
 
+static unsigned
+uaudio_get_child_index_by_dev(struct uaudio_softc *sc, device_t dev)
+{
+	unsigned i;
+
+	for (i = 0; i != UAUDIO_MAX_CHILD; i++) {
+		if (dev == sc->sc_child[i].pcm_device)
+			return (i);
+	}
+	panic("uaudio_get_child_index_dev: Invalid device: %p\n", dev);
+	return (0);
+}
+
+static unsigned
+uaudio_get_child_index_by_chan(struct uaudio_softc *sc, struct uaudio_chan *ch)
+{
+	unsigned i;
+
+	for (i = 0; i != UAUDIO_MAX_CHILD; i++) {
+		if ((sc->sc_play_chan + i) == ch ||
+		    (sc->sc_rec_chan + i) == ch)
+			return (i);
+	}
+	panic("uaudio_get_child_index_by_chan: Invalid chan: %p\n", ch);
+	return (0);
+}
+
 static int
 uaudio_probe(device_t dev)
 {
@@ -915,10 +952,8 @@ uaudio_attach(device_t dev)
 	struct uaudio_softc *sc = device_get_softc(dev);
 	struct usb_interface_descriptor *id;
 	usb_error_t err;
-	device_t child;
+	unsigned i;
 
-	sc->sc_play_chan.priv_sc = sc;
-	sc->sc_rec_chan.priv_sc = sc;
 	sc->sc_udev = uaa->device;
 	sc->sc_mixer_iface_index = uaa->info.bIfaceIndex;
 	sc->sc_mixer_iface_no = uaa->info.bIfaceNum;
@@ -976,57 +1011,63 @@ uaudio_attach(device_t dev)
 	DPRINTF("%d mixer controls\n",
 	    sc->sc_mixer_count);
 
-	if (sc->sc_play_chan.num_alt > 0) {
+	for (i = 0; i != UAUDIO_MAX_CHILD; i++) {
 		uint8_t x;
 
+		if (sc->sc_play_chan[i].num_alt <= 0)
+			break;
+
 		/*
 		 * Need to set a default alternate interface, else
 		 * some USB audio devices might go into an infinte
 		 * re-enumeration loop:
 		 */
 		err = usbd_set_alt_interface_index(sc->sc_udev,
-		    sc->sc_play_chan.usb_alt[0].iface_index,
-		    sc->sc_play_chan.usb_alt[0].iface_alt_index);
+		    sc->sc_play_chan[i].usb_alt[0].iface_index,
+		    sc->sc_play_chan[i].usb_alt[0].iface_alt_index);
 		if (err) {
 			DPRINTF("setting of alternate index failed: %s!\n",
 			    usbd_errstr(err));
 		}
-		for (x = 0; x != sc->sc_play_chan.num_alt; x++) {
-			device_printf(dev, "Play: %d Hz, %d ch, %s format, "
-			    "2x8ms buffer.\n",
-			    sc->sc_play_chan.usb_alt[x].sample_rate,
-			    sc->sc_play_chan.usb_alt[x].channels,
-			    sc->sc_play_chan.usb_alt[x].p_fmt->description);
+		for (x = 0; x != sc->sc_play_chan[i].num_alt; x++) {
+			device_printf(dev, "Play[%u]: %d Hz, %d ch, %s format, "
+			    "2x8ms buffer.\n", i,
+			    sc->sc_play_chan[i].usb_alt[x].sample_rate,
+			    sc->sc_play_chan[i].usb_alt[x].channels,
+			    sc->sc_play_chan[i].usb_alt[x].p_fmt->description);
 		}
-	} else {
-		device_printf(dev, "No playback.\n");
 	}
+	if (i == 0)
+		device_printf(dev, "No playback.\n");
 
-	if (sc->sc_rec_chan.num_alt > 0) {
+	for (i = 0; i != UAUDIO_MAX_CHILD; i++) {
 		uint8_t x;
 
+		if (sc->sc_rec_chan[i].num_alt <= 0)
+			break;
+
 		/*
 		 * Need to set a default alternate interface, else
 		 * some USB audio devices might go into an infinte
 		 * re-enumeration loop:
 		 */
 		err = usbd_set_alt_interface_index(sc->sc_udev,
-		    sc->sc_rec_chan.usb_alt[0].iface_index,
-		    sc->sc_rec_chan.usb_alt[0].iface_alt_index);
+		    sc->sc_rec_chan[i].usb_alt[0].iface_index,
+		    sc->sc_rec_chan[i].usb_alt[0].iface_alt_index);
 		if (err) {
 			DPRINTF("setting of alternate index failed: %s!\n",
 			    usbd_errstr(err));
 		}
-		for (x = 0; x != sc->sc_rec_chan.num_alt; x++) {
-			device_printf(dev, "Record: %d Hz, %d ch, %s format, "
-			    "2x8ms buffer.\n",
-			    sc->sc_rec_chan.usb_alt[x].sample_rate,
-			    sc->sc_rec_chan.usb_alt[x].channels,
-			    sc->sc_rec_chan.usb_alt[x].p_fmt->description);
+		for (x = 0; x != sc->sc_rec_chan[i].num_alt; x++) {
+			device_printf(dev, "Record[%u]: %d Hz, %d ch, %s format, "
+			    "2x8ms buffer.\n", i,
+			    sc->sc_rec_chan[i].usb_alt[x].sample_rate,
+			    sc->sc_rec_chan[i].usb_alt[x].channels,
+			    sc->sc_rec_chan[i].usb_alt[x].p_fmt->description);
 		}
-	} else {
-		device_printf(dev, "No recording.\n");
 	}
+	if (i == 0)
+		device_printf(dev, "No recording.\n");
 
 	if (sc->sc_midi_chan.valid == 0) {
 		if (usbd_lookup_id_by_uaa(uaudio_vendor_midi,
@@ -1058,16 +1099,20 @@ uaudio_attach(device_t dev)
 	 * Only attach a PCM device if we have a playback, recording
 	 * or mixer device present:
 	 */
-	if (sc->sc_play_chan.num_alt > 0 ||
-	    sc->sc_rec_chan.num_alt > 0 ||
-	    sc->sc_mix_info) {
-		child = device_add_child(dev, "pcm", -1);
+	for (i = 0; i != UAUDIO_MAX_CHILD; i++) {
+		if (sc->sc_play_chan[i].num_alt <= 0 &&
+		    sc->sc_rec_chan[i].num_alt <= 0 &&
+		    sc->sc_child[i].mix_info == 0)
+			continue;
+		sc->sc_child[i].pcm_device =
+		    device_add_child(dev, "pcm", -1);
 
-		if (child == NULL) {
+		if (sc->sc_child[i].pcm_device == NULL) {
 			DPRINTF("out of memory\n");
 			goto detach;
 		}
-		device_set_ivars(child, &sc->sc_sndcard_func);
+		device_set_ivars(sc->sc_child[i].pcm_device,
+		    &sc->sc_sndcard_func);
 	}
 
 	if (bus_generic_attach(dev)) {
@@ -1106,16 +1151,17 @@ int
 uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_class)
 {
 	struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
+	unsigned i = uaudio_get_child_index_by_dev(sc, dev);
 	char status[SND_STATUSLEN];
 
-	uaudio_mixer_init(sc);
+	uaudio_mixer_init(sc, i);
 
 	if (sc->sc_uq_audio_swap_lr) {
 		DPRINTF("hardware has swapped left and right\n");
 		/* uaudio_pcm_setflags(dev, SD_F_PSWAPLR); */
 	}
-	if (sc->sc_play_chan.num_alt > 0 &&
-	    (sc->sc_mix_info & SOUND_MASK_PCM) == 0) {
+	if (sc->sc_play_chan[i].num_alt > 0 &&
+	    (sc->sc_child[i].mix_info & SOUND_MASK_PCM) == 0) {
 
 		DPRINTF("emulating master volume\n");
 
@@ -1131,34 +1177,39 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_cla
 	}
 	if (mixer_init(dev, mixer_class, sc))
 		goto detach;
-	sc->sc_mixer_init = 1;
+	sc->sc_child[i].mixer_init = 1;
 
 	mixer_hwvol_init(dev);
 
 	snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
 
 	if (pcm_register(dev, sc,
-	    (sc->sc_play_chan.num_alt > 0) ? 1 : 0,
-	    (sc->sc_rec_chan.num_alt > 0) ? 1 : 0)) {
+	    (sc->sc_play_chan[i].num_alt > 0) ? 1 : 0,
+	    (sc->sc_rec_chan[i].num_alt > 0) ? 1 : 0)) {
 		goto detach;
 	}
 
 	uaudio_pcm_setflags(dev, SD_F_MPSAFE);
-	sc->sc_pcm_registered = 1;
+	sc->sc_child[i].pcm_registered = 1;
 
-	if (sc->sc_play_chan.num_alt > 0) {
-		pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc);
+	if (sc->sc_play_chan[i].num_alt > 0) {
+		sc->sc_play_chan[i].priv_sc = sc;
+		pcm_addchan(dev, PCMDIR_PLAY, chan_class,
+		    &sc->sc_play_chan[i]);
 	}
-	if (sc->sc_rec_chan.num_alt > 0) {
-		pcm_addchan(dev, PCMDIR_REC, chan_class, sc);
+
+	if (sc->sc_rec_chan[i].num_alt > 0) {
+		sc->sc_rec_chan[i].priv_sc = sc;
+		pcm_addchan(dev, PCMDIR_REC, chan_class,
+		    &sc->sc_rec_chan[i]);
 	}
 	pcm_setstatus(dev, status);
 
-	uaudio_mixer_register_sysctl(sc, dev);
+	uaudio_mixer_register_sysctl(sc, dev, i);
 
 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
-	    "feedback_rate", CTLFLAG_RD, &sc->sc_play_chan.feedback_rate,
+	    "feedback_rate", CTLFLAG_RD, &sc->sc_play_chan[i].feedback_rate,
 	    0, "Feedback sample rate in Hz");
 
 	return (0);			/* success */
@@ -1172,18 +1223,15 @@ int
 uaudio_detach_sub(device_t dev)
 {
 	struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
+	unsigned i = uaudio_get_child_index_by_dev(sc, dev);
 	int error = 0;
 
-	/* disable S/PDIF output, if any */
-	(void) sc->sc_set_spdif_fn(sc, 0);
-
 repeat:
-	if (sc->sc_pcm_registered) {
+	if (sc->sc_child[i].pcm_registered) {
 		error = pcm_unregister(dev);
 	} else {
-		if (sc->sc_mixer_init) {
+		if (sc->sc_child[i].mixer_init)
 			error = mixer_uninit(dev);
-		}
 	}
 
 	if (error) {
@@ -1198,6 +1246,7 @@ static int
 uaudio_detach(device_t dev)
 {
 	struct uaudio_softc *sc = device_get_softc(dev);
+	unsigned i;
 
 	/*
 	 * Stop USB transfers early so that any audio applications
@@ -1205,14 +1254,18 @@ uaudio_detach(device_t dev)
 	 * any.
 	 */
 	usb_proc_explore_lock(sc->sc_udev);
-	sc->sc_play_chan.operation = CHAN_OP_DRAIN;
-	sc->sc_rec_chan.operation = CHAN_OP_DRAIN;
+	for (i = 0; i != UAUDIO_MAX_CHILD; i++) {
+		sc->sc_play_chan[i].operation = CHAN_OP_DRAIN;
+		sc->sc_rec_chan[i].operation = CHAN_OP_DRAIN;
+	}
 	usb_proc_explore_mwait(sc->sc_udev,
 	    &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
 	usb_proc_explore_unlock(sc->sc_udev);
 
-	usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1);
-	usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1);
+	for (i = 0; i != UAUDIO_MAX_CHILD; i++) {
+		usbd_transfer_unsetup(sc->sc_play_chan[i].xfer, UAUDIO_NCHANBUFS + 1);
+		usbd_transfer_unsetup(sc->sc_rec_chan[i].xfer, UAUDIO_NCHANBUFS + 1);
+	}
 
 	uaudio_hid_detach(sc);
 
@@ -1228,6 +1281,9 @@ uaudio_detach(device_t dev)
 
 	uaudio_mixer_ctl_free(sc);
 
+	/* disable S/PDIF output, if any */
+	(void) sc->sc_set_spdif_fn(sc, 0);
+
 	return (0);
 }
 
@@ -1418,10 +1474,13 @@ static void
 uaudio_configure_msg(struct usb_proc_msg *pm)
 {
 	struct uaudio_softc *sc = ((struct uaudio_configure_msg *)pm)->sc;
+	unsigned i;
 
 	usb_proc_explore_unlock(sc->sc_udev);
-	uaudio_configure_msg_sub(sc, &sc->sc_play_chan, PCMDIR_PLAY);
-	uaudio_configure_msg_sub(sc, &sc->sc_rec_chan, PCMDIR_REC);
+	for (i = 0; i != UAUDIO_MAX_CHILD; i++) {
+		uaudio_configure_msg_sub(sc, &sc->sc_play_chan[i], PCMDIR_PLAY);
+		uaudio_configure_msg_sub(sc, &sc->sc_rec_chan[i], PCMDIR_REC);
+	}
 	usb_proc_explore_lock(sc->sc_udev);
 }
 
@@ -1587,6 +1646,22 @@ uaudio20_check_rate(struct usb_device *udev, uint8_t i
 	return (USB_ERR_INVAL);
 }
 
+static struct uaudio_chan *
+uaudio_get_chan(struct uaudio_softc *sc, struct uaudio_chan *chan,
+    uint8_t iface_index)
+{
+	unsigned i;
+
+	for (i = 0; i != UAUDIO_MAX_CHILD; i++, chan++) {
+		if (chan->num_alt == 0) {
+			chan->iface_index = iface_index;
+			return (chan);
+		} else if (chan->iface_index == iface_index)
+			return (chan);
+	}
+	return (NULL);
+}
+
 static void
 uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
     uint32_t rate, uint8_t channels, uint8_t bit_resolution)
@@ -1894,8 +1969,12 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, str
 			}
 		}
 
-		chan = (ep_dir == UE_DIR_IN) ?
-		    &sc->sc_rec_chan : &sc->sc_play_chan;
+		chan = uaudio_get_chan(sc, (ep_dir == UE_DIR_OUT) ? &sc->sc_play_chan[0] :
+		    &sc->sc_rec_chan[0], curidx);
+		if (chan == NULL) {
+			DPRINTF("More than %d sub devices. (skipped)\n", UAUDIO_MAX_CHILD);
+			goto next_ep;
+		}
 
 		if (usbd_get_iface(udev, curidx) == NULL) {
 			DPRINTF("Interface is not valid\n");
@@ -1914,8 +1993,8 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, str
 		uaudio_chan_dump_ep_desc(ed1);
 #endif
 		DPRINTF("Sample rate = %dHz, channels = %d, "
-		    "bits = %d, format = %s\n", rate, channels,
-		    bit_resolution, p_fmt->description);
+		    "bits = %d, format = %s, ep 0x%02x, chan %p\n", rate, channels,
+		    bit_resolution, p_fmt->description, ed1->bEndpointAddress, chan);
 
 		chan_alt->sample_rate = rate;
 		chan_alt->p_asf1d = asf1d;
@@ -2099,12 +2178,15 @@ uaudio_chan_play_sync_callback(struct usb_xfer *xfer, 
 	uint64_t sample_rate;
 	uint8_t buf[4];
 	uint64_t temp;
+	unsigned i;
 	int len;
 	int actlen;
 	int nframes;
 
 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
 
+	i = uaudio_get_child_index_by_chan(ch->priv_sc, ch);
+
 	switch (USB_GET_STATE(xfer)) {
 	case USB_ST_TRANSFERRED:
 
@@ -2155,7 +2237,7 @@ uaudio_chan_play_sync_callback(struct usb_xfer *xfer, 
 		 * Use feedback value as fallback when there is no
 		 * recording channel:
 		 */
-		if (ch->priv_sc->sc_rec_chan.num_alt == 0) {
+		if (ch->priv_sc->sc_rec_chan[i].num_alt == 0) {
 			int32_t jitter_max = howmany(sample_rate, 16000);
 
 			/*
@@ -2181,7 +2263,7 @@ uaudio_chan_play_sync_callback(struct usb_xfer *xfer, 
 		 * source of jitter information to save some
 		 * isochronous bandwidth:
 		 */
-		if (ch->priv_sc->sc_rec_chan.num_alt != 0 &&
+		if (ch->priv_sc->sc_rec_chan[i].num_alt != 0 &&
 		    uaudio_debug == 0)
 			break;
 		usbd_xfer_set_frames(xfer, 1);
@@ -2212,6 +2294,7 @@ uaudio_chan_play_callback(struct usb_xfer *xfer, usb_e
 	uint32_t blockcount;
 	uint32_t n;
 	uint32_t offset;
+	unsigned i;
 	int sample_size;
 	int actlen;
 	int sumlen;
@@ -2221,10 +2304,12 @@ uaudio_chan_play_callback(struct usb_xfer *xfer, usb_e
 		return;
 	}
 
-	/* check if there is a record channel */
-	if (ch->priv_sc->sc_rec_chan.num_alt > 0)
-		ch_rec = &ch->priv_sc->sc_rec_chan;
-	else
+	i = uaudio_get_child_index_by_chan(ch->priv_sc, ch);
+
+	/* check if there is a valid record channel */
+	ch_rec = ch->priv_sc->sc_rec_chan + i;
+
+	if (ch_rec->num_alt == 0)
 		ch_rec = NULL;
 
 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
@@ -2488,11 +2573,9 @@ tr_setup:
 }
 
 void   *
-uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
+uaudio_chan_init(struct uaudio_chan *ch, struct snd_dbuf *b,
     struct pcm_channel *c, int dir)
 {
-	struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ?
-	    &sc->sc_play_chan : &sc->sc_rec_chan);
 	uint32_t buf_size;
 	uint8_t x;
 
@@ -2683,36 +2766,36 @@ uaudio_chan_start_sub(struct uaudio_chan *ch)
 }
 
 static int
-uaudio_chan_need_both(struct uaudio_softc *sc)
+uaudio_chan_need_both(struct uaudio_chan *pchan, struct uaudio_chan *rchan)
 {
-	return (sc->sc_play_chan.num_alt > 0 &&
-	    sc->sc_play_chan.running != 0 &&
-	    uaudio_chan_is_async(&sc->sc_play_chan,
-	    sc->sc_play_chan.set_alt) != 0 &&
-	    sc->sc_rec_chan.num_alt > 0 &&
-	    sc->sc_rec_chan.running == 0);
+	return (pchan->num_alt > 0 &&
+	    pchan->running != 0 &&
+	    uaudio_chan_is_async(pchan, pchan->set_alt) != 0 &&
+	    rchan->num_alt > 0 &&
+	    rchan->running == 0);
 }
 
 static int
-uaudio_chan_need_none(struct uaudio_softc *sc)
+uaudio_chan_need_none(struct uaudio_chan *pchan, struct uaudio_chan *rchan)
 {
-	return (sc->sc_play_chan.num_alt > 0 &&
-	    sc->sc_play_chan.running == 0 &&
-	    sc->sc_rec_chan.num_alt > 0 &&
-	    sc->sc_rec_chan.running == 0);
+	return (pchan->num_alt > 0 &&
+	    pchan->running == 0 &&
+	    rchan->num_alt > 0 &&
+	    rchan->running == 0);
 }
 
 void
 uaudio_chan_start(struct uaudio_chan *ch)
 {
 	struct uaudio_softc *sc = ch->priv_sc;
+	unsigned i = uaudio_get_child_index_by_chan(sc, ch);
 
 	/* make operation atomic */
 	usb_proc_explore_lock(sc->sc_udev);
 
 	/* check if not running */
 	if (ch->running == 0) {
-	  	uint32_t temp;
+		uint32_t temp;
 
 		/* get current buffer size */
 		temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
@@ -2725,13 +2808,15 @@ uaudio_chan_start(struct uaudio_chan *ch)
 		ch->end = ch->buf + temp;
 		ch->cur = ch->buf;
 
-		if (uaudio_chan_need_both(sc)) {
+		if (uaudio_chan_need_both(
+		    &sc->sc_play_chan[i],
+		    &sc->sc_rec_chan[i])) {
 			/*
 			 * Start both endpoints because of need for
 			 * jitter information:
 			 */
-			uaudio_chan_start_sub(&sc->sc_rec_chan);
-			uaudio_chan_start_sub(&sc->sc_play_chan);
+			uaudio_chan_start_sub(&sc->sc_rec_chan[i]);
+			uaudio_chan_start_sub(&sc->sc_play_chan[i]);
 		} else {
 			uaudio_chan_start_sub(ch);
 		}
@@ -2769,6 +2854,7 @@ void
 uaudio_chan_stop(struct uaudio_chan *ch)
 {
 	struct uaudio_softc *sc = ch->priv_sc;
+	unsigned i = uaudio_get_child_index_by_chan(sc, ch);
 
 	/* make operation atomic */
 	usb_proc_explore_lock(sc->sc_udev);
@@ -2778,18 +2864,22 @@ uaudio_chan_stop(struct uaudio_chan *ch)
 		/* clear running flag */
 		ch->running = 0;
 
-		if (uaudio_chan_need_both(sc)) {
+		if (uaudio_chan_need_both(
+		    &sc->sc_play_chan[i],
+		    &sc->sc_rec_chan[i])) {
 			/*
 			 * Leave the endpoints running because we need
 			 * information about jitter!
 			 */
-		} else if (uaudio_chan_need_none(sc)) {
+		} else if (uaudio_chan_need_none(
+		    &sc->sc_play_chan[i],
+		    &sc->sc_rec_chan[i])) {
 			/*
 			 * Stop both endpoints in case the one was used for
 			 * jitter information:
 			 */
-			uaudio_chan_stop_sub(&sc->sc_rec_chan);
-			uaudio_chan_stop_sub(&sc->sc_play_chan);
+			uaudio_chan_stop_sub(&sc->sc_rec_chan[i]);
+			uaudio_chan_stop_sub(&sc->sc_play_chan[i]);
 		} else {
 			uaudio_chan_stop_sub(ch);
 		}
@@ -2816,12 +2906,12 @@ uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS)
 	sc = (struct uaudio_softc *)oidp->oid_arg1;
 	hint = oidp->oid_arg2;
 
-	if (sc->sc_mixer_lock == NULL)
+	if (sc->sc_child[0].mixer_lock == NULL)
 		return (ENXIO);
 
 	/* lookup mixer node */
 
-	mtx_lock(sc->sc_mixer_lock);
+	mtx_lock(sc->sc_child[0].mixer_lock);
 	for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) {
 		for (chan = 0; chan != (int)pmc->nchan; chan++) {
 			if (pmc->wValue[chan] != -1 &&
@@ -2832,7 +2922,7 @@ uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS)
 		}
 	}
 found:
-	mtx_unlock(sc->sc_mixer_lock);
+	mtx_unlock(sc->sc_child[0].mixer_lock);
 
 	error = sysctl_handle_int(oidp, &temp, 0, req);
 	if (error != 0 || req->newptr == NULL)
@@ -2840,7 +2930,7 @@ found:
 
 	/* update mixer value */
 
-	mtx_lock(sc->sc_mixer_lock);
+	mtx_lock(sc->sc_child[0].mixer_lock);
 	if (pmc != NULL &&
 	    temp >= pmc->minval &&
 	    temp <= pmc->maxval) {
@@ -2851,7 +2941,7 @@ found:
 		/* start the transfer, if not already started */
 		usbd_transfer_start(sc->sc_mixer_xfer[0]);
 	}
-	mtx_unlock(sc->sc_mixer_lock);
+	mtx_unlock(sc->sc_child[0].mixer_lock);
 
 	return (0);
 }
@@ -2868,7 +2958,8 @@ uaudio_mixer_ctl_free(struct uaudio_softc *sc)
 }
 
 static void
-uaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev)
+uaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev,
+    unsigned index)
 {
 	struct uaudio_mixer_node *pmc;
 	struct sysctl_oid *mixer_tree;
@@ -2877,6 +2968,9 @@ uaudio_mixer_register_sysctl(struct uaudio_softc *sc, 
 	int chan;
 	int n;
 
+	if (index != 0)
+		return;
+
 	mixer_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "mixer",
 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
@@ -3082,10 +3176,10 @@ uaudio_mixer_reload_all(struct uaudio_softc *sc)
 	struct uaudio_mixer_node *pmc;
 	int chan;
 
-	if (sc->sc_mixer_lock == NULL)
+	if (sc->sc_child[0].mixer_lock == NULL)
 		return;
 
-	mtx_lock(sc->sc_mixer_lock);
+	mtx_lock(sc->sc_child[0].mixer_lock);
 	for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) {
 		/* use reset defaults for non-oss controlled settings */
 		if (pmc->ctl == SOUND_MIXER_NRDEVICES)
@@ -3097,7 +3191,7 @@ uaudio_mixer_reload_all(struct uaudio_softc *sc)
 
 	/* start HID volume keys, if any */
 	usbd_transfer_start(sc->sc_hid.xfer[0]);
-	mtx_unlock(sc->sc_mixer_lock);
+	mtx_unlock(sc->sc_child[0].mixer_lock);
 }
 
 static void
@@ -5291,11 +5385,13 @@ uaudio_mixer_ctl_set(struct uaudio_softc *sc, struct u
 }
 
 static void
-uaudio_mixer_init(struct uaudio_softc *sc)
+uaudio_mixer_init(struct uaudio_softc *sc, unsigned index)
 {
 	struct uaudio_mixer_node *mc;
 	int32_t i;
 
+	if (index != 0)
+		return;
 	for (mc = sc->sc_mixer_root; mc; mc = mc->next) {
 
 		if (mc->ctl != SOUND_MIXER_NRDEVICES) {
@@ -5303,7 +5399,7 @@ uaudio_mixer_init(struct uaudio_softc *sc)
 			 * Set device mask bits. See
 			 * /usr/include/machine/soundcard.h
 			 */
-			sc->sc_mix_info |= 1U << mc->ctl;
+			sc->sc_child[index].mix_info |= 1U << mc->ctl;
 		}
 		if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
 		    (mc->type == MIX_SELECTOR)) {
@@ -5311,7 +5407,7 @@ uaudio_mixer_init(struct uaudio_softc *sc)
 			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
 				if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES)
 					continue;
-				sc->sc_recsrc_info |= 1U << mc->slctrtype[i - 1];
+				sc->sc_child[index].recsrc_info |= 1U << mc->slctrtype[i - 1];
 			}
 		}
 	}
@@ -5320,50 +5416,57 @@ uaudio_mixer_init(struct uaudio_softc *sc)
 int
 uaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m)
 {
-	DPRINTF("\n");
+	unsigned i = uaudio_get_child_index_by_dev(sc, mix_get_dev(m));
 
-	sc->sc_mixer_lock = mixer_get_lock(m);
-	sc->sc_mixer_dev = m;
+	DPRINTF("child=%u\n", i);
 
-	if (usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index,
+	sc->sc_child[i].mixer_lock = mixer_get_lock(m);
+	sc->sc_child[i].mixer_dev = m;
+
+	if (i == 0 &&
+	    usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index,
 	    sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc,
-	    sc->sc_mixer_lock)) {
-		DPRINTFN(0, "could not allocate USB "
-		    "transfer for audio mixer!\n");
+	    sc->sc_child[i].mixer_lock)) {
+		DPRINTFN(0, "could not allocate USB transfer for mixer!\n");
 		return (ENOMEM);
 	}
 
-	if (sc->sc_play_chan.num_alt > 0 &&
-	    (sc->sc_mix_info & SOUND_MASK_VOLUME) == 0) {
+	if (sc->sc_play_chan[i].num_alt > 0 &&
+	    (sc->sc_child[i].mix_info & SOUND_MASK_VOLUME) == 0) {
 		mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM);
 		mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
 	}
-	mix_setdevs(m, sc->sc_mix_info);
-	mix_setrecdevs(m, sc->sc_recsrc_info);
+	mix_setdevs(m, sc->sc_child[i].mix_info);
+	mix_setrecdevs(m, sc->sc_child[i].recsrc_info);
 	return (0);
 }
 
 int
-uaudio_mixer_uninit_sub(struct uaudio_softc *sc)
+uaudio_mixer_uninit_sub(struct uaudio_softc *sc, struct snd_mixer *m)
 {
-	DPRINTF("\n");
+  	unsigned index = uaudio_get_child_index_by_dev(sc, mix_get_dev(m));
 
-	usbd_transfer_unsetup(sc->sc_mixer_xfer, 1);
+	DPRINTF("child=%u\n", index);
 
-	sc->sc_mixer_lock = NULL;
+	if (index == 0)
+		usbd_transfer_unsetup(sc->sc_mixer_xfer, 1);
 
+	sc->sc_child[index].mixer_lock = NULL;
+
 	return (0);
 }
 
 void
-uaudio_mixer_set(struct uaudio_softc *sc, unsigned type,
-    unsigned left, unsigned right)
+uaudio_mixer_set(struct uaudio_softc *sc, struct snd_mixer *m,
+    unsigned type, unsigned left, unsigned right)
 {
+    	unsigned index = uaudio_get_child_index_by_dev(sc, mix_get_dev(m));
 	struct uaudio_mixer_node *mc;
 	int chan;
 
+	if (index != 0)
+		return;
 	for (mc = sc->sc_mixer_root; mc != NULL; mc = mc->next) {
-
 		if (mc->ctl == type) {
 			for (chan = 0; chan < mc->nchan; chan++) {
 				uaudio_mixer_ctl_set(sc, mc, chan,
@@ -5374,13 +5477,16 @@ uaudio_mixer_set(struct uaudio_softc *sc, unsigned typ
 }
 
 uint32_t
-uaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src)
+uaudio_mixer_setrecsrc(struct uaudio_softc *sc, struct snd_mixer *m, uint32_t src)
 {
+      	unsigned index = uaudio_get_child_index_by_dev(sc, mix_get_dev(m));
 	struct uaudio_mixer_node *mc;
 	uint32_t mask;
 	uint32_t temp;
 	int32_t i;
 
+	if (index != 0)
+		return (0);
 	for (mc = sc->sc_mixer_root; mc; mc = mc->next) {
 
 		if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
@@ -6008,7 +6114,7 @@ uaudio_hid_rx_callback(struct usb_xfer *xfer, usb_erro
 			id = 0;
 		}
 
-		m = sc->sc_mixer_dev;
+		m = sc->sc_child[0].mixer_dev;
 
 		if ((sc->sc_hid.flags & UAUDIO_HID_HAS_MUTE) &&
 		    (sc->sc_hid.mute_id == id) &&
@@ -6073,7 +6179,7 @@ uaudio_hid_probe(struct uaudio_softc *sc,
 	if (!(sc->sc_hid.flags & UAUDIO_HID_VALID))
 		return (-1);
 
-	if (sc->sc_mixer_lock == NULL)
+	if (sc->sc_child[0].mixer_lock == NULL)
 		return (-1);
 
 	/* Get HID descriptor */
@@ -6134,7 +6240,7 @@ uaudio_hid_probe(struct uaudio_softc *sc,
 	/* 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);
+	    sc, sc->sc_child[0].mixer_lock);
 	if (error) {
 		DPRINTF("error=%s\n", usbd_errstr(error));
 		return (-1);

Modified: head/sys/dev/sound/usb/uaudio.h
==============================================================================
--- head/sys/dev/sound/usb/uaudio.h	Mon Mar 30 16:32:55 2020	(r359445)
+++ head/sys/dev/sound/usb/uaudio.h	Mon Mar 30 16:50:32 2020	(r359446)
@@ -41,7 +41,7 @@ struct pcm_channel;
 extern int	uaudio_attach_sub(device_t dev, kobj_class_t mixer_class,
 		    kobj_class_t chan_class);
 extern int	uaudio_detach_sub(device_t dev);
-extern void	*uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
+extern void	*uaudio_chan_init(struct uaudio_chan *ch, struct snd_dbuf *b,
 		    struct pcm_channel *c, int dir);
 extern int	uaudio_chan_free(struct uaudio_chan *ch);
 extern int	uaudio_chan_set_param_blocksize(struct uaudio_chan *ch,
@@ -58,12 +58,12 @@ extern int	uaudio_chan_set_param_format(struct uaudio_
 		    uint32_t format);
 extern void	uaudio_chan_start(struct uaudio_chan *ch);
 extern void	uaudio_chan_stop(struct uaudio_chan *ch);
-extern int	uaudio_mixer_init_sub(struct uaudio_softc *sc,
-		    struct snd_mixer *m);
-extern int	uaudio_mixer_uninit_sub(struct uaudio_softc *sc);
-extern void	uaudio_mixer_set(struct uaudio_softc *sc, unsigned type,
-		    unsigned left, unsigned right);
-extern uint32_t	uaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src);
+extern int	uaudio_mixer_init_sub(struct uaudio_softc *, struct snd_mixer *);
+extern int	uaudio_mixer_uninit_sub(struct uaudio_softc *, struct snd_mixer *);
+extern void	uaudio_mixer_set(struct uaudio_softc *, struct snd_mixer *,
+		    unsigned type, unsigned left, unsigned right);
+extern uint32_t	uaudio_mixer_setrecsrc(struct uaudio_softc *, struct snd_mixer *,
+		    uint32_t src);
 
 int	uaudio_get_vendor(device_t dev);
 int	uaudio_get_product(device_t dev);

Modified: head/sys/dev/sound/usb/uaudio_pcm.c
==============================================================================
--- head/sys/dev/sound/usb/uaudio_pcm.c	Mon Mar 30 16:32:55 2020	(r359445)
+++ head/sys/dev/sound/usb/uaudio_pcm.c	Mon Mar 30 16:50:32 2020	(r359446)
@@ -146,7 +146,7 @@ ua_mixer_set(struct snd_mixer *m, unsigned type, unsig
 		do_unlock = 1;
 		mtx_lock(mtx);
 	}
-	uaudio_mixer_set(mix_getdevinfo(m), type, left, right);
+	uaudio_mixer_set(mix_getdevinfo(m), m, type, left, right);
 	if (do_unlock) {
 		mtx_unlock(mtx);
 	}
@@ -166,7 +166,7 @@ ua_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
 		do_unlock = 1;
 		mtx_lock(mtx);
 	}
-	retval = uaudio_mixer_setrecsrc(mix_getdevinfo(m), src);
+	retval = uaudio_mixer_setrecsrc(mix_getdevinfo(m), m, src);
 	if (do_unlock) {
 		mtx_unlock(mtx);
 	}
@@ -176,7 +176,7 @@ ua_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
 static int
 ua_mixer_uninit(struct snd_mixer *m)
 {
-	return (uaudio_mixer_uninit_sub(mix_getdevinfo(m)));
+	return (uaudio_mixer_uninit_sub(mix_getdevinfo(m), m));
 }
 
 static kobj_method_t ua_mixer_methods[] = {


More information about the svn-src-all mailing list