- add support for more than 2 audio channels. [1]

- add support for more sample rates

Submitted by:	[1] ariff (earlier version), Hans Petter Selasky
This commit is contained in:
Andrew Thompson 2009-12-22 02:11:37 +00:00
parent f1ea98c024
commit afbfddd901
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=200825

View file

@ -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 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
}
}
/*
* 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");