summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/usb/uaudio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/usb/uaudio.c')
-rw-r--r--sys/dev/sound/usb/uaudio.c285
1 files changed, 214 insertions, 71 deletions
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index cc39334..9eaab2f 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -239,6 +239,7 @@ struct uaudio_softc {
#define HAS_MULAW 0x10
#define UA_NOFRAC 0x20 /* don't do sample rate adjustment */
#define HAS_24 0x40
+#define HAS_32 0x80
int sc_mode; /* play/record capability */
struct mixerctl *sc_ctls; /* mixer controls */
int sc_nctls; /* # of mixer controls */
@@ -2050,7 +2051,7 @@ uaudio_process_as(struct uaudio_softc *sc, const char *buf, int *offsp,
format = UGETW(asid->wFormatTag);
chan = asf1d->bNrChannels;
prec = asf1d->bBitResolution;
- if (prec != 8 && prec != 16 && prec != 24) {
+ if (prec != 8 && prec != 16 && prec != 24 && prec != 32) {
printf("%s: ignored setting with precision %d\n",
USBDEVNAME(sc->sc_dev), prec);
return USBD_NORMAL_COMPLETION;
@@ -2063,6 +2064,8 @@ uaudio_process_as(struct uaudio_softc *sc, const char *buf, int *offsp,
sc->sc_altflags |= HAS_16;
} else if (prec == 24) {
sc->sc_altflags |= HAS_24;
+ } else if (prec == 32) {
+ sc->sc_altflags |= HAS_32;
}
enc = AUDIO_ENCODING_SLINEAR_LE;
format_str = "pcm";
@@ -3742,7 +3745,7 @@ uaudio_init_params(struct uaudio_softc *sc, struct chan *ch, int mode)
if ((sc->sc_playchan.pipe != NULL) || (sc->sc_recchan.pipe != NULL))
return (-1);
- switch(ch->format & 0x0000FFFF) {
+ switch(ch->format & 0x000FFFFF) {
case AFMT_U8:
enc = AUDIO_ENCODING_ULINEAR_LE;
ch->precision = 8;
@@ -3775,6 +3778,38 @@ uaudio_init_params(struct uaudio_softc *sc, struct chan *ch, int mode)
enc = AUDIO_ENCODING_ULINEAR_BE;
ch->precision = 16;
break;
+ case AFMT_S24_LE:
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ ch->precision = 24;
+ break;
+ case AFMT_S24_BE:
+ enc = AUDIO_ENCODING_SLINEAR_BE;
+ ch->precision = 24;
+ break;
+ case AFMT_U24_LE:
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ ch->precision = 24;
+ break;
+ case AFMT_U24_BE:
+ enc = AUDIO_ENCODING_ULINEAR_BE;
+ ch->precision = 24;
+ break;
+ case AFMT_S32_LE:
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ ch->precision = 32;
+ break;
+ case AFMT_S32_BE:
+ enc = AUDIO_ENCODING_SLINEAR_BE;
+ ch->precision = 32;
+ break;
+ case AFMT_U32_LE:
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ ch->precision = 32;
+ break;
+ case AFMT_U32_BE:
+ enc = AUDIO_ENCODING_ULINEAR_BE;
+ ch->precision = 32;
+ break;
default:
enc = 0;
ch->precision = 16;
@@ -3857,83 +3892,135 @@ uaudio_init_params(struct uaudio_softc *sc, struct chan *ch, int mode)
return (0);
}
-void
-uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt)
+struct uaudio_conversion {
+ uint8_t uaudio_fmt;
+ uint8_t uaudio_prec;
+ uint32_t freebsd_fmt;
+};
+
+const struct uaudio_conversion const accepted_conversion[] = {
+ {AUDIO_ENCODING_ULINEAR_LE, 8, AFMT_U8},
+ {AUDIO_ENCODING_ULINEAR_LE, 16, AFMT_U16_LE},
+ {AUDIO_ENCODING_ULINEAR_LE, 24, AFMT_U24_LE},
+ {AUDIO_ENCODING_ULINEAR_LE, 32, AFMT_U32_LE},
+ {AUDIO_ENCODING_ULINEAR_BE, 16, AFMT_U16_BE},
+ {AUDIO_ENCODING_ULINEAR_BE, 24, AFMT_U24_BE},
+ {AUDIO_ENCODING_ULINEAR_BE, 32, AFMT_U32_BE},
+ {AUDIO_ENCODING_SLINEAR_LE, 8, AFMT_S8},
+ {AUDIO_ENCODING_SLINEAR_LE, 16, AFMT_S16_LE},
+ {AUDIO_ENCODING_SLINEAR_LE, 24, AFMT_S24_LE},
+ {AUDIO_ENCODING_SLINEAR_LE, 24, AFMT_S32_LE},
+ {AUDIO_ENCODING_SLINEAR_BE, 16, AFMT_S16_BE},
+ {AUDIO_ENCODING_SLINEAR_BE, 24, AFMT_S24_BE},
+ {AUDIO_ENCODING_SLINEAR_BE, 24, AFMT_S32_BE},
+ {AUDIO_ENCODING_ALAW, 8, AFMT_A_LAW},
+ {AUDIO_ENCODING_ULAW, 8, AFMT_MU_LAW},
+ {0,0,0}
+};
+
+unsigned
+uaudio_query_formats(device_t dev, int reqdir, unsigned maxfmt, struct pcmchan_caps *cap)
{
- int i, pn=0, rn=0;
- int prec, dir;
- u_int32_t fmt;
struct uaudio_softc *sc;
-
- const struct usb_audio_streaming_type1_descriptor *a1d;
+ const struct usb_audio_streaming_type1_descriptor *asf1d;
+ const struct uaudio_conversion *iterator;
+ unsigned fmtcount, foundcount;
+ u_int32_t fmt;
+ uint8_t format, numchan, subframesize, prec, dir, iscontinuous;
+ int freq, freq_min, freq_max;
+ char *numchannel_descr;
+ char freq_descr[64];
+ int i,r;
sc = device_get_softc(dev);
+ if (sc == NULL)
+ return 0;
+
+ cap->minspeed = cap->maxspeed = 0;
+ foundcount = fmtcount = 0;
for (i = 0; i < sc->sc_nalts; i++) {
- fmt = 0;
- a1d = sc->sc_alts[i].asf1desc;
- prec = a1d->bBitResolution; /* precision */
+ dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
- switch (sc->sc_alts[i].encoding) {
- case AUDIO_ENCODING_ULINEAR_LE:
- if (prec == 8) {
- fmt = AFMT_U8;
- } else if (prec == 16) {
- fmt = AFMT_U16_LE;
- }
- break;
- case AUDIO_ENCODING_SLINEAR_LE:
- if (prec == 8) {
- fmt = AFMT_S8;
- } else if (prec == 16) {
- fmt = AFMT_S16_LE;
- }
- break;
- case AUDIO_ENCODING_ULINEAR_BE:
- if (prec == 16) {
- fmt = AFMT_U16_BE;
- }
- break;
- case AUDIO_ENCODING_SLINEAR_BE:
- if (prec == 16) {
- fmt = AFMT_S16_BE;
- }
- break;
- case AUDIO_ENCODING_ALAW:
- if (prec == 8) {
- fmt = AFMT_A_LAW;
- }
- break;
- case AUDIO_ENCODING_ULAW:
- if (prec == 8) {
- fmt = AFMT_MU_LAW;
- }
- break;
- }
+ if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY))
+ continue;
- if (fmt != 0) {
- if (a1d->bNrChannels == 2) { /* stereo/mono */
- fmt |= AFMT_STEREO;
- } else if (a1d->bNrChannels != 1) {
- fmt = 0;
- }
+ asf1d = sc->sc_alts[i].asf1desc;
+ format = sc->sc_alts[i].encoding;
+
+ numchan = asf1d->bNrChannels;
+ subframesize = asf1d->bSubFrameSize;
+ prec = asf1d->bBitResolution; /* precision */
+ iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS;
+
+ if (iscontinuous)
+ snprintf(freq_descr, sizeof(freq_descr), "continous min %d max %d", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+ else
+ snprintf(freq_descr, sizeof(freq_descr), "fixed frequency (%d listed formats)", asf1d->bSamFreqType);
+
+ if (numchan == 1)
+ numchannel_descr = " (mono)";
+ else if (numchan == 2)
+ numchannel_descr = " (stereo)";
+ else
+ numchannel_descr = "";
+
+ if (bootverbose) {
+ device_printf(dev, "uaudio_query_formats: found a native %s channel%s %s %dbit %dbytes/subframe X %d channels = %d bytes per sample\n",
+ (dir==UE_DIR_OUT)?"playback":"record",
+ numchannel_descr, freq_descr,
+ prec, subframesize, numchan, subframesize*numchan);
}
+ /*
+ * Now start rejecting the ones that don't map to FreeBSD
+ */
+
+ if (numchan != 1 && numchan != 2)
+ continue;
+
+ for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++)
+ if (iterator->uaudio_fmt == format && iterator->uaudio_prec == prec)
+ break;
+
+ if (iterator->uaudio_fmt == 0)
+ continue;
+
+ fmt = iterator->freebsd_fmt;
+
+ if (numchan == 2)
+ fmt |= AFMT_STEREO;
+
+ foundcount++;
- if (fmt != 0) {
- dir= UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
- if (dir == UE_DIR_OUT) {
- pfmt[pn++] = fmt;
- } else if (dir == UE_DIR_IN) {
- rfmt[rn++] = fmt;
+ if (fmtcount >= maxfmt)
+ continue;
+
+ cap->fmtlist[fmtcount++] = fmt;
+
+ if (iscontinuous) {
+ freq_min = UA_SAMP_LO(asf1d);
+ freq_max = UA_SAMP_HI(asf1d);
+
+ if (cap->minspeed == 0 || freq_min < cap->minspeed)
+ cap->minspeed = freq_min;
+ if (cap->maxspeed == 0)
+ cap->maxspeed = cap->minspeed;
+ if (freq_max > cap->maxspeed)
+ cap->maxspeed = freq_max;
+ } else {
+ for (r = 0; r < asf1d->bSamFreqType; r++) {
+ freq = UA_GETSAMP(asf1d, r);
+ if (cap->minspeed == 0 || freq < cap->minspeed)
+ cap->minspeed = freq;
+ if (cap->maxspeed == 0)
+ cap->maxspeed = cap->minspeed;
+ if (freq > cap->maxspeed)
+ cap->maxspeed = freq;
}
}
-
- if ((pn > 8*2) || (rn > 8*2))
- break;
}
- pfmt[pn] = 0;
- rfmt[rn] = 0;
- return;
+ cap->fmtlist[fmtcount] = 0;
+ return foundcount;
}
void
@@ -3982,25 +4069,81 @@ uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir)
return;
}
-void
-uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int dir)
+int
+uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir)
{
+ const struct uaudio_conversion *iterator;
struct uaudio_softc *sc;
struct chan *ch;
+ int i, r, score, hiscore, bestspeed;
sc = device_get_softc(dev);
#ifndef NO_RECORDING
- if (dir == PCMDIR_PLAY)
+ if (reqdir == PCMDIR_PLAY)
ch = &sc->sc_playchan;
else
ch = &sc->sc_recchan;
#else
ch = &sc->sc_playchan;
#endif
+ /*
+ * We are successful if we find an endpoint that matches our selected format and it
+ * supports the requested speed.
+ */
+ hiscore = 0;
+ bestspeed = 1;
+ for (i = 0; i < sc->sc_nalts; i++) {
+ int dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
+ int format = sc->sc_alts[i].encoding;
+ const struct usb_audio_streaming_type1_descriptor *asf1d = sc->sc_alts[i].asf1desc;
+ int iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS;
- ch->sample_rate = speed;
+ if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY))
+ continue;
- return;
+ for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++)
+ if (iterator->uaudio_fmt != format || iterator->freebsd_fmt != (ch->format&0xfffffff))
+ continue;
+ if (iscontinuous) {
+ if (speed >= UA_SAMP_LO(asf1d) && speed <= UA_SAMP_HI(asf1d)) {
+ ch->sample_rate = speed;
+ return speed;
+ } else if (speed < UA_SAMP_LO(asf1d)) {
+ score = 0xfff * speed / UA_SAMP_LO(asf1d);
+ if (score > hiscore) {
+ bestspeed = UA_SAMP_LO(asf1d);
+ hiscore = score;
+ }
+ } else if (speed < UA_SAMP_HI(asf1d)) {
+ score = 0xfff * UA_SAMP_HI(asf1d) / speed;
+ if (score > hiscore) {
+ bestspeed = UA_SAMP_HI(asf1d);
+ hiscore = score;
+ }
+ }
+ continue;
+ }
+ for (r = 0; r < asf1d->bSamFreqType; r++) {
+ if (speed == UA_GETSAMP(asf1d, r)) {
+ ch->sample_rate = speed;
+ return speed;
+ }
+ if (speed > UA_GETSAMP(asf1d, r))
+ score = 0xfff * UA_GETSAMP(asf1d, r) / speed;
+ else
+ score = 0xfff * speed / UA_GETSAMP(asf1d, r);
+ if (score > hiscore) {
+ bestspeed = UA_GETSAMP(asf1d, r);
+ hiscore = score;
+ }
+ }
+ }
+ if (bestspeed != 1) {
+ ch->sample_rate = bestspeed;
+ return bestspeed;
+ }
+
+ return 0;
}
int
OpenPOWER on IntegriCloud