summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2009-12-22 02:11:37 +0000
committerthompsa <thompsa@FreeBSD.org>2009-12-22 02:11:37 +0000
commit1bf0795d566502df5793e19d0a7bda8109d3333e (patch)
tree86339e22591f9ccb8e7a315e7d27ac49544e958c
parent7f0926e29662226d7a0fa3de147ddf388f14fa3c (diff)
downloadFreeBSD-src-1bf0795d566502df5793e19d0a7bda8109d3333e.zip
FreeBSD-src-1bf0795d566502df5793e19d0a7bda8109d3333e.tar.gz
- add support for more than 2 audio channels. [1]
- add support for more sample rates Submitted by: [1] ariff (earlier version), Hans Petter Selasky
-rw-r--r--sys/dev/sound/usb/uaudio.c216
1 files changed, 146 insertions, 70 deletions
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index ddae8c3..dfbeb02 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -87,20 +87,27 @@
#include <dev/sound/chip.h>
#include "feeder_if.h"
-static int uaudio_default_rate = 96000;
+static int uaudio_default_rate = 0; /* use rate list */
static int uaudio_default_bits = 32;
-static int uaudio_default_channels = 2;
+static int uaudio_default_channels = 0; /* use default */
#if USB_DEBUG
static int uaudio_debug = 0;
SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio");
+
SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW,
&uaudio_debug, 0, "uaudio debug level");
+
+TUNABLE_INT("hw.usb.uaudio.default_rate", &uaudio_default_rate);
SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_rate, CTLFLAG_RW,
&uaudio_default_rate, 0, "uaudio default sample rate");
+
+TUNABLE_INT("hw.usb.uaudio.default_bits", &uaudio_default_bits);
SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_bits, CTLFLAG_RW,
&uaudio_default_bits, 0, "uaudio default sample bits");
+
+TUNABLE_INT("hw.usb.uaudio.default_channels", &uaudio_default_channels);
SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW,
&uaudio_default_channels, 0, "uaudio default sample channels");
#endif
@@ -169,10 +176,16 @@ struct uaudio_chan {
uint32_t intr_size; /* in bytes */
uint32_t intr_frames; /* in units */
uint32_t sample_rate;
+ uint32_t frames_per_second;
+ uint32_t sample_rem;
+ uint32_t sample_curr;
+
uint32_t format;
uint32_t pcm_format[2];
- uint16_t bytes_per_frame;
+ uint16_t bytes_per_frame[2];
+
+ uint16_t sample_size;
uint8_t valid;
uint8_t iface_index;
@@ -330,7 +343,7 @@ static usb_callback_t umidi_write_clear_stall_callback;
static usb_callback_t umidi_bulk_write_callback;
static void uaudio_chan_fill_info_sub(struct uaudio_softc *,
- struct usb_device *, uint32_t, uint16_t, uint8_t, uint8_t);
+ struct usb_device *, uint32_t, uint8_t, uint8_t);
static void uaudio_chan_fill_info(struct uaudio_softc *,
struct usb_device *);
static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *,
@@ -787,8 +800,7 @@ uaudio_chan_dump_ep_desc(const usb2_endpoint_descriptor_audio_t *ed)
static void
uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
- uint32_t rate, uint16_t fps, uint8_t channels,
- uint8_t bit_resolution)
+ uint32_t rate, uint8_t channels, uint8_t bit_resolution)
{
struct usb_descriptor *desc = NULL;
const struct usb2_audio_streaming_interface_descriptor *asid = NULL;
@@ -811,7 +823,6 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
uint8_t bBitResolution;
uint8_t x;
uint8_t audio_if = 0;
- uint8_t sample_size;
while ((desc = usb_desc_foreach(cd, desc))) {
@@ -1040,16 +1051,10 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
chan->usb2_cfg =
uaudio_cfg_play;
- sample_size = ((
+ chan->sample_size = ((
UAUDIO_MAX_CHAN(chan->p_asf1d->bNrChannels) *
chan->p_asf1d->bBitResolution) / 8);
- /*
- * NOTE: "chan->bytes_per_frame"
- * should not be zero!
- */
- chan->bytes_per_frame = ((rate / fps) * sample_size);
-
if (sc->sc_sndstat_valid) {
sbuf_printf(&sc->sc_sndstat, "\n\t"
"mode %d.%d:(%s) %dch, %d/%dbit, %s, %dHz",
@@ -1067,12 +1072,32 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
}
}
+/* This structure defines all the supported rates. */
+
+static const uint32_t uaudio_rate_list[] = {
+ 96000,
+ 88000,
+ 80000,
+ 72000,
+ 64000,
+ 56000,
+ 48000,
+ 44100,
+ 40000,
+ 32000,
+ 24000,
+ 22050,
+ 16000,
+ 11025,
+ 8000,
+ 0
+};
+
static void
uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
{
uint32_t rate = uaudio_default_rate;
- uint32_t z;
- uint16_t fps = usbd_get_isoc_fps(udev);
+ uint8_t z;
uint8_t bits = uaudio_default_bits;
uint8_t y;
uint8_t channels = uaudio_default_channels;
@@ -1083,14 +1108,24 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
/* set a valid value */
bits = 32;
}
- rate -= (rate % fps);
- if ((rate == 0) || (rate > 192000)) {
- /* set a valid value */
- rate = 192000 - (192000 % fps);
- }
- if ((channels == 0) || (channels > 2)) {
- /* set a valid value */
- channels = 2;
+ if (channels == 0) {
+ switch (usbd_get_speed(udev)) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ /*
+ * Due to high bandwidth usage and problems
+ * with HIGH-speed split transactions we
+ * disable surround setups on FULL-speed USB
+ * by default
+ */
+ channels = 2;
+ break;
+ default:
+ channels = 16;
+ break;
+ }
+ } else if (channels > 16) {
+ channels = 16;
}
if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND)) {
sc->sc_sndstat_valid = 1;
@@ -1099,8 +1134,14 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
for (x = channels; x; x--) {
for (y = bits; y; y -= 8) {
- for (z = rate; z; z -= fps) {
- uaudio_chan_fill_info_sub(sc, udev, z, fps, x, y);
+
+ /* try user defined rate, if any */
+ if (rate != 0)
+ uaudio_chan_fill_info_sub(sc, udev, rate, x, y);
+
+ /* try find a matching rate, if any */
+ for (z = 0; uaudio_rate_list[z]; z++) {
+ uaudio_chan_fill_info_sub(sc, udev, uaudio_rate_list[z], x, y);
if (sc->sc_rec_chan.valid &&
sc->sc_play_chan.valid) {
@@ -1116,18 +1157,6 @@ done:
}
}
-/*
- * The following function sets up data size and block count for the
- * next audio transfer.
- */
-static void
-uaudio_setup_blockcount(struct uaudio_chan *ch,
- uint32_t *total, uint32_t *blockcount)
-{
- *total = ch->intr_size;
- *blockcount = ch->intr_frames;
-}
-
static void
uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
{
@@ -1137,12 +1166,11 @@ uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
uint32_t blockcount;
uint32_t n;
uint32_t offset;
- int actlen, sumlen;
+ int actlen;
+ int sumlen;
usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
- uaudio_setup_blockcount(ch, &total, &blockcount);
-
if (ch->end == ch->start) {
DPRINTF("no buffer!\n");
return;
@@ -1153,22 +1181,39 @@ uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
tr_transferred:
if (actlen < sumlen) {
DPRINTF("short transfer, "
- "%d of %d bytes\n", actlen, total);
+ "%d of %d bytes\n", actlen, sumlen);
}
chn_intr(ch->pcm_ch);
case USB_ST_SETUP:
- if (ch->bytes_per_frame > usbd_xfer_max_framelen(xfer)) {
+ if (ch->bytes_per_frame[1] > usbd_xfer_max_framelen(xfer)) {
DPRINTF("bytes per transfer, %d, "
"exceeds maximum, %d!\n",
- ch->bytes_per_frame,
+ ch->bytes_per_frame[1],
usbd_xfer_max_framelen(xfer));
break;
}
- /* setup frame length */
+
+ blockcount = ch->intr_frames;
+
+ /* setup number of frames */
usbd_xfer_set_frames(xfer, blockcount);
- for (n = 0; n != blockcount; n++)
- usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame);
+
+ /* reset total length */
+ total = 0;
+
+ /* setup frame lengths */
+ for (n = 0; n != blockcount; n++) {
+ ch->sample_curr += ch->sample_rem;
+ if (ch->sample_curr >= ch->frames_per_second) {
+ ch->sample_curr -= ch->frames_per_second;
+ usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[1]);
+ total += ch->bytes_per_frame[1];
+ } else {
+ usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[0]);
+ total += ch->bytes_per_frame[0];
+ }
+ }
DPRINTFN(6, "transfer %d bytes\n", total);
@@ -1210,7 +1255,6 @@ uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
struct usb_page_cache *pc;
uint32_t n;
uint32_t m;
- uint32_t total;
uint32_t blockcount;
uint32_t offset0;
uint32_t offset1;
@@ -1222,8 +1266,6 @@ uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
mfl = usbd_xfer_max_framelen(xfer);
- uaudio_setup_blockcount(ch, &total, &blockcount);
-
if (ch->end == ch->start) {
DPRINTF("no buffer!\n");
return;
@@ -1231,12 +1273,8 @@ uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
- if (actlen < total) {
- DPRINTF("short transfer, "
- "%d of %d bytes\n", actlen, total);
- } else {
- DPRINTFN(6, "transferred %d bytes\n", actlen);
- }
+
+ DPRINTFN(6, "transferred %d bytes\n", actlen);
offset0 = 0;
pc = usbd_xfer_get_frame(xfer, 0);
@@ -1271,6 +1309,8 @@ uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
tr_setup:
+ blockcount = ch->intr_frames;
+
usbd_xfer_set_frames(xfer, blockcount);
for (n = 0; n < blockcount; n++) {
usbd_xfer_set_frame_len(xfer, n, mfl);
@@ -1295,6 +1335,8 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
&sc->sc_play_chan : &sc->sc_rec_chan);
uint32_t buf_size;
uint32_t frames;
+ uint32_t format;
+ uint16_t fps;
uint8_t endpoint;
uint8_t blocks;
uint8_t iface_index;
@@ -1302,7 +1344,9 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
uint8_t fps_shift;
usb_error_t err;
- if (usbd_get_isoc_fps(sc->sc_udev) < 8000) {
+ fps = usbd_get_isoc_fps(sc->sc_udev);
+
+ if (fps < 8000) {
/* FULL speed USB */
frames = 8;
} else {
@@ -1310,10 +1354,6 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
frames = UAUDIO_NFRAMES;
}
- /* compute required buffer size */
-
- buf_size = (ch->bytes_per_frame * frames);
-
/* setup play/record format */
ch->pcm_cap.fmtlist = ch->pcm_format;
@@ -1329,15 +1369,34 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
ch->pcm_ch = c;
ch->pcm_mtx = c->lock;
- if (ch->p_asf1d->bNrChannels >= 2)
- ch->pcm_cap.fmtlist[0] =
- SND_FORMAT(ch->p_fmt->freebsd_fmt, 2, 0);
- else
- ch->pcm_cap.fmtlist[0] =
- SND_FORMAT(ch->p_fmt->freebsd_fmt, 1, 0);
+ format = ch->p_fmt->freebsd_fmt;
+ switch (ch->p_asf1d->bNrChannels) {
+ case 2:
+ /* stereo */
+ format = SND_FORMAT(format, 2, 0);
+ break;
+ case 1:
+ /* mono */
+ format = SND_FORMAT(format, 1, 0);
+ break;
+ default:
+ /* surround and more */
+ format = feeder_matrix_default_format(
+ SND_FORMAT(format, ch->p_asf1d->bNrChannels, 0));
+ break;
+ }
+
+ ch->pcm_cap.fmtlist[0] = format;
ch->pcm_cap.fmtlist[1] = 0;
+ /* check if format is not supported */
+
+ if (format == 0) {
+ DPRINTF("The selected audio format is not supported\n");
+ goto error;
+ }
+
/* set alternate interface corresponding to the mode */
endpoint = ch->p_ed1->bEndpointAddress;
@@ -1377,10 +1436,27 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
fps_shift = usbd_xfer_get_fps_shift(ch->xfer[0]);
- /* setup frame sizes */
+ /* down shift number of frames per second, if any */
+ fps >>= fps_shift;
+ frames >>= fps_shift;
+
+ /* bytes per frame should not be zero */
+ ch->bytes_per_frame[0] = ((ch->sample_rate / fps) * ch->sample_size);
+ ch->bytes_per_frame[1] = (((ch->sample_rate + fps - 1) / fps) * ch->sample_size);
+
+ /* setup data rate dithering, if any */
+ ch->frames_per_second = fps;
+ ch->sample_rem = ch->sample_rate % fps;
+ ch->sample_curr = 0;
+ ch->frames_per_second = fps;
+
+ /* compute required buffer size */
+ buf_size = (ch->bytes_per_frame[1] * frames);
+
ch->intr_size = buf_size;
- ch->intr_frames = (frames >> fps_shift);
- ch->bytes_per_frame <<= fps_shift;
+ ch->intr_frames = frames;
+
+ DPRINTF("fps=%d sample_rem=%d\n", fps, ch->sample_rem);
if (ch->intr_frames == 0) {
DPRINTF("frame shift is too high!\n");
OpenPOWER on IntegriCloud