From owner-svn-src-all@FreeBSD.ORG Tue Nov 13 16:59:43 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 2E01FBAA; Tue, 13 Nov 2012 16:59:43 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 06CC28FC08; Tue, 13 Nov 2012 16:59:43 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.5/8.14.5) with ESMTP id qADGxg9k083582; Tue, 13 Nov 2012 16:59:42 GMT (envelope-from hselasky@svn.freebsd.org) Received: (from hselasky@localhost) by svn.freebsd.org (8.14.5/8.14.5/Submit) id qADGxgGL083580; Tue, 13 Nov 2012 16:59:42 GMT (envelope-from hselasky@svn.freebsd.org) Message-Id: <201211131659.qADGxgGL083580@svn.freebsd.org> From: Hans Petter Selasky Date: Tue, 13 Nov 2012 16:59:42 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org Subject: svn commit: r242983 - in stable/8/sys/dev: sound/usb usb usb/quirk X-SVN-Group: stable-8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 13 Nov 2012 16:59:43 -0000 Author: hselasky Date: Tue Nov 13 16:59:42 2012 New Revision: 242983 URL: http://svnweb.freebsd.org/changeset/base/242983 Log: MFC r242127, r240078, r240609, r241988, r242129, r242223, r242438, r242453, r242455 and r242458: Add full support for Fast Track Ultra 8R from M-audio. Implement support for USB Audio v2.0. Remove some redundant USB audio v1.0 debug data. Use lsusb instead. Implement support for the so-called USB feedback endpoint for USB audio devices. Export all mixer nodes into dev.pcm.X.mixer.Y sysctl nodes. Fix a memory leak of mixer data at USB audio device detach. PR: usb/171254 Modified: stable/8/sys/dev/sound/usb/uaudio.c stable/8/sys/dev/sound/usb/uaudioreg.h stable/8/sys/dev/usb/quirk/usb_quirk.c stable/8/sys/dev/usb/usbdevs Directory Properties: stable/8/sys/ (props changed) stable/8/sys/dev/ (props changed) stable/8/sys/dev/sound/ (props changed) stable/8/sys/dev/sound/usb/ (props changed) stable/8/sys/dev/usb/ (props changed) Modified: stable/8/sys/dev/sound/usb/uaudio.c ============================================================================== --- stable/8/sys/dev/sound/usb/uaudio.c Tue Nov 13 16:48:52 2012 (r242982) +++ stable/8/sys/dev/sound/usb/uaudio.c Tue Nov 13 16:59:42 2012 (r242983) @@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #define USB_DEBUG_VAR uaudio_debug #include @@ -115,22 +116,39 @@ SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, def #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 { + const char *name; + int32_t minval; int32_t maxval; -#define MIX_MAX_CHAN 8 +#define MIX_MAX_CHAN 16 int32_t wValue[MIX_MAX_CHAN]; /* using nchan */ uint32_t mul; uint32_t ctl; - uint16_t wData[MIX_MAX_CHAN]; /* using nchan */ + int wData[MIX_MAX_CHAN]; /* using nchan */ uint16_t wIndex; uint8_t update[(MIX_MAX_CHAN + 7) / 8]; @@ -149,6 +167,9 @@ struct uaudio_mixer_node { #define MAX_SELECTOR_INPUT_PIN 256 uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN]; uint8_t class; + uint8_t val_default; + + uint8_t desc[64]; struct uaudio_mixer_node *next; }; @@ -161,12 +182,10 @@ struct uaudio_chan { struct mtx *pcm_mtx; /* lock protecting this structure */ 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; + struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1]; + union uaudio_asf1d p_asf1d; + union uaudio_sed p_sed; const usb_endpoint_descriptor_audio_t *p_ed1; - const usb_endpoint_descriptor_audio_t *p_ed2; const struct uaudio_format *p_fmt; uint8_t *buf; /* pointer to buffer */ @@ -192,6 +211,13 @@ struct uaudio_chan { uint8_t valid; uint8_t iface_index; uint8_t iface_alt_index; + uint8_t channels; + + uint8_t last_sync_time; + uint8_t last_sync_state; +#define UAUDIO_SYNC_NONE 0 +#define UAUDIO_SYNC_MORE 1 +#define UAUDIO_SYNC_LESS 2 }; #define UMIDI_CABLES_MAX 16 /* units */ @@ -242,13 +268,23 @@ 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 mtx *sc_mixer_lock; struct usb_device *sc_udev; struct usb_xfer *sc_mixer_xfer[1]; struct uaudio_mixer_node *sc_mixer_root; @@ -273,24 +309,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 +343,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 +361,24 @@ static const struct uaudio_format uaudio {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 @@ -341,23 +399,23 @@ static device_attach_t uaudio_attach; static device_detach_t uaudio_detach; static usb_callback_t uaudio_chan_play_callback; +static usb_callback_t uaudio_chan_play_sync_callback; static usb_callback_t uaudio_chan_record_callback; +static usb_callback_t uaudio_chan_record_sync_callback; 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 mixer ==== */ + +static int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS); +static void uaudio_mixer_ctl_free(struct uaudio_softc *); +static void uaudio_mixer_register_sysctl(struct uaudio_softc *, device_t); +static void uaudio_mixer_reload_all(struct uaudio_softc *); +static void uaudio_mixer_controls_create_ftu(struct uaudio_softc *); + +/* ==== 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 +436,56 @@ static uint16_t uaudio_mixer_determine_c 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,13 +502,10 @@ 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 - uaudio_cfg_record[UAUDIO_NCHANBUFS] = { + uaudio_cfg_record[UAUDIO_NCHANBUFS + 1] = { [0] = { .type = UE_ISOCHRONOUS, .endpoint = UE_ADDR_ANY, @@ -439,10 +525,20 @@ static const struct usb_config .flags = {.short_xfer_ok = 1,}, .callback = &uaudio_chan_record_callback, }, + + [2] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = 0, /* use "wMaxPacketSize * frames" */ + .frames = 1, + .flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,}, + .callback = &uaudio_chan_record_sync_callback, + }, }; static const struct usb_config - uaudio_cfg_play[UAUDIO_NCHANBUFS] = { + uaudio_cfg_play[UAUDIO_NCHANBUFS + 1] = { [0] = { .type = UE_ISOCHRONOUS, .endpoint = UE_ADDR_ANY, @@ -462,6 +558,16 @@ static const struct usb_config .flags = {.short_xfer_ok = 1,}, .callback = &uaudio_chan_play_callback, }, + + [2] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = 0, /* use "wMaxPacketSize * frames" */ + .frames = 1, + .flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,}, + .callback = &uaudio_chan_play_sync_callback, + }, }; static const struct usb_config @@ -555,7 +661,8 @@ uaudio_probe(device_t dev) /* lookup non-standard device */ if (uaa->info.bInterfaceClass != UICLASS_AUDIO) { - if (usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0) + if (uaa->info.bInterfaceClass != UICLASS_VENDOR || + usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0) return (ENXIO); } @@ -614,30 +721,43 @@ 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); + if (sc->sc_mixer_count == 0) { + if (uaa->info.idVendor == USB_VENDOR_MAUDIO && + (uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA || + uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA8R)) { + DPRINTF("Generating mixer descriptors\n"); + uaudio_mixer_controls_create_ftu(sc); + } + } + DPRINTF("%d mixer controls\n", sc->sc_mixer_count); if (sc->sc_play_chan.valid) { - device_printf(dev, "Play: %d Hz, %d ch, %s format.\n", + device_printf(dev, "Play: %d Hz, %d ch, %s format, " + "2x8ms buffer.\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"); } if (sc->sc_rec_chan.valid) { - device_printf(dev, "Record: %d Hz, %d ch, %s format.\n", + device_printf(dev, "Record: %d Hz, %d ch, %s format, " + "2x8ms buffer.\n", sc->sc_rec_chan.sample_rate, - sc->sc_rec_chan.p_asf1d->bNrChannels, + sc->sc_rec_chan.channels, sc->sc_rec_chan.p_fmt->description); } else { device_printf(dev, "No recording.\n"); @@ -679,6 +799,10 @@ uaudio_attach(device_t dev) DPRINTF("child attach failed\n"); goto detach; } + + /* reload all mixer settings */ + uaudio_mixer_reload_all(sc); + return (0); /* success */ detach: @@ -714,9 +838,8 @@ uaudio_attach_sub(device_t dev, kobj_cla */ uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL); } - if (mixer_init(dev, mixer_class, sc)) { + if (mixer_init(dev, mixer_class, sc)) goto detach; - } sc->sc_mixer_init = 1; snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio)); @@ -738,6 +861,8 @@ uaudio_attach_sub(device_t dev, kobj_cla } pcm_setstatus(dev, status); + uaudio_mixer_register_sysctl(sc, dev); + return (0); /* success */ detach: @@ -779,9 +904,9 @@ uaudio_detach(device_t dev) * any. */ if (sc->sc_play_chan.valid) - usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS); + usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1); if (sc->sc_rec_chan.valid) - usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS); + usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1); if (bus_generic_detach(dev) != 0) { DPRINTF("detach failed!\n"); @@ -791,6 +916,10 @@ uaudio_detach(device_t dev) umidi_detach(dev); + /* free mixer data */ + + uaudio_mixer_ctl_free(sc); + return (0); } @@ -857,28 +986,88 @@ uaudio_record_fix_fs(usb_endpoint_descri } } +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 +1112,264 @@ uaudio_chan_fill_info_sub(struct uaudio_ 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 ((desc->bDescriptorType == UDESC_CS_INTERFACE) && + + if (audio_if == 0) { + if ((acdp == NULL) && + (desc->bDescriptorType == UDESC_CS_INTERFACE) && + (desc->bDescriptorSubtype == UDESCSUB_AC_HEADER) && + (desc->bLength >= sizeof(*acdp))) { + acdp = (void *)desc; + audio_rev = UGETW(acdp->bcdADC); + } + + /* + * Don't collect any USB audio descriptors if + * this is not an USB audio stream interface. + */ + continue; + } + + 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 (asid.v1 == NULL || asf1d.v1 == NULL || + ed1 == NULL || sed.v1 == NULL) { + /* need more descriptors */ + continue; + } + + ep_dir = UE_GET_DIR(ed1->bEndpointAddress); - ep_dir = UE_GET_DIR(ed1->bEndpointAddress); + /* We ignore sync endpoint information until further. */ - /* We ignore sync endpoint information until further. */ + if (audio_rev >= UAUDIO_VERSION_30) { + goto next_ep; + } else if (audio_rev >= UAUDIO_VERSION_20) { - wFormat = UGETW(asid->wFormatTag); - bChannels = UAUDIO_MAX_CHAN(asf1d->bNrChannels); - bBitResolution = asf1d->bBitResolution; + uint32_t dwFormat; + uint8_t bSubslotSize; - if (asf1d->bSamFreqType == 0) { + 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; + } + } + + DPRINTF("Checking clock ID=%d\n", x); + + 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.v1->wFormatTag); + bChannels = UAUDIO_MAX_CHAN(asf1d.v1->bNrChannels); + bBitResolution = asf1d.v1->bBitResolution; + + 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 (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; - } + 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(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; } } @@ -1137,7 +1419,7 @@ uaudio_chan_fill_info(struct uaudio_soft * disable surround setups on FULL-speed USB * by default */ - channels = 2; + channels = 4; break; default: channels = 16; @@ -1177,72 +1459,186 @@ done: } static void -uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error) +uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error) { struct uaudio_chan *ch = usbd_xfer_softc(xfer); struct usb_page_cache *pc; - uint32_t total; - uint32_t blockcount; - uint32_t n; - uint32_t offset; + uint8_t buf[4]; + uint64_t temp; + int len; int actlen; - int sumlen; - - usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); + int nframes; - if (ch->end == ch->start) { - DPRINTF("no buffer!\n"); - return; - } + usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: -tr_transferred: - if (actlen < sumlen) { - DPRINTF("short transfer, " - "%d of %d bytes\n", actlen, sumlen); - } - chn_intr(ch->pcm_ch); - case USB_ST_SETUP: *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***