Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 8 Nov 2009 21:00:50 +0000 (UTC)
From:      Andrew Thompson <thompsa@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r199060 - head/sys/dev/sound/usb
Message-ID:  <200911082100.nA8L0o9w097805@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: thompsa
Date: Sun Nov  8 21:00:50 2009
New Revision: 199060
URL: http://svn.freebsd.org/changeset/base/199060

Log:
  Improve support for High-speed USB audio devices.
  - fix issues regarding the mixer, where the interface number was not set in
    time.
  - fix wrong use of resolution parameter.
  
  Submitted by:	Hans Petter Selasky

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

Modified: head/sys/dev/sound/usb/uaudio.c
==============================================================================
--- head/sys/dev/sound/usb/uaudio.c	Sun Nov  8 20:54:03 2009	(r199059)
+++ head/sys/dev/sound/usb/uaudio.c	Sun Nov  8 21:00:50 2009	(r199060)
@@ -105,10 +105,9 @@ SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, def
     &uaudio_default_channels, 0, "uaudio default sample channels");
 #endif
 
-#define	UAUDIO_MINFRAMES       16	/* must be factor of 8 due HS-USB */
+#define	UAUDIO_NFRAMES		64	/* must be factor of 8 due HS-USB */
 #define	UAUDIO_NCHANBUFS        2	/* number of outstanding request */
 #define	UAUDIO_RECURSE_LIMIT   24	/* rounds */
-#define	UAUDIO_MINFRAMES_ALIGN(x) ((x) & ~(UAUDIO_MINFRAMES - 1))
 
 #define	MAKE_WORD(h,l) (((h) << 8) | (l))
 #define	BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1)
@@ -119,7 +118,7 @@ struct uaudio_mixer_node {
 	int32_t	maxval;
 #define	MIX_MAX_CHAN 8
 	int32_t	wValue[MIX_MAX_CHAN];	/* using nchan */
-	uint32_t delta;
+	uint32_t mod;		/* modulus */
 	uint32_t mul;
 	uint32_t ctl;
 
@@ -169,7 +168,7 @@ struct uaudio_chan {
 					 * buffer */
 
 	uint32_t intr_size;		/* in bytes */
-	uint32_t block_size;
+	uint32_t intr_frames;		/* in units */
 	uint32_t sample_rate;
 	uint32_t format;
 	uint32_t pcm_format[2];
@@ -410,7 +409,7 @@ static const struct usb_config
 		.endpoint = UE_ADDR_ANY,
 		.direction = UE_DIR_IN,
 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
-		.frames = UAUDIO_MINFRAMES,
+		.frames = UAUDIO_NFRAMES,
 		.flags = {.short_xfer_ok = 1,},
 		.callback = &uaudio_chan_record_callback,
 	},
@@ -420,7 +419,7 @@ static const struct usb_config
 		.endpoint = UE_ADDR_ANY,
 		.direction = UE_DIR_IN,
 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
-		.frames = UAUDIO_MINFRAMES,
+		.frames = UAUDIO_NFRAMES,
 		.flags = {.short_xfer_ok = 1,},
 		.callback = &uaudio_chan_record_callback,
 	},
@@ -433,7 +432,7 @@ static const struct usb_config
 		.endpoint = UE_ADDR_ANY,
 		.direction = UE_DIR_OUT,
 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
-		.frames = UAUDIO_MINFRAMES,
+		.frames = UAUDIO_NFRAMES,
 		.flags = {.short_xfer_ok = 1,},
 		.callback = &uaudio_chan_play_callback,
 	},
@@ -443,7 +442,7 @@ static const struct usb_config
 		.endpoint = UE_ADDR_ANY,
 		.direction = UE_DIR_OUT,
 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
-		.frames = UAUDIO_MINFRAMES,
+		.frames = UAUDIO_NFRAMES,
 		.flags = {.short_xfer_ok = 1,},
 		.callback = &uaudio_chan_play_callback,
 	},
@@ -506,7 +505,6 @@ static const struct usb_config
 		.endpoint = 0x00,	/* Control pipe */
 		.direction = UE_DIR_ANY,
 		.bufsize = sizeof(struct usb_device_request),
-		.flags = {},
 		.callback = &umidi_write_clear_stall_callback,
 		.timeout = 1000,	/* 1 second */
 		.interval = 50,	/* 50ms */
