From e2524b2ec945fce902a3f4d7dc13a1955c1f10b8 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Mon, 17 Sep 2012 15:43:57 +0000 Subject: Implement support for USB Audio v2.0. Remove some redundant USB audio v1.0 debug data, hence userspace tools like lsusb exist to show this information properly. --- sys/dev/sound/usb/uaudio.c | 2025 +++++++++++++++++++++++++++++------------ sys/dev/sound/usb/uaudioreg.h | 28 +- 2 files changed, 1475 insertions(+), 578 deletions(-) (limited to 'sys') diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c index 271d9a2778c4..a5b2b3c27687 100644 --- a/sys/dev/sound/usb/uaudio.c +++ b/sys/dev/sound/usb/uaudio.c @@ -115,13 +115,28 @@ SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW, #endif #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_NCHANBUFS 2 /* number of outstanding request */ +#define UAUDIO_RECURSE_LIMIT 255 /* rounds */ #define MAKE_WORD(h,l) (((h) << 8) | (l)) #define BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1) #define UAUDIO_MAX_CHAN(x) (x) +union uaudio_asid { + const struct usb_audio_streaming_interface_descriptor *v1; + const struct usb_audio20_streaming_interface_descriptor *v2; +}; + +union uaudio_asf1d { + const struct usb_audio_streaming_type1_descriptor *v1; + const struct usb_audio20_streaming_type1_descriptor *v2; +}; + +union uaudio_sed { + const struct usb_audio_streaming_endpoint_descriptor *v1; + const struct usb_audio20_streaming_endpoint_descriptor *v2; +}; + struct uaudio_mixer_node { int32_t minval; int32_t maxval; @@ -162,11 +177,9 @@ struct uaudio_chan { struct uaudio_softc *priv_sc; struct pcm_channel *pcm_ch; struct usb_xfer *xfer[UAUDIO_NCHANBUFS]; - const struct usb_audio_streaming_interface_descriptor *p_asid; - const struct usb_audio_streaming_type1_descriptor *p_asf1d; - const struct usb_audio_streaming_endpoint_descriptor *p_sed; + union uaudio_asf1d p_asf1d; + union uaudio_sed p_sed; const usb_endpoint_descriptor_audio_t *p_ed1; - const usb_endpoint_descriptor_audio_t *p_ed2; const struct uaudio_format *p_fmt; uint8_t *buf; /* pointer to buffer */ @@ -192,6 +205,7 @@ struct uaudio_chan { uint8_t valid; uint8_t iface_index; uint8_t iface_alt_index; + uint8_t channels; }; #define UMIDI_CABLES_MAX 16 /* units */ @@ -242,12 +256,21 @@ struct umidi_chan { uint8_t single_command; }; +struct uaudio_search_result { + uint8_t bit_input[(256 + 7) / 8]; + uint8_t bit_output[(256 + 7) / 8]; + uint8_t recurse_level; + uint8_t id_max; + uint8_t is_input; +}; + struct uaudio_softc { struct sbuf sc_sndstat; struct sndcard_func sc_sndcard_func; struct uaudio_chan sc_rec_chan; struct uaudio_chan sc_play_chan; struct umidi_chan sc_midi_chan; + struct uaudio_search_result sc_mixer_clocks; struct usb_device *sc_udev; struct usb_xfer *sc_mixer_xfer[1]; @@ -273,24 +296,28 @@ struct uaudio_softc { uint8_t sc_uq_au_vendor_class:1; }; -struct uaudio_search_result { - uint8_t bit_input[(256 + 7) / 8]; - uint8_t bit_output[(256 + 7) / 8]; - uint8_t bit_visited[(256 + 7) / 8]; - uint8_t recurse_level; - uint8_t id_max; -}; - struct uaudio_terminal_node { union { const struct usb_descriptor *desc; - const struct usb_audio_input_terminal *it; - const struct usb_audio_output_terminal *ot; - const struct usb_audio_mixer_unit_0 *mu; - const struct usb_audio_selector_unit *su; - const struct usb_audio_feature_unit *fu; - const struct usb_audio_processing_unit_0 *pu; - const struct usb_audio_extension_unit_0 *eu; + const struct usb_audio_input_terminal *it_v1; + const struct usb_audio_output_terminal *ot_v1; + const struct usb_audio_mixer_unit_0 *mu_v1; + const struct usb_audio_selector_unit *su_v1; + const struct usb_audio_feature_unit *fu_v1; + const struct usb_audio_processing_unit_0 *pu_v1; + const struct usb_audio_extension_unit_0 *eu_v1; + const struct usb_audio20_clock_source_unit *csrc_v2; + const struct usb_audio20_clock_selector_unit_0 *csel_v2; + const struct usb_audio20_clock_multiplier_unit *cmul_v2; + const struct usb_audio20_input_terminal *it_v2; + const struct usb_audio20_output_terminal *ot_v2; + const struct usb_audio20_mixer_unit_0 *mu_v2; + const struct usb_audio20_selector_unit *su_v2; + const struct usb_audio20_feature_unit *fu_v2; + const struct usb_audio20_sample_rate_unit *ru_v2; + const struct usb_audio20_processing_unit_0 *pu_v2; + const struct usb_audio20_extension_unit_0 *eu_v2; + const struct usb_audio20_effect_unit *ef_v2; } u; struct uaudio_search_result usr; struct uaudio_terminal_node *root; @@ -303,7 +330,7 @@ struct uaudio_format { const char *description; }; -static const struct uaudio_format uaudio_formats[] = { +static const struct uaudio_format uaudio10_formats[] = { {UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"}, {UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"}, @@ -321,6 +348,24 @@ static const struct uaudio_format uaudio_formats[] = { {0, 0, 0, NULL} }; +static const struct uaudio_format uaudio20_formats[] = { + + {UA20_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"}, + {UA20_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"}, + {UA20_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"}, + {UA20_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"}, + + {UA20_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"}, + {UA20_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"}, + {UA20_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"}, + {UA20_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"}, + + {UA20_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"}, + {UA20_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"}, + + {0, 0, 0, NULL} +}; + #define UAC_OUTPUT 0 #define UAC_INPUT 1 #define UAC_EQUAL 2 @@ -346,18 +391,8 @@ static usb_callback_t uaudio_mixer_write_cfg_callback; static usb_callback_t umidi_bulk_read_callback; static usb_callback_t umidi_bulk_write_callback; -static void uaudio_chan_fill_info_sub(struct uaudio_softc *, - 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 *, - struct uaudio_mixer_node *); -static void uaudio_mixer_add_ctl(struct uaudio_softc *, - struct uaudio_mixer_node *); -static void uaudio_mixer_add_input(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); -static void uaudio_mixer_add_output(struct uaudio_softc *, - const struct uaudio_terminal_node *, int); +/* ==== USB audio v1.0 ==== */ + static void uaudio_mixer_add_mixer(struct uaudio_softc *, const struct uaudio_terminal_node *, int); static void uaudio_mixer_add_selector(struct uaudio_softc *, @@ -378,25 +413,56 @@ static uint16_t uaudio_mixer_determine_class(const struct uaudio_terminal_node * struct uaudio_mixer_node *); static uint16_t uaudio_mixer_feature_name(const struct uaudio_terminal_node *, struct uaudio_mixer_node *); -static const struct uaudio_terminal_node *uaudio_mixer_get_input( - const struct uaudio_terminal_node *, uint8_t); -static const struct uaudio_terminal_node *uaudio_mixer_get_output( - const struct uaudio_terminal_node *, uint8_t); static void uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *, const uint8_t *, uint8_t, struct uaudio_search_result *); -static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *, - uint8_t, uint8_t, struct uaudio_search_result *); +static const void *uaudio_mixer_verify_desc(const void *, uint32_t); +static usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t); +static int uaudio_mixer_get(struct usb_device *, uint16_t, uint8_t, + struct uaudio_mixer_node *); + +/* ==== USB audio v2.0 ==== */ + +static void uaudio20_mixer_add_mixer(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static void uaudio20_mixer_add_selector(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static void uaudio20_mixer_add_feature(struct uaudio_softc *, + const struct uaudio_terminal_node *, int); +static struct usb_audio20_cluster uaudio20_mixer_get_cluster(uint8_t, + const struct uaudio_terminal_node *); +static uint16_t uaudio20_mixer_determine_class(const struct uaudio_terminal_node *, + struct uaudio_mixer_node *); +static uint16_t uaudio20_mixer_feature_name(const struct uaudio_terminal_node *, + struct uaudio_mixer_node *); +static void uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *, + const uint8_t *, uint8_t, struct uaudio_search_result *); +static const void *uaudio20_mixer_verify_desc(const void *, uint32_t); +static usb_error_t uaudio20_set_speed(struct usb_device *, uint8_t, + uint8_t, uint32_t); + +/* USB audio v1.0 and v2.0 */ + +static void uaudio_chan_fill_info_sub(struct uaudio_softc *, + 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 *, + struct uaudio_mixer_node *); +static void uaudio_mixer_add_ctl(struct uaudio_softc *, + struct uaudio_mixer_node *); static void uaudio_mixer_fill_info(struct uaudio_softc *, struct usb_device *, void *); -static uint16_t uaudio_mixer_get(struct usb_device *, uint8_t, - struct uaudio_mixer_node *); static void uaudio_mixer_ctl_set(struct uaudio_softc *, struct uaudio_mixer_node *, uint8_t, int32_t val); -static usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t); static int uaudio_mixer_signext(uint8_t, int); static int uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val); -static const void *uaudio_mixer_verify_desc(const void *, uint32_t); static void uaudio_mixer_init(struct uaudio_softc *); +static const struct uaudio_terminal_node *uaudio_mixer_get_input( + const struct uaudio_terminal_node *, uint8_t); +static const struct uaudio_terminal_node *uaudio_mixer_get_output( + const struct uaudio_terminal_node *, uint8_t); +static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *, + uint8_t, uint8_t, struct uaudio_search_result *); static uint8_t umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t); static struct umidi_sub_chan *umidi_sub_by_fifo(struct usb_fifo *); static void umidi_start_read(struct usb_fifo *); @@ -413,9 +479,6 @@ static int umidi_detach(device_t dev); #ifdef USB_DEBUG static void uaudio_chan_dump_ep_desc( const usb_endpoint_descriptor_audio_t *); -static void uaudio_mixer_dump_cluster(uint8_t, - const struct uaudio_terminal_node *); -static const char *uaudio_mixer_get_terminal_name(uint16_t); #endif static const struct usb_config @@ -614,10 +677,12 @@ uaudio_attach(device_t dev) id = usbd_get_interface_descriptor(uaa->iface); - uaudio_chan_fill_info(sc, uaa->device); - + /* must fill mixer info before channel info */ uaudio_mixer_fill_info(sc, uaa->device, id); + /* fill channel info */ + uaudio_chan_fill_info(sc, uaa->device); + DPRINTF("audio rev %d.%02x\n", sc->sc_audio_rev >> 8, sc->sc_audio_rev & 0xff); @@ -628,7 +693,7 @@ uaudio_attach(device_t dev) if (sc->sc_play_chan.valid) { device_printf(dev, "Play: %d Hz, %d ch, %s format.\n", sc->sc_play_chan.sample_rate, - sc->sc_play_chan.p_asf1d->bNrChannels, + sc->sc_play_chan.channels, sc->sc_play_chan.p_fmt->description); } else { device_printf(dev, "No playback.\n"); @@ -637,7 +702,7 @@ uaudio_attach(device_t dev) if (sc->sc_rec_chan.valid) { device_printf(dev, "Record: %d Hz, %d ch, %s format.\n", sc->sc_rec_chan.sample_rate, - sc->sc_rec_chan.p_asf1d->bNrChannels, + sc->sc_play_chan.channels, sc->sc_rec_chan.p_fmt->description); } else { device_printf(dev, "No recording.\n"); @@ -857,28 +922,88 @@ uaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep, } } +static usb_error_t +uaudio20_check_rate(struct usb_device *udev, uint8_t iface_no, + uint8_t clockid, uint32_t rate) +{ + struct usb_device_request req; + usb_error_t error; + uint8_t data[255]; + uint16_t actlen; + uint16_t rates; + uint16_t x; + + DPRINTFN(6, "ifaceno=%d clockid=%d rate=%u\n", + iface_no, clockid, rate); + + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UA20_CS_RANGE; + USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0); + USETW2(req.wIndex, clockid, iface_no); + USETW(req.wLength, 255); + + error = usbd_do_request_flags(udev, NULL, &req, data, + USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT); + + if (error != 0 || actlen < 2) + return (USB_ERR_INVAL); + + rates = data[0] | (data[1] << 8); + actlen = (actlen - 2) / 12; + + if (rates > actlen) { + DPRINTF("Too many rates\n"); + rates = actlen; + } + + for (x = 0; x != rates; x++) { + uint32_t min = UGETDW(data + 2 + (12 * x)); + uint32_t max = UGETDW(data + 6 + (12 * x)); + uint32_t res = UGETDW(data + 10 + (12 * x)); + + if (res == 0) { + DPRINTF("Zero residue\n"); + res = 1; + } + + if (min > max) { + DPRINTF("Swapped max and min\n"); + uint32_t temp; + temp = min; + min = max; + max = temp; + } + + if (rate >= min && rate <= max && + (((rate - min) % res) == 0)) { + return (0); + } + } + return (USB_ERR_INVAL); +} + static void uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev, uint32_t rate, uint8_t channels, uint8_t bit_resolution) { struct usb_descriptor *desc = NULL; - const struct usb_audio_streaming_interface_descriptor *asid = NULL; - const struct usb_audio_streaming_type1_descriptor *asf1d = NULL; - const struct usb_audio_streaming_endpoint_descriptor *sed = NULL; + union uaudio_asid asid = { NULL }; + union uaudio_asf1d asf1d = { NULL }; + union uaudio_sed sed = { NULL }; usb_endpoint_descriptor_audio_t *ed1 = NULL; - const usb_endpoint_descriptor_audio_t *ed2 = NULL; + const struct usb_audio_control_descriptor *acdp = NULL; struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); struct usb_interface_descriptor *id; - const struct uaudio_format *p_fmt; + const struct uaudio_format *p_fmt = NULL; struct uaudio_chan *chan; uint16_t curidx = 0xFFFF; uint16_t lastidx = 0xFFFF; uint16_t alt_index = 0; - uint16_t wFormat; + uint16_t audio_rev = 0; + uint16_t x; uint8_t ep_dir; uint8_t bChannels; uint8_t bBitResolution; - uint8_t x; uint8_t audio_if = 0; uint8_t uma_if_class; @@ -923,171 +1048,258 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev, sc->sc_midi_chan.valid = 1; } } - asid = NULL; - asf1d = NULL; + asid.v1 = NULL; + asf1d.v1 = NULL; ed1 = NULL; - ed2 = NULL; - sed = NULL; + sed.v1 = NULL; + } + + if ((acdp == NULL) && + (desc->bDescriptorType == UDESC_CS_INTERFACE) && + (desc->bDescriptorSubtype == AS_GENERAL) && + (desc->bDescriptorSubtype == UDESCSUB_AC_HEADER) && + (desc->bLength >= sizeof(*acdp))) { + acdp = (void *)desc; + audio_rev = UGETW(acdp->bcdADC); } - if ((desc->bDescriptorType == UDESC_CS_INTERFACE) && + + if ((acdp != NULL) && + (desc->bDescriptorType == UDESC_CS_INTERFACE) && (desc->bDescriptorSubtype == AS_GENERAL) && - (desc->bLength >= sizeof(*asid))) { - if (asid == NULL) { - asid = (void *)desc; + (asid.v1 == NULL)) { + if (audio_rev >= UAUDIO_VERSION_30) { + /* FALLTHROUGH */ + } else if (audio_rev >= UAUDIO_VERSION_20) { + if (desc->bLength >= sizeof(*asid.v2)) { + asid.v2 = (void *)desc; + } + } else { + if (desc->bLength >= sizeof(*asid.v1)) { + asid.v1 = (void *)desc; + } } } - if ((desc->bDescriptorType == UDESC_CS_INTERFACE) && + if ((acdp != NULL) && + (desc->bDescriptorType == UDESC_CS_INTERFACE) && (desc->bDescriptorSubtype == FORMAT_TYPE) && - (desc->bLength >= sizeof(*asf1d))) { - if (asf1d == NULL) { - asf1d = (void *)desc; - if (asf1d->bFormatType != FORMAT_TYPE_I) { - DPRINTFN(11, "ignored bFormatType = %d\n", - asf1d->bFormatType); - asf1d = NULL; - continue; - } - if (asf1d->bLength < (sizeof(*asf1d) + - ((asf1d->bSamFreqType == 0) ? 6 : - (asf1d->bSamFreqType * 3)))) { - DPRINTFN(11, "'asf1d' descriptor is too short\n"); - asf1d = NULL; - continue; + (asf1d.v1 == NULL)) { + if (audio_rev >= UAUDIO_VERSION_30) { + /* FALLTHROUGH */ + } else if (audio_rev >= UAUDIO_VERSION_20) { + if (desc->bLength >= sizeof(*asf1d.v2)) + asf1d.v2 = (void *)desc; + } else { + if (desc->bLength >= sizeof(*asf1d.v1)) { + asf1d.v1 = (void *)desc; + + if (asf1d.v1->bFormatType != FORMAT_TYPE_I) { + DPRINTFN(11, "ignored bFormatType = %d\n", + asf1d.v1->bFormatType); + asf1d.v1 = NULL; + continue; + } + if (desc->bLength < (sizeof(*asf1d.v1) + + ((asf1d.v1->bSamFreqType == 0) ? 6 : + (asf1d.v1->bSamFreqType * 3)))) { + DPRINTFN(11, "invalid descriptor, " + "too short\n"); + asf1d.v1 = NULL; + continue; + } } } } if ((desc->bDescriptorType == UDESC_ENDPOINT) && - (desc->bLength >= UEP_MINSIZE)) { - if (ed1 == NULL) { - ed1 = (void *)desc; - if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) { - ed1 = NULL; - } + (desc->bLength >= UEP_MINSIZE) && + (ed1 == NULL)) { + ed1 = (void *)desc; + if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) { + ed1 = NULL; + continue; } } - if ((desc->bDescriptorType == UDESC_CS_ENDPOINT) && + if ((acdp != NULL) && + (desc->bDescriptorType == UDESC_CS_ENDPOINT) && (desc->bDescriptorSubtype == AS_GENERAL) && - (desc->bLength >= sizeof(*sed))) { - if (sed == NULL) { - sed = (void *)desc; + (sed.v1 == NULL)) { + if (audio_rev >= UAUDIO_VERSION_30) { + /* FALLTHROUGH */ + } else if (audio_rev >= UAUDIO_VERSION_20) { + if (desc->bLength >= sizeof(*sed.v2)) + sed.v2 = (void *)desc; + } else { + if (desc->bLength >= sizeof(*sed.v1)) + sed.v1 = (void *)desc; } } - if (audio_if && asid && asf1d && ed1 && sed) { + if (audio_if == 0 || asid.v1 == NULL || + asf1d.v1 == NULL || ed1 == NULL || + sed.v1 == NULL) { + /* need more descriptors */ + continue; + } + + ep_dir = UE_GET_DIR(ed1->bEndpointAddress); + + /* We ignore sync endpoint information until further. */ + + if (audio_rev >= UAUDIO_VERSION_30) { + goto next_ep; + } else if (audio_rev >= UAUDIO_VERSION_20) { + + uint32_t dwFormat; + uint8_t bSubslotSize; + + dwFormat = UGETDW(asid.v2->bmFormats); + bChannels = asid.v2->bNrChannels; + bBitResolution = asf1d.v2->bBitResolution; + bSubslotSize = asf1d.v2->bSubslotSize; + + if (bBitResolution != (bSubslotSize * 8)) { + DPRINTF("Invalid bSubslotSize\n"); + goto next_ep; + } + + if ((bChannels != channels) || + (bBitResolution != bit_resolution)) { + DPRINTF("Wrong number of channels\n"); + goto next_ep; + } + + for (p_fmt = uaudio20_formats; + p_fmt->wFormat != 0; p_fmt++) { + if ((p_fmt->wFormat & dwFormat) && + (p_fmt->bPrecision == bBitResolution)) + break; + } + + if (p_fmt->wFormat == 0) { + DPRINTF("Unsupported audio format\n"); + goto next_ep; + } + + for (x = 0; x != 256; x++) { + if (ep_dir == UE_DIR_OUT) { + 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; + } + } - ep_dir = UE_GET_DIR(ed1->bEndpointAddress); + DPRINTF("Checking clock ID=%d\n", x); - /* We ignore sync endpoint information until further. */ + if (uaudio20_check_rate(udev, + sc->sc_mixer_iface_no, x, rate)) { + DPRINTF("Unsupported sampling " + "rate, id=%d\n", x); + goto next_ep; + } + } + } else { + uint16_t wFormat; - wFormat = UGETW(asid->wFormatTag); - bChannels = UAUDIO_MAX_CHAN(asf1d->bNrChannels); - bBitResolution = asf1d->bBitResolution; + wFormat = UGETW(asid.v1->wFormatTag); + bChannels = UAUDIO_MAX_CHAN(asf1d.v1->bNrChannels); + bBitResolution = asf1d.v1->bBitResolution; - if (asf1d->bSamFreqType == 0) { + if (asf1d.v1->bSamFreqType == 0) { DPRINTFN(16, "Sample rate: %d-%dHz\n", - UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d)); + UA_SAMP_LO(asf1d.v1), + UA_SAMP_HI(asf1d.v1)); - if ((rate >= UA_SAMP_LO(asf1d)) && - (rate <= UA_SAMP_HI(asf1d))) { + if ((rate >= UA_SAMP_LO(asf1d.v1)) && + (rate <= UA_SAMP_HI(asf1d.v1))) goto found_rate; - } } else { - for (x = 0; x < asf1d->bSamFreqType; x++) { + for (x = 0; x < asf1d.v1->bSamFreqType; x++) { DPRINTFN(16, "Sample rate = %dHz\n", - UA_GETSAMP(asf1d, x)); + UA_GETSAMP(asf1d.v1, x)); - if (rate == UA_GETSAMP(asf1d, x)) { + if (rate == UA_GETSAMP(asf1d.v1, x)) goto found_rate; - } } } - - audio_if = 0; - continue; + goto next_ep; found_rate: - - for (p_fmt = uaudio_formats; - p_fmt->wFormat; - p_fmt++) { + for (p_fmt = uaudio10_formats; + p_fmt->wFormat != 0; p_fmt++) { if ((p_fmt->wFormat == wFormat) && - (p_fmt->bPrecision == bBitResolution)) { - goto found_format; - } + (p_fmt->bPrecision == bBitResolution)) + break; + } + if (p_fmt->wFormat == 0) { + DPRINTF("Unsupported audio format\n"); + goto next_ep; } - audio_if = 0; - continue; - - found_format: - - if ((bChannels == channels) && - (bBitResolution == bit_resolution)) { + if ((bChannels != channels) || + (bBitResolution != bit_resolution)) { + DPRINTF("Wrong number of channels\n"); + goto next_ep; + } + } - chan = (ep_dir == UE_DIR_IN) ? - &sc->sc_rec_chan : - &sc->sc_play_chan; + chan = (ep_dir == UE_DIR_IN) ? + &sc->sc_rec_chan : &sc->sc_play_chan; - if ((chan->valid == 0) && usbd_get_iface(udev, curidx)) { + if (chan->valid != 0 || + usbd_get_iface(udev, curidx) == NULL) { + DPRINTF("Channel already exists or " + "interface is not valid\n"); + goto next_ep; + } - chan->valid = 1; + chan->valid = 1; #ifdef USB_DEBUG - uaudio_chan_dump_ep_desc(ed1); - uaudio_chan_dump_ep_desc(ed2); - - if (sed->bmAttributes & UA_SED_FREQ_CONTROL) { - DPRINTFN(2, "FREQ_CONTROL\n"); - } - if (sed->bmAttributes & UA_SED_PITCH_CONTROL) { - DPRINTFN(2, "PITCH_CONTROL\n"); - } + uaudio_chan_dump_ep_desc(ed1); #endif - DPRINTF("Sample rate = %dHz, channels = %d, " - "bits = %d, format = %s\n", rate, channels, - bit_resolution, p_fmt->description); - - chan->sample_rate = rate; - chan->p_asid = asid; - chan->p_asf1d = asf1d; - chan->p_ed1 = ed1; - chan->p_ed2 = ed2; - chan->p_fmt = p_fmt; - chan->p_sed = sed; - chan->iface_index = curidx; - chan->iface_alt_index = alt_index; - - if (ep_dir == UE_DIR_IN) - chan->usb_cfg = - uaudio_cfg_record; - else - chan->usb_cfg = - uaudio_cfg_play; - - chan->sample_size = (( - UAUDIO_MAX_CHAN(chan->p_asf1d->bNrChannels) * - chan->p_asf1d->bBitResolution) / 8); - - 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)); - } + DPRINTF("Sample rate = %dHz, channels = %d, " + "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; + + if (ep_dir == UE_DIR_IN) + chan->usb_cfg = uaudio_cfg_record; + else + chan->usb_cfg = uaudio_cfg_play; - if (sc->sc_sndstat_valid) { - sbuf_printf(&sc->sc_sndstat, "\n\t" - "mode %d.%d:(%s) %dch, %d/%dbit, %s, %dHz", - curidx, alt_index, - (ep_dir == UE_DIR_IN) ? "input" : "output", - asf1d->bNrChannels, asf1d->bBitResolution, - asf1d->bSubFrameSize * 8, - p_fmt->description, rate); - } - } - } - audio_if = 0; - continue; + chan->sample_size = (UAUDIO_MAX_CHAN(channels) * + p_fmt->bPrecision) / 8; + chan->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)); + } + + if (sc->sc_sndstat_valid != 0) { + sbuf_printf(&sc->sc_sndstat, "\n\t" + "mode %d.%d:(%s) %dch, %dbit, %s, %dHz", + curidx, alt_index, + (ep_dir == UE_DIR_IN) ? "input" : "output", + channels, p_fmt->bPrecision, + p_fmt->description, rate); } + + next_ep: + sed.v1 = NULL; + ed1 = NULL; } } @@ -1390,7 +1602,7 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, format = ch->p_fmt->freebsd_fmt; - switch (ch->p_asf1d->bNrChannels) { + switch (ch->channels) { case 2: /* stereo */ format = SND_FORMAT(format, 2, 0); @@ -1402,7 +1614,7 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, default: /* surround and more */ format = feeder_matrix_default_format( - SND_FORMAT(format, ch->p_asf1d->bNrChannels, 0)); + SND_FORMAT(format, ch->channels, 0)); break; } @@ -1438,13 +1650,43 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, * Only set the sample rate if the channel reports that it * supports the frequency control. */ - if (ch->p_sed->bmAttributes & UA_SED_FREQ_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. + * If the endpoint is adaptive setting the + * speed may fail. */ - DPRINTF("setting of sample rate failed! (continuing anyway)\n"); + DPRINTF("setting of sample rate failed! " + "(continuing anyway)\n"); } } if (usbd_transfer_setup(sc->sc_udev, &iface_index, ch->xfer, @@ -1679,13 +1921,10 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) /* determine min and max values */ - mc->minval = uaudio_mixer_get(sc->sc_udev, GET_MIN, mc); - - mc->minval = uaudio_mixer_signext(mc->type, mc->minval); - - mc->maxval = uaudio_mixer_get(sc->sc_udev, GET_MAX, mc); - - mc->maxval = uaudio_mixer_signext(mc->type, mc->maxval); + mc->minval = uaudio_mixer_get(sc->sc_udev, + sc->sc_audio_rev, GET_MIN, mc); + mc->maxval = uaudio_mixer_get(sc->sc_udev, + sc->sc_audio_rev, GET_MAX, mc); /* check if max and min was swapped */ @@ -1701,7 +1940,8 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) mc->mul = 1; /* compute value alignment */ - res = uaudio_mixer_get(sc->sc_udev, GET_RES, mc); + res = uaudio_mixer_get(sc->sc_udev, + sc->sc_audio_rev, GET_RES, mc); DPRINTF("Resolution = %d\n", (int)res); } @@ -1723,43 +1963,13 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) #endif } -static void -uaudio_mixer_add_input(struct uaudio_softc *sc, - const struct uaudio_terminal_node *iot, int id) -{ -#ifdef USB_DEBUG - const struct usb_audio_input_terminal *d = iot[id].u.it; - - DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x " - "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d " - "iChannelNames=%d\n", - d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, - d->bNrChannels, UGETW(d->wChannelConfig), - d->iChannelNames); -#endif -} - -static void -uaudio_mixer_add_output(struct uaudio_softc *sc, - const struct uaudio_terminal_node *iot, int id) -{ -#ifdef USB_DEBUG - const struct usb_audio_output_terminal *d = iot[id].u.ot; - - DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x " - "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", - d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, - d->bSourceId, d->iTerminal); -#endif -} - static void uaudio_mixer_add_mixer(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { struct uaudio_mixer_node mix; - const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu; + const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu_v1; const struct usb_audio_mixer_unit_1 *d1; uint32_t bno; /* bit number */ @@ -1780,8 +1990,8 @@ uaudio_mixer_add_mixer(struct uaudio_softc *sc, ichs = 0; for (i = 0; i < d0->bNrInPins; i++) { - ichs += (uaudio_mixer_get_cluster(d0->baSourceId[i], iot) - .bNrChannels); + ichs += uaudio_mixer_get_cluster( + d0->baSourceId[i], iot).bNrChannels; } d1 = (const void *)(d0->baSourceId + d0->bNrInPins); @@ -1798,23 +2008,22 @@ uaudio_mixer_add_mixer(struct uaudio_softc *sc, uaudio_mixer_determine_class(&iot[id], &mix); mix.type = MIX_SIGNED_16; - if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) { + if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) return; - } + for (p = i = 0; i < d0->bNrInPins; i++) { - chs = uaudio_mixer_get_cluster(d0->baSourceId[i], iot).bNrChannels; + chs = uaudio_mixer_get_cluster( + d0->baSourceId[i], iot).bNrChannels; mc = 0; for (c = 0; c < chs; c++) { mo = 0; for (o = 0; o < ochs; o++) { bno = ((p + c) * ochs) + o; - if (BIT_TEST(d1->bmControls, bno)) { + if (BIT_TEST(d1->bmControls, bno)) mo++; - } } - if (mo == 1) { + if (mo == 1) mc++; - } } if ((mc == chs) && (chs <= MIX_MAX_CHAN)) { @@ -1824,60 +2033,181 @@ uaudio_mixer_add_mixer(struct uaudio_softc *sc, for (c = 0; c < chs; c++) { for (o = 0; o < ochs; o++) { bno = ((p + c) * ochs) + o; - if (BIT_TEST(d1->bmControls, bno)) { + if (BIT_TEST(d1->bmControls, bno)) mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); - } } } mix.nchan = chs; uaudio_mixer_add_ctl(sc, &mix); - } else { - /* XXX */ } p += chs; } } static void -uaudio_mixer_add_selector(struct uaudio_softc *sc, +uaudio20_mixer_add_mixer(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb_audio_selector_unit *d = iot[id].u.su; struct uaudio_mixer_node mix; - uint16_t i; - DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", - d->bUnitId, d->bNrInPins); + const struct usb_audio20_mixer_unit_0 *d0 = iot[id].u.mu_v2; + const struct usb_audio20_mixer_unit_1 *d1; - if (d->bNrInPins == 0) { - return; - } - memset(&mix, 0, sizeof(mix)); + uint32_t bno; /* bit number */ + uint32_t p; /* bit number accumulator */ + uint32_t mo; /* matching outputs */ + uint32_t mc; /* matching channels */ + uint32_t ichs; /* input channels */ + uint32_t ochs; /* output channels */ + uint32_t c; + uint32_t chs; /* channels */ + uint32_t i; + uint32_t o; - mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); - mix.wValue[0] = MAKE_WORD(0, 0); - uaudio_mixer_determine_class(&iot[id], &mix); - mix.nchan = 1; - mix.type = MIX_SELECTOR; + DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", + d0->bUnitId, d0->bNrInPins); - mix.ctl = SOUND_MIXER_NRDEVICES; - mix.minval = 1; - mix.maxval = d->bNrInPins; + /* compute the number of input channels */ - if (mix.maxval > MAX_SELECTOR_INPUT_PIN) { - mix.maxval = MAX_SELECTOR_INPUT_PIN; - } - mix.mul = (mix.maxval - mix.minval); - for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) { - mix.slctrtype[i] = SOUND_MIXER_NRDEVICES; + ichs = 0; + for (i = 0; i < d0->bNrInPins; i++) { + ichs += uaudio20_mixer_get_cluster( + d0->baSourceId[i], iot).bNrChannels; } - for (i = 0; i < mix.maxval; i++) { - mix.slctrtype[i] = uaudio_mixer_feature_name - (&iot[d->baSourceId[i]], &mix); - } + d1 = (const void *)(d0->baSourceId + d0->bNrInPins); - mix.class = 0; /* not used */ + /* and the number of output channels */ + + ochs = d1->bNrChannels; + + DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs); + + memset(&mix, 0, sizeof(mix)); + + mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + uaudio20_mixer_determine_class(&iot[id], &mix); + mix.type = MIX_SIGNED_16; + + if (uaudio20_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) + return; + + for (p = i = 0; i < d0->bNrInPins; i++) { + chs = uaudio20_mixer_get_cluster( + d0->baSourceId[i], iot).bNrChannels; + mc = 0; + for (c = 0; c < chs; c++) { + mo = 0; + for (o = 0; o < ochs; o++) { + bno = ((p + c) * ochs) + o; + if (BIT_TEST(d1->bmControls, bno)) + mo++; + } + if (mo == 1) + mc++; + } + if ((mc == chs) && (chs <= MIX_MAX_CHAN)) { + + /* repeat bit-scan */ + + mc = 0; + for (c = 0; c < chs; c++) { + for (o = 0; o < ochs; o++) { + bno = ((p + c) * ochs) + o; + if (BIT_TEST(d1->bmControls, bno)) + mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); + } + } + mix.nchan = chs; + uaudio_mixer_add_ctl(sc, &mix); + } + p += chs; + } +} + +static void +uaudio_mixer_add_selector(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + const struct usb_audio_selector_unit *d = iot[id].u.su_v1; + struct uaudio_mixer_node mix; + uint16_t i; + + DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", + d->bUnitId, d->bNrInPins); + + if (d->bNrInPins == 0) { + return; + } + memset(&mix, 0, sizeof(mix)); + + mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + mix.wValue[0] = MAKE_WORD(0, 0); + uaudio_mixer_determine_class(&iot[id], &mix); + mix.nchan = 1; + mix.type = MIX_SELECTOR; + + mix.ctl = SOUND_MIXER_NRDEVICES; + mix.minval = 1; + mix.maxval = d->bNrInPins; + + if (mix.maxval > MAX_SELECTOR_INPUT_PIN) { + mix.maxval = MAX_SELECTOR_INPUT_PIN; + } + mix.mul = (mix.maxval - mix.minval); + for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) { + mix.slctrtype[i] = SOUND_MIXER_NRDEVICES; + } + + for (i = 0; i < mix.maxval; i++) { + mix.slctrtype[i] = uaudio_mixer_feature_name( + &iot[d->baSourceId[i]], &mix); + } + + mix.class = 0; /* not used */ + + uaudio_mixer_add_ctl(sc, &mix); +} + +static void +uaudio20_mixer_add_selector(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + const struct usb_audio20_selector_unit *d = iot[id].u.su_v2; + struct uaudio_mixer_node mix; + uint16_t i; + + DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", + d->bUnitId, d->bNrInPins); + + if (d->bNrInPins == 0) + return; + + memset(&mix, 0, sizeof(mix)); + + mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + mix.wValue[0] = MAKE_WORD(0, 0); + uaudio20_mixer_determine_class(&iot[id], &mix); + mix.nchan = 1; + mix.type = MIX_SELECTOR; + + mix.ctl = SOUND_MIXER_NRDEVICES; + mix.minval = 1; + mix.maxval = d->bNrInPins; + + if (mix.maxval > MAX_SELECTOR_INPUT_PIN) + mix.maxval = MAX_SELECTOR_INPUT_PIN; + + mix.mul = (mix.maxval - mix.minval); + for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) + mix.slctrtype[i] = SOUND_MIXER_NRDEVICES; + + for (i = 0; i < mix.maxval; i++) { + mix.slctrtype[i] = uaudio20_mixer_feature_name( + &iot[d->baSourceId[i]], &mix); + } + + mix.class = 0; /* not used */ uaudio_mixer_add_ctl(sc, &mix); } @@ -1908,7 +2238,7 @@ static void uaudio_mixer_add_feature(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb_audio_feature_unit *d = iot[id].u.fu; + const struct usb_audio_feature_unit *d = iot[id].u.fu_v1; struct uaudio_mixer_node mix; uint32_t fumask; uint32_t mmask; @@ -2024,9 +2354,129 @@ uaudio_mixer_add_feature(struct uaudio_softc *sc, break; } - if (mix.type != MIX_UNKNOWN) { + if (mix.type != MIX_UNKNOWN) uaudio_mixer_add_ctl(sc, &mix); + } +} + +static void +uaudio20_mixer_add_feature(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + const struct usb_audio20_feature_unit *d = iot[id].u.fu_v2; + struct uaudio_mixer_node mix; + uint32_t ctl; + uint32_t mmask; + uint32_t cmask; + uint16_t mixernumber; + uint8_t nchan; + uint8_t chan; + uint8_t i; + uint8_t what; + + if (UGETDW(d->bmaControls[0]) == 0) + return; + + memset(&mix, 0, sizeof(mix)); + + nchan = (d->bLength - 6) / 4; + mmask = UGETDW(d->bmaControls[0]); + cmask = 0; + + if (nchan == 0) + return; + + /* figure out what we can control */ + + for (chan = 1; chan < nchan; chan++) + cmask |= UGETDW(d->bmaControls[chan]); + + if (nchan > MIX_MAX_CHAN) + nchan = MIX_MAX_CHAN; + + mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + + for (ctl = 3; ctl != 0; ctl <<= 2) { + + mixernumber = uaudio20_mixer_feature_name(&iot[id], &mix); + + switch (ctl) { + case (3 << 0): + mix.type = MIX_ON_OFF; + mix.ctl = SOUND_MIXER_NRDEVICES; + what = MUTE_CONTROL; + break; + case (3 << 2): + mix.type = MIX_SIGNED_16; + mix.ctl = mixernumber; + what = VOLUME_CONTROL; + break; + case (3 << 4): + mix.type = MIX_SIGNED_8; + mix.ctl = SOUND_MIXER_BASS; + what = BASS_CONTROL; + break; + case (3 << 6): + mix.type = MIX_SIGNED_8; + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + what = MID_CONTROL; + break; + case (3 << 8): + mix.type = MIX_SIGNED_8; + mix.ctl = SOUND_MIXER_TREBLE; + what = TREBLE_CONTROL; + break; + case (3 << 12): + mix.type = MIX_ON_OFF; + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + what = AGC_CONTROL; + break; + case (3 << 14): + mix.type = MIX_UNSIGNED_16; + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + what = DELAY_CONTROL; + break; + case (3 << 16): + mix.type = MIX_ON_OFF; + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + what = BASS_BOOST_CONTROL; + break; + case (3 << 18): + mix.type = MIX_ON_OFF; + mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ + what = LOUDNESS_CONTROL; + break; + case (3 << 20): + mix.type = MIX_SIGNED_16; + mix.ctl = mixernumber; + what = INPUT_GAIN_CONTROL; + break; + case (3 << 22): + mix.type = MIX_SIGNED_16; + mix.ctl = mixernumber; + what = INPUT_GAIN_PAD_CONTROL; + break; + default: + continue; + } + + if ((mmask & ctl) == ctl) { + mix.nchan = 1; + mix.wValue[0] = MAKE_WORD(what, 0); + } else if ((cmask & ctl) == ctl) { + mix.nchan = nchan - 1; + for (i = 1; i < nchan; i++) { + if ((UGETDW(d->bmaControls[i]) & ctl) == ctl) + mix.wValue[i - 1] = MAKE_WORD(what, i); + else + mix.wValue[i - 1] = -1; + } + } else { + continue; } + + if (mix.type != MIX_UNKNOWN) + uaudio_mixer_add_ctl(sc, &mix); } } @@ -2034,7 +2484,7 @@ static void uaudio_mixer_add_processing_updown(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu; + const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1; const struct usb_audio_processing_unit_1 *d1 = (const void *)(d0->baSourceId + d0->bNrInPins); const struct usb_audio_processing_unit_updown *ud = @@ -2076,7 +2526,7 @@ static void uaudio_mixer_add_processing(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu; + const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1; const struct usb_audio_processing_unit_1 *d1 = (const void *)(d0->baSourceId + d0->bNrInPins); struct uaudio_mixer_node mix; @@ -2121,7 +2571,7 @@ static void uaudio_mixer_add_extension(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu; + const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu_v1; const struct usb_audio_extension_unit_1 *d1 = (const void *)(d0->baSourceId + d0->bNrInPins); struct uaudio_mixer_node mix; @@ -2276,34 +2726,161 @@ error: return (NULL); } -#ifdef USB_DEBUG -static void -uaudio_mixer_dump_cluster(uint8_t id, const struct uaudio_terminal_node *iot) -{ - static const char *channel_names[16] = { - "LEFT", "RIGHT", "CENTER", "LFE", - "LEFT_SURROUND", "RIGHT_SURROUND", "LEFT_CENTER", "RIGHT_CENTER", - "SURROUND", "LEFT_SIDE", "RIGHT_SIDE", "TOP", - "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15", - }; - uint16_t cc; - uint8_t i; - const struct usb_audio_cluster cl = uaudio_mixer_get_cluster(id, iot); +static const void * +uaudio20_mixer_verify_desc(const void *arg, uint32_t len) +{ + const struct usb_audio20_mixer_unit_1 *d1; + const struct usb_audio20_extension_unit_1 *e1; + const struct usb_audio20_processing_unit_1 *u1; + const struct usb_audio20_clock_selector_unit_1 *c1; - cc = UGETW(cl.wChannelConfig); + union { + const struct usb_descriptor *desc; + const struct usb_audio20_clock_source_unit *csrc; + const struct usb_audio20_clock_selector_unit_0 *csel; + const struct usb_audio20_clock_multiplier_unit *cmul; + const struct usb_audio20_input_terminal *it; + const struct usb_audio20_output_terminal *ot; + const struct usb_audio20_mixer_unit_0 *mu; + const struct usb_audio20_selector_unit *su; + const struct usb_audio20_feature_unit *fu; + const struct usb_audio20_sample_rate_unit *ru; + const struct usb_audio20_processing_unit_0 *pu; + const struct usb_audio20_extension_unit_0 *eu; + const struct usb_audio20_effect_unit *ef; + } u; - DPRINTF("cluster: bNrChannels=%u iChannelNames=%u wChannelConfig=" - "0x%04x:\n", cl.iChannelNames, cl.bNrChannels, cc); + u.desc = arg; - for (i = 0; cc; i++) { - if (cc & 1) { - DPRINTF(" - %s\n", channel_names[i]); - } - cc >>= 1; + if (u.desc == NULL) + goto error; + + if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) + goto error; + + switch (u.desc->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + len += sizeof(*u.it); + break; + + case UDESCSUB_AC_OUTPUT: + len += sizeof(*u.ot); + break; + + case UDESCSUB_AC_MIXER: + len += sizeof(*u.mu); + + if (u.desc->bLength < len) + goto error; + len += u.mu->bNrInPins; + + if (u.desc->bLength < len) + goto error; + + d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins); + + len += sizeof(*d1) + d1->bNrChannels; + break; + + case UDESCSUB_AC_SELECTOR: + len += sizeof(*u.su); + + if (u.desc->bLength < len) + goto error; + + len += u.su->bNrInPins; + break; + + case UDESCSUB_AC_FEATURE: + len += sizeof(*u.fu) + 1; + + if (u.desc->bLength < len) + goto error; + break; + + case UDESCSUB_AC_EFFECT: + len += sizeof(*u.ef) + 4; + break; + + case UDESCSUB_AC_PROCESSING_V2: + len += sizeof(*u.pu); + + if (u.desc->bLength < len) + goto error; + + len += u.pu->bNrInPins; + + if (u.desc->bLength < len) + goto error; + + u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins); + + len += sizeof(*u1); + break; + + case UDESCSUB_AC_EXTENSION_V2: + len += sizeof(*u.eu); + + if (u.desc->bLength < len) + goto error; + + len += u.eu->bNrInPins; + + if (u.desc->bLength < len) + goto error; + + e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins); + + len += sizeof(*e1); + break; + + case UDESCSUB_AC_CLOCK_SRC: + len += sizeof(*u.csrc); + break; + + case UDESCSUB_AC_CLOCK_SEL: + len += sizeof(*u.csel); + + if (u.desc->bLength < len) + goto error; + + len += u.csel->bNrInPins; + + if (u.desc->bLength < len) + goto error; + + c1 = (const void *)(u.csel->baCSourceId + u.csel->bNrInPins); + + len += sizeof(*c1); + break; + + case UDESCSUB_AC_CLOCK_MUL: + len += sizeof(*u.cmul); + break; + + case UDESCSUB_AC_SAMPLE_RT: + len += sizeof(*u.ru); + break; + + default: + goto error; } -} -#endif + if (u.desc->bLength < len) + goto error; + + return (u.desc); + +error: + if (u.desc) { + DPRINTF("invalid descriptor, type=%d, " + "sub_type=%d, len=%d of %d bytes\n", + u.desc->bDescriptorType, + u.desc->bDescriptorSubtype, + u.desc->bLength, len); + } + return (NULL); +} static struct usb_audio_cluster uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) @@ -2319,43 +2896,43 @@ uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) } switch (dp->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: - r.bNrChannels = iot[id].u.it->bNrChannels; - r.wChannelConfig[0] = iot[id].u.it->wChannelConfig[0]; - r.wChannelConfig[1] = iot[id].u.it->wChannelConfig[1]; - r.iChannelNames = iot[id].u.it->iChannelNames; + r.bNrChannels = iot[id].u.it_v1->bNrChannels; + r.wChannelConfig[0] = iot[id].u.it_v1->wChannelConfig[0]; + r.wChannelConfig[1] = iot[id].u.it_v1->wChannelConfig[1]; + r.iChannelNames = iot[id].u.it_v1->iChannelNames; goto done; case UDESCSUB_AC_OUTPUT: - id = iot[id].u.ot->bSourceId; + id = iot[id].u.ot_v1->bSourceId; break; case UDESCSUB_AC_MIXER: r = *(const struct usb_audio_cluster *) - &iot[id].u.mu->baSourceId[iot[id].u.mu-> - bNrInPins]; + &iot[id].u.mu_v1->baSourceId[ + iot[id].u.mu_v1->bNrInPins]; goto done; case UDESCSUB_AC_SELECTOR: - if (iot[id].u.su->bNrInPins > 0) { + if (iot[id].u.su_v1->bNrInPins > 0) { /* XXX This is not really right */ - id = iot[id].u.su->baSourceId[0]; + id = iot[id].u.su_v1->baSourceId[0]; } break; case UDESCSUB_AC_FEATURE: - id = iot[id].u.fu->bSourceId; + id = iot[id].u.fu_v1->bSourceId; break; case UDESCSUB_AC_PROCESSING: r = *((const struct usb_audio_cluster *) - &iot[id].u.pu->baSourceId[iot[id].u.pu-> - bNrInPins]); + &iot[id].u.pu_v1->baSourceId[ + iot[id].u.pu_v1->bNrInPins]); goto done; case UDESCSUB_AC_EXTENSION: r = *((const struct usb_audio_cluster *) - &iot[id].u.eu->baSourceId[iot[id].u.eu-> - bNrInPins]); + &iot[id].u.eu_v1->baSourceId[ + iot[id].u.eu_v1->bNrInPins]); goto done; default: @@ -2369,110 +2946,138 @@ done: return (r); } -#ifdef USB_DEBUG +static struct usb_audio20_cluster +uaudio20_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) +{ + struct usb_audio20_cluster r; + const struct usb_descriptor *dp; + uint8_t i; -struct uaudio_tt_to_string { - uint16_t terminal_type; - const char *desc; -}; + for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) { /* avoid infinite loops */ + dp = iot[id].u.desc; + if (dp == NULL) + goto error; + + switch (dp->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + r.bNrChannels = iot[id].u.it_v2->bNrChannels; + r.bmChannelConfig[0] = iot[id].u.it_v2->bmChannelConfig[0]; + r.bmChannelConfig[1] = iot[id].u.it_v2->bmChannelConfig[1]; + r.bmChannelConfig[2] = iot[id].u.it_v2->bmChannelConfig[2]; + r.bmChannelConfig[3] = iot[id].u.it_v2->bmChannelConfig[3]; + r.iChannelNames = iot[id].u.it_v2->iTerminal; + goto done; -static const struct uaudio_tt_to_string uaudio_tt_to_string[] = { + case UDESCSUB_AC_OUTPUT: + id = iot[id].u.ot_v2->bSourceId; + break; - /* USB terminal types */ - {UAT_UNDEFINED, "UAT_UNDEFINED"}, - {UAT_STREAM, "UAT_STREAM"}, - {UAT_VENDOR, "UAT_VENDOR"}, + case UDESCSUB_AC_MIXER: + r = *(const struct usb_audio20_cluster *) + &iot[id].u.mu_v2->baSourceId[ + iot[id].u.mu_v2->bNrInPins]; + goto done; - /* input terminal types */ - {UATI_UNDEFINED, "UATI_UNDEFINED"}, - {UATI_MICROPHONE, "UATI_MICROPHONE"}, - {UATI_DESKMICROPHONE, "UATI_DESKMICROPHONE"}, - {UATI_PERSONALMICROPHONE, "UATI_PERSONALMICROPHONE"}, - {UATI_OMNIMICROPHONE, "UATI_OMNIMICROPHONE"}, - {UATI_MICROPHONEARRAY, "UATI_MICROPHONEARRAY"}, - {UATI_PROCMICROPHONEARR, "UATI_PROCMICROPHONEARR"}, + case UDESCSUB_AC_SELECTOR: + if (iot[id].u.su_v2->bNrInPins > 0) { + /* XXX This is not really right */ + id = iot[id].u.su_v2->baSourceId[0]; + } + break; - /* output terminal types */ - {UATO_UNDEFINED, "UATO_UNDEFINED"}, - {UATO_SPEAKER, "UATO_SPEAKER"}, - {UATO_HEADPHONES, "UATO_HEADPHONES"}, - {UATO_DISPLAYAUDIO, "UATO_DISPLAYAUDIO"}, - {UATO_DESKTOPSPEAKER, "UATO_DESKTOPSPEAKER"}, - {UATO_ROOMSPEAKER, "UATO_ROOMSPEAKER"}, - {UATO_COMMSPEAKER, "UATO_COMMSPEAKER"}, - {UATO_SUBWOOFER, "UATO_SUBWOOFER"}, + case UDESCSUB_AC_SAMPLE_RT: + id = iot[id].u.ru_v2->bSourceId; + break; - /* bidir terminal types */ - {UATB_UNDEFINED, "UATB_UNDEFINED"}, - {UATB_HANDSET, "UATB_HANDSET"}, - {UATB_HEADSET, "UATB_HEADSET"}, - {UATB_SPEAKERPHONE, "UATB_SPEAKERPHONE"}, - {UATB_SPEAKERPHONEESUP, "UATB_SPEAKERPHONEESUP"}, - {UATB_SPEAKERPHONEECANC, "UATB_SPEAKERPHONEECANC"}, + case UDESCSUB_AC_EFFECT: + id = iot[id].u.ef_v2->bSourceId; + break; - /* telephony terminal types */ - {UATT_UNDEFINED, "UATT_UNDEFINED"}, - {UATT_PHONELINE, "UATT_PHONELINE"}, - {UATT_TELEPHONE, "UATT_TELEPHONE"}, - {UATT_DOWNLINEPHONE, "UATT_DOWNLINEPHONE"}, + case UDESCSUB_AC_FEATURE: + id = iot[id].u.fu_v2->bSourceId; + break; - /* external terminal types */ - {UATE_UNDEFINED, "UATE_UNDEFINED"}, - {UATE_ANALOGCONN, "UATE_ANALOGCONN"}, - {UATE_LINECONN, "UATE_LINECONN"}, - {UATE_LEGACYCONN, "UATE_LEGACYCONN"}, - {UATE_DIGITALAUIFC, "UATE_DIGITALAUIFC"}, - {UATE_SPDIF, "UATE_SPDIF"}, - {UATE_1394DA, "UATE_1394DA"}, - {UATE_1394DV, "UATE_1394DV"}, + case UDESCSUB_AC_PROCESSING_V2: + r = *((const struct usb_audio20_cluster *) + &iot[id].u.pu_v2->baSourceId[ + iot[id].u.pu_v2->bNrInPins]); + goto done; - /* embedded function terminal types */ - {UATF_UNDEFINED, "UATF_UNDEFINED"}, - {UATF_CALIBNOISE, "UATF_CALIBNOISE"}, - {UATF_EQUNOISE, "UATF_EQUNOISE"}, - {UATF_CDPLAYER, "UATF_CDPLAYER"}, - {UATF_DAT, "UATF_DAT"}, - {UATF_DCC, "UATF_DCC"}, - {UATF_MINIDISK, "UATF_MINIDISK"}, - {UATF_ANALOGTAPE, "UATF_ANALOGTAPE"}, - {UATF_PHONOGRAPH, "UATF_PHONOGRAPH"}, - {UATF_VCRAUDIO, "UATF_VCRAUDIO"}, - {UATF_VIDEODISCAUDIO, "UATF_VIDEODISCAUDIO"}, - {UATF_DVDAUDIO, "UATF_DVDAUDIO"}, - {UATF_TVTUNERAUDIO, "UATF_TVTUNERAUDIO"}, - {UATF_SATELLITE, "UATF_SATELLITE"}, - {UATF_CABLETUNER, "UATF_CABLETUNER"}, - {UATF_DSS, "UATF_DSS"}, - {UATF_RADIORECV, "UATF_RADIORECV"}, - {UATF_RADIOXMIT, "UATF_RADIOXMIT"}, - {UATF_MULTITRACK, "UATF_MULTITRACK"}, - {UATF_SYNTHESIZER, "UATF_SYNTHESIZER"}, - - /* unknown */ - {0x0000, "UNKNOWN"}, -}; + case UDESCSUB_AC_EXTENSION_V2: + r = *((const struct usb_audio20_cluster *) + &iot[id].u.eu_v2->baSourceId[ + iot[id].u.eu_v2->bNrInPins]); + goto done; + + default: + goto error; + } + } +error: + DPRINTF("Bad data!\n"); + memset(&r, 0, sizeof(r)); +done: + return (r); +} -static const char * -uaudio_mixer_get_terminal_name(uint16_t terminal_type) +static uint16_t +uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, + struct uaudio_mixer_node *mix) { - const struct uaudio_tt_to_string *uat = uaudio_tt_to_string; + uint16_t terminal_type = 0x0000; + const struct uaudio_terminal_node *input[2]; + const struct uaudio_terminal_node *output[2]; - while (uat->terminal_type) { - if (uat->terminal_type == terminal_type) { - break; + input[0] = uaudio_mixer_get_input(iot, 0); + input[1] = uaudio_mixer_get_input(iot, 1); + + output[0] = uaudio_mixer_get_output(iot, 0); + output[1] = uaudio_mixer_get_output(iot, 1); + + /* + * check if there is only + * one output terminal: + */ + if (output[0] && (!output[1])) { + terminal_type = + UGETW(output[0]->u.ot_v1->wTerminalType); + } + /* + * If the only output terminal is USB, + * the class is UAC_RECORD. + */ + if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) { + + mix->class = UAC_RECORD; + if (input[0] && (!input[1])) { + terminal_type = + UGETW(input[0]->u.it_v1->wTerminalType); + } else { + terminal_type = 0; } - uat++; + goto done; } - if (uat->terminal_type == 0) { - DPRINTF("unknown terminal type (0x%04x)", terminal_type); + /* + * if the unit is connected to just + * one input terminal, the + * class is UAC_INPUT: + */ + if (input[0] && (!input[1])) { + mix->class = UAC_INPUT; + terminal_type = + UGETW(input[0]->u.it_v1->wTerminalType); + goto done; } - return (uat->desc); + /* + * Otherwise, the class is UAC_OUTPUT. + */ + mix->class = UAC_OUTPUT; +done: + return (terminal_type); } -#endif - static uint16_t -uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, +uaudio20_mixer_determine_class(const struct uaudio_terminal_node *iot, struct uaudio_mixer_node *mix) { uint16_t terminal_type = 0x0000; @@ -2489,9 +3094,8 @@ uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, * check if there is only * one output terminal: */ - if (output[0] && (!output[1])) { - terminal_type = UGETW(output[0]->u.ot->wTerminalType); - } + if (output[0] && (!output[1])) + terminal_type = UGETW(output[0]->u.ot_v2->wTerminalType); /* * If the only output terminal is USB, * the class is UAC_RECORD. @@ -2500,7 +3104,8 @@ uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, mix->class = UAC_RECORD; if (input[0] && (!input[1])) { - terminal_type = UGETW(input[0]->u.it->wTerminalType); + terminal_type = + UGETW(input[0]->u.it_v2->wTerminalType); } else { terminal_type = 0; } @@ -2513,7 +3118,8 @@ uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, */ if (input[0] && (!input[1])) { mix->class = UAC_INPUT; - terminal_type = UGETW(input[0]->u.it->wTerminalType); + terminal_type = + UGETW(input[0]->u.it_v2->wTerminalType); goto done; } /* @@ -2629,8 +3235,28 @@ uaudio_mixer_feature_name(const struct uaudio_terminal_node *iot, uat++; } - DPRINTF("terminal_type=%s (0x%04x) -> %d\n", - uaudio_mixer_get_terminal_name(terminal_type), + DPRINTF("terminal_type=0x%04x -> %d\n", + terminal_type, uat->feature); + + return (uat->feature); +} + +static uint16_t +uaudio20_mixer_feature_name(const struct uaudio_terminal_node *iot, + struct uaudio_mixer_node *mix) +{ + const struct uaudio_tt_to_feature *uat; + uint16_t terminal_type = uaudio20_mixer_determine_class(iot, mix); + + if ((mix->class == UAC_RECORD) && (terminal_type == 0)) + return (SOUND_MIXER_IMIX); + + for (uat = uaudio_tt_to_feature; uat->terminal_type != 0; uat++) { + if (uat->terminal_type == terminal_type) + break; + } + + DPRINTF("terminal_type=0x%04x -> %d\n", terminal_type, uat->feature); return (uat->feature); @@ -2678,74 +3304,322 @@ uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root, struct uaudio_terminal_node *iot; uint8_t n; uint8_t i; + uint8_t is_last; - if (info->recurse_level >= UAUDIO_RECURSE_LIMIT) { - return; - } - info->recurse_level++; - +top: for (n = 0; n < n_id; n++) { i = p_id[n]; - if (info->bit_visited[i / 8] & (1 << (i % 8))) { - /* don't go into a circle */ + if (info->recurse_level == UAUDIO_RECURSE_LIMIT) { DPRINTF("avoided going into a circle at id=%d!\n", i); - continue; - } else { - info->bit_visited[i / 8] |= (1 << (i % 8)); + return; } + info->recurse_level++; + iot = (root + i); - if (iot->u.desc == NULL) { + if (iot->u.desc == NULL) continue; - } + + is_last = ((n + 1) == n_id); + switch (iot->u.desc->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: info->bit_input[i / 8] |= (1 << (i % 8)); break; case UDESCSUB_AC_FEATURE: - uaudio_mixer_find_inputs_sub - (root, &iot->u.fu->bSourceId, 1, info); + if (is_last) { + p_id = &iot->u.fu_v1->bSourceId; + n_id = 1; + goto top; + } + uaudio_mixer_find_inputs_sub( + root, &iot->u.fu_v1->bSourceId, 1, info); break; case UDESCSUB_AC_OUTPUT: - uaudio_mixer_find_inputs_sub - (root, &iot->u.ot->bSourceId, 1, info); + if (is_last) { + p_id = &iot->u.ot_v1->bSourceId; + n_id = 1; + goto top; + } + uaudio_mixer_find_inputs_sub( + root, &iot->u.ot_v1->bSourceId, 1, info); break; case UDESCSUB_AC_MIXER: - uaudio_mixer_find_inputs_sub - (root, iot->u.mu->baSourceId, - iot->u.mu->bNrInPins, info); + if (is_last) { + p_id = iot->u.mu_v1->baSourceId; + n_id = iot->u.mu_v1->bNrInPins; + goto top; + } + uaudio_mixer_find_inputs_sub( + root, iot->u.mu_v1->baSourceId, + iot->u.mu_v1->bNrInPins, info); break; case UDESCSUB_AC_SELECTOR: - uaudio_mixer_find_inputs_sub - (root, iot->u.su->baSourceId, - iot->u.su->bNrInPins, info); + if (is_last) { + p_id = iot->u.su_v1->baSourceId; + n_id = iot->u.su_v1->bNrInPins; + goto top; + } + uaudio_mixer_find_inputs_sub( + root, iot->u.su_v1->baSourceId, + iot->u.su_v1->bNrInPins, info); break; case UDESCSUB_AC_PROCESSING: - uaudio_mixer_find_inputs_sub - (root, iot->u.pu->baSourceId, - iot->u.pu->bNrInPins, info); + if (is_last) { + p_id = iot->u.pu_v1->baSourceId; + n_id = iot->u.pu_v1->bNrInPins; + goto top; + } + uaudio_mixer_find_inputs_sub( + root, iot->u.pu_v1->baSourceId, + iot->u.pu_v1->bNrInPins, info); break; case UDESCSUB_AC_EXTENSION: - uaudio_mixer_find_inputs_sub - (root, iot->u.eu->baSourceId, - iot->u.eu->bNrInPins, info); + if (is_last) { + p_id = iot->u.eu_v1->baSourceId; + n_id = iot->u.eu_v1->bNrInPins; + goto top; + } + uaudio_mixer_find_inputs_sub( + root, iot->u.eu_v1->baSourceId, + iot->u.eu_v1->bNrInPins, info); + break; + + default: + break; + } + } +} + +static void +uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *root, + const uint8_t *p_id, uint8_t n_id, + struct uaudio_search_result *info) +{ + struct uaudio_terminal_node *iot; + uint8_t n; + uint8_t i; + uint8_t is_last; + +top: + for (n = 0; n < n_id; n++) { + + i = p_id[n]; + + if (info->recurse_level == UAUDIO_RECURSE_LIMIT) { + DPRINTF("avoided going into a circle at id=%d!\n", i); + return; + } + + info->recurse_level++; + + iot = (root + i); + + if (iot->u.desc == NULL) + continue; + + is_last = ((n + 1) == n_id); + + switch (iot->u.desc->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + info->bit_input[i / 8] |= (1 << (i % 8)); + break; + + case UDESCSUB_AC_OUTPUT: + if (is_last) { + p_id = &iot->u.ot_v2->bSourceId; + n_id = 1; + goto top; + } + uaudio20_mixer_find_inputs_sub( + root, &iot->u.ot_v2->bSourceId, 1, info); + break; + + case UDESCSUB_AC_MIXER: + if (is_last) { + p_id = iot->u.mu_v2->baSourceId; + n_id = iot->u.mu_v2->bNrInPins; + goto top; + } + uaudio20_mixer_find_inputs_sub( + root, iot->u.mu_v2->baSourceId, + iot->u.mu_v2->bNrInPins, info); + break; + + case UDESCSUB_AC_SELECTOR: + if (is_last) { + p_id = iot->u.su_v2->baSourceId; + n_id = iot->u.su_v2->bNrInPins; + goto top; + } + uaudio20_mixer_find_inputs_sub( + root, iot->u.su_v2->baSourceId, + iot->u.su_v2->bNrInPins, info); + break; + + case UDESCSUB_AC_SAMPLE_RT: + if (is_last) { + p_id = &iot->u.ru_v2->bSourceId; + n_id = 1; + goto top; + } + uaudio20_mixer_find_inputs_sub( + root, &iot->u.ru_v2->bSourceId, + 1, info); + break; + + case UDESCSUB_AC_EFFECT: + if (is_last) { + p_id = &iot->u.ef_v2->bSourceId; + n_id = 1; + goto top; + } + uaudio20_mixer_find_inputs_sub( + root, &iot->u.ef_v2->bSourceId, + 1, info); + break; + + case UDESCSUB_AC_FEATURE: + if (is_last) { + p_id = &iot->u.fu_v2->bSourceId; + n_id = 1; + goto top; + } + uaudio20_mixer_find_inputs_sub( + root, &iot->u.fu_v2->bSourceId, 1, info); + break; + + case UDESCSUB_AC_PROCESSING_V2: + if (is_last) { + p_id = iot->u.pu_v2->baSourceId; + n_id = iot->u.pu_v2->bNrInPins; + goto top; + } + uaudio20_mixer_find_inputs_sub( + root, iot->u.pu_v2->baSourceId, + iot->u.pu_v2->bNrInPins, info); + break; + + case UDESCSUB_AC_EXTENSION_V2: + if (is_last) { + p_id = iot->u.eu_v2->baSourceId; + n_id = iot->u.eu_v2->bNrInPins; + goto top; + } + uaudio20_mixer_find_inputs_sub( + root, iot->u.eu_v2->baSourceId, + iot->u.eu_v2->bNrInPins, info); + break; + default: + break; + } + } +} + +static void +uaudio20_mixer_find_clocks_sub(struct uaudio_terminal_node *root, + const uint8_t *p_id, uint8_t n_id, + struct uaudio_search_result *info) +{ + struct uaudio_terminal_node *iot; + uint8_t n; + uint8_t i; + uint8_t is_last; + uint8_t id; + +top: + for (n = 0; n < n_id; n++) { + + i = p_id[n]; + + if (info->recurse_level == UAUDIO_RECURSE_LIMIT) { + DPRINTF("avoided going into a circle at id=%d!\n", i); + return; + } + + info->recurse_level++; + + iot = (root + i); + + if (iot->u.desc == NULL) + continue; + + is_last = ((n + 1) == n_id); + + switch (iot->u.desc->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + info->is_input = 1; + if (is_last) { + p_id = &iot->u.it_v2->bCSourceId; + n_id = 1; + goto top; + } + uaudio20_mixer_find_clocks_sub(root, + &iot->u.it_v2->bCSourceId, 1, info); + break; + + case UDESCSUB_AC_OUTPUT: + info->is_input = 0; + if (is_last) { + p_id = &iot->u.ot_v2->bCSourceId; + n_id = 1; + goto top; + } + uaudio20_mixer_find_clocks_sub(root, + &iot->u.ot_v2->bCSourceId, 1, info); + break; + + case UDESCSUB_AC_CLOCK_SEL: + if (is_last) { + p_id = iot->u.csel_v2->baCSourceId; + n_id = iot->u.csel_v2->bNrInPins; + goto top; + } + uaudio20_mixer_find_clocks_sub(root, + iot->u.csel_v2->baCSourceId, + iot->u.csel_v2->bNrInPins, info); + break; + + case UDESCSUB_AC_CLOCK_MUL: + if (is_last) { + p_id = &iot->u.cmul_v2->bCSourceId; + n_id = 1; + goto top; + } + uaudio20_mixer_find_clocks_sub(root, + &iot->u.cmul_v2->bCSourceId, + 1, info); + break; + + case UDESCSUB_AC_CLOCK_SRC: + + id = iot->u.csrc_v2->bClockId; + + switch (info->is_input) { + case 0: + info->bit_output[id / 8] |= (1 << (id % 8)); + break; + case 1: + info->bit_input[id / 8] |= (1 << (id % 8)); + break; + default: + break; + } break; - case UDESCSUB_AC_HEADER: default: break; } } - info->recurse_level--; } static void @@ -2774,8 +3648,8 @@ uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id, } static void -uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, - void *desc) +uaudio_mixer_fill_info(struct uaudio_softc *sc, + struct usb_device *udev, void *desc) { const struct usb_audio_control_descriptor *acdp; struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); @@ -2809,15 +3683,6 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, DPRINTFN(3, "found AC header, vers=%03x, len=%d\n", sc->sc_audio_rev, wTotalLen); - if (sc->sc_audio_rev != UAUDIO_VERSION) { - - if (sc->sc_uq_bad_adc) { - - } else { - DPRINTF("invalid audio version\n"); - goto done; - } - } iot = malloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP, M_WAITOK | M_ZERO); @@ -2835,13 +3700,17 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, wTotalLen -= dp->bLength; } - au = uaudio_mixer_verify_desc(dp, 0); + if (sc->sc_audio_rev >= UAUDIO_VERSION_30) + au = NULL; + else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) + au = uaudio20_mixer_verify_desc(dp, 0); + else + au = uaudio_mixer_verify_desc(dp, 0); if (au) { iot[au->bUnitId].u.desc = (const void *)au; - if (au->bUnitId > ID_max) { + if (au->bUnitId > ID_max) ID_max = au->bUnitId; - } } } @@ -2853,7 +3722,21 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, */ i = ID_max; do { - uaudio_mixer_find_inputs_sub(iot, &i, 1, &((iot + i)->usr)); + if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { + /* FALLTHROUGH */ + } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { + uaudio20_mixer_find_inputs_sub(iot, + &i, 1, &((iot + i)->usr)); + + sc->sc_mixer_clocks.is_input = 255; + sc->sc_mixer_clocks.recurse_level = 0; + + uaudio20_mixer_find_clocks_sub(iot, + &i, 1, &sc->sc_mixer_clocks); + } else { + uaudio_mixer_find_inputs_sub(iot, + &i, 1, &((iot + i)->usr)); + } } while (i--); /* @@ -2862,7 +3745,8 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, */ i = ID_max; do { - uaudio_mixer_find_outputs_sub(iot, i, ID_max, &((iot + i)->usr)); + uaudio_mixer_find_outputs_sub(iot, + i, ID_max, &((iot + i)->usr)); } while (i--); /* set "id_max" and "root" */ @@ -2873,106 +3757,59 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, (iot + i)->root = iot; } while (i--); -#ifdef USB_DEBUG + /* + * Scan the config to create a linked list of "mixer" nodes: + */ + i = ID_max; do { - uint8_t j; + dp = iot[i].u.desc; - if (iot[i].u.desc == NULL) { + if (dp == NULL) continue; - } - DPRINTF("id %d:\n", i); - - switch (iot[i].u.desc->bDescriptorSubtype) { - case UDESCSUB_AC_INPUT: - DPRINTF(" - AC_INPUT type=%s\n", - uaudio_mixer_get_terminal_name - (UGETW(iot[i].u.it->wTerminalType))); - uaudio_mixer_dump_cluster(i, iot); - break; - - case UDESCSUB_AC_OUTPUT: - DPRINTF(" - AC_OUTPUT type=%s " - "src=%d\n", uaudio_mixer_get_terminal_name - (UGETW(iot[i].u.ot->wTerminalType)), - iot[i].u.ot->bSourceId); - break; - - case UDESCSUB_AC_MIXER: - DPRINTF(" - AC_MIXER src:\n"); - for (j = 0; j < iot[i].u.mu->bNrInPins; j++) { - DPRINTF(" - %d\n", iot[i].u.mu->baSourceId[j]); - } - uaudio_mixer_dump_cluster(i, iot); - break; - case UDESCSUB_AC_SELECTOR: - DPRINTF(" - AC_SELECTOR src:\n"); - for (j = 0; j < iot[i].u.su->bNrInPins; j++) { - DPRINTF(" - %d\n", iot[i].u.su->baSourceId[j]); - } - break; - - case UDESCSUB_AC_FEATURE: - DPRINTF(" - AC_FEATURE src=%d\n", iot[i].u.fu->bSourceId); - break; + DPRINTFN(11, "id=%d subtype=%d\n", + i, dp->bDescriptorSubtype); - case UDESCSUB_AC_PROCESSING: - DPRINTF(" - AC_PROCESSING src:\n"); - for (j = 0; j < iot[i].u.pu->bNrInPins; j++) { - DPRINTF(" - %d\n", iot[i].u.pu->baSourceId[j]); - } - uaudio_mixer_dump_cluster(i, iot); - break; + if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { + continue; + } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { - case UDESCSUB_AC_EXTENSION: - DPRINTF(" - AC_EXTENSION src:\n"); - for (j = 0; j < iot[i].u.eu->bNrInPins; j++) { - DPRINTF("%d ", iot[i].u.eu->baSourceId[j]); - } - uaudio_mixer_dump_cluster(i, iot); - break; + switch (dp->bDescriptorSubtype) { + case UDESCSUB_AC_HEADER: + DPRINTF("unexpected AC header\n"); + break; - default: - DPRINTF("unknown audio control (subtype=%d)\n", - iot[i].u.desc->bDescriptorSubtype); - } + case UDESCSUB_AC_INPUT: + case UDESCSUB_AC_OUTPUT: + case UDESCSUB_AC_PROCESSING_V2: + case UDESCSUB_AC_EXTENSION_V2: + case UDESCSUB_AC_EFFECT: + case UDESCSUB_AC_CLOCK_SRC: + case UDESCSUB_AC_CLOCK_SEL: + case UDESCSUB_AC_CLOCK_MUL: + case UDESCSUB_AC_SAMPLE_RT: + break; - DPRINTF("Inputs to this ID are:\n"); + case UDESCSUB_AC_MIXER: + uaudio20_mixer_add_mixer(sc, iot, i); + break; - j = ID_max; - do { - if (iot[i].usr.bit_input[j / 8] & (1 << (j % 8))) { - DPRINTF(" -- ID=%d\n", j); - } - } while (j--); + case UDESCSUB_AC_SELECTOR: + uaudio20_mixer_add_selector(sc, iot, i); + break; - DPRINTF("Outputs from this ID are:\n"); + case UDESCSUB_AC_FEATURE: + uaudio20_mixer_add_feature(sc, iot, i); + break; - j = ID_max; - do { - if (iot[i].usr.bit_output[j / 8] & (1 << (j % 8))) { - DPRINTF(" -- ID=%d\n", j); + default: + DPRINTF("bad AC desc subtype=0x%02x\n", + dp->bDescriptorSubtype); + break; } - } while (j--); - - } while (i--); -#endif - - /* - * scan the config to create a linked - * list of "mixer" nodes: - */ - - i = ID_max; - do { - dp = iot[i].u.desc; - - if (dp == NULL) { continue; } - DPRINTFN(11, "id=%d subtype=%d\n", - i, dp->bDescriptorSubtype); switch (dp->bDescriptorSubtype) { case UDESCSUB_AC_HEADER: @@ -2980,11 +3817,7 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, break; case UDESCSUB_AC_INPUT: - uaudio_mixer_add_input(sc, iot, i); - break; - case UDESCSUB_AC_OUTPUT: - uaudio_mixer_add_output(sc, iot, i); break; case UDESCSUB_AC_MIXER: @@ -3016,42 +3849,76 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, } while (i--); done: - if (iot) { - free(iot, M_TEMP); - } + free(iot, M_TEMP); } -static uint16_t -uaudio_mixer_get(struct usb_device *udev, uint8_t what, - struct uaudio_mixer_node *mc) +static int +uaudio_mixer_get(struct usb_device *udev, uint16_t audio_rev, + uint8_t what, struct uaudio_mixer_node *mc) { struct usb_device_request req; - uint16_t val; - uint16_t len = MIX_SIZE(mc->type); - uint8_t data[4]; + int val; + uint8_t data[2 + (2 * 3)]; usb_error_t err; - if (mc->wValue[0] == -1) { + if (mc->wValue[0] == -1) return (0); + + if (audio_rev >= UAUDIO_VERSION_30) + return (0); + else if (audio_rev >= UAUDIO_VERSION_20) { + if (what == GET_CUR) { + req.bRequest = UA20_CS_CUR; + USETW(req.wLength, 2); + } else { + req.bRequest = UA20_CS_RANGE; + USETW(req.wLength, 8); + } + } else { + uint16_t len = MIX_SIZE(mc->type); + + req.bRequest = what; + USETW(req.wLength, len); } + req.bmRequestType = UT_READ_CLASS_INTERFACE; - req.bRequest = what; USETW(req.wValue, mc->wValue[0]); USETW(req.wIndex, mc->wIndex); - USETW(req.wLength, len); + + memset(data, 0, sizeof(data)); err = usbd_do_request(udev, NULL, &req, data); if (err) { DPRINTF("err=%s\n", usbd_errstr(err)); return (0); } - if (len < 1) { - data[0] = 0; - } - if (len < 2) { - data[1] = 0; + + if (audio_rev >= UAUDIO_VERSION_30) { + val = 0; + } else if (audio_rev >= UAUDIO_VERSION_20) { + switch (what) { + case GET_CUR: + val = (data[0] | (data[1] << 8)); + break; + case GET_MIN: + val = (data[2] | (data[3] << 8)); + break; + case GET_MAX: + val = (data[4] | (data[5] << 8)); + break; + case GET_RES: + val = (data[6] | (data[7] << 8)); + break; + default: + val = 0; + break; + } + } else { + val = (data[0] | (data[1] << 8)); } - val = (data[0] | (data[1] << 8)); + + if (what == GET_CUR || what == GET_MIN || what == GET_MAX) + val = uaudio_mixer_signext(mc->type, val); DPRINTFN(3, "val=%d\n", val); @@ -3088,8 +3955,6 @@ tr_setup: while (mc) { while (sc->sc_mixer_chan < mc->nchan) { - len = MIX_SIZE(mc->type); - chan = sc->sc_mixer_chan; sc->sc_mixer_chan++; @@ -3102,17 +3967,24 @@ tr_setup: if (update) { req.bmRequestType = UT_WRITE_CLASS_INTERFACE; - req.bRequest = SET_CUR; USETW(req.wValue, mc->wValue[chan]); USETW(req.wIndex, mc->wIndex); - USETW(req.wLength, len); - if (len > 0) { - buf[0] = (mc->wData[chan] & 0xFF); - } - if (len > 1) { - buf[1] = (mc->wData[chan] >> 8) & 0xFF; + if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { + return; + } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { + len = 2; + req.bRequest = UA20_CS_CUR; + USETW(req.wLength, len); + } else { + len = MIX_SIZE(mc->type); + req.bRequest = SET_CUR; + USETW(req.wLength, len); } + + buf[0] = (mc->wData[chan] & 0xFF); + buf[1] = (mc->wData[chan] >> 8) & 0xFF; + pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &req, sizeof(req)); pc = usbd_xfer_get_frame(xfer, 1); @@ -3166,6 +4038,29 @@ uaudio_set_speed(struct usb_device *udev, uint8_t endpt, uint32_t speed) return (usbd_do_request(udev, NULL, &req, data)); } +static usb_error_t +uaudio20_set_speed(struct usb_device *udev, uint8_t iface_no, + uint8_t clockid, uint32_t speed) +{ + struct usb_device_request req; + uint8_t data[4]; + + DPRINTFN(6, "ifaceno=%d clockid=%d speed=%u\n", + iface_no, clockid, speed); + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UA20_CS_CUR; + USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0); + USETW2(req.wIndex, clockid, iface_no); + USETW(req.wLength, 4); + data[0] = speed; + data[1] = speed >> 8; + data[2] = speed >> 16; + data[3] = speed >> 24; + + return (usbd_do_request(udev, NULL, &req, data)); +} + static int uaudio_mixer_signext(uint8_t type, int val) { diff --git a/sys/dev/sound/usb/uaudioreg.h b/sys/dev/sound/usb/uaudioreg.h index d04bb908f989..c2c1ad4b077f 100644 --- a/sys/dev/sound/usb/uaudioreg.h +++ b/sys/dev/sound/usb/uaudioreg.h @@ -36,6 +36,7 @@ #define UAUDIO_VERSION 0x0100 #define UAUDIO_VERSION_20 0x0200 +#define UAUDIO_VERSION_30 0x0300 #define UAUDIO_PROTOCOL_20 0x20 @@ -54,7 +55,7 @@ #define UDESCSUB_AC_FEATURE 6 #define UDESCSUB_AC_PROCESSING 7 #define UDESCSUB_AC_EXTENSION 8 -/* ==== USB audio 2.0 ==== */ +/* ==== USB audio v2.0 ==== */ #define UDESCSUB_AC_EFFECT 7 #define UDESCSUB_AC_PROCESSING_V2 8 #define UDESCSUB_AC_EXTENSION_V2 9 @@ -365,7 +366,7 @@ struct usb_audio_extension_unit_1 { #define DELAY_CONTROL 0x08 #define BASS_BOOST_CONTROL 0x09 #define LOUDNESS_CONTROL 0x0a -/* ==== USB audio 2.0 ==== */ +/* ==== USB audio v2.0 ==== */ #define INPUT_GAIN_CONTROL 0x0b #define INPUT_GAIN_PAD_CONTROL 0x0c #define PHASE_INVERTER_CONTROL 0x0d @@ -380,7 +381,7 @@ struct usb_audio_extension_unit_1 { #define AS_GENERAL 1 #define FORMAT_TYPE 2 #define FORMAT_SPECIFIC 3 -/* ==== USB audio 2.0 ==== */ +/* ==== USB audio v2.0 ==== */ #define FORMAT_ENCODER 3 #define FORMAT_DECODER 4 @@ -434,7 +435,7 @@ struct usb_audio_extension_unit_1 { * USB audio v2.0 definitions *------------------------------------------------------------------------*/ -struct usb_audio20_as_iface_descriptor { +struct usb_audio20_streaming_interface_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -488,12 +489,9 @@ struct usb_audio20_feedback_endpoint_descriptor { uByte bInterval; } __packed; -#define UA20_GET_CUR 0x81 -#define UA20_SET_CUR 0x01 -#define UA20_GET_RANGE 0x82 -#define UA20_SET_RANGE 0x02 -#define UA20_GET_MEM 0x83 -#define UA20_SET_MEM 0x03 +#define UA20_CS_CUR 0x01 +#define UA20_CS_RANGE 0x02 +#define UA20_CS_MEM 0x03 struct usb_audio20_cur1_parameter { uByte bCur; @@ -595,12 +593,18 @@ struct usb_audio20_clock_source_unit { } __packed; /* UDESCSUB_AC_CLOCK_SEL */ -struct usb_audio20_clock_selector_unit { +struct usb_audio20_clock_selector_unit_0 { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte bClockId; uByte bNrInPins; + uByte baCSourceId[0]; +} __packed; + +struct usb_audio20_clock_selector_unit_1 { + uByte bmControls; + uByte iClockSelector; } __packed; /* UDESCSUB_AC_CLOCK_MUL */ @@ -768,8 +772,6 @@ struct usb_audio20_cluster { #define UA20_TF_CONTROL_PANEL 0x0C #define UA20_TF_OTHER 0xFF -#define UA20_EP_GENERAL 0x01 - #define UA20_CS_SAM_FREQ_CONTROL 0x01 #define UA20_CS_CLOCK_VALID_CONTROL 0x02 -- cgit v1.2.3