mirror of
https://git.FreeBSD.org/src.git
synced 2025-02-04 17:15:50 +00:00
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>
This commit is contained in:
parent
751aaf5a93
commit
455a367f95
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=249796
@ -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_chan {
|
||||
struct pcmchan_caps pcm_cap; /* capabilities */
|
||||
struct uaudio_configure_msg {
|
||||
struct usb_proc_msg hdr;
|
||||
struct uaudio_softc *sc;
|
||||
};
|
||||
|
||||
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];
|
||||
#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;
|
||||
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];
|
||||
|
||||
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;
|
||||
uint8_t x;
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
/* compute worst case buffer */
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
#if (UAUDIO_NCHANBUFS != 2)
|
||||
#error "please update code"
|
||||
#endif
|
||||
usbd_transfer_start(ch->xfer[0]);
|
||||
usbd_transfer_start(ch->xfer[1]);
|
||||
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 (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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user