@@ -517,7 +515,6 @@ static const struct usb_config
 		.endpoint = 0x00,	/* Control pipe */
 		.direction = UE_DIR_ANY,
 		.bufsize = sizeof(struct usb_device_request),
-		.flags = {},
 		.callback = &umidi_read_clear_stall_callback,
 		.timeout = 1000,	/* 1 second */
 		.interval = 50,	/* 50ms */
@@ -577,6 +574,8 @@ uaudio_attach(device_t dev)
 	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;
 
 	if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR))
 		sc->sc_uq_audio_swap_lr = 1;
@@ -600,9 +599,6 @@ uaudio_attach(device_t dev)
 
 	uaudio_mixer_fill_info(sc, uaa->device, id);
 
-	sc->sc_mixer_iface_index = uaa->info.bIfaceIndex;
-	sc->sc_mixer_iface_no = uaa->info.bIfaceNum;
-
 	DPRINTF("audio rev %d.%02x\n",
 	    sc->sc_audio_rev >> 8,
 	    sc->sc_audio_rev & 0xff);
@@ -1119,34 +1115,11 @@ done:
  * next audio transfer.
  */
 static void
-uaudio_setup_blockcount(struct uaudio_chan *ch, usb_frcount_t max_frames,
+uaudio_setup_blockcount(struct uaudio_chan *ch,
     uint32_t *total, uint32_t *blockcount)
 {
-	uint32_t temp;
-	uint32_t isiz;
-
-	/* allow dynamic sizing of play buffer */
-	isiz = ch->intr_size;
-
-	/* allow dynamic sizing of play buffer */
-	temp = isiz / ch->bytes_per_frame;
-
-	/* align units */
-	temp = UAUDIO_MINFRAMES_ALIGN(temp);
-
-	/* range check - min */
-	if (temp == 0)
-		temp = UAUDIO_MINFRAMES;
-
-	/* range check - max */
-	if (temp > max_frames)
-		temp = max_frames;
-
-	/* store blockcount */
-	*blockcount = temp;
-
-	/* compute the total length */
-	*total = temp * ch->bytes_per_frame;
+	*total = ch->intr_size;
+	*blockcount = ch->intr_frames;
 }
 
 static void
@@ -1162,8 +1135,12 @@ uaudio_chan_play_callback(struct usb_xfe
 
 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
 
-	uaudio_setup_blockcount(ch, usbd_xfer_max_frames(xfer),
-		&total, &blockcount);
+	uaudio_setup_blockcount(ch, &total, &blockcount);
+
+	if (ch->end == ch->start) {
+		DPRINTF("no buffer!\n");
+		return;
+	}
 
 	switch (USB_GET_STATE(xfer)) {
 	case USB_ST_TRANSFERRED:
@@ -1187,10 +1164,6 @@ tr_transferred:
 		for (n = 0; n != blockcount; n++)
 			usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame);
 
-		if (ch->end == ch->start) {
-			DPRINTF("no buffer!\n");
-			break;
-		}
 		DPRINTFN(6, "transfer %d bytes\n", total);
 
 		offset = 0;
@@ -1235,17 +1208,23 @@ uaudio_chan_record_callback(struct usb_x
 	uint32_t blockcount;
 	uint32_t offset0;
 	uint32_t offset1;
+	uint32_t mfl;
 	int len;
-	int actlen, nframes;
+	int actlen;
+	int nframes;
 
 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
+	mfl = usbd_xfer_max_framelen(xfer);
 
-	uaudio_setup_blockcount(ch, usbd_xfer_max_frames(xfer),
-		&total, &blockcount);
+	uaudio_setup_blockcount(ch, &total, &blockcount);
+
+	if (ch->end == ch->start) {
+		DPRINTF("no buffer!\n");
+		return;
+	}
 
 	switch (USB_GET_STATE(xfer)) {
 	case USB_ST_TRANSFERRED:
-tr_transferred:
 		if (actlen < total) {
 			DPRINTF("short transfer, "
 			    "%d of %d bytes\n", actlen, total);
@@ -1254,11 +1233,11 @@ tr_transferred:
 		}
 
 		offset0 = 0;
+		pc = usbd_xfer_get_frame(xfer, 0);
 
 		for (n = 0; n != nframes; n++) {
 
 			offset1 = offset0;
-			pc = usbd_xfer_get_frame(xfer, 0);
 			len = usbd_xfer_frame_len(xfer, n);
 
 			while (len > 0) {
@@ -1279,36 +1258,26 @@ tr_transferred:
 				}
 			}
 
-			offset0 += ch->bytes_per_frame;
+			offset0 += mfl;
 		}
 
 		chn_intr(ch->pcm_ch);
 
 	case USB_ST_SETUP:
-		if (ch->bytes_per_frame > usbd_xfer_max_framelen(xfer)) {
-			DPRINTF("bytes per transfer, %d, "
-			    "exceeds maximum, %d!\n",
-			    ch->bytes_per_frame,
-			    usbd_xfer_max_framelen(xfer));
-			return;
-		}
+tr_setup:
 		usbd_xfer_set_frames(xfer, blockcount);
 		for (n = 0; n < blockcount; n++) {
-			usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame);
+			usbd_xfer_set_frame_len(xfer, n, mfl);
 		}
 
-		if (ch->end == ch->start) {
-			DPRINTF("no buffer!\n");
-			return;
-		}
 		usbd_transfer_submit(xfer);
-		return;
+		break;
 
 	default:			/* Error */
 		if (error == USB_ERR_CANCELLED) {
-			return;
+			break;
 		}
-		goto tr_transferred;
+		goto tr_setup;
 	}
 }
 
