summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2009-11-08 21:00:50 +0000
committerthompsa <thompsa@FreeBSD.org>2009-11-08 21:00:50 +0000
commit267005bd551bd79f1d5788b8b92b2ebd84f67d8c (patch)
treec1128b053dfd8591d0075f4c3d1a5161d129b522
parentc6a81c309174934d240db225d172ee92b4d3b800 (diff)
downloadFreeBSD-src-267005bd551bd79f1d5788b8b92b2ebd84f67d8c.zip
FreeBSD-src-267005bd551bd79f1d5788b8b92b2ebd84f67d8c.tar.gz
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
-rw-r--r--sys/dev/sound/usb/uaudio.c241
1 files changed, 120 insertions, 121 deletions
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index f5d4740..2ac8766 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -105,10 +105,9 @@ SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW,
&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_xfer *xfer, usb_error_t error)
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_xfer *xfer, usb_error_t error)
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 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;
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);
+ if (usbd_get_isoc_fps(sc->sc_udev) < 8000) {
+ /* FULL speed USB */
+ frames = 8;
+ } else {
+ /* HIGH speed USB */
+ frames = UAUDIO_NFRAMES;
+ }
- /* setup interrupt interval */
- ch->intr_size = buf_size;
+ /* compute required buffer size */
- /* double buffering */
- buf_size *= 2;
+ buf_size = (ch->bytes_per_frame * frames);
- 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;
-
- 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, struct snd_dbuf *b,
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, struct snd_dbuf *b,
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 *sc, struct uaudio_mixer_node *mc)
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 *sc, struct uaudio_mixer_node *mc)
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_mixer_node *mc, int32_t val)
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",
OpenPOWER on IntegriCloud