summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2013-04-23 10:48:14 +0000
committerhselasky <hselasky@FreeBSD.org>2013-04-23 10:48:14 +0000
commit75e877a897c7db8438b705430aeaa0570f365289 (patch)
tree3d40592906d473124ac7055f96827741cf865e9c /sys/dev/sound
parent17ea3ba57c1c6799a0b013b788d6f81400ce3a67 (diff)
downloadFreeBSD-src-75e877a897c7db8438b705430aeaa0570f365289.zip
FreeBSD-src-75e877a897c7db8438b705430aeaa0570f365289.tar.gz
Add support for runtime switching of sample rate for
USB audio devices. Previously the highest sample rate was unconditionally selected. Requested by: Craig Leres <leres@ee.lbl.gov>
Diffstat (limited to 'sys/dev/sound')
-rw-r--r--sys/dev/sound/usb/uaudio.c681
1 files changed, 431 insertions, 250 deletions
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index 4c8f2ba..acd4ac9 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbhid.h>
#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_process.h>
#define USB_DEBUG_VAR uaudio_debug
#include <dev/usb/usb_debug.h>
@@ -176,19 +177,34 @@ struct uaudio_mixer_node {
struct uaudio_mixer_node *next;
};
+struct uaudio_configure_msg {
+ struct usb_proc_msg hdr;
+ struct uaudio_softc *sc;
+};
+
+#define CHAN_MAX_ALT 20
+
+struct uaudio_chan_alt {
+ union uaudio_asf1d p_asf1d;
+ union uaudio_sed p_sed;
+ const usb_endpoint_descriptor_audio_t *p_ed1;
+ const struct uaudio_format *p_fmt;
+ const struct usb_config *usb_cfg;
+ uint32_t sample_rate; /* in Hz */
+ uint16_t sample_size;
+ uint8_t iface_index;
+ uint8_t iface_alt_index;
+ uint8_t channels;
+};
+
struct uaudio_chan {
struct pcmchan_caps pcm_cap; /* capabilities */
-
+ struct uaudio_chan_alt usb_alt[CHAN_MAX_ALT];
struct snd_dbuf *pcm_buf;
- const struct usb_config *usb_cfg;
struct mtx *pcm_mtx; /* lock protecting this structure */
struct uaudio_softc *priv_sc;
struct pcm_channel *pcm_ch;
struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1];
- union uaudio_asf1d p_asf1d;
- union uaudio_sed p_sed;
- const usb_endpoint_descriptor_audio_t *p_ed1;
- const struct uaudio_format *p_fmt;
uint8_t *buf; /* pointer to buffer */
uint8_t *start; /* upper layer buffer start */
@@ -196,24 +212,24 @@ struct uaudio_chan {
uint8_t *cur; /* current position in upper layer
* buffer */
- 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 max_buf;
- uint32_t format;
uint32_t pcm_format[2];
uint16_t bytes_per_frame[2];
- uint16_t sample_size;
-
- uint8_t valid;
- uint8_t iface_index;
- uint8_t iface_alt_index;
- uint8_t channels;
+ uint8_t num_alt;
+ uint8_t cur_alt;
+ uint8_t set_alt;
+ uint8_t operation;
+#define CHAN_OP_NONE 0
+#define CHAN_OP_START 1
+#define CHAN_OP_STOP 2
+#define CHAN_OP_DRAIN 3
uint8_t last_sync_time;
uint8_t last_sync_state;
@@ -309,6 +325,7 @@ struct uaudio_softc {
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 mtx *sc_mixer_lock;
struct snd_mixer *sc_mixer_dev;
@@ -434,6 +451,8 @@ static usb_callback_t umidi_bulk_read_callback;
static usb_callback_t umidi_bulk_write_callback;
static usb_callback_t uaudio_hid_rx_callback;
+static usb_proc_callback_t uaudio_configure_msg;
+
/* ==== USB mixer ==== */
static int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS);
@@ -856,6 +875,10 @@ uaudio_attach(device_t dev)
sc->sc_udev = uaa->device;
sc->sc_mixer_iface_index = uaa->info.bIfaceIndex;
sc->sc_mixer_iface_no = uaa->info.bIfaceNum;
+ sc->sc_config_msg[0].hdr.pm_callback = &uaudio_configure_msg;
+ sc->sc_config_msg[0].sc = sc;
+ sc->sc_config_msg[1].hdr.pm_callback = &uaudio_configure_msg;
+ sc->sc_config_msg[1].sc = sc;
if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR))
sc->sc_uq_audio_swap_lr = 1;
@@ -900,22 +923,28 @@ uaudio_attach(device_t dev)
DPRINTF("%d mixer controls\n",
sc->sc_mixer_count);
- if (sc->sc_play_chan.valid) {
- device_printf(dev, "Play: %d Hz, %d ch, %s format, "
- "2x8ms buffer.\n",
- sc->sc_play_chan.sample_rate,
- sc->sc_play_chan.channels,
- sc->sc_play_chan.p_fmt->description);
+ if (sc->sc_play_chan.num_alt > 0) {
+ uint8_t x;
+ 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);
+ }
} else {
device_printf(dev, "No playback.\n");
}
- if (sc->sc_rec_chan.valid) {
- device_printf(dev, "Record: %d Hz, %d ch, %s format, "
- "2x8ms buffer.\n",
- sc->sc_rec_chan.sample_rate,
- sc->sc_rec_chan.channels,
- sc->sc_rec_chan.p_fmt->description);
+ if (sc->sc_rec_chan.num_alt > 0) {
+ uint8_t x;
+ 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);
+ }
} else {
device_printf(dev, "No recording.\n");
}
@@ -950,8 +979,8 @@ 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.valid ||
- sc->sc_rec_chan.valid ||
+ 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);
@@ -1020,18 +1049,18 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas
snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
if (pcm_register(dev, sc,
- sc->sc_play_chan.valid ? 1 : 0,
- sc->sc_rec_chan.valid ? 1 : 0)) {
+ (sc->sc_play_chan.num_alt > 0) ? 1 : 0,
+ (sc->sc_rec_chan.num_alt > 0) ? 1 : 0)) {
goto detach;
}
uaudio_pcm_setflags(dev, SD_F_MPSAFE);
sc->sc_pcm_registered = 1;
- if (sc->sc_play_chan.valid) {
+ if (sc->sc_play_chan.num_alt > 0) {
pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc);
}
- if (sc->sc_rec_chan.valid) {
+ if (sc->sc_rec_chan.num_alt > 0) {
pcm_addchan(dev, PCMDIR_REC, chan_class, sc);
}
pcm_setstatus(dev, status);
@@ -1078,10 +1107,15 @@ uaudio_detach(device_t dev)
* will time out and close opened /dev/dspX.Y device(s), if
* any.
*/
- if (sc->sc_play_chan.valid)
- usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1);
- if (sc->sc_rec_chan.valid)
- usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1);
+ usb_proc_explore_lock(sc->sc_udev);
+ sc->sc_play_chan.operation = CHAN_OP_DRAIN;
+ sc->sc_rec_chan.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);
uaudio_hid_detach(sc);
@@ -1100,6 +1134,201 @@ uaudio_detach(device_t dev)
return (0);
}
+static uint32_t
+uaudio_get_buffer_size(struct uaudio_chan *ch, uint8_t alt)
+{
+ struct uaudio_chan_alt *chan_alt = &ch->usb_alt[alt];
+ /* We use 2 times 8ms of buffer */
+ uint32_t buf_size = (((chan_alt->sample_rate * (UAUDIO_NFRAMES / 8)) +
+ 1000 - 1) / 1000) * chan_alt->sample_size;
+ return (buf_size);
+}
+
+static void
+uaudio_configure_msg_sub(struct uaudio_softc *sc,
+ struct uaudio_chan *chan, int dir)
+{
+ struct uaudio_chan_alt *chan_alt;
+ uint32_t frames;
+ uint32_t buf_size;
+ uint16_t fps;
+ uint8_t set_alt;
+ uint8_t fps_shift;
+ uint8_t operation;
+ usb_error_t err;
+
+ if (chan->num_alt <= 0)
+ return;
+
+ DPRINTF("\n");
+
+ usb_proc_explore_lock(sc->sc_udev);
+ operation = chan->operation;
+ chan->operation = CHAN_OP_NONE;
+ usb_proc_explore_unlock(sc->sc_udev);
+
+ mtx_lock(chan->pcm_mtx);
+ if (chan->cur_alt != chan->set_alt)
+ set_alt = chan->set_alt;
+ else
+ set_alt = CHAN_MAX_ALT;
+ mtx_unlock(chan->pcm_mtx);
+
+ if (set_alt >= chan->num_alt)
+ goto done;
+
+ chan_alt = chan->usb_alt + set_alt;
+
+ usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
+
+ err = usbd_set_alt_interface_index(sc->sc_udev,
+ chan_alt->iface_index, chan_alt->iface_alt_index);
+ if (err) {
+ DPRINTF("setting of alternate index failed: %s!\n",
+ usbd_errstr(err));
+ goto error;
+ }
+
+ /*
+ * Only set the sample rate if the channel reports that it
+ * supports the frequency control.
+ */
+
+ if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
+ /* FALLTHROUGH */
+ } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
+ unsigned int x;
+
+ for (x = 0; x != 256; x++) {
+ if (dir == PCMDIR_PLAY) {
+ if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
+ (1 << (x % 8)))) {
+ continue;
+ }
+ } else {
+ if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
+ (1 << (x % 8)))) {
+ continue;
+ }
+ }
+
+ if (uaudio20_set_speed(sc->sc_udev,
+ sc->sc_mixer_iface_no, x, chan_alt->sample_rate)) {
+ /*
+ * If the endpoint is adaptive setting
+ * the speed may fail.
+ */
+ DPRINTF("setting of sample rate failed! "
+ "(continuing anyway)\n");
+ }
+ }
+ } else if (chan_alt->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) {
+ if (uaudio_set_speed(sc->sc_udev,
+ chan_alt->p_ed1->bEndpointAddress, chan_alt->sample_rate)) {
+ /*
+ * If the endpoint is adaptive setting the
+ * speed may fail.
+ */
+ DPRINTF("setting of sample rate failed! "
+ "(continuing anyway)\n");
+ }
+ }
+ if (usbd_transfer_setup(sc->sc_udev, &chan_alt->iface_index, chan->xfer,
+ chan_alt->usb_cfg, UAUDIO_NCHANBUFS + 1, chan, chan->pcm_mtx)) {
+ DPRINTF("could not allocate USB transfers!\n");
+ goto error;
+ }
+
+ fps = usbd_get_isoc_fps(sc->sc_udev);
+
+ if (fps < 8000) {
+ /* FULL speed USB */
+ frames = 8;
+ } else {
+ /* HIGH speed USB */
+ frames = UAUDIO_NFRAMES;
+ }
+
+ fps_shift = usbd_xfer_get_fps_shift(chan->xfer[0]);
+
+ /* down shift number of frames per second, if any */
+ fps >>= fps_shift;
+ frames >>= fps_shift;
+
+ /* bytes per frame should not be zero */
+ chan->bytes_per_frame[0] =
+ ((chan_alt->sample_rate / fps) * chan_alt->sample_size);
+ chan->bytes_per_frame[1] =
+ (((chan_alt->sample_rate + fps - 1) / fps) * chan_alt->sample_size);
+
+ /* setup data rate dithering, if any */
+ chan->frames_per_second = fps;
+ chan->sample_rem = chan_alt->sample_rate % fps;
+ chan->sample_curr = 0;
+ chan->frames_per_second = fps;
+
+ /* compute required buffer size */
+ buf_size = (chan->bytes_per_frame[1] * frames);
+
+ if (buf_size > (chan->end - chan->start)) {
+ DPRINTF("buffer size is too big\n");
+ goto error;
+ }
+
+ chan->intr_frames = frames;
+
+ DPRINTF("fps=%d sample_rem=%d\n", (int)fps, (int)chan->sample_rem);
+
+ if (chan->intr_frames == 0) {
+ DPRINTF("frame shift is too high!\n");
+ goto error;
+ }
+
+ mtx_lock(chan->pcm_mtx);
+ chan->cur_alt = set_alt;
+ mtx_unlock(chan->pcm_mtx);
+
+done:
+#if (UAUDIO_NCHANBUFS != 2)
+#error "please update code"
+#endif
+ switch (operation) {
+ case CHAN_OP_START:
+ mtx_lock(chan->pcm_mtx);
+ usbd_transfer_start(chan->xfer[0]);
+ usbd_transfer_start(chan->xfer[1]);
+ mtx_unlock(chan->pcm_mtx);
+ break;
+ case CHAN_OP_STOP:
+ mtx_lock(chan->pcm_mtx);
+ usbd_transfer_stop(chan->xfer[0]);
+ usbd_transfer_stop(chan->xfer[1]);
+ mtx_unlock(chan->pcm_mtx);
+ break;
+ default:
+ break;
+ }
+ return;
+
+error:
+ usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
+
+ mtx_lock(chan->pcm_mtx);
+ chan->cur_alt = CHAN_MAX_ALT;
+ mtx_unlock(chan->pcm_mtx);
+}
+
+static void
+uaudio_configure_msg(struct usb_proc_msg *pm)
+{
+ struct uaudio_softc *sc = ((struct uaudio_configure_msg *)pm)->sc;
+
+ 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);
+ usb_proc_explore_lock(sc->sc_udev);
+}
+
/*========================================================================*
* AS - Audio Stream - routines
*========================================================================*/
@@ -1237,6 +1466,8 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
struct usb_interface_descriptor *id;
const struct uaudio_format *p_fmt = NULL;
struct uaudio_chan *chan;
+ struct uaudio_chan_alt *chan_alt;
+ uint32_t format;
uint16_t curidx = 0xFFFF;
uint16_t lastidx = 0xFFFF;
uint16_t alt_index = 0;
@@ -1508,14 +1739,19 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
chan = (ep_dir == UE_DIR_IN) ?
&sc->sc_rec_chan : &sc->sc_play_chan;
- if (chan->valid != 0 ||
- usbd_get_iface(udev, curidx) == NULL) {
- DPRINTF("Channel already exists or "
- "interface is not valid\n");
+ if (usbd_get_iface(udev, curidx) == NULL) {
+ DPRINTF("Interface is not valid\n");
+ goto next_ep;
+ }
+ if (chan->num_alt == CHAN_MAX_ALT) {
+ DPRINTF("Too many alternate settings\n");
goto next_ep;
}
+ chan->set_alt = 0;
+ chan->cur_alt = CHAN_MAX_ALT;
+
+ chan_alt = &chan->usb_alt[chan->num_alt++];
- chan->valid = 1;
#ifdef USB_DEBUG
uaudio_chan_dump_ep_desc(ed1);
#endif
@@ -1523,30 +1759,74 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
"bits = %d, format = %s\n", rate, channels,
bit_resolution, p_fmt->description);
- chan->sample_rate = rate;
- chan->p_asf1d = asf1d;
- chan->p_ed1 = ed1;
- chan->p_fmt = p_fmt;
- chan->p_sed = sed;
- chan->iface_index = curidx;
- chan->iface_alt_index = alt_index;
+ chan_alt->sample_rate = rate;
+ chan_alt->p_asf1d = asf1d;
+ chan_alt->p_ed1 = ed1;
+ chan_alt->p_fmt = p_fmt;
+ chan_alt->p_sed = sed;
+ chan_alt->iface_index = curidx;
+ chan_alt->iface_alt_index = alt_index;
+
+ usbd_set_parent_iface(sc->sc_udev, curidx,
+ sc->sc_mixer_iface_index);
if (ep_dir == UE_DIR_IN)
- chan->usb_cfg = uaudio_cfg_record;
+ chan_alt->usb_cfg = uaudio_cfg_record;
else
- chan->usb_cfg = uaudio_cfg_play;
+ chan_alt->usb_cfg = uaudio_cfg_play;
- chan->sample_size = (UAUDIO_MAX_CHAN(channels) *
+ chan_alt->sample_size = (UAUDIO_MAX_CHAN(channels) *
p_fmt->bPrecision) / 8;
- chan->channels = channels;
+ chan_alt->channels = channels;
if (ep_dir == UE_DIR_IN &&
usbd_get_speed(udev) == USB_SPEED_FULL) {
uaudio_record_fix_fs(ed1,
- chan->sample_size * (rate / 1000),
- chan->sample_size * (rate / 4000));
+ chan_alt->sample_size * (rate / 1000),
+ chan_alt->sample_size * (rate / 4000));
+ }
+
+ /* setup play/record format */
+
+ format = chan_alt->p_fmt->freebsd_fmt;
+
+ switch (chan_alt->channels) {
+ 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, chan_alt->channels, 0));
+ break;
+ }
+
+ /* check if format is not supported */
+ if (format == 0) {
+ DPRINTF("The selected audio format is not supported\n");
+ chan->num_alt--;
+ goto next_ep;
+ }
+ /* we only accumulate one format at different sample rates */
+ if (chan->num_alt > 1 && chan->pcm_format[0] != format) {
+ DPRINTF("Multiple formats is not supported\n");
+ chan->num_alt--;
+ goto next_ep;
}
+ chan->pcm_cap.fmtlist = chan->pcm_format;
+ chan->pcm_cap.fmtlist[0] = format;
+
+ if (rate < chan->pcm_cap.minspeed || chan->pcm_cap.minspeed == 0)
+ chan->pcm_cap.minspeed = rate;
+ if (rate > chan->pcm_cap.maxspeed || chan->pcm_cap.maxspeed == 0)
+ chan->pcm_cap.maxspeed = rate;
+
if (sc->sc_sndstat_valid != 0) {
sbuf_printf(&sc->sc_sndstat, "\n\t"
"mode %d.%d:(%s) %dch, %dbit, %s, %dHz",
@@ -1564,8 +1844,9 @@ 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[] = {
+static const uint32_t uaudio_rate_list[CHAN_MAX_ALT] = {
96000,
+ 88200,
88000,
80000,
72000,
@@ -1630,21 +1911,12 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
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++) {
+ 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) {
- goto done;
- }
- }
}
}
-
-done:
- if (sc->sc_sndstat_valid) {
+ if (sc->sc_sndstat_valid)
sbuf_finish(&sc->sc_sndstat);
- }
}
static void
@@ -1652,6 +1924,7 @@ uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct uaudio_chan *ch = usbd_xfer_softc(xfer);
struct usb_page_cache *pc;
+ uint64_t sample_rate = ch->usb_alt[ch->cur_alt].sample_rate;
uint8_t buf[4];
uint64_t temp;
int len;
@@ -1698,24 +1971,24 @@ uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
/* auto adjust */
- while (temp < (ch->sample_rate - (ch->sample_rate / 4)))
+ while (temp < (sample_rate - (sample_rate / 4)))
temp *= 2;
- while (temp > (ch->sample_rate + (ch->sample_rate / 2)))
+ while (temp > (sample_rate + (sample_rate / 2)))
temp /= 2;
/* bias */
- temp += (ch->sample_rate + 1999) / 2000;
+ temp += (sample_rate + 1999) / 2000;
/* compare */
DPRINTF("Comparing %d < %d\n",
- (int)temp, (int)ch->sample_rate);
+ (int)temp, (int)sample_rate);
- if (temp == ch->sample_rate)
+ if (temp == sample_rate)
ch->last_sync_state = UAUDIO_SYNC_NONE;
- else if (temp > ch->sample_rate)
+ else if (temp > sample_rate)
ch->last_sync_state = UAUDIO_SYNC_MORE;
else
ch->last_sync_state = UAUDIO_SYNC_LESS;
@@ -1737,6 +2010,7 @@ uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct uaudio_chan *ch = usbd_xfer_softc(xfer);
struct usb_page_cache *pc;
+ uint32_t sample_size = ch->usb_alt[ch->cur_alt].sample_size;
uint32_t mfl;
uint32_t total;
uint32_t blockcount;
@@ -1800,14 +2074,14 @@ tr_transferred:
switch (ch->last_sync_state) {
case UAUDIO_SYNC_MORE:
DPRINTFN(6, "sending one sample more\n");
- if ((frame_len + ch->sample_size) <= mfl)
- frame_len += ch->sample_size;
+ if ((frame_len + sample_size) <= mfl)
+ frame_len += sample_size;
ch->last_sync_state = UAUDIO_SYNC_NONE;
break;
case UAUDIO_SYNC_LESS:
DPRINTFN(6, "sending one sample less\n");
- if (frame_len >= ch->sample_size)
- frame_len -= ch->sample_size;
+ if (frame_len >= sample_size)
+ frame_len -= sample_size;
ch->last_sync_state = UAUDIO_SYNC_NONE;
break;
default:
@@ -1944,187 +2218,43 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ?
&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;
- uint8_t alt_index;
- uint8_t fps_shift;
- usb_error_t err;
-
- fps = usbd_get_isoc_fps(sc->sc_udev);
-
- if (fps < 8000) {
- /* FULL speed USB */
- frames = 8;
- } else {
- /* HIGH speed USB */
- frames = UAUDIO_NFRAMES;
- }
-
- /* setup play/record format */
-
- ch->pcm_cap.fmtlist = ch->pcm_format;
-
- ch->pcm_format[0] = 0;
- ch->pcm_format[1] = 0;
-
- ch->pcm_cap.minspeed = ch->sample_rate;
- ch->pcm_cap.maxspeed = ch->sample_rate;
+ uint8_t x;
- /* setup mutex and PCM channel */
+ /* store mutex and PCM channel */
ch->pcm_ch = c;
ch->pcm_mtx = c->lock;
- format = ch->p_fmt->freebsd_fmt;
-
- switch (ch->channels) {
- 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->channels, 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;
- iface_index = ch->iface_index;
- alt_index = ch->iface_alt_index;
-
- DPRINTF("endpoint=0x%02x, speed=%d, iface=%d alt=%d\n",
- endpoint, ch->sample_rate, iface_index, alt_index);
-
- err = usbd_set_alt_interface_index(sc->sc_udev, iface_index, alt_index);
- if (err) {
- DPRINTF("setting of alternate index failed: %s!\n",
- usbd_errstr(err));
- goto error;
- }
- usbd_set_parent_iface(sc->sc_udev, iface_index,
- sc->sc_mixer_iface_index);
-
- /*
- * Only set the sample rate if the channel reports that it
- * supports the frequency control.
- */
-
- if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
- /* FALLTHROUGH */
- } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
- unsigned int x;
-
- for (x = 0; x != 256; x++) {
- if (dir == PCMDIR_PLAY) {
- if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
- (1 << (x % 8)))) {
- continue;
- }
- } else {
- if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
- (1 << (x % 8)))) {
- continue;
- }
- }
-
- if (uaudio20_set_speed(sc->sc_udev,
- sc->sc_mixer_iface_no, x, ch->sample_rate)) {
- /*
- * If the endpoint is adaptive setting
- * the speed may fail.
- */
- DPRINTF("setting of sample rate failed! "
- "(continuing anyway)\n");
- }
- }
- } else if (ch->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) {
- if (uaudio_set_speed(sc->sc_udev, endpoint, ch->sample_rate)) {
- /*
- * If the endpoint is adaptive setting the
- * speed may fail.
- */
- DPRINTF("setting of sample rate failed! "
- "(continuing anyway)\n");
- }
- }
- if (usbd_transfer_setup(sc->sc_udev, &iface_index, ch->xfer,
- ch->usb_cfg, UAUDIO_NCHANBUFS + 1, ch, ch->pcm_mtx)) {
- DPRINTF("could not allocate USB transfers!\n");
- goto error;
- }
-
- fps_shift = usbd_xfer_get_fps_shift(ch->xfer[0]);
-
- /* 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;
+ /* compute worst case buffer */
- DPRINTF("fps=%d sample_rem=%d\n", fps, ch->sample_rem);
-
- if (ch->intr_frames == 0) {
- DPRINTF("frame shift is too high!\n");
- goto error;
+ buf_size = 0;
+ for (x = 0; x != ch->num_alt; x++) {
+ uint32_t temp = uaudio_get_buffer_size(ch, x);
+ if (temp > buf_size)
+ buf_size = temp;
}
- /* setup double buffering */
+ /* allow double buffering */
buf_size *= 2;
- blocks = 2;
+
+ DPRINTF("Worst case buffer is %d bytes\n", (int)buf_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;
- 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_buf = b;
+ ch->max_buf = buf_size;
if (ch->pcm_mtx == NULL) {
DPRINTF("ERROR: PCM channels does not have a mutex!\n");
goto error;
}
-
return (ch);
error:
@@ -2141,7 +2271,7 @@ uaudio_chan_free(struct uaudio_chan *ch)
}
usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS + 1);
- ch->valid = 0;
+ ch->num_alt = 0;
return (0);
}
@@ -2149,7 +2279,15 @@ uaudio_chan_free(struct uaudio_chan *ch)
int
uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
{
- return (ch->intr_size);
+ uint32_t temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
+
+ sndbuf_setup(ch->pcm_buf, ch->buf, temp);
+
+ ch->start = ch->buf;
+ ch->end = ch->buf + temp;
+ ch->cur = ch->buf;
+
+ return (temp / 2);
}
int
@@ -2162,10 +2300,23 @@ uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize,
int
uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed)
{
- if (speed != ch->sample_rate) {
- DPRINTF("rate conversion required\n");
+ uint8_t x;
+
+ for (x = 0; x < ch->num_alt; x++) {
+ if (ch->usb_alt[x].sample_rate < speed) {
+ /* sample rate is too low */
+ break;
+ }
}
- return (ch->sample_rate);
+
+ if (x != 0)
+ x--;
+
+ ch->set_alt = x;
+
+ DPRINTF("Selecting alt %d\n", (int)x);
+
+ return (ch->usb_alt[x].sample_rate);
}
int
@@ -2228,31 +2379,61 @@ uaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format)
int
uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format)
{
- ch->format = format;
+ DPRINTF("Selecting format 0x%08x\n", (unsigned int)format);
return (0);
}
int
uaudio_chan_start(struct uaudio_chan *ch)
{
- ch->cur = ch->start;
+ struct uaudio_softc *sc = ch->priv_sc;
+ int do_start = 0;
+
+ usb_proc_explore_lock(sc->sc_udev);
+ if (ch->operation != CHAN_OP_DRAIN) {
+ if (ch->cur_alt == ch->set_alt &&
+ ch->operation == CHAN_OP_NONE) {
+ /* save doing the explore task */
+ do_start = 1;
+ } else {
+ ch->operation = CHAN_OP_START;
+ (void)usb_proc_explore_msignal(sc->sc_udev,
+ &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
+ }
+ }
+ usb_proc_explore_unlock(sc->sc_udev);
-#if (UAUDIO_NCHANBUFS != 2)
-#error "please update code"
-#endif
- usbd_transfer_start(ch->xfer[0]);
- usbd_transfer_start(ch->xfer[1]);
+ if (do_start) {
+ usbd_transfer_start(ch->xfer[0]);
+ usbd_transfer_start(ch->xfer[1]);
+ }
return (0);
}
int
uaudio_chan_stop(struct uaudio_chan *ch)
{
-#if (UAUDIO_NCHANBUFS != 2)
-#error "please update code"
-#endif
- usbd_transfer_stop(ch->xfer[0]);
- usbd_transfer_stop(ch->xfer[1]);
+ struct uaudio_softc *sc = ch->priv_sc;
+ int do_stop = 0;
+
+ usb_proc_explore_lock(sc->sc_udev);
+ if (ch->operation != CHAN_OP_DRAIN) {
+ if (ch->cur_alt == ch->set_alt &&
+ ch->operation == CHAN_OP_NONE) {
+ /* save doing the explore task */
+ do_stop = 1;
+ } else {
+ ch->operation = CHAN_OP_STOP;
+ (void)usb_proc_explore_msignal(sc->sc_udev,
+ &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
+ }
+ }
+ usb_proc_explore_unlock(sc->sc_udev);
+
+ if (do_stop) {
+ usbd_transfer_stop(ch->xfer[0]);
+ usbd_transfer_stop(ch->xfer[1]);
+ }
return (0);
}
OpenPOWER on IntegriCloud