@@ -1319,38 +1288,26 @@ uaudio_chan_init(struct uaudio_softc *sc
 	struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ?
 	    &sc->sc_play_chan : &sc->sc_rec_chan);
 	uint32_t buf_size;
+	uint32_t frames;
 	uint8_t endpoint;
+	uint8_t blocks;
 	uint8_t iface_index;
 	uint8_t alt_index;
+	uint8_t fps_shift;
 	usb_error_t err;
 
-	/* compute required buffer size */
-	buf_size = (ch->bytes_per_frame * UAUDIO_MINFRAMES);
-
-	/* setup interrupt interval */
-	ch->intr_size = buf_size;
+	if (usbd_get_isoc_fps(sc->sc_udev) < 8000) {
+		/* FULL speed USB */
+		frames = 8;
+	} else {
+		/* HIGH speed USB */
+		frames = UAUDIO_NFRAMES;
+	}
 
-	/* double buffering */
-	buf_size *= 2;
+	/* compute required buffer size */
 
-	ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO);
-	if (ch->buf == NULL) {
-		goto error;
-	}
-	if (sndbuf_setup(b, ch->buf, buf_size) != 0) {
-		goto error;
-	}
-	ch->start = ch->buf;
-	ch->end = ch->buf + buf_size;
-	ch->cur = ch->buf;
-	ch->pcm_ch = c;
-	ch->pcm_mtx = c->lock;
-	ch->pcm_buf = b;
+	buf_size = (ch->bytes_per_frame * frames);
 
-	if (ch->pcm_mtx == NULL) {
-		DPRINTF("ERROR: PCM channels does not have a mutex!\n");
-		goto error;
-	}
 	/* setup play/record format */
 
 	ch->pcm_cap.fmtlist = ch->pcm_format;
@@ -1370,7 +1327,6 @@ uaudio_chan_init(struct uaudio_softc *sc
 
 	ch->pcm_cap.fmtlist[1] = 0;
 
-
 	/* set alternate interface corresponding to the mode */
 
 	endpoint = ch->p_ed1->bEndpointAddress;
@@ -1407,6 +1363,43 @@ uaudio_chan_init(struct uaudio_softc *sc
 		DPRINTF("could not allocate USB transfers!\n");
 		goto error;
 	}
+
+	fps_shift = usbd_xfer_get_fps_shift(ch->xfer[0]);
+
+	/* setup frame sizes */
+	ch->intr_size = buf_size;
+	ch->intr_frames = (frames >> fps_shift);
+	ch->bytes_per_frame <<= fps_shift;
+
+	if (ch->intr_frames == 0) {
+		DPRINTF("frame shift is too high!\n");
+		goto error;
+	}
+
+	/* setup double buffering */
+	buf_size *= 2;
+	blocks = 2;
+
+	ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO);
+	if (ch->buf == NULL)
+		goto error;
+	if (sndbuf_setup(b, ch->buf, buf_size) != 0)
+		goto error;
+	if (sndbuf_resize(b, blocks, ch->intr_size)) 
+		goto error;
+
+	ch->start = ch->buf;
+	ch->end = ch->buf + buf_size;
+	ch->cur = ch->buf;
+	ch->pcm_ch = c;
+	ch->pcm_mtx = c->lock;
+	ch->pcm_buf = b;
+
+	if (ch->pcm_mtx == NULL) {
+		DPRINTF("ERROR: PCM channels does not have a mutex!\n");
+		goto error;
+	}
+
 	return (ch);
 
 error:
@@ -1431,30 +1424,13 @@ uaudio_chan_free(struct uaudio_chan *ch)
 int
 uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
 {
-	uaudio_chan_set_param_fragments(ch, blocksize, 0 - 1);
-
-	return (ch->block_size);
+	return (ch->intr_size);
 }
 
 int
 uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize,
     uint32_t blockcount)
 {
-	/* we only support one size */
-	blocksize = ch->intr_size;
-	blockcount = 2;
-
-	if ((sndbuf_getblksz(ch->pcm_buf) != blocksize) ||
-	    (sndbuf_getblkcnt(ch->pcm_buf) != blockcount)) {
-		DPRINTFN(1, "resizing to %u x "
-		    "%u bytes\n", blockcount, blocksize);
-		if (sndbuf_resize(ch->pcm_buf, blockcount, blocksize)) {
-			DPRINTFN(0, "failed to resize sound buffer, count=%u, "
-			    "size=%u\n", blockcount, blocksize);
-		}
-	}
-	ch->block_size = sndbuf_getblksz(ch->pcm_buf);
-
 	return (1);
 }
 
@@ -1591,12 +1567,12 @@ uaudio_mixer_add_ctl(struct uaudio_softc
 		DPRINTF("adding %d\n", mc->ctl);
 	}
 
-	mc->delta = 0;
 	if (mc->type == MIX_ON_OFF) {
 		mc->minval = 0;
 		mc->maxval = 1;
+		mc->mod = 1;
 	} else if (mc->type == MIX_SELECTOR) {
-
+		mc->mod = 1;
 	} else {
 
 		/* determine min and max values */
@@ -1607,21 +1583,30 @@ uaudio_mixer_add_ctl(struct uaudio_softc
 
 		mc->maxval = uaudio_mixer_get(sc->sc_udev, GET_MAX, mc);
 
-		mc->maxval = 1 + uaudio_mixer_signext(mc->type, mc->maxval);
+		mc->maxval = uaudio_mixer_signext(mc->type, mc->maxval);
+
+		/* check if max and min was swapped */
+
+		if (mc->maxval < mc->minval) {
+			res = mc->maxval;
+			mc->maxval = mc->minval;
+			mc->minval = res;
+		}
 
+		/* compute value range */
 		mc->mul = mc->maxval - mc->minval;
-		if (mc->mul == 0) {
+		if (mc->mul == 0)
 			mc->mul = 1;
-		}
+
+		/* compute value alignment */
 		res = uaudio_mixer_get(sc->sc_udev, GET_RES, mc);
-		if (res > 0) {
-			mc->delta = ((res * 255) + (mc->mul / 2)) / mc->mul;
-		}
+		if (res == 0)
+			res = 1;
+		mc->mod = mc->mul / res;
+		if (mc->mod == 0)
+			mc->mod = 1;
 	}
 
-	if (mc->maxval < mc->minval) {
-		mc->maxval = mc->minval;
-	}
 	uaudio_mixer_add_ctl_sub(sc, mc);
 
 #if USB_DEBUG
@@ -3108,7 +3093,21 @@ uaudio_mixer_bsd2value(struct uaudio_mix
 			val = mc->minval;
 		}
 	} else {
-		val = (((val + (mc->delta / 2)) * mc->mul) / 255) + mc->minval;
+
+		/* compute actual volume */
+		val = (val * mc->mul) / 255;
+
+		/* align volume level */
+		val = val - (val % mc->mod);
+
+		/* add lower offset */
+		val = val + mc->minval;
+
+		/* make sure we don't write a value out of range */
+		if (val > mc->maxval)
+			val = mc->maxval;
+		else if (val < mc->minval)
+			val = mc->minval;
 	}
 
 	DPRINTFN(6, "type=0x%03x val=%d min=%d max=%d val=%d\n",



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