From a963377a020657ee1077d8d6dba3325ec28e0e42 Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Fri, 9 Jan 2015 18:18:21 +0100 Subject: [PATCH] kernel/usb4bsd: Update uaudio driver to FreeBSD r276701 --- etc/devd/usb.conf | 26 +- sys/bus/u4b/audio/Makefile | 10 +- sys/bus/u4b/audio/uaudio.c | 4064 ++++++++++++++++++++++++-------- sys/bus/u4b/audio/uaudio.h | 2 +- sys/bus/u4b/audio/uaudio_pcm.c | 18 +- sys/bus/u4b/audio/uaudioreg.h | 411 +++- 6 files changed, 3488 insertions(+), 1043 deletions(-) diff --git a/etc/devd/usb.conf b/etc/devd/usb.conf index f40676898a..02c57ee761 100644 --- a/etc/devd/usb.conf +++ b/etc/devd/usb.conf @@ -291,6 +291,14 @@ nomatch 32 { action "kldload -n uslcom"; }; +nomatch 32 { + match "bus" "uhub[0-9]+"; + match "mode" "host"; + match "vendor" "0x0499"; + match "product" "(0x1000|0x1001|0x1002|0x1003|0x1004|0x1005|0x1006|0x1007|0x1008|0x1009|0x100a|0x100c|0x100d|0x100e|0x100f|0x1010|0x1011|0x1012|0x1013|0x1014|0x1015|0x1016|0x1017|0x1018|0x1019|0x101a|0x101b|0x101c|0x101d|0x101e|0x101f|0x1020|0x1021|0x1022|0x1023|0x1024|0x1025|0x1026|0x1027|0x1028|0x1029|0x102a|0x102b|0x102e|0x1030|0x1031|0x1032|0x1033|0x1034|0x1035|0x1036|0x1037|0x1038|0x1039|0x103a|0x103b|0x103c|0x103d|0x103e|0x103f|0x1040|0x1041|0x1042|0x1043|0x1044|0x1045|0x104e|0x104f|0x1050|0x1051|0x1052|0x1053|0x1054|0x1055|0x1056|0x1057|0x1058|0x1059|0x105a|0x105b|0x105c|0x105d|0x1503|0x2000|0x2001|0x2002|0x2003|0x5000|0x5001|0x5002|0x5003|0x5004|0x5005|0x5006|0x5007|0x5008|0x5009|0x500a|0x500b|0x500c|0x500d|0x500e|0x500f|0x7000|0x7010)"; + action "kldload -n uaudio"; +}; + nomatch 32 { match "bus" "uhub[0-9]+"; match "mode" "host"; @@ -3401,6 +3409,22 @@ nomatch 32 { action "kldload -n uhid"; }; +nomatch 32 { + match "bus" "uhub[0-9]+"; + match "mode" "host"; + match "intclass" "0x01"; + match "intsubclass" "0x01"; + action "kldload -n uaudio"; +}; + +nomatch 32 { + match "bus" "uhub[0-9]+"; + match "mode" "host"; + match "intclass" "0x01"; + match "intsubclass" "0x03"; + action "kldload -n uaudio"; +}; + nomatch 32 { match "bus" "uhub[0-9]+"; match "mode" "host"; @@ -3423,5 +3447,5 @@ nomatch 32 { action "kldload -n umass"; }; -# 1538 USB entries processed +# 1643 USB entries processed diff --git a/sys/bus/u4b/audio/Makefile b/sys/bus/u4b/audio/Makefile index 8d4ee342ba..5100e29e83 100644 --- a/sys/bus/u4b/audio/Makefile +++ b/sys/bus/u4b/audio/Makefile @@ -1,6 +1,8 @@ -KMOD= uaudio +# $FreeBSD: head/sys/modules/sound/driver/uaudio/Makefile 266006 2014-05-14 07:33:06Z hselasky $ -SRCS= opt_bus.h opt_usb.h opt_snd.h device_if.h bus_if.h usb_if.h usbdevs.h \ - mixer_if.h feeder_if.h channel_if.h uaudio.c uaudio_pcm.c +KMOD= uaudio +SRCS= bus_if.h device_if.h mixer_if.h usb_if.h +SRCS+= opt_usb.h opt_bus.h opt_snd.h feeder_if.h channel_if.h usbdevs.h +SRCS+= uaudio_pcm.c uaudio.c -.include +.include diff --git a/sys/bus/u4b/audio/uaudio.c b/sys/bus/u4b/audio/uaudio.c index 039661d609..c7bb431d9c 100644 --- a/sys/bus/u4b/audio/uaudio.c +++ b/sys/bus/u4b/audio/uaudio.c @@ -1,5 +1,5 @@ /* $NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $ */ -/* $FreeBSD$ */ +/* $FreeBSD: head/sys/dev/sound/usb/uaudio.c 276701 2015-01-05 15:04:17Z hselasky $ */ /*- * Copyright (c) 1999 The NetBSD Foundation, Inc. @@ -65,6 +65,9 @@ #include #include #include +#include +#include +#include #define USB_DEBUG_VAR uaudio_debug #include @@ -73,9 +76,7 @@ #include /* for bootverbose */ -#ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_snd.h" -#endif #include #include @@ -109,22 +110,40 @@ 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) +#define MIX(sc) ((sc)->sc_mixer_node) + +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]; @@ -143,25 +162,42 @@ 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; }; +struct uaudio_configure_msg { + struct usb_proc_msg hdr; + struct uaudio_softc *sc; +}; + +#define CHAN_MAX_ALT 24 + +struct uaudio_chan_alt { + union uaudio_asf1d p_asf1d; + union uaudio_sed p_sed; + const usb_endpoint_descriptor_audio_t *p_ed1; + const struct uaudio_format *p_fmt; + const struct usb_config *usb_cfg; + uint32_t sample_rate; /* in Hz */ + uint16_t sample_size; + uint8_t iface_index; + uint8_t iface_alt_index; + uint8_t channels; + uint8_t enable_sync; +}; + struct uaudio_chan { struct pcmchan_caps pcm_cap; /* capabilities */ - + struct uaudio_chan_alt usb_alt[CHAN_MAX_ALT]; struct snd_dbuf *pcm_buf; - const struct usb_config *usb_cfg; struct lock *pcm_lock; /* 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; - const usb_endpoint_descriptor_audio_t *p_ed1; - const usb_endpoint_descriptor_audio_t *p_ed2; - const struct uaudio_format *p_fmt; + struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1]; uint8_t *buf; /* pointer to buffer */ uint8_t *start; /* upper layer buffer start */ @@ -169,26 +205,33 @@ struct uaudio_chan { uint8_t *cur; /* current position in upper layer * buffer */ - uint32_t intr_size; /* in bytes */ uint32_t intr_frames; /* in units */ - uint32_t sample_rate; uint32_t frames_per_second; uint32_t sample_rem; uint32_t sample_curr; + uint32_t max_buf; - uint32_t format; uint32_t pcm_format[2]; uint16_t bytes_per_frame[2]; - uint16_t sample_size; - - uint8_t valid; - uint8_t iface_index; - uint8_t iface_alt_index; + uint8_t num_alt; + uint8_t cur_alt; + uint8_t set_alt; + uint8_t operation; +#define CHAN_OP_NONE 0 +#define CHAN_OP_START 1 +#define CHAN_OP_STOP 2 +#define CHAN_OP_DRAIN 3 + + uint8_t last_sync_time; + uint8_t last_sync_state; +#define UAUDIO_SYNC_NONE 0 +#define UAUDIO_SYNC_MORE 1 +#define UAUDIO_SYNC_LESS 2 }; -#define UMIDI_CABLES_MAX 16 /* units */ +#define UMIDI_EMB_JACK_MAX 16 /* units */ #define UMIDI_TX_FRAMES 256 /* units */ #define UMIDI_TX_BUFFER (UMIDI_TX_FRAMES * 4) /* bytes */ @@ -219,7 +262,7 @@ struct umidi_sub_chan { struct umidi_chan { - struct umidi_sub_chan sub[UMIDI_CABLES_MAX]; + struct umidi_sub_chan sub[UMIDI_EMB_JACK_MAX]; struct lock lock; struct usb_xfer *xfer[UMIDI_N_TRANSFER]; @@ -231,20 +274,54 @@ struct umidi_chan { uint8_t write_open_refcount; uint8_t curr_cable; - uint8_t max_cable; + uint8_t max_emb_jack; uint8_t valid; uint8_t single_command; }; -struct uaudio_softc { - struct lock sc_lock; +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; +}; + +enum { + UAUDIO_HID_RX_TRANSFER, + UAUDIO_HID_N_TRANSFER, +}; + +struct uaudio_hid { + struct usb_xfer *xfer[UAUDIO_HID_N_TRANSFER]; + struct hid_location volume_up_loc; + struct hid_location volume_down_loc; + struct hid_location mute_loc; + uint32_t flags; +#define UAUDIO_HID_VALID 0x0001 +#define UAUDIO_HID_HAS_ID 0x0002 +#define UAUDIO_HID_HAS_VOLUME_UP 0x0004 +#define UAUDIO_HID_HAS_VOLUME_DOWN 0x0008 +#define UAUDIO_HID_HAS_MUTE 0x0010 + uint8_t iface_index; + uint8_t volume_up_id; + uint8_t volume_down_id; + uint8_t mute_id; +}; +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_hid sc_hid; + struct uaudio_search_result sc_mixer_clocks; + struct uaudio_mixer_node sc_mixer_node; + struct uaudio_configure_msg sc_config_msg[2]; + struct lock *sc_mixer_lock; + struct snd_mixer *sc_mixer_dev; struct usb_device *sc_udev; struct usb_xfer *sc_mixer_xfer[1]; struct uaudio_mixer_node *sc_mixer_root; @@ -269,24 +346,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; @@ -299,7 +380,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"}, @@ -317,6 +398,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 @@ -337,23 +436,26 @@ 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 usb_callback_t uaudio_hid_rx_callback; + +static usb_proc_callback_t uaudio_configure_msg; + +/* ==== USB mixer ==== */ + +static int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS); +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_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); static void uaudio_mixer_add_mixer(struct uaudio_softc *, const struct uaudio_terminal_node *, int); static void uaudio_mixer_add_selector(struct uaudio_softc *, @@ -374,25 +476,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 *); @@ -405,17 +538,17 @@ static void umidi_close(struct usb_fifo *, int); static void umidi_init(device_t dev); static int umidi_probe(device_t dev); static int umidi_detach(device_t dev); +static int uaudio_hid_probe(struct uaudio_softc *sc, + struct usb_attach_arg *uaa); +static void uaudio_hid_detach(struct uaudio_softc *sc); #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, @@ -435,10 +568,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, @@ -458,6 +601,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 @@ -512,6 +665,18 @@ static const struct usb_config }, }; +static const struct usb_config + uaudio_hid_config[UAUDIO_HID_N_TRANSFER] = { + [UAUDIO_HID_RX_TRANSFER] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = 0, /* use wMaxPacketSize */ + .flags = {.short_xfer_ok = 1,}, + .callback = &uaudio_hid_rx_callback, + }, +}; + static devclass_t uaudio_devclass; static device_method_t uaudio_methods[] = { @@ -531,6 +696,115 @@ static driver_t uaudio_driver = { .size = sizeof(struct uaudio_softc), }; +/* The following table is derived from Linux's quirks-table.h */ +static const STRUCT_USB_HOST_ID uaudio_vendor_midi[] = { + { USB_VPI(USB_VENDOR_YAMAHA, 0x1000, 0) }, /* UX256 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1001, 0) }, /* MU1000 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1002, 0) }, /* MU2000 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1003, 0) }, /* MU500 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1004, 3) }, /* UW500 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1005, 0) }, /* MOTIF6 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1006, 0) }, /* MOTIF7 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1007, 0) }, /* MOTIF8 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1008, 0) }, /* UX96 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1009, 0) }, /* UX16 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x100a, 3) }, /* EOS BX */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x100c, 0) }, /* UC-MX */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x100d, 0) }, /* UC-KX */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x100e, 0) }, /* S08 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x100f, 0) }, /* CLP-150 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1010, 0) }, /* CLP-170 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1011, 0) }, /* P-250 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1012, 0) }, /* TYROS */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1013, 0) }, /* PF-500 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1014, 0) }, /* S90 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1015, 0) }, /* MOTIF-R */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1016, 0) }, /* MDP-5 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1017, 0) }, /* CVP-204 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1018, 0) }, /* CVP-206 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1019, 0) }, /* CVP-208 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x101a, 0) }, /* CVP-210 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x101b, 0) }, /* PSR-1100 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x101c, 0) }, /* PSR-2100 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x101d, 0) }, /* CLP-175 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x101e, 0) }, /* PSR-K1 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x101f, 0) }, /* EZ-J24 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1020, 0) }, /* EZ-250i */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1021, 0) }, /* MOTIF ES 6 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1022, 0) }, /* MOTIF ES 7 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1023, 0) }, /* MOTIF ES 8 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1024, 0) }, /* CVP-301 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1025, 0) }, /* CVP-303 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1026, 0) }, /* CVP-305 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1027, 0) }, /* CVP-307 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1028, 0) }, /* CVP-309 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1029, 0) }, /* CVP-309GP */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x102a, 0) }, /* PSR-1500 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x102b, 0) }, /* PSR-3000 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x102e, 0) }, /* ELS-01/01C */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1030, 0) }, /* PSR-295/293 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1031, 0) }, /* DGX-205/203 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1032, 0) }, /* DGX-305 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1033, 0) }, /* DGX-505 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1034, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1035, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1036, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1037, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1038, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1039, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x103a, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x103b, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x103c, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x103d, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x103e, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x103f, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1040, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1041, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1042, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1043, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1044, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1045, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x104e, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x104f, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1050, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1051, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1052, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1053, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1054, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1055, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1056, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1057, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1058, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1059, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x105a, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x105b, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x105c, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x105d, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x1503, 3) }, /* MOX6/MOX8 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x2000, 0) }, /* DGP-7 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x2001, 0) }, /* DGP-5 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x2002, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x2003, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5000, 0) }, /* CS1D */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5001, 0) }, /* DSP1D */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5002, 0) }, /* DME32 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5003, 0) }, /* DM2000 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5004, 0) }, /* 02R96 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5005, 0) }, /* ACU16-C */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5006, 0) }, /* NHB32-C */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5007, 0) }, /* DM1000 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5008, 0) }, /* 01V96 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x5009, 0) }, /* SPX2000 */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x500a, 0) }, /* PM5D */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x500b, 0) }, /* DME64N */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x500c, 0) }, /* DME24N */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x500d, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x500e, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x500f, 0) }, /* NULL */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x7000, 0) }, /* DTX */ + { USB_VPI(USB_VENDOR_YAMAHA, 0x7010, 0) }, /* UB99 */ +}; + static const STRUCT_USB_HOST_ID __used uaudio_devs[] = { /* Generic USB audio class match */ {USB_IFACE_CLASS(UICLASS_AUDIO), @@ -548,10 +822,16 @@ uaudio_probe(device_t dev) if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); - /* lookup non-standard device */ + /* lookup non-standard device(s) */ + + if (usbd_lookup_id_by_uaa(uaudio_vendor_midi, + sizeof(uaudio_vendor_midi), uaa) == 0) { + return (BUS_PROBE_SPECIFIC); + } 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); } @@ -581,15 +861,18 @@ uaudio_attach(device_t dev) struct usb_attach_arg *uaa = device_get_ivars(dev); struct uaudio_softc *sc = device_get_softc(dev); struct usb_interface_descriptor *id; + usb_error_t err; device_t child; - lockinit(&sc->sc_lock, "uaudio", 0, 0); - sc->sc_play_chan.priv_sc = sc; sc->sc_rec_chan.priv_sc = sc; sc->sc_udev = uaa->device; sc->sc_mixer_iface_index = uaa->info.bIfaceIndex; sc->sc_mixer_iface_no = uaa->info.bIfaceNum; + sc->sc_config_msg[0].hdr.pm_callback = &uaudio_configure_msg; + sc->sc_config_msg[0].sc = sc; + sc->sc_config_msg[1].hdr.pm_callback = &uaudio_configure_msg; + sc->sc_config_msg[1].sc = sc; if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR)) sc->sc_uq_audio_swap_lr = 1; @@ -612,33 +895,88 @@ 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", - sc->sc_play_chan.sample_rate, - sc->sc_play_chan.p_asf1d->bNrChannels, - sc->sc_play_chan.p_fmt->description); + if (sc->sc_play_chan.num_alt > 0) { + uint8_t x; + + /* + * Need to set a default alternate interface, else + * some USB audio devices might go into an infinte + * re-enumeration loop: + */ + err = usbd_set_alt_interface_index(sc->sc_udev, + sc->sc_play_chan.usb_alt[0].iface_index, + sc->sc_play_chan.usb_alt[0].iface_alt_index); + if (err) { + DPRINTF("setting of alternate index failed: %s!\n", + usbd_errstr(err)); + } + for (x = 0; x != sc->sc_play_chan.num_alt; x++) { + device_printf(dev, "Play: %d Hz, %d ch, %s format, " + "2x8ms buffer.\n", + sc->sc_play_chan.usb_alt[x].sample_rate, + sc->sc_play_chan.usb_alt[x].channels, + sc->sc_play_chan.usb_alt[x].p_fmt->description); + } } else { - device_printf(dev, "No playback!\n"); + device_printf(dev, "No playback.\n"); } - 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_rec_chan.p_fmt->description); + if (sc->sc_rec_chan.num_alt > 0) { + uint8_t x; + + /* + * Need to set a default alternate interface, else + * some USB audio devices might go into an infinte + * re-enumeration loop: + */ + err = usbd_set_alt_interface_index(sc->sc_udev, + sc->sc_rec_chan.usb_alt[0].iface_index, + sc->sc_rec_chan.usb_alt[0].iface_alt_index); + if (err) { + DPRINTF("setting of alternate index failed: %s!\n", + usbd_errstr(err)); + } + for (x = 0; x != sc->sc_rec_chan.num_alt; x++) { + device_printf(dev, "Record: %d Hz, %d ch, %s format, " + "2x8ms buffer.\n", + sc->sc_rec_chan.usb_alt[x].sample_rate, + sc->sc_rec_chan.usb_alt[x].channels, + sc->sc_rec_chan.usb_alt[x].p_fmt->description); + } } else { - device_printf(dev, "No recording!\n"); + device_printf(dev, "No recording.\n"); + } + + if (sc->sc_midi_chan.valid == 0) { + if (usbd_lookup_id_by_uaa(uaudio_vendor_midi, + sizeof(uaudio_vendor_midi), uaa) == 0) { + sc->sc_midi_chan.iface_index = + (uint8_t)uaa->driver_info; + sc->sc_midi_chan.iface_alt_index = 0; + sc->sc_midi_chan.valid = 1; + } } if (sc->sc_midi_chan.valid) { @@ -646,9 +984,9 @@ uaudio_attach(device_t dev) if (umidi_probe(dev)) { goto detach; } - device_printf(dev, "MIDI sequencer\n"); + device_printf(dev, "MIDI sequencer.\n"); } else { - device_printf(dev, "No midi sequencer\n"); + device_printf(dev, "No MIDI sequencer.\n"); } DPRINTF("doing child attach\n"); @@ -657,18 +995,36 @@ uaudio_attach(device_t dev) sc->sc_sndcard_func.func = SCF_PCM; - child = device_add_child(dev, "pcm", -1); + /* + * Only attach a PCM device if we have a playback, recording + * or mixer device present: + */ + if (sc->sc_play_chan.num_alt > 0 || + sc->sc_rec_chan.num_alt > 0 || + sc->sc_mix_info) { + child = device_add_child(dev, "pcm", -1); - if (child == NULL) { - DPRINTF("out of memory\n"); - goto detach; + if (child == NULL) { + DPRINTF("out of memory\n"); + goto detach; + } + device_set_ivars(child, &sc->sc_sndcard_func); } - device_set_ivars(child, &sc->sc_sndcard_func); if (bus_generic_attach(dev)) { DPRINTF("child attach failed\n"); goto detach; } + + if (uaudio_hid_probe(sc, uaa) == 0) { + device_printf(dev, "HID volume keys found.\n"); + } else { + device_printf(dev, "No HID volume keys found.\n"); + } + + /* reload all mixer settings */ + uaudio_mixer_reload_all(sc); + return (0); /* success */ detach: @@ -704,33 +1060,33 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas */ 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; + mixer_hwvol_init(dev); + ksnprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio)); if (pcm_register(dev, sc, - sc->sc_play_chan.valid ? 1 : 0, - sc->sc_rec_chan.valid ? 1 : 0)) { + (sc->sc_play_chan.num_alt > 0) ? 1 : 0, + (sc->sc_rec_chan.num_alt > 0) ? 1 : 0)) { goto detach; } -#if 0 /* XXX */ uaudio_pcm_setflags(dev, SD_F_MPSAFE); -#endif - uaudio_pcm_setflags(dev, 0); sc->sc_pcm_registered = 1; - if (sc->sc_play_chan.valid) { + if (sc->sc_play_chan.num_alt > 0) { pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc); } - if (sc->sc_rec_chan.valid) { + if (sc->sc_rec_chan.num_alt > 0) { pcm_addchan(dev, PCMDIR_REC, chan_class, sc); } pcm_setstatus(dev, status); + uaudio_mixer_register_sysctl(sc, dev); + return (0); /* success */ detach: @@ -771,10 +1127,17 @@ uaudio_detach(device_t dev) * will time out and close opened /dev/dspX.Y device(s), if * any. */ - if (sc->sc_play_chan.valid) - usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS); - if (sc->sc_rec_chan.valid) - usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS); + usb_proc_explore_lock(sc->sc_udev); + sc->sc_play_chan.operation = CHAN_OP_DRAIN; + sc->sc_rec_chan.operation = CHAN_OP_DRAIN; + usb_proc_explore_mwait(sc->sc_udev, + &sc->sc_config_msg[0], &sc->sc_config_msg[1]); + usb_proc_explore_unlock(sc->sc_udev); + + usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1); + usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1); + + uaudio_hid_detach(sc); if (bus_generic_detach(dev) != 0) { DPRINTF("detach failed!\n"); @@ -783,97 +1146,359 @@ uaudio_detach(device_t dev) sc->sc_sndstat_valid = 0; umidi_detach(dev); - lockuninit(&sc->sc_lock); + + /* free mixer data */ + + uaudio_mixer_ctl_free(sc); return (0); } -/*========================================================================* - * AS - Audio Stream - routines - *========================================================================*/ - -#ifdef USB_DEBUG -static void -uaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed) +static uint32_t +uaudio_get_buffer_size(struct uaudio_chan *ch, uint8_t alt) { - if (ed) { - DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n" - "bEndpointAddress=%d bmAttributes=0x%x \n" - "wMaxPacketSize=%d bInterval=%d \n" - "bRefresh=%d bSynchAddress=%d\n", - ed, ed->bLength, ed->bDescriptorType, - ed->bEndpointAddress, ed->bmAttributes, - UGETW(ed->wMaxPacketSize), ed->bInterval, - UEP_HAS_REFRESH(ed) ? ed->bRefresh : 0, - UEP_HAS_SYNCADDR(ed) ? ed->bSynchAddress : 0); - } + struct uaudio_chan_alt *chan_alt = &ch->usb_alt[alt]; + /* We use 2 times 8ms of buffer */ + uint32_t buf_size = (((chan_alt->sample_rate * (UAUDIO_NFRAMES / 8)) + + 1000 - 1) / 1000) * chan_alt->sample_size; + return (buf_size); } -#endif - -/* - * The following is a workaround for broken no-name USB audio devices - * sold by dealextreme called "3D sound". The problem is that the - * manufacturer computed wMaxPacketSize is too small to hold the - * actual data sent. In other words the device sometimes sends more - * data than it actually reports it can send in a single isochronous - * packet. - */ static void -uaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep, - uint32_t xps, uint32_t add) +uaudio_configure_msg_sub(struct uaudio_softc *sc, + struct uaudio_chan *chan, int dir) { - uint32_t mps; + struct uaudio_chan_alt *chan_alt; + uint32_t frames; + uint32_t buf_size; + uint16_t fps; + uint8_t set_alt; + uint8_t fps_shift; + uint8_t operation; + usb_error_t err; - mps = UGETW(ep->wMaxPacketSize); + if (chan->num_alt <= 0) + return; - /* - * If the device indicates it can send more data than what the - * sample rate indicates, we apply the workaround. - */ - if (mps > xps) { + DPRINTF("\n"); - /* allow additional data */ - xps += add; + usb_proc_explore_lock(sc->sc_udev); + operation = chan->operation; + chan->operation = CHAN_OP_NONE; + usb_proc_explore_unlock(sc->sc_udev); - /* check against the maximum USB 1.x length */ - if (xps > 1023) - xps = 1023; + lockmgr(chan->pcm_lock, LK_EXCLUSIVE); + if (chan->cur_alt != chan->set_alt) + set_alt = chan->set_alt; + else + set_alt = CHAN_MAX_ALT; + lockmgr(chan->pcm_lock, LK_RELEASE); - /* check if we should do an update */ - if (mps < xps) { - /* simply update the wMaxPacketSize field */ - USETW(ep->wMaxPacketSize, xps); - DPRINTF("Workaround: Updated wMaxPacketSize " - "from %d to %d bytes.\n", - (int)mps, (int)xps); - } + if (set_alt >= chan->num_alt) + goto done; + + chan_alt = chan->usb_alt + set_alt; + + usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1); + + err = usbd_set_alt_interface_index(sc->sc_udev, + chan_alt->iface_index, chan_alt->iface_alt_index); + if (err) { + DPRINTF("setting of alternate index failed: %s!\n", + usbd_errstr(err)); + goto error; } -} -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; - usb_endpoint_descriptor_audio_t *ed1 = NULL; - const usb_endpoint_descriptor_audio_t *ed2 = NULL; - struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); - struct usb_interface_descriptor *id; - const struct uaudio_format *p_fmt; + /* + * Only set the sample rate if the channel reports that it + * supports the frequency control. + */ + + if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { + /* FALLTHROUGH */ + } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { + unsigned int x; + + for (x = 0; x != 256; x++) { + if (dir == PCMDIR_PLAY) { + if (!(sc->sc_mixer_clocks.bit_output[x / 8] & + (1 << (x % 8)))) { + continue; + } + } else { + if (!(sc->sc_mixer_clocks.bit_input[x / 8] & + (1 << (x % 8)))) { + continue; + } + } + + if (uaudio20_set_speed(sc->sc_udev, + sc->sc_mixer_iface_no, x, chan_alt->sample_rate)) { + /* + * If the endpoint is adaptive setting + * the speed may fail. + */ + DPRINTF("setting of sample rate failed! " + "(continuing anyway)\n"); + } + } + } else if (chan_alt->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) { + if (uaudio_set_speed(sc->sc_udev, + chan_alt->p_ed1->bEndpointAddress, chan_alt->sample_rate)) { + /* + * If the endpoint is adaptive setting the + * speed may fail. + */ + DPRINTF("setting of sample rate failed! " + "(continuing anyway)\n"); + } + } + if (usbd_transfer_setup(sc->sc_udev, &chan_alt->iface_index, chan->xfer, + chan_alt->usb_cfg, UAUDIO_NCHANBUFS + 1, chan, chan->pcm_lock)) { + DPRINTF("could not allocate USB transfers!\n"); + goto error; + } + + fps = usbd_get_isoc_fps(sc->sc_udev); + + if (fps < 8000) { + /* FULL speed USB */ + frames = 8; + } else { + /* HIGH speed USB */ + frames = UAUDIO_NFRAMES; + } + + fps_shift = usbd_xfer_get_fps_shift(chan->xfer[0]); + + /* down shift number of frames per second, if any */ + fps >>= fps_shift; + frames >>= fps_shift; + + /* bytes per frame should not be zero */ + chan->bytes_per_frame[0] = + ((chan_alt->sample_rate / fps) * chan_alt->sample_size); + chan->bytes_per_frame[1] = + (((chan_alt->sample_rate + fps - 1) / fps) * chan_alt->sample_size); + + /* setup data rate dithering, if any */ + chan->frames_per_second = fps; + chan->sample_rem = chan_alt->sample_rate % fps; + chan->sample_curr = 0; + chan->frames_per_second = fps; + + /* compute required buffer size */ + buf_size = (chan->bytes_per_frame[1] * frames); + + if (buf_size > (chan->end - chan->start)) { + DPRINTF("buffer size is too big\n"); + goto error; + } + + chan->intr_frames = frames; + + DPRINTF("fps=%d sample_rem=%d\n", (int)fps, (int)chan->sample_rem); + + if (chan->intr_frames == 0) { + DPRINTF("frame shift is too high!\n"); + goto error; + } + + lockmgr(chan->pcm_lock, LK_EXCLUSIVE); + chan->cur_alt = set_alt; + lockmgr(chan->pcm_lock, LK_RELEASE); + +done: +#if (UAUDIO_NCHANBUFS != 2) +#error "please update code" +#endif + switch (operation) { + case CHAN_OP_START: + lockmgr(chan->pcm_lock, LK_EXCLUSIVE); + usbd_transfer_start(chan->xfer[0]); + usbd_transfer_start(chan->xfer[1]); + lockmgr(chan->pcm_lock, LK_RELEASE); + break; + case CHAN_OP_STOP: + lockmgr(chan->pcm_lock, LK_EXCLUSIVE); + usbd_transfer_stop(chan->xfer[0]); + usbd_transfer_stop(chan->xfer[1]); + lockmgr(chan->pcm_lock, LK_RELEASE); + break; + default: + break; + } + return; + +error: + usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1); + + lockmgr(chan->pcm_lock, LK_EXCLUSIVE); + chan->cur_alt = CHAN_MAX_ALT; + lockmgr(chan->pcm_lock, LK_RELEASE); +} + +static void +uaudio_configure_msg(struct usb_proc_msg *pm) +{ + struct uaudio_softc *sc = ((struct uaudio_configure_msg *)pm)->sc; + + usb_proc_explore_unlock(sc->sc_udev); + uaudio_configure_msg_sub(sc, &sc->sc_play_chan, PCMDIR_PLAY); + uaudio_configure_msg_sub(sc, &sc->sc_rec_chan, PCMDIR_REC); + usb_proc_explore_lock(sc->sc_udev); +} + +/*========================================================================* + * AS - Audio Stream - routines + *========================================================================*/ + +#ifdef USB_DEBUG +static void +uaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed) +{ + if (ed) { + DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n" + "bEndpointAddress=%d bmAttributes=0x%x \n" + "wMaxPacketSize=%d bInterval=%d \n" + "bRefresh=%d bSynchAddress=%d\n", + ed, ed->bLength, ed->bDescriptorType, + ed->bEndpointAddress, ed->bmAttributes, + UGETW(ed->wMaxPacketSize), ed->bInterval, + UEP_HAS_REFRESH(ed) ? ed->bRefresh : 0, + UEP_HAS_SYNCADDR(ed) ? ed->bSynchAddress : 0); + } +} + +#endif + +/* + * The following is a workaround for broken no-name USB audio devices + * sold by dealextreme called "3D sound". The problem is that the + * manufacturer computed wMaxPacketSize is too small to hold the + * actual data sent. In other words the device sometimes sends more + * data than it actually reports it can send in a single isochronous + * packet. + */ +static void +uaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep, + uint32_t xps, uint32_t add) +{ + uint32_t mps; + + mps = UGETW(ep->wMaxPacketSize); + + /* + * If the device indicates it can send more data than what the + * sample rate indicates, we apply the workaround. + */ + if (mps > xps) { + + /* allow additional data */ + xps += add; + + /* check against the maximum USB 1.x length */ + if (xps > 1023) + xps = 1023; + + /* check if we should do an update */ + if (mps < xps) { + /* simply update the wMaxPacketSize field */ + USETW(ep->wMaxPacketSize, xps); + DPRINTF("Workaround: Updated wMaxPacketSize " + "from %d to %d bytes.\n", + (int)mps, (int)xps); + } + } +} + +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; + union uaudio_asid asid = { NULL }; + union uaudio_asf1d asf1d = { NULL }; + union uaudio_sed sed = { NULL }; + struct usb_midi_streaming_endpoint_descriptor *msid = NULL; + usb_endpoint_descriptor_audio_t *ed1 = 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 = NULL; struct uaudio_chan *chan; + struct uaudio_chan_alt *chan_alt; + uint32_t format; uint16_t curidx = 0xFFFF; uint16_t lastidx = 0xFFFF; uint16_t alt_index = 0; - 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 midi_if = 0; uint8_t uma_if_class; while ((desc = usb_desc_foreach(cd, desc))) { @@ -892,12 +1517,25 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev, alt_index++; } + if ((!(sc->sc_hid.flags & UAUDIO_HID_VALID)) && + (id->bInterfaceClass == UICLASS_HID) && + (id->bInterfaceSubClass == 0) && + (id->bInterfaceProtocol == 0) && + (alt_index == 0) && + usbd_get_iface(udev, curidx) != NULL) { + DPRINTF("Found HID interface at %d\n", + curidx); + sc->sc_hid.flags |= UAUDIO_HID_VALID; + sc->sc_hid.iface_index = curidx; + } + uma_if_class = ((id->bInterfaceClass == UICLASS_AUDIO) || ((id->bInterfaceClass == UICLASS_VENDOR) && (sc->sc_uq_au_vendor_class != 0))); - if ((uma_if_class != 0) && (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) { + if ((uma_if_class != 0) && + (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) { audio_if = 1; } else { audio_if = 0; @@ -909,186 +1547,355 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev, /* * XXX could allow multiple MIDI interfaces */ + midi_if = 1; if ((sc->sc_midi_chan.valid == 0) && - usbd_get_iface(udev, curidx)) { + (usbd_get_iface(udev, curidx) != NULL)) { sc->sc_midi_chan.iface_index = curidx; sc->sc_midi_chan.iface_alt_index = alt_index; sc->sc_midi_chan.valid = 1; } + } else { + midi_if = 0; } - asid = NULL; - asf1d = NULL; + asid.v1 = NULL; + asf1d.v1 = NULL; ed1 = NULL; - ed2 = NULL; - sed = NULL; + sed.v1 = NULL; + } + + if (audio_if == 0) { + if (midi_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); + } + } else { + msid = (void *)desc; + + /* get the maximum number of embedded jacks in use, if any */ + if (msid->bLength >= sizeof(*msid) && + msid->bDescriptorType == UDESC_CS_ENDPOINT && + msid->bDescriptorSubtype == MS_GENERAL && + msid->bNumEmbMIDIJack > sc->sc_midi_chan.max_emb_jack) { + sc->sc_midi_chan.max_emb_jack = msid->bNumEmbMIDIJack; + } + } + /* + * Don't collect any USB audio descriptors if + * this is not an USB audio stream interface. + */ + continue; } - 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 (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. */ - wFormat = UGETW(asid->wFormatTag); - bChannels = UAUDIO_MAX_CHAN(asf1d->bNrChannels); - bBitResolution = asf1d->bBitResolution; + if (audio_rev >= UAUDIO_VERSION_30) { + goto next_ep; + } else if (audio_rev >= UAUDIO_VERSION_20) { - if (asf1d->bSamFreqType == 0) { + uint32_t dwFormat; + + dwFormat = UGETDW(asid.v2->bmFormats); + bChannels = asid.v2->bNrChannels; + bBitResolution = asf1d.v2->bSubslotSize * 8; + + 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->bSubFrameSize * 8; + + 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)) { + DPRINTF("Wrong number of channels\n"); + goto next_ep; + } + } - if ((bChannels == channels) && - (bBitResolution == bit_resolution)) { + 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 (usbd_get_iface(udev, curidx) == NULL) { + DPRINTF("Interface is not valid\n"); + goto next_ep; + } + if (chan->num_alt == CHAN_MAX_ALT) { + DPRINTF("Too many alternate settings\n"); + goto next_ep; + } + chan->set_alt = 0; + chan->cur_alt = CHAN_MAX_ALT; - if ((chan->valid == 0) && usbd_get_iface(udev, curidx)) { + chan_alt = &chan->usb_alt[chan->num_alt++]; - 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_alt->sample_rate = rate; + chan_alt->p_asf1d = asf1d; + chan_alt->p_ed1 = ed1; + chan_alt->p_fmt = p_fmt; + chan_alt->p_sed = sed; + chan_alt->iface_index = curidx; + chan_alt->iface_alt_index = alt_index; + + if (UEP_HAS_SYNCADDR(ed1) && ed1->bSynchAddress != 0) { + DPRINTF("Sync endpoint will be used, if present\n"); + chan_alt->enable_sync = 1; + } else { + DPRINTF("Sync endpoint will not be used\n"); + chan_alt->enable_sync = 0; + } - 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); - } - } + usbd_set_parent_iface(sc->sc_udev, curidx, + sc->sc_mixer_iface_index); + + if (ep_dir == UE_DIR_IN) + chan_alt->usb_cfg = uaudio_cfg_record; + else + chan_alt->usb_cfg = uaudio_cfg_play; + + chan_alt->sample_size = (UAUDIO_MAX_CHAN(channels) * + p_fmt->bPrecision) / 8; + chan_alt->channels = channels; + + if (ep_dir == UE_DIR_IN && + usbd_get_speed(udev) == USB_SPEED_FULL) { + uaudio_record_fix_fs(ed1, + chan_alt->sample_size * (rate / 1000), + chan_alt->sample_size * (rate / 4000)); + } + + /* setup play/record format */ + + format = chan_alt->p_fmt->freebsd_fmt; + + switch (chan_alt->channels) { + case 2: + /* stereo */ + format = SND_FORMAT(format, 2, 0); + break; + case 1: + /* mono */ + format = SND_FORMAT(format, 1, 0); + break; + default: + /* surround and more */ + format = feeder_matrix_default_format( + SND_FORMAT(format, chan_alt->channels, 0)); + break; + } + + /* check if format is not supported */ + if (format == 0) { + DPRINTF("The selected audio format is not supported\n"); + chan->num_alt--; + goto next_ep; + } + if (chan->num_alt > 1) { + /* we only accumulate one format at different sample rates */ + if (chan->pcm_format[0] != format) { + DPRINTF("Multiple formats is not supported\n"); + chan->num_alt--; + goto next_ep; + } + /* ignore if duplicate sample rate entry */ + if (rate == chan->usb_alt[chan->num_alt - 2].sample_rate) { + DPRINTF("Duplicate sample rate detected\n"); + chan->num_alt--; + goto next_ep; } - audio_if = 0; - continue; } + chan->pcm_cap.fmtlist = chan->pcm_format; + chan->pcm_cap.fmtlist[0] = format; + + if (rate < chan->pcm_cap.minspeed || chan->pcm_cap.minspeed == 0) + chan->pcm_cap.minspeed = rate; + if (rate > chan->pcm_cap.maxspeed || chan->pcm_cap.maxspeed == 0) + chan->pcm_cap.maxspeed = rate; + + if (sc->sc_sndstat_valid != 0) { + sbuf_printf(&sc->sc_sndstat, "\n\t" + "mode %d.%d:(%s) %dch, %dbit, %s, %dHz", + 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; } } /* This structure defines all the supported rates. */ -static const uint32_t uaudio_rate_list[] = { +static const uint32_t uaudio_rate_list[CHAN_MAX_ALT] = { + 384000, + 352800, + 192000, + 176400, 96000, + 88200, 88000, 80000, 72000, @@ -1131,7 +1938,7 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev) * disable surround setups on FULL-speed USB * by default */ - channels = 2; + channels = 4; break; default: channels = 16; @@ -1153,20 +1960,93 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev) uaudio_chan_fill_info_sub(sc, udev, rate, x, y); /* try find a matching rate, if any */ - for (z = 0; uaudio_rate_list[z]; z++) { + for (z = 0; uaudio_rate_list[z]; z++) uaudio_chan_fill_info_sub(sc, udev, uaudio_rate_list[z], x, y); - - if (sc->sc_rec_chan.valid && - sc->sc_play_chan.valid) { - goto done; - } - } } } - -done: - if (sc->sc_sndstat_valid) { + if (sc->sc_sndstat_valid) sbuf_finish(&sc->sc_sndstat); +} + +static void +uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct uaudio_chan *ch = usbd_xfer_softc(xfer); + struct usb_page_cache *pc; + uint64_t sample_rate = ch->usb_alt[ch->cur_alt].sample_rate; + uint8_t buf[4]; + uint64_t temp; + int len; + int actlen; + int nframes; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(6, "transferred %d bytes\n", actlen); + + if (nframes == 0) + break; + len = usbd_xfer_frame_len(xfer, 0); + if (len == 0) + break; + if (len > sizeof(buf)) + len = sizeof(buf); + + memset(buf, 0, sizeof(buf)); + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, buf, len); + + temp = UGETDW(buf); + + DPRINTF("Value = 0x%08x\n", (int)temp); + + /* auto-detect SYNC format */ + + if (len == 4) + temp &= 0x0fffffff; + + /* check for no data */ + + if (temp == 0) + break; + + /* correctly scale value */ + + temp = (temp * 125ULL) - 64; + + /* auto adjust */ + + while (temp < (sample_rate - (sample_rate / 4))) + temp *= 2; + + while (temp > (sample_rate + (sample_rate / 2))) + temp /= 2; + + /* compare */ + + DPRINTF("Comparing %d < %d\n", + (int)temp, (int)sample_rate); + + if (temp == sample_rate) + ch->last_sync_state = UAUDIO_SYNC_NONE; + else if (temp > sample_rate) + ch->last_sync_state = UAUDIO_SYNC_MORE; + else + ch->last_sync_state = UAUDIO_SYNC_LESS; + break; + + case USB_ST_SETUP: + usbd_xfer_set_frames(xfer, 1); + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_framelen(xfer)); + usbd_transfer_submit(xfer); + break; + + default: /* Error */ + break; } } @@ -1175,6 +2055,8 @@ uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error) { struct uaudio_chan *ch = usbd_xfer_softc(xfer); struct usb_page_cache *pc; + uint32_t sample_size = ch->usb_alt[ch->cur_alt].sample_size; + uint32_t mfl; uint32_t total; uint32_t blockcount; uint32_t n; @@ -1198,12 +2080,20 @@ tr_transferred: } chn_intr(ch->pcm_ch); + /* start SYNC transfer, if any */ + if (ch->usb_alt[ch->cur_alt].enable_sync != 0) { + if ((ch->last_sync_time++ & 7) == 0) + usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]); + } + case USB_ST_SETUP: - if (ch->bytes_per_frame[1] > usbd_xfer_max_framelen(xfer)) { + mfl = usbd_xfer_max_framelen(xfer); + + if (ch->bytes_per_frame[1] > mfl) { DPRINTF("bytes per transfer, %d, " "exceeds maximum, %d!\n", ch->bytes_per_frame[1], - usbd_xfer_max_framelen(xfer)); + mfl); break; } @@ -1217,15 +2107,37 @@ tr_transferred: /* setup frame lengths */ for (n = 0; n != blockcount; n++) { + uint32_t frame_len; + ch->sample_curr += ch->sample_rem; if (ch->sample_curr >= ch->frames_per_second) { ch->sample_curr -= ch->frames_per_second; - usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[1]); - total += ch->bytes_per_frame[1]; + frame_len = ch->bytes_per_frame[1]; } else { - usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[0]); - total += ch->bytes_per_frame[0]; + frame_len = ch->bytes_per_frame[0]; } + + if (n == (blockcount - 1)) { + switch (ch->last_sync_state) { + case UAUDIO_SYNC_MORE: + DPRINTFN(6, "sending one sample more\n"); + if ((frame_len + sample_size) <= mfl) + frame_len += sample_size; + ch->last_sync_state = UAUDIO_SYNC_NONE; + break; + case UAUDIO_SYNC_LESS: + DPRINTFN(6, "sending one sample less\n"); + if (frame_len >= sample_size) + frame_len -= sample_size; + ch->last_sync_state = UAUDIO_SYNC_NONE; + break; + default: + break; + } + } + + usbd_xfer_set_frame_len(xfer, n, frame_len); + total += frame_len; } DPRINTFN(6, "transfer %d bytes\n", total); @@ -1261,20 +2173,26 @@ tr_transferred: } } +static void +uaudio_chan_record_sync_callback(struct usb_xfer *xfer, usb_error_t error) +{ + /* TODO */ +} + static void uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error) { struct uaudio_chan *ch = usbd_xfer_softc(xfer); struct usb_page_cache *pc; - uint32_t n; - uint32_t m; - uint32_t blockcount; uint32_t offset0; uint32_t offset1; uint32_t mfl; + int m; + int n; int len; int actlen; int nframes; + int blockcount; usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); mfl = usbd_xfer_max_framelen(xfer); @@ -1301,9 +2219,9 @@ uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error) m = (ch->end - ch->cur); - if (m > len) { + if (m > len) m = len; - } + usbd_copy_out(pc, offset1, ch->cur, m); len -= m; @@ -1347,164 +2265,41 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ? &sc->sc_play_chan : &sc->sc_rec_chan); uint32_t buf_size; - uint32_t frames; - uint32_t format; - uint16_t fps; - uint8_t endpoint; - uint8_t blocks; - uint8_t iface_index; - uint8_t alt_index; - uint8_t fps_shift; - usb_error_t err; - - fps = usbd_get_isoc_fps(sc->sc_udev); - - if (fps < 8000) { - /* FULL speed USB */ - frames = 8; - } else { - /* HIGH speed USB */ - frames = UAUDIO_NFRAMES; - } - - /* setup play/record format */ - - ch->pcm_cap.fmtlist = ch->pcm_format; - - ch->pcm_format[0] = 0; - ch->pcm_format[1] = 0; - - ch->pcm_cap.minspeed = ch->sample_rate; - ch->pcm_cap.maxspeed = ch->sample_rate; + uint8_t x; - /* setup mutex and PCM channel */ + /* store mutex and PCM channel */ ch->pcm_ch = c; ch->pcm_lock = c->lock; - format = ch->p_fmt->freebsd_fmt; - -#ifdef XXX - switch (ch->p_asf1d->bNrChannels) { - case 2: - /* stereo */ - format = SND_FORMAT(format, 2, 0); - break; - case 1: - /* mono */ - format = SND_FORMAT(format, 1, 0); - break; - default: - /* surround and more */ - format = feeder_matrix_default_format( - SND_FORMAT(format, ch->p_asf1d->bNrChannels, 0)); - break; - } -#endif - /* - * KLUDGE: - * Add support at least for 2 channels - */ - if (ch->p_asf1d->bNrChannels == 2) format |= AFMT_STEREO; - - ch->pcm_cap.fmtlist[0] = format; - ch->pcm_cap.fmtlist[1] = 0; - - /* check if format is not supported */ - - if (format == 0) { - DPRINTF("The selected audio format is not supported\n"); - goto error; - } - - /* set alternate interface corresponding to the mode */ - - endpoint = ch->p_ed1->bEndpointAddress; - iface_index = ch->iface_index; - alt_index = ch->iface_alt_index; - - DPRINTF("endpoint=0x%02x, speed=%d, iface=%d alt=%d\n", - endpoint, ch->sample_rate, iface_index, alt_index); - - err = usbd_set_alt_interface_index(sc->sc_udev, iface_index, alt_index); - if (err) { - DPRINTF("setting of alternate index failed: %s!\n", - usbd_errstr(err)); - goto error; - } - usbd_set_parent_iface(sc->sc_udev, iface_index, - sc->sc_mixer_iface_index); - - /* - * Only set the sample rate if the channel reports that it - * supports the frequency control. - */ - if (ch->p_sed->bmAttributes & UA_SED_FREQ_CONTROL) { - if (uaudio_set_speed(sc->sc_udev, endpoint, ch->sample_rate)) { - /* - * If the endpoint is adaptive setting the speed may - * fail. - */ - DPRINTF("setting of sample rate failed! (continuing anyway)\n"); - } - } - if (usbd_transfer_setup(sc->sc_udev, &iface_index, ch->xfer, - ch->usb_cfg, UAUDIO_NCHANBUFS, ch, ch->pcm_lock)) { - DPRINTF("could not allocate USB transfers!\n"); - goto error; - } - - fps_shift = usbd_xfer_get_fps_shift(ch->xfer[0]); - - /* down shift number of frames per second, if any */ - fps >>= fps_shift; - frames >>= fps_shift; - - /* bytes per frame should not be zero */ - ch->bytes_per_frame[0] = ((ch->sample_rate / fps) * ch->sample_size); - ch->bytes_per_frame[1] = (((ch->sample_rate + fps - 1) / fps) * ch->sample_size); - - /* setup data rate dithering, if any */ - ch->frames_per_second = fps; - ch->sample_rem = ch->sample_rate % fps; - ch->sample_curr = 0; - ch->frames_per_second = fps; - - /* compute required buffer size */ - buf_size = (ch->bytes_per_frame[1] * frames); - - ch->intr_size = buf_size; - ch->intr_frames = frames; + /* compute worst case buffer */ - DPRINTF("fps=%d sample_rem=%d\n", fps, ch->sample_rem); - - if (ch->intr_frames == 0) { - DPRINTF("frame shift is too high!\n"); - goto error; + buf_size = 0; + for (x = 0; x != ch->num_alt; x++) { + uint32_t temp = uaudio_get_buffer_size(ch, x); + if (temp > buf_size) + buf_size = temp; } - /* setup double buffering */ + /* allow double buffering */ buf_size *= 2; - blocks = 2; + + DPRINTF("Worst case buffer is %d bytes\n", (int)buf_size); ch->buf = kmalloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO); - if (ch->buf == NULL) - goto error; if (sndbuf_setup(b, ch->buf, buf_size) != 0) goto error; - if (sndbuf_resize(b, blocks, ch->intr_size)) - goto error; ch->start = ch->buf; ch->end = ch->buf + buf_size; ch->cur = ch->buf; ch->pcm_buf = b; + ch->max_buf = buf_size; if (ch->pcm_lock == NULL) { DPRINTF("ERROR: PCM channels does not have a mutex!\n"); goto error; } - return (ch); error: @@ -1519,9 +2314,9 @@ uaudio_chan_free(struct uaudio_chan *ch) kfree(ch->buf, M_DEVBUF); ch->buf = NULL; } - usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS); + usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS + 1); - ch->valid = 0; + ch->num_alt = 0; return (0); } @@ -1529,7 +2324,15 @@ uaudio_chan_free(struct uaudio_chan *ch) int uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize) { - return (ch->intr_size); + uint32_t temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt); + + sndbuf_setup(ch->pcm_buf, ch->buf, temp); + + ch->start = ch->buf; + ch->end = ch->buf + temp; + ch->cur = ch->buf; + + return (temp / 2); } int @@ -1542,10 +2345,23 @@ uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize, int uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed) { - if (speed != ch->sample_rate) { - DPRINTF("rate conversion required\n"); + uint8_t x; + + for (x = 0; x < ch->num_alt; x++) { + if (ch->usb_alt[x].sample_rate < speed) { + /* sample rate is too low */ + break; + } } - return (ch->sample_rate); + + if (x != 0) + x--; + + ch->set_alt = x; + + DPRINTF("Selecting alt %d\n", (int)x); + + return (ch->usb_alt[x].sample_rate); } int @@ -1560,7 +2376,6 @@ uaudio_chan_getcaps(struct uaudio_chan *ch) return (&ch->pcm_cap); } -#ifdef XXXDF static struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = { .id = SND_CHN_MATRIX_DRV, .channels = 2, @@ -1605,26 +2420,36 @@ uaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format) return (feeder_matrix_format_map(format)); } -#endif + int uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format) { - ch->format = format; + DPRINTF("Selecting format 0x%08x\n", (unsigned int)format); return (0); } int uaudio_chan_start(struct uaudio_chan *ch) { - ch->cur = ch->start; + struct uaudio_softc *sc = ch->priv_sc; + int do_start = 0; + + usb_proc_explore_lock(sc->sc_udev); + if (ch->operation != CHAN_OP_DRAIN) { + if (ch->cur_alt == ch->set_alt && + ch->operation == CHAN_OP_NONE) { + /* save doing the explore task */ + do_start = 1; + } else { + ch->operation = CHAN_OP_START; + (void)usb_proc_explore_msignal(sc->sc_udev, + &sc->sc_config_msg[0], &sc->sc_config_msg[1]); + } + } + usb_proc_explore_unlock(sc->sc_udev); -#if (UAUDIO_NCHANBUFS != 2) -#error "please update code" -#endif - if (ch->xfer[0]) { + if (do_start) { usbd_transfer_start(ch->xfer[0]); - } - if (ch->xfer[1]) { usbd_transfer_start(ch->xfer[1]); } return (0); @@ -1633,11 +2458,27 @@ uaudio_chan_start(struct uaudio_chan *ch) int uaudio_chan_stop(struct uaudio_chan *ch) { -#if (UAUDIO_NCHANBUFS != 2) -#error "please update code" -#endif - usbd_transfer_stop(ch->xfer[0]); - usbd_transfer_stop(ch->xfer[1]); + struct uaudio_softc *sc = ch->priv_sc; + int do_stop = 0; + + usb_proc_explore_lock(sc->sc_udev); + if (ch->operation != CHAN_OP_DRAIN) { + if (ch->cur_alt == ch->set_alt && + ch->operation == CHAN_OP_NONE) { + /* save doing the explore task */ + do_stop = 1; + } else { + ch->operation = CHAN_OP_STOP; + (void)usb_proc_explore_msignal(sc->sc_udev, + &sc->sc_config_msg[0], &sc->sc_config_msg[1]); + } + } + usb_proc_explore_unlock(sc->sc_udev); + + if (do_stop) { + usbd_transfer_stop(ch->xfer[0]); + usbd_transfer_stop(ch->xfer[1]); + } return (0); } @@ -1645,19 +2486,333 @@ uaudio_chan_stop(struct uaudio_chan *ch) * AC - Audio Controller - routines *========================================================================*/ +static int +uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS) +{ + struct uaudio_softc *sc; + struct uaudio_mixer_node *pmc; + int hint; + int error; + int temp = 0; + int chan = 0; + + sc = (struct uaudio_softc *)oidp->oid_arg1; + hint = oidp->oid_arg2; + + if (sc->sc_mixer_lock == NULL) + return (ENXIO); + + /* lookup mixer node */ + + lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE); + for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) { + for (chan = 0; chan != (int)pmc->nchan; chan++) { + if (pmc->wValue[chan] != -1 && + pmc->wValue[chan] == hint) { + temp = pmc->wData[chan]; + goto found; + } + } + } +found: + lockmgr(sc->sc_mixer_lock, LK_RELEASE); + + error = sysctl_handle_int(oidp, &temp, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + + /* update mixer value */ + + lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE); + if (pmc != NULL && + temp >= pmc->minval && + temp <= pmc->maxval) { + + pmc->wData[chan] = temp; + pmc->update[(chan / 8)] |= (1 << (chan % 8)); + + /* start the transfer, if not already started */ + usbd_transfer_start(sc->sc_mixer_xfer[0]); + } + lockmgr(sc->sc_mixer_lock, LK_RELEASE); + + return (0); +} + +static void +uaudio_mixer_ctl_free(struct uaudio_softc *sc) +{ + struct uaudio_mixer_node *p_mc; + + while ((p_mc = sc->sc_mixer_root) != NULL) { + sc->sc_mixer_root = p_mc->next; + kfree(p_mc, M_USBDEV); + } +} + +static void +uaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev) +{ + struct uaudio_mixer_node *pmc; + struct sysctl_oid *mixer_tree; + struct sysctl_oid *control_tree; + char buf[32]; + int chan; + int n; + + mixer_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "mixer", + CTLFLAG_RD, NULL, ""); + + if (mixer_tree == NULL) + return; + + for (n = 0, pmc = sc->sc_mixer_root; pmc != NULL; + pmc = pmc->next, n++) { + + for (chan = 0; chan < pmc->nchan; chan++) { + + if (pmc->nchan > 1) { + ksnprintf(buf, sizeof(buf), "%s_%d_%d", + pmc->name, n, chan); + } else { + ksnprintf(buf, sizeof(buf), "%s_%d", + pmc->name, n); + } + + control_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(mixer_tree), OID_AUTO, buf, + CTLFLAG_RD, NULL, "Mixer control nodes"); + + if (control_tree == NULL) + continue; + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(control_tree), + OID_AUTO, "val", CTLTYPE_INT | CTLFLAG_RW, sc, + pmc->wValue[chan], + uaudio_mixer_sysctl_handler, "I", "Current value"); + + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(control_tree), + OID_AUTO, "min", CTLFLAG_RD, 0, pmc->minval, + "Minimum value"); + + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(control_tree), + OID_AUTO, "max", CTLFLAG_RD, 0, pmc->maxval, + "Maximum value"); + + SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(control_tree), + OID_AUTO, "desc", CTLFLAG_RD, pmc->desc, 0, + "Description"); + } + } +} + +/* M-Audio FastTrack Ultra Mixer Description */ +/* Origin: Linux USB Audio driver */ +static void +uaudio_mixer_controls_create_ftu(struct uaudio_softc *sc) +{ + int chx; + int chy; + + memset(&MIX(sc), 0, sizeof(MIX(sc))); + MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no); + MIX(sc).wValue[0] = MAKE_WORD(8, 0); + MIX(sc).class = UAC_OUTPUT; + MIX(sc).type = MIX_UNSIGNED_16; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "effect"; + MIX(sc).minval = 0; + MIX(sc).maxval = 7; + MIX(sc).mul = 7; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + strlcpy(MIX(sc).desc, "Room1,2,3,Hall1,2,Plate,Delay,Echo", sizeof(MIX(sc).desc)); + uaudio_mixer_add_ctl_sub(sc, &MIX(sc)); + + memset(&MIX(sc), 0, sizeof(MIX(sc))); + MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no); + + for (chx = 0; chx != 8; chx++) { + for (chy = 0; chy != 8; chy++) { + + MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1); + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "mix_rec"; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + MIX(sc).val_default = 0; + ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc), + "AIn%d - Out%d Record Volume", chy + 1, chx + 1); + + uaudio_mixer_add_ctl(sc, &MIX(sc)); + + MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1 + 8); + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "mix_play"; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + MIX(sc).val_default = (chx == chy) ? 2 : 0; + ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc), + "DIn%d - Out%d Playback Volume", chy + 1, chx + 1); + + uaudio_mixer_add_ctl(sc, &MIX(sc)); + } + } + + memset(&MIX(sc), 0, sizeof(MIX(sc))); + MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no); + MIX(sc).wValue[0] = MAKE_WORD(2, 0); + MIX(sc).class = UAC_OUTPUT; + MIX(sc).type = MIX_SIGNED_8; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "effect_vol"; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + MIX(sc).minval = 0; + MIX(sc).maxval = 0x7f; + MIX(sc).mul = 0x7f; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + strlcpy(MIX(sc).desc, "Effect Volume", sizeof(MIX(sc).desc)); + uaudio_mixer_add_ctl_sub(sc, &MIX(sc)); + + memset(&MIX(sc), 0, sizeof(MIX(sc))); + MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no); + MIX(sc).wValue[0] = MAKE_WORD(3, 0); + MIX(sc).class = UAC_OUTPUT; + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "effect_dur"; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + MIX(sc).minval = 0; + MIX(sc).maxval = 0x7f00; + MIX(sc).mul = 0x7f00; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + strlcpy(MIX(sc).desc, "Effect Duration", sizeof(MIX(sc).desc)); + uaudio_mixer_add_ctl_sub(sc, &MIX(sc)); + + memset(&MIX(sc), 0, sizeof(MIX(sc))); + MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no); + MIX(sc).wValue[0] = MAKE_WORD(4, 0); + MIX(sc).class = UAC_OUTPUT; + MIX(sc).type = MIX_SIGNED_8; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "effect_fb"; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + MIX(sc).minval = 0; + MIX(sc).maxval = 0x7f; + MIX(sc).mul = 0x7f; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + strlcpy(MIX(sc).desc, "Effect Feedback Volume", sizeof(MIX(sc).desc)); + uaudio_mixer_add_ctl_sub(sc, &MIX(sc)); + + memset(&MIX(sc), 0, sizeof(MIX(sc))); + MIX(sc).wIndex = MAKE_WORD(7, sc->sc_mixer_iface_no); + for (chy = 0; chy != 4; chy++) { + + MIX(sc).wValue[0] = MAKE_WORD(7, chy + 1); + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "effect_ret"; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc), + "Effect Return %d Volume", chy + 1); + + uaudio_mixer_add_ctl(sc, &MIX(sc)); + } + + memset(&MIX(sc), 0, sizeof(MIX(sc))); + MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no); + + for (chy = 0; chy != 8; chy++) { + MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1); + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "effect_send"; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc), + "Effect Send AIn%d Volume", chy + 1); + + uaudio_mixer_add_ctl(sc, &MIX(sc)); + + MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1 + 8); + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "effect_send"; + MIX(sc).nchan = 1; + MIX(sc).update[0] = 1; + ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc), + "Effect Send DIn%d Volume", chy + 1); + + uaudio_mixer_add_ctl(sc, &MIX(sc)); + } +} + +static void +uaudio_mixer_reload_all(struct uaudio_softc *sc) +{ + struct uaudio_mixer_node *pmc; + int chan; + + if (sc->sc_mixer_lock == NULL) + return; + + lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE); + for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) { + /* use reset defaults for non-oss controlled settings */ + if (pmc->ctl == SOUND_MIXER_NRDEVICES) + continue; + for (chan = 0; chan < pmc->nchan; chan++) + pmc->update[chan / 8] |= (1 << (chan % 8)); + } + usbd_transfer_start(sc->sc_mixer_xfer[0]); + + /* start HID volume keys, if any */ + usbd_transfer_start(sc->sc_hid.xfer[0]); + lockmgr(sc->sc_mixer_lock, LK_RELEASE); +} + static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) { struct uaudio_mixer_node *p_mc_new = kmalloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK); - - if (p_mc_new != NULL) { - memcpy(p_mc_new, mc, sizeof(*p_mc_new)); - p_mc_new->next = sc->sc_mixer_root; - sc->sc_mixer_root = p_mc_new; - sc->sc_mixer_count++; - } else { - DPRINTF("out of memory\n"); + int ch; + + memcpy(p_mc_new, mc, sizeof(*p_mc_new)); + p_mc_new->next = sc->sc_mixer_root; + sc->sc_mixer_root = p_mc_new; + sc->sc_mixer_count++; + + /* set default value for all channels */ + for (ch = 0; ch < p_mc_new->nchan; ch++) { + switch (p_mc_new->val_default) { + case 1: + /* 50% */ + p_mc_new->wData[ch] = (p_mc_new->maxval + p_mc_new->minval) / 2; + break; + case 2: + /* 100% */ + p_mc_new->wData[ch] = p_mc_new->maxval; + break; + default: + /* 0% */ + p_mc_new->wData[ch] = p_mc_new->minval; + break; + } } } @@ -1681,13 +2836,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 */ @@ -1697,72 +2849,120 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) mc->minval = res; } - /* compute value range */ - mc->mul = mc->maxval - mc->minval; - if (mc->mul == 0) - mc->mul = 1; + /* compute value range */ + mc->mul = mc->maxval - mc->minval; + if (mc->mul == 0) + mc->mul = 1; + + /* compute value alignment */ + res = uaudio_mixer_get(sc->sc_udev, + sc->sc_audio_rev, GET_RES, mc); + + DPRINTF("Resolution = %d\n", (int)res); + } + + uaudio_mixer_add_ctl_sub(sc, mc); + +#ifdef USB_DEBUG + if (uaudio_debug > 2) { + uint8_t i; + + for (i = 0; i < mc->nchan; i++) { + DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]); + } + DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' " + "min=%d max=%d\n", + mc->wIndex, mc->type, mc->ctl, + mc->minval, mc->maxval); + } +#endif +} + +static void +uaudio_mixer_add_mixer(struct uaudio_softc *sc, + const struct uaudio_terminal_node *iot, int id) +{ + 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 */ + 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; + + DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", + d0->bUnitId, d0->bNrInPins); + + /* compute the number of input channels */ + + ichs = 0; + for (i = 0; i < d0->bNrInPins; i++) { + ichs += uaudio_mixer_get_cluster( + d0->baSourceId[i], iot).bNrChannels; + } + + d1 = (const void *)(d0->baSourceId + d0->bNrInPins); + + /* and the number of output channels */ + + ochs = d1->bNrChannels; - /* compute value alignment */ - res = uaudio_mixer_get(sc->sc_udev, GET_RES, mc); + DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs); - DPRINTF("Resolution = %d\n", (int)res); - } + memset(&MIX(sc), 0, sizeof(MIX(sc))); - uaudio_mixer_add_ctl_sub(sc, mc); + MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + uaudio_mixer_determine_class(&iot[id], &MIX(sc)); + MIX(sc).type = MIX_SIGNED_16; -#ifdef USB_DEBUG - if (uaudio_debug > 2) { - uint8_t i; + if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) + return; - for (i = 0; i < mc->nchan; i++) { - DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]); + for (p = i = 0; i < d0->bNrInPins; i++) { + 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)) + mo++; + } + if (mo == 1) + mc++; } - DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' " - "min=%d max=%d\n", - mc->wIndex, mc->type, mc->ctl, - mc->minval, mc->maxval); - } -#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 -} + if ((mc == chs) && (chs <= MIX_MAX_CHAN)) { -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; + /* repeat bit-scan */ - 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 + 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(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); + } + } + MIX(sc).nchan = chs; + uaudio_mixer_add_ctl(sc, &MIX(sc)); + } + p += chs; + } } static void -uaudio_mixer_add_mixer(struct uaudio_softc *sc, +uaudio20_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_1 *d1; + const struct usb_audio20_mixer_unit_0 *d0 = iot[id].u.mu_v2; + const struct usb_audio20_mixer_unit_1 *d1; uint32_t bno; /* bit number */ uint32_t p; /* bit number accumulator */ @@ -1782,8 +2982,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 += uaudio20_mixer_get_cluster( + d0->baSourceId[i], iot).bNrChannels; } d1 = (const void *)(d0->baSourceId + d0->bNrInPins); @@ -1794,29 +2994,28 @@ uaudio_mixer_add_mixer(struct uaudio_softc *sc, DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs); - memset(&mix, 0, sizeof(mix)); + memset(&MIX(sc), 0, sizeof(MIX(sc))); - mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); - uaudio_mixer_determine_class(&iot[id], &mix); - mix.type = MIX_SIGNED_16; + MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + uaudio20_mixer_determine_class(&iot[id], &MIX(sc)); + MIX(sc).type = MIX_SIGNED_16; - if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) { + if (uaudio20_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 = 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)) { + if (BIT_TEST(d1->bmControls, bno)) mo++; - } } - if (mo == 1) { + if (mo == 1) mc++; - } } if ((mc == chs) && (chs <= MIX_MAX_CHAN)) { @@ -1826,15 +3025,12 @@ 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)) { - mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); - } + if (BIT_TEST(d1->bmControls, bno)) + MIX(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); } } - mix.nchan = chs; - uaudio_mixer_add_ctl(sc, &mix); - } else { - /* XXX */ + MIX(sc).nchan = chs; + uaudio_mixer_add_ctl(sc, &MIX(sc)); } p += chs; } @@ -1844,52 +3040,107 @@ 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; - struct uaudio_mixer_node mix; + const struct usb_audio_selector_unit *d = iot[id].u.su_v1; uint16_t i; DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins); - if (d->bNrInPins == 0) { + 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; + memset(&MIX(sc), 0, sizeof(MIX(sc))); + + MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + MIX(sc).wValue[0] = MAKE_WORD(0, 0); + uaudio_mixer_determine_class(&iot[id], &MIX(sc)); + MIX(sc).nchan = 1; + MIX(sc).type = MIX_SELECTOR; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).minval = 1; + MIX(sc).maxval = d->bNrInPins; + MIX(sc).name = "selector"; + + i = d->baSourceId[d->bNrInPins]; + if (i == 0 || + usbd_req_get_string_any(sc->sc_udev, NULL, + MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) { + MIX(sc).desc[0] = 0; + } - if (mix.maxval > MAX_SELECTOR_INPUT_PIN) { - mix.maxval = MAX_SELECTOR_INPUT_PIN; + if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN) { + MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN; } - mix.mul = (mix.maxval - mix.minval); + MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval); for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) { - mix.slctrtype[i] = SOUND_MIXER_NRDEVICES; + MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES; + } + + for (i = 0; i < MIX(sc).maxval; i++) { + MIX(sc).slctrtype[i] = uaudio_mixer_feature_name( + &iot[d->baSourceId[i]], &MIX(sc)); + } + + MIX(sc).class = 0; /* not used */ + + uaudio_mixer_add_ctl(sc, &MIX(sc)); +} + +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; + uint16_t i; + + DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", + d->bUnitId, d->bNrInPins); + + if (d->bNrInPins == 0) + return; + + memset(&MIX(sc), 0, sizeof(MIX(sc))); + + MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + MIX(sc).wValue[0] = MAKE_WORD(0, 0); + uaudio20_mixer_determine_class(&iot[id], &MIX(sc)); + MIX(sc).nchan = 1; + MIX(sc).type = MIX_SELECTOR; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).minval = 1; + MIX(sc).maxval = d->bNrInPins; + MIX(sc).name = "selector"; + + i = d->baSourceId[d->bNrInPins]; + if (i == 0 || + usbd_req_get_string_any(sc->sc_udev, NULL, + MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) { + MIX(sc).desc[0] = 0; } - for (i = 0; i < mix.maxval; i++) { - mix.slctrtype[i] = uaudio_mixer_feature_name - (&iot[d->baSourceId[i]], &mix); + if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN) + MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN; + + MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval); + for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) + MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES; + + for (i = 0; i < MIX(sc).maxval; i++) { + MIX(sc).slctrtype[i] = uaudio20_mixer_feature_name( + &iot[d->baSourceId[i]], &MIX(sc)); } - mix.class = 0; /* not used */ + MIX(sc).class = 0; /* not used */ - uaudio_mixer_add_ctl(sc, &mix); + uaudio_mixer_add_ctl(sc, &MIX(sc)); } static uint32_t uaudio_mixer_feature_get_bmaControls(const struct usb_audio_feature_unit *d, - uint8_t index) + uint8_t i) { uint32_t temp = 0; - uint32_t offset = (index * d->bControlSize); + uint32_t offset = (i * d->bControlSize); if (d->bControlSize > 0) { temp |= d->bmaControls[offset]; @@ -1910,8 +3161,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; - struct uaudio_mixer_node mix; + const struct usb_audio_feature_unit *d = iot[id].u.fu_v1; uint32_t fumask; uint32_t mmask; uint32_t cmask; @@ -1921,18 +3171,18 @@ uaudio_mixer_add_feature(struct uaudio_softc *sc, uint8_t ctl; uint8_t i; - if (d->bControlSize == 0) { + if (d->bControlSize == 0) return; - } - memset(&mix, 0, sizeof(mix)); + + memset(&MIX(sc), 0, sizeof(MIX(sc))); nchan = (d->bLength - 7) / d->bControlSize; mmask = uaudio_mixer_feature_get_bmaControls(d, 0); cmask = 0; - if (nchan == 0) { + if (nchan == 0) return; - } + /* figure out what we can control */ for (chan = 1; chan < nchan; chan++) { @@ -1945,7 +3195,14 @@ uaudio_mixer_add_feature(struct uaudio_softc *sc, if (nchan > MIX_MAX_CHAN) { nchan = MIX_MAX_CHAN; } - mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + + i = d->bmaControls[d->bControlSize]; + if (i == 0 || + usbd_req_get_string_any(sc->sc_udev, NULL, + MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) { + MIX(sc).desc[0] = 0; + } for (ctl = 1; ctl <= LOUDNESS_CONTROL; ctl++) { @@ -1955,80 +3212,225 @@ uaudio_mixer_add_feature(struct uaudio_softc *sc, ctl, fumask); if (mmask & fumask) { - mix.nchan = 1; - mix.wValue[0] = MAKE_WORD(ctl, 0); + MIX(sc).nchan = 1; + MIX(sc).wValue[0] = MAKE_WORD(ctl, 0); } else if (cmask & fumask) { - mix.nchan = nchan - 1; + MIX(sc).nchan = nchan - 1; for (i = 1; i < nchan; i++) { if (uaudio_mixer_feature_get_bmaControls(d, i) & fumask) - mix.wValue[i - 1] = MAKE_WORD(ctl, i); + MIX(sc).wValue[i - 1] = MAKE_WORD(ctl, i); else - mix.wValue[i - 1] = -1; + MIX(sc).wValue[i - 1] = -1; } } else { continue; } - mixernumber = uaudio_mixer_feature_name(&iot[id], &mix); + mixernumber = uaudio_mixer_feature_name(&iot[id], &MIX(sc)); switch (ctl) { case MUTE_CONTROL: - mix.type = MIX_ON_OFF; - mix.ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).type = MIX_ON_OFF; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "mute"; break; case VOLUME_CONTROL: - mix.type = MIX_SIGNED_16; - mix.ctl = mixernumber; + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = mixernumber; + MIX(sc).name = "vol"; break; case BASS_CONTROL: - mix.type = MIX_SIGNED_8; - mix.ctl = SOUND_MIXER_BASS; + MIX(sc).type = MIX_SIGNED_8; + MIX(sc).ctl = SOUND_MIXER_BASS; + MIX(sc).name = "bass"; break; case MID_CONTROL: - mix.type = MIX_SIGNED_8; - mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).type = MIX_SIGNED_8; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).name = "mid"; break; case TREBLE_CONTROL: - mix.type = MIX_SIGNED_8; - mix.ctl = SOUND_MIXER_TREBLE; + MIX(sc).type = MIX_SIGNED_8; + MIX(sc).ctl = SOUND_MIXER_TREBLE; + MIX(sc).name = "treble"; break; case GRAPHIC_EQUALIZER_CONTROL: continue; /* XXX don't add anything */ - break; case AGC_CONTROL: - mix.type = MIX_ON_OFF; - mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).type = MIX_ON_OFF; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).name = "agc"; break; case DELAY_CONTROL: - mix.type = MIX_UNSIGNED_16; - mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).type = MIX_UNSIGNED_16; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).name = "delay"; break; case BASS_BOOST_CONTROL: - mix.type = MIX_ON_OFF; - mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).type = MIX_ON_OFF; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).name = "boost"; break; case LOUDNESS_CONTROL: - mix.type = MIX_ON_OFF; - mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ + MIX(sc).type = MIX_ON_OFF; + MIX(sc).ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ + MIX(sc).name = "loudness"; break; default: - mix.type = MIX_UNKNOWN; + MIX(sc).type = MIX_UNKNOWN; + break; + } + + if (MIX(sc).type != MIX_UNKNOWN) + uaudio_mixer_add_ctl(sc, &MIX(sc)); + } +} + +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; + 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(sc), 0, sizeof(MIX(sc))); + + 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(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); + + i = d->bmaControls[nchan][0]; + if (i == 0 || + usbd_req_get_string_any(sc->sc_udev, NULL, + MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) { + MIX(sc).desc[0] = 0; + } + + for (ctl = 3; ctl != 0; ctl <<= 2) { + + mixernumber = uaudio20_mixer_feature_name(&iot[id], &MIX(sc)); + + switch (ctl) { + case (3 << 0): + MIX(sc).type = MIX_ON_OFF; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; + MIX(sc).name = "mute"; + what = MUTE_CONTROL; break; + case (3 << 2): + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = mixernumber; + MIX(sc).name = "vol"; + what = VOLUME_CONTROL; + break; + case (3 << 4): + MIX(sc).type = MIX_SIGNED_8; + MIX(sc).ctl = SOUND_MIXER_BASS; + MIX(sc).name = "bass"; + what = BASS_CONTROL; + break; + case (3 << 6): + MIX(sc).type = MIX_SIGNED_8; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).name = "mid"; + what = MID_CONTROL; + break; + case (3 << 8): + MIX(sc).type = MIX_SIGNED_8; + MIX(sc).ctl = SOUND_MIXER_TREBLE; + MIX(sc).name = "treble"; + what = TREBLE_CONTROL; + break; + case (3 << 12): + MIX(sc).type = MIX_ON_OFF; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).name = "agc"; + what = AGC_CONTROL; + break; + case (3 << 14): + MIX(sc).type = MIX_UNSIGNED_16; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).name = "delay"; + what = DELAY_CONTROL; + break; + case (3 << 16): + MIX(sc).type = MIX_ON_OFF; + MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + MIX(sc).name = "boost"; + what = BASS_BOOST_CONTROL; + break; + case (3 << 18): + MIX(sc).type = MIX_ON_OFF; + MIX(sc).ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ + MIX(sc).name = "loudness"; + what = LOUDNESS_CONTROL; + break; + case (3 << 20): + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = mixernumber; + MIX(sc).name = "igain"; + what = INPUT_GAIN_CONTROL; + break; + case (3 << 22): + MIX(sc).type = MIX_SIGNED_16; + MIX(sc).ctl = mixernumber; + MIX(sc).name = "igainpad"; + what = INPUT_GAIN_PAD_CONTROL; + break; + default: + continue; } - if (mix.type != MIX_UNKNOWN) { - uaudio_mixer_add_ctl(sc, &mix); + if ((mmask & ctl) == ctl) { + MIX(sc).nchan = 1; + MIX(sc).wValue[0] = MAKE_WORD(what, 0); + } else if ((cmask & ctl) == ctl) { + MIX(sc).nchan = nchan - 1; + for (i = 1; i < nchan; i++) { + if ((UGETDW(d->bmaControls[i]) & ctl) == ctl) + MIX(sc).wValue[i - 1] = MAKE_WORD(what, i); + else + MIX(sc).wValue[i - 1] = -1; + } + } else { + continue; } + + if (MIX(sc).type != MIX_UNKNOWN) + uaudio_mixer_add_ctl(sc, &MIX(sc)); } } @@ -2036,12 +3438,11 @@ 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 void *)(d0->baSourceId + d0->bNrInPins); const struct usb_audio_processing_unit_updown *ud = - (const void *)(d1->bmControls + d1->bControlSize); - struct uaudio_mixer_node mix; + (const void *)(d1->bmControls + d1->bControlSize); uint8_t i; if (uaudio_mixer_verify_desc(d0, sizeof(*ud)) == NULL) { @@ -2058,33 +3459,32 @@ uaudio_mixer_add_processing_updown(struct uaudio_softc *sc, DPRINTF("no mode select\n"); return; } - memset(&mix, 0, sizeof(mix)); + memset(&MIX(sc), 0, sizeof(MIX(sc))); - mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); - mix.nchan = 1; - mix.wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0); - uaudio_mixer_determine_class(&iot[id], &mix); - mix.type = MIX_ON_OFF; /* XXX */ + MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + MIX(sc).nchan = 1; + MIX(sc).wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0); + uaudio_mixer_determine_class(&iot[id], &MIX(sc)); + MIX(sc).type = MIX_ON_OFF; /* XXX */ for (i = 0; i < ud->bNrModes; i++) { DPRINTFN(3, "i=%d bm=0x%x\n", i, UGETW(ud->waModes[i])); /* XXX */ } - uaudio_mixer_add_ctl(sc, &mix); + uaudio_mixer_add_ctl(sc, &MIX(sc)); } 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; + (const void *)(d0->baSourceId + d0->bNrInPins); uint16_t ptype; - memset(&mix, 0, sizeof(mix)); + memset(&MIX(sc), 0, sizeof(MIX(sc))); ptype = UGETW(d0->wProcessType); @@ -2095,12 +3495,12 @@ uaudio_mixer_add_processing(struct uaudio_softc *sc, return; } if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) { - mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); - mix.nchan = 1; - mix.wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0); - uaudio_mixer_determine_class(&iot[id], &mix); - mix.type = MIX_ON_OFF; - uaudio_mixer_add_ctl(sc, &mix); + MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + MIX(sc).nchan = 1; + MIX(sc).wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0); + uaudio_mixer_determine_class(&iot[id], &MIX(sc)); + MIX(sc).type = MIX_ON_OFF; + uaudio_mixer_add_ctl(sc, &MIX(sc)); } switch (ptype) { case UPDOWNMIX_PROCESS: @@ -2123,10 +3523,9 @@ 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; + (const void *)(d0->baSourceId + d0->bNrInPins); DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", d0->bUnitId, d0->bNrInPins); @@ -2139,15 +3538,15 @@ uaudio_mixer_add_extension(struct uaudio_softc *sc, } if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) { - memset(&mix, 0, sizeof(mix)); + memset(&MIX(sc), 0, sizeof(MIX(sc))); - mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); - mix.nchan = 1; - mix.wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0); - uaudio_mixer_determine_class(&iot[id], &mix); - mix.type = MIX_ON_OFF; + MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); + MIX(sc).nchan = 1; + MIX(sc).wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0); + uaudio_mixer_determine_class(&iot[id], &MIX(sc)); + MIX(sc).type = MIX_ON_OFF; - uaudio_mixer_add_ctl(sc, &mix); + uaudio_mixer_add_ctl(sc, &MIX(sc)); } } @@ -2208,11 +3607,16 @@ uaudio_mixer_verify_desc(const void *arg, uint32_t len) if (u.desc->bLength < len) { goto error; } - len += u.su->bNrInPins; + len += u.su->bNrInPins + 1; break; case UDESCSUB_AC_FEATURE: - len += (sizeof(*u.fu) + 1); + len += sizeof(*u.fu) + 1; + + if (u.desc->bLength < len) + goto error; + + len += u.fu->bControlSize; break; case UDESCSUB_AC_PROCESSING: @@ -2247,24 +3651,177 @@ uaudio_mixer_verify_desc(const void *arg, uint32_t len) if (u.desc->bLength < len) { goto error; - } - e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins); + } + e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins); + + len += sizeof(*e1); + + if (u.desc->bLength < len) { + goto error; + } + len += e1->bControlSize; + break; + + default: + goto error; + } + + 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 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; + + 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; + + u.desc = arg; + + 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 + 1; + break; + + case UDESCSUB_AC_FEATURE: + len += sizeof(*u.fu) + 1; + 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 += sizeof(*e1); + len += u.csel->bNrInPins; - if (u.desc->bLength < len) { + if (u.desc->bLength < len) goto error; - } - len += e1->bControlSize; + + 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; } - if (u.desc->bLength < len) { + if (u.desc->bLength < len) goto error; - } + return (u.desc); error: @@ -2278,35 +3835,6 @@ 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); - - cc = UGETW(cl.wChannelConfig); - - DPRINTF("cluster: bNrChannels=%u iChannelNames=%u wChannelConfig=" - "0x%04x:\n", cl.iChannelNames, cl.bNrChannels, cc); - - for (i = 0; cc; i++) { - if (cc & 1) { - DPRINTF(" - %s\n", channel_names[i]); - } - cc >>= 1; - } -} - -#endif - static struct usb_audio_cluster uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) { @@ -2321,43 +3849,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: @@ -2371,108 +3899,80 @@ 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; -static const struct uaudio_tt_to_string uaudio_tt_to_string[] = { + 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; - /* USB terminal types */ - {UAT_UNDEFINED, "UAT_UNDEFINED"}, - {UAT_STREAM, "UAT_STREAM"}, - {UAT_VENDOR, "UAT_VENDOR"}, + case UDESCSUB_AC_OUTPUT: + id = iot[id].u.ot_v2->bSourceId; + break; - /* 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_MIXER: + r = *(const struct usb_audio20_cluster *) + &iot[id].u.mu_v2->baSourceId[ + iot[id].u.mu_v2->bNrInPins]; + goto done; - /* 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_SELECTOR: + if (iot[id].u.su_v2->bNrInPins > 0) { + /* XXX This is not really right */ + id = iot[id].u.su_v2->baSourceId[0]; + } + 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_SAMPLE_RT: + id = iot[id].u.ru_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_EFFECT: + id = iot[id].u.ef_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_FEATURE: + id = iot[id].u.fu_v2->bSourceId; + break; - /* 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_PROCESSING_V2: + r = *((const struct usb_audio20_cluster *) + &iot[id].u.pu_v2->baSourceId[ + iot[id].u.pu_v2->bNrInPins]); + goto done; -static const char * -uaudio_mixer_get_terminal_name(uint16_t terminal_type) -{ - const struct uaudio_tt_to_string *uat = uaudio_tt_to_string; + 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; - while (uat->terminal_type) { - if (uat->terminal_type == terminal_type) { - break; + default: + goto error; } - uat++; } - if (uat->terminal_type == 0) { - DPRINTF("unknown terminal type (0x%04x)", terminal_type); - } - return (uat->desc); +error: + DPRINTF("Bad data!\n"); + memset(&r, 0, sizeof(r)); +done: + return (r); } -#endif - static uint16_t uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, struct uaudio_mixer_node *mix) @@ -2492,8 +3992,63 @@ uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, * one output terminal: */ if (output[0] && (!output[1])) { - terminal_type = UGETW(output[0]->u.ot->wTerminalType); + 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; + } + goto done; + } + /* + * 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; } + /* + * Otherwise, the class is UAC_OUTPUT. + */ + mix->class = UAC_OUTPUT; +done: + return (terminal_type); +} + +static uint16_t +uaudio20_mixer_determine_class(const struct uaudio_terminal_node *iot, + struct uaudio_mixer_node *mix) +{ + uint16_t terminal_type = 0x0000; + const struct uaudio_terminal_node *input[2]; + const struct uaudio_terminal_node *output[2]; + + 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_v2->wTerminalType); /* * If the only output terminal is USB, * the class is UAC_RECORD. @@ -2502,7 +4057,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; } @@ -2515,7 +4071,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; } /* @@ -2631,125 +4188,391 @@ 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); } static const struct uaudio_terminal_node * -uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t index) +uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t i) +{ + struct uaudio_terminal_node *root = iot->root; + uint8_t n; + + n = iot->usr.id_max; + do { + if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) { + if (!i--) + return (root + n); + } + } while (n--); + + return (NULL); +} + +static const struct uaudio_terminal_node * +uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t i) { struct uaudio_terminal_node *root = iot->root; uint8_t n; - n = iot->usr.id_max; - do { - if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) { - if (!index--) { - return (root + n); + n = iot->usr.id_max; + do { + if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) { + if (!i--) + return (root + n); + } + } while (n--); + + return (NULL); +} + +static void +uaudio_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_FEATURE: + 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: + 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: + 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: + 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: + 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: + 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; } - } - } while (n--); + uaudio20_mixer_find_inputs_sub( + root, &iot->u.ef_v2->bSourceId, + 1, info); + break; - return (NULL); -} + 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; -static const struct uaudio_terminal_node * -uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t index) -{ - struct uaudio_terminal_node *root = iot->root; - uint8_t n; + 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; - n = iot->usr.id_max; - do { - if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) { - if (!index--) { - return (root + n); + 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; } - } while (n--); - - return (NULL); + } } static void -uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root, +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; - 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); + 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: - uaudio_mixer_find_inputs_sub - (root, &iot->u.ot->bSourceId, 1, info); + 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_MIXER: - uaudio_mixer_find_inputs_sub - (root, iot->u.mu->baSourceId, - iot->u.mu->bNrInPins, info); + 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_SELECTOR: - uaudio_mixer_find_inputs_sub - (root, iot->u.su->baSourceId, - iot->u.su->bNrInPins, info); + 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_PROCESSING: - uaudio_mixer_find_inputs_sub - (root, iot->u.pu->baSourceId, - iot->u.pu->bNrInPins, info); - break; + case UDESCSUB_AC_CLOCK_SRC: - case UDESCSUB_AC_EXTENSION: - uaudio_mixer_find_inputs_sub - (root, iot->u.eu->baSourceId, - iot->u.eu->bNrInPins, info); + 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 @@ -2778,8 +4601,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); @@ -2813,15 +4636,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 = kmalloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP, M_WAITOK | M_ZERO); @@ -2835,13 +4649,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 +4671,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 +4694,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 +4706,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 +4766,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 +4798,76 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, } while (i--); done: - if (iot) { - kfree(iot, M_TEMP); - } + kfree(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 +4904,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 +4916,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 +4987,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) { @@ -3257,9 +5101,12 @@ uaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m) { DPRINTF("\n"); + sc->sc_mixer_lock = mixer_get_lock(m); + sc->sc_mixer_dev = m; + if (usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index, sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc, - &sc->sc_lock)) { + sc->sc_mixer_lock)) { DPRINTFN(0, "could not allocate USB " "transfer for audio mixer!\n"); return (ENOMEM); @@ -3280,6 +5127,8 @@ uaudio_mixer_uninit_sub(struct uaudio_softc *sc) usbd_transfer_unsetup(sc->sc_mixer_xfer, 1); + sc->sc_mixer_lock = NULL; + return (0); } @@ -3290,13 +5139,13 @@ uaudio_mixer_set(struct uaudio_softc *sc, unsigned type, struct uaudio_mixer_node *mc; int chan; - for (mc = sc->sc_mixer_root; mc; - mc = mc->next) { + for (mc = sc->sc_mixer_root; mc != NULL; mc = mc->next) { if (mc->ctl == type) { for (chan = 0; chan < mc->nchan; chan++) { uaudio_mixer_ctl_set(sc, mc, chan, - (int)((chan == 0 ? left : right) * 255) / 100); + (int)((chan == 0 ? left : right) * + 255) / 100); } } } @@ -3386,8 +5235,7 @@ umidi_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) */ sub = &chan->sub[cn]; - if ((cmd_len != 0) && - (cn < chan->max_cable) && + if ((cmd_len != 0) && (cn < chan->max_emb_jack) && (sub->read_open != 0)) { /* Send data to the application */ @@ -3623,7 +5471,7 @@ tr_setup: } chan->curr_cable++; - if (chan->curr_cable >= chan->max_cable) + if (chan->curr_cable >= chan->max_emb_jack) chan->curr_cable = 0; if (chan->curr_cable == start_cable) { @@ -3660,7 +5508,7 @@ umidi_sub_by_fifo(struct usb_fifo *fifo) struct umidi_sub_chan *sub; uint32_t n; - for (n = 0; n < UMIDI_CABLES_MAX; n++) { + for (n = 0; n < UMIDI_EMB_JACK_MAX; n++) { sub = &chan->sub[n]; if ((sub->fifo.fp[USB_FIFO_RX] == fifo) || (sub->fifo.fp[USB_FIFO_TX] == fifo)) { @@ -3746,7 +5594,6 @@ umidi_open(struct usb_fifo *fifo, int fflags) } /* clear stall first */ lockmgr(&chan->lock, LK_EXCLUSIVE); - usbd_xfer_set_stall(chan->xfer[UMIDI_TX_TRANSFER]); chan->write_open_refcount++; sub->write_open = 1; @@ -3825,12 +5672,31 @@ umidi_probe(device_t dev) DPRINTF("error=%s\n", usbd_errstr(error)); goto detach; } - if ((chan->max_cable > UMIDI_CABLES_MAX) || - (chan->max_cable == 0)) { - chan->max_cable = UMIDI_CABLES_MAX; + + /* + * Some USB MIDI device makers couldn't resist using + * wMaxPacketSize = 4 for RX and TX BULK endpoints, although + * that size is an unsupported value for FULL speed BULK + * endpoints. The same applies to some HIGH speed MIDI devices + * which are using a wMaxPacketSize different from 512 bytes. + * + * Refer to section 5.8.3 in USB 2.0 PDF: Cite: "All Host + * Controllers are required to have support for 8-, 16-, 32-, + * and 64-byte maximum packet sizes for full-speed bulk + * endpoints and 512 bytes for high-speed bulk endpoints." + */ + if (usbd_xfer_maxp_was_clamped(chan->xfer[UMIDI_TX_TRANSFER])) + chan->single_command = 1; + + if (chan->single_command != 0) + device_printf(dev, "Single command MIDI quirk enabled\n"); + + if ((chan->max_emb_jack == 0) || + (chan->max_emb_jack > UMIDI_EMB_JACK_MAX)) { + chan->max_emb_jack = UMIDI_EMB_JACK_MAX; } - for (n = 0; n < chan->max_cable; n++) { + for (n = 0; n < chan->max_emb_jack; n++) { sub = &chan->sub[n]; @@ -3845,9 +5711,6 @@ umidi_probe(device_t dev) lockmgr(&chan->lock, LK_EXCLUSIVE); - /* clear stall first */ - usbd_xfer_set_stall(chan->xfer[UMIDI_RX_TRANSFER]); - /* * NOTE: At least one device will not work properly unless the * BULK IN pipe is open all the time. This might have to do @@ -3871,9 +5734,8 @@ umidi_detach(device_t dev) struct umidi_chan *chan = &sc->sc_midi_chan; uint32_t n; - for (n = 0; n < UMIDI_CABLES_MAX; n++) { + for (n = 0; n < UMIDI_EMB_JACK_MAX; n++) usb_fifo_detach(&chan->sub[n].fifo); - } lockmgr(&chan->lock, LK_EXCLUSIVE); @@ -3888,7 +5750,171 @@ umidi_detach(device_t dev) return (0); } -DRIVER_MODULE(uaudio, uhub, uaudio_driver, uaudio_devclass, NULL, NULL); +static void +uaudio_hid_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct uaudio_softc *sc = usbd_xfer_softc(xfer); + const uint8_t *buffer = usbd_xfer_get_frame_buffer(xfer, 0); + struct snd_mixer *m; + uint8_t id; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTF("actlen=%d\n", actlen); + + if (actlen != 0 && + (sc->sc_hid.flags & UAUDIO_HID_HAS_ID)) { + id = *buffer; + buffer++; + actlen--; + } else { + id = 0; + } + + m = sc->sc_mixer_dev; + + if ((sc->sc_hid.flags & UAUDIO_HID_HAS_MUTE) && + (sc->sc_hid.mute_id == id) && + hid_get_data(buffer, actlen, + &sc->sc_hid.mute_loc)) { + + DPRINTF("Mute toggle\n"); + + mixer_hwvol_mute_locked(m); + } + + if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_UP) && + (sc->sc_hid.volume_up_id == id) && + hid_get_data(buffer, actlen, + &sc->sc_hid.volume_up_loc)) { + + DPRINTF("Volume Up\n"); + + mixer_hwvol_step_locked(m, 1, 1); + } + + if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_DOWN) && + (sc->sc_hid.volume_down_id == id) && + hid_get_data(buffer, actlen, + &sc->sc_hid.volume_down_loc)) { + + DPRINTF("Volume Down\n"); + + mixer_hwvol_step_locked(m, -1, -1); + } + + case USB_ST_SETUP: +tr_setup: + /* check if we can put more data into the FIFO */ + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + break; + + default: /* Error */ + + DPRINTF("error=%s\n", usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static int +uaudio_hid_probe(struct uaudio_softc *sc, + struct usb_attach_arg *uaa) +{ + void *d_ptr; + uint32_t flags; + uint16_t d_len; + uint8_t id; + int error; + + if (!(sc->sc_hid.flags & UAUDIO_HID_VALID)) + return (-1); + + if (sc->sc_mixer_lock == NULL) + return (-1); + + /* Get HID descriptor */ + error = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr, + &d_len, M_TEMP, sc->sc_hid.iface_index); + + if (error) { + DPRINTF("error reading report description\n"); + return (-1); + } + + /* check if there is an ID byte */ + hid_report_size(d_ptr, d_len, hid_input, &id); + + if (id != 0) + sc->sc_hid.flags |= UAUDIO_HID_HAS_ID; + + if (hid_locate(d_ptr, d_len, + HID_USAGE2(HUP_CONSUMER, 0xE9 /* Volume Increment */), + hid_input, 0, &sc->sc_hid.volume_up_loc, &flags, + &sc->sc_hid.volume_up_id)) { + if (flags & HIO_VARIABLE) + sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_UP; + DPRINTFN(1, "Found Volume Up key\n"); + } + + if (hid_locate(d_ptr, d_len, + HID_USAGE2(HUP_CONSUMER, 0xEA /* Volume Decrement */), + hid_input, 0, &sc->sc_hid.volume_down_loc, &flags, + &sc->sc_hid.volume_down_id)) { + if (flags & HIO_VARIABLE) + sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_DOWN; + DPRINTFN(1, "Found Volume Down key\n"); + } + + if (hid_locate(d_ptr, d_len, + HID_USAGE2(HUP_CONSUMER, 0xE2 /* Mute */), + hid_input, 0, &sc->sc_hid.mute_loc, &flags, + &sc->sc_hid.mute_id)) { + if (flags & HIO_VARIABLE) + sc->sc_hid.flags |= UAUDIO_HID_HAS_MUTE; + DPRINTFN(1, "Found Mute key\n"); + } + + kfree(d_ptr, M_TEMP); + + if (!(sc->sc_hid.flags & (UAUDIO_HID_HAS_VOLUME_UP | + UAUDIO_HID_HAS_VOLUME_DOWN | + UAUDIO_HID_HAS_MUTE))) { + DPRINTFN(1, "Did not find any volume related keys\n"); + return (-1); + } + + /* prevent the uhid driver from attaching */ + usbd_set_parent_iface(uaa->device, sc->sc_hid.iface_index, + sc->sc_mixer_iface_index); + + /* allocate USB transfers */ + error = usbd_transfer_setup(uaa->device, &sc->sc_hid.iface_index, + sc->sc_hid.xfer, uaudio_hid_config, UAUDIO_HID_N_TRANSFER, + sc, sc->sc_mixer_lock); + if (error) { + DPRINTF("error=%s\n", usbd_errstr(error)); + return (-1); + } + return (0); +} + +static void +uaudio_hid_detach(struct uaudio_softc *sc) +{ + usbd_transfer_unsetup(sc->sc_hid.xfer, UAUDIO_HID_N_TRANSFER); +} + +DRIVER_MODULE_ORDERED(uaudio, uhub, uaudio_driver, uaudio_devclass, NULL, 0, SI_ORDER_ANY); MODULE_DEPEND(uaudio, usb, 1, 1, 1); MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(uaudio, 1); diff --git a/sys/bus/u4b/audio/uaudio.h b/sys/bus/u4b/audio/uaudio.h index 10c10de4ce..44479df4d0 100644 --- a/sys/bus/u4b/audio/uaudio.h +++ b/sys/bus/u4b/audio/uaudio.h @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: head/sys/dev/sound/usb/uaudio.h 228484 2011-12-14 00:48:20Z hselasky $ */ /*- * Copyright (c) 2000-2002 Hiroyuki Aizu diff --git a/sys/bus/u4b/audio/uaudio_pcm.c b/sys/bus/u4b/audio/uaudio_pcm.c index b0372409e1..f632404dd8 100644 --- a/sys/bus/u4b/audio/uaudio_pcm.c +++ b/sys/bus/u4b/audio/uaudio_pcm.c @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: head/sys/dev/sound/usb/uaudio_pcm.c 246128 2013-01-30 18:01:20Z sbz $ */ /*- * Copyright (c) 2000-2002 Hiroyuki Aizu @@ -27,9 +27,7 @@ */ -#ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_snd.h" -#endif #include #include @@ -72,13 +70,11 @@ ua_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) return (uaudio_chan_set_param_blocksize(data, blocksize)); } -#if 0 static int ua_chan_setfragments(kobj_t obj, void *data, uint32_t blocksize, uint32_t blockcount) { return (uaudio_chan_set_param_fragments(data, blocksize, blockcount)); } -#endif static int ua_chan_trigger(kobj_t obj, void *data, int go) @@ -105,13 +101,11 @@ ua_chan_getcaps(kobj_t obj, void *data) return (uaudio_chan_getcaps(data)); } -#if 0 static struct pcmchan_matrix * ua_chan_getmatrix(kobj_t obj, void *data, uint32_t format) { return (uaudio_chan_getmatrix(data, format)); } -#endif static kobj_method_t ua_chan_methods[] = { KOBJMETHOD(channel_init, ua_chan_init), @@ -119,15 +113,11 @@ static kobj_method_t ua_chan_methods[] = { KOBJMETHOD(channel_setformat, ua_chan_setformat), KOBJMETHOD(channel_setspeed, ua_chan_setspeed), KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize), -#if 0 KOBJMETHOD(channel_setfragments, ua_chan_setfragments), -#endif KOBJMETHOD(channel_trigger, ua_chan_trigger), KOBJMETHOD(channel_getptr, ua_chan_getptr), KOBJMETHOD(channel_getcaps, ua_chan_getcaps), -#if 0 KOBJMETHOD(channel_getmatrix, ua_chan_getmatrix), -#endif KOBJMETHOD_END }; @@ -146,7 +136,7 @@ ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right) struct lock *lock = mixer_get_lock(m); uint8_t do_unlock; - if (lockstatus(lock, curthread)) { + if (lockowned(lock)) { do_unlock = 0; } else { do_unlock = 1; @@ -166,7 +156,7 @@ ua_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) int retval; uint8_t do_unlock; - if (lockstatus(lock, curthread)) { + if (lockowned(lock)) { do_unlock = 0; } else { do_unlock = 1; @@ -244,7 +234,7 @@ static driver_t ua_pcm_driver = { PCM_SOFTC_SIZE, }; -DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, NULL, NULL); +DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, 0, 0); MODULE_DEPEND(ua_pcm, uaudio, 1, 1, 1); MODULE_DEPEND(ua_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(ua_pcm, 1); diff --git a/sys/bus/u4b/audio/uaudioreg.h b/sys/bus/u4b/audio/uaudioreg.h index eb2a9a4bbb..362604ce92 100644 --- a/sys/bus/u4b/audio/uaudioreg.h +++ b/sys/bus/u4b/audio/uaudioreg.h @@ -1,5 +1,5 @@ /* $NetBSD: uaudioreg.h,v 1.12 2004/11/05 19:08:29 kent Exp $ */ -/* $FreeBSD$ */ +/* $FreeBSD: head/sys/dev/sound/usb/uaudioreg.h 272254 2014-09-28 12:55:13Z hselasky $ */ /*- * Copyright (c) 1999 The NetBSD Foundation, Inc. @@ -31,8 +31,17 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#define UAUDIO_VERSION 0x100 +#ifndef _UAUDIOREG_H_ +#define _UAUDIOREG_H_ +#define UAUDIO_VERSION 0x0100 +#define UAUDIO_VERSION_20 0x0200 +#define UAUDIO_VERSION_30 0x0300 + +#define UAUDIO_PROTOCOL_20 0x20 + +#define UDESC_CS_UNDEFINED 0x20 +#define UDESC_CS_DEVICE 0x21 #define UDESC_CS_CONFIG 0x22 #define UDESC_CS_STRING 0x23 #define UDESC_CS_INTERFACE 0x24 @@ -46,6 +55,14 @@ #define UDESCSUB_AC_FEATURE 6 #define UDESCSUB_AC_PROCESSING 7 #define UDESCSUB_AC_EXTENSION 8 +/* ==== USB audio v2.0 ==== */ +#define UDESCSUB_AC_EFFECT 7 +#define UDESCSUB_AC_PROCESSING_V2 8 +#define UDESCSUB_AC_EXTENSION_V2 9 +#define UDESCSUB_AC_CLOCK_SRC 10 +#define UDESCSUB_AC_CLOCK_SEL 11 +#define UDESCSUB_AC_CLOCK_MUL 12 +#define UDESCSUB_AC_SAMPLE_RT 13 /* These macros check if the endpoint descriptor has additional fields */ #define UEP_MINSIZE 7 @@ -102,6 +119,13 @@ struct usb_audio_streaming_endpoint_descriptor { uWord wLockDelay; } __packed; +struct usb_midi_streaming_endpoint_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bNumEmbMIDIJack; +} __packed; + struct usb_audio_streaming_type1_descriptor { uByte bLength; uByte bDescriptorType; @@ -113,9 +137,9 @@ struct usb_audio_streaming_type1_descriptor { uByte bSamFreqType; #define UA_SAMP_CONTNUOUS 0 uByte tSamFreq[0]; -#define UA_GETSAMP(p, n) (((p)->tSamFreq[((n)*3)+0]) | \ +#define UA_GETSAMP(p, n) ((uint32_t)((((p)->tSamFreq[((n)*3)+0]) | \ ((p)->tSamFreq[((n)*3)+1] << 8) | \ - ((p)->tSamFreq[((n)*3)+2] << 16)) + ((p)->tSamFreq[((n)*3)+2] << 16)))) #define UA_SAMP_LO(p) UA_GETSAMP(p, 0) #define UA_SAMP_HI(p) UA_GETSAMP(p, 1) } __packed; @@ -250,6 +274,7 @@ struct usb_audio_extension_unit_0 { uByte baSourceId[0]; /* [bNrInPins] */ /* struct usb_audio_extension_unit_1 */ } __packed; + struct usb_audio_extension_unit_1 { uByte bNrChannels; uWord wChannelConfig; @@ -348,14 +373,25 @@ struct usb_audio_extension_unit_1 { #define DELAY_CONTROL 0x08 #define BASS_BOOST_CONTROL 0x09 #define LOUDNESS_CONTROL 0x0a +/* ==== USB audio v2.0 ==== */ +#define INPUT_GAIN_CONTROL 0x0b +#define INPUT_GAIN_PAD_CONTROL 0x0c +#define PHASE_INVERTER_CONTROL 0x0d +#define UNDERFLOW_CONTROL 0x0e +#define OVERFLOW_CONTROL 0x0f +#define LATENCY_CONTROL 0x10 #define FU_MASK(u) (1 << ((u)-1)) #define MASTER_CHAN 0 +#define MS_GENERAL 1 #define AS_GENERAL 1 #define FORMAT_TYPE 2 #define FORMAT_SPECIFIC 3 +/* ==== USB audio v2.0 ==== */ +#define FORMAT_ENCODER 3 +#define FORMAT_DECODER 4 #define UA_FMT_PCM 1 #define UA_FMT_PCM8 2 @@ -402,3 +438,370 @@ struct usb_audio_extension_unit_1 { #define DR_THRESHOLD_CONTROL 4 #define DR_ATTACK_TIME_CONTROL 5 #define DR_RELEASE_TIME_CONTROL 6 + +/*------------------------------------------------------------------------* + * USB audio v2.0 definitions + *------------------------------------------------------------------------*/ + +struct usb_audio20_streaming_interface_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalLink; + uByte bmControls; + uByte bFormatType; + uDWord bmFormats; + uByte bNrChannels; + uDWord bmChannelConfig; + uByte iChannelNames; +} __packed; + +struct usb_audio20_encoder_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bEncoderID; + uByte bEncoder; + uDWord bmControls; + uByte iParam1; + uByte iParam2; + uByte iParam3; + uByte iParam4; + uByte iParam5; + uByte iParam6; + uByte iParam7; + uByte iParam8; + uByte iEncoder; +} __packed; + +struct usb_audio20_streaming_endpoint_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bmAttributes; +#define UA20_MPS_ONLY 0x80 + uByte bmControls; +#define UA20_PITCH_CONTROL_MASK 0x03 +#define UA20_DATA_OVERRUN_MASK 0x0C +#define UA20_DATA_UNDERRUN_MASK 0x30 + uByte bLockDelayUnits; + uWord wLockDelay; +} __packed; + +struct usb_audio20_feedback_endpoint_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bEndpointAddress; + uByte bmAttributes; + uWord wMaxPacketSize; + uByte bInterval; +} __packed; + +#define UA20_CS_CUR 0x01 +#define UA20_CS_RANGE 0x02 +#define UA20_CS_MEM 0x03 + +struct usb_audio20_cur1_parameter { + uByte bCur; +} __packed; + +struct usb_audio20_ctl1_range_sub { + uByte bMIN; + uByte bMAX; + uByte bRES; +} __packed; + +struct usb_audio20_ctl1_range { + uWord wNumSubRanges; + struct usb_audio20_ctl1_range_sub sub[1]; +} __packed; + +struct usb_audio20_cur2_parameter { + uWord wCur; +} __packed; + +struct usb_audio20_ctl2_range_sub { + uWord wMIN; + uWord wMAX; + uWord wRES; +} __packed; + +struct usb_audio20_ctl2_range { + uWord wNumSubRanges; + struct usb_audio20_ctl2_range_sub sub[1]; +} __packed; + +struct usb_audio20_cur4_parameter { + uDWord dCur; +} __packed; + +struct usb_audio20_ctl4_range_sub { + uDWord dMIN; + uDWord dMAX; + uDWord dRES; +} __packed; + +struct usb_audio20_ctl4_range { + uWord wNumSubRanges; + struct usb_audio20_ctl4_range_sub sub[1]; +} __packed; + +struct usb_audio20_cc_cluster_descriptor { + uByte bNrChannels; + uDWord bmChannelConfig; + uByte iChannelNames; +} __packed; + +struct usb_audio20_streaming_type1_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bFormatType; + uByte bSubslotSize; + uByte bBitResolution; +} __packed; + +#define UA20_EERROR_NONE 0 +#define UA20_EERROR_MEMORY 1 +#define UA20_EERROR_BANDWIDTH 2 +#define UA20_EERROR_CPU 3 +#define UA20_EERROR_FORMATFR_ER 4 +#define UA20_EERROR_FORMATFR_SM 5 +#define UA20_EERROR_FORMATFR_LG 6 +#define UA20_EERROR_DATAFORMAT 7 +#define UA20_EERROR_NUMCHANNELS 8 +#define UA20_EERROR_SAMPLERATE 9 +#define UA20_EERROR_BITRATE 10 +#define UA20_EERROR_PARAM 11 +#define UA20_EERROR_NREADY 12 +#define UA20_EERROR_BUSY 13 + +struct usb_audio20_cc_alt_setting { + uByte bControlSize; + uDWord bmValidAltSettings; +} __packed; + +struct usb_audio20_interrupt_message { + uByte bInfo; + uByte bAttribute; + uDWord wValue; + uDWord wIndex; +} __packed; + +/* UDESCSUB_AC_CLOCK_SRC */ +struct usb_audio20_clock_source_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bClockId; + uByte bmAttributes; + uByte bmControls; + uByte bAssocTerminal; + uByte iClockSource; +} __packed; + +/* UDESCSUB_AC_CLOCK_SEL */ +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 */ +struct usb_audio20_clock_multiplier_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bClockId; + uByte bCSourceId; + uByte bmControls; + uByte iClockMultiplier; +} __packed; + +/* UDESCSUB_AC_INPUT */ +struct usb_audio20_input_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bCSourceId; + uByte bNrChannels; + uDWord bmChannelConfig; + uByte iCHannelNames; + uWord bmControls; + uByte iTerminal; +} __packed; + +/* UDESCSUB_AC_OUTPUT */ +struct usb_audio20_output_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bSourceId; + uByte bCSourceId; + uWord bmControls; + uByte iTerminal; +} __packed; + +/* UDESCSUB_AC_MIXER */ +struct usb_audio20_mixer_unit_0 { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[0]; + /* struct usb_audio20_mixer_unit_1 */ +} __packed; + +struct usb_audio20_mixer_unit_1 { + uByte bNrChannels; + uDWord bmChannelConfig; + uByte iChannelNames; + uByte bmControls[0]; + /* uByte iMixer; */ +} __packed; + +/* UDESCSUB_AC_SELECTOR */ +struct usb_audio20_selector_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[0]; + /* uByte iSelector; */ +} __packed; + +/* UDESCSUB_AC_FEATURE */ +struct usb_audio20_feature_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bSourceId; + uDWord bmaControls[0]; + /* uByte iFeature; */ +} __packed; + +/* UDESCSUB_AC_SAMPLE_RT */ +struct usb_audio20_sample_rate_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bSourceId; + uByte bSourceInId; + uByte bSourceOutId; + uByte iSrc; +} __packed; + +/* UDESCSUB_AC_EFFECT */ +struct usb_audio20_effect_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wEffectType; + uByte bSourceId; + uDWord bmaControls[0]; + uByte iEffects; +} __packed; + +/* UDESCSUB_AC_PROCESSING_V2 */ +struct usb_audio20_processing_unit_0 { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wProcessType; + uByte bNrInPins; + uByte baSourceId[0]; /* [bNrInPins] */ +} __packed; + +struct usb_audio20_processing_unit_1 { + uByte bNrChannels; + uDWord bmChannelConfig; + uByte iChannelNames; + uWord bmControls; + uByte iProcessing; +} __packed; + +/* UDESCSUB_AC_EXTENSION_V2 */ +struct usb_audio20_extension_unit_0 { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wExtensionCode; + uByte bNrInPins; + uByte baSourceId[0]; +} __packed; + +struct usb_audio20_extension_unit_1 { + uByte bNrChannels; + uDWord bmChannelConfig; + uByte iChannelNames; + uByte bmControls; + uByte iExtension; +} __packed; + +struct usb_audio20_cluster { + uByte bNrChannels; + uDWord bmChannelConfig; + uByte iChannelNames; +} __packed; + +#define UA20_TF_UNDEFINED 0x00 +#define UA20_TF_DESKTOP_SPEAKER 0x01 +#define UA20_TF_HOME_THEATER 0x02 +#define UA20_TF_MICROPHONE 0x03 +#define UA20_TF_HEADSET 0x04 +#define UA20_TF_TELEPHONE 0x05 +#define UA20_TF_CONVERTER 0x06 +#define UA20_TF_SOUND_RECORDER 0x07 +#define UA20_TF_IO_BOX 0x08 +#define UA20_TF_MUSICAL_INSTRUMENT 0x09 +#define UA20_TF_PRO_AUDIO 0x0A +#define UA20_TF_AV 0x0B +#define UA20_TF_CONTROL_PANEL 0x0C +#define UA20_TF_OTHER 0xFF + +#define UA20_CS_SAM_FREQ_CONTROL 0x01 +#define UA20_CS_CLOCK_VALID_CONTROL 0x02 + +#define UA20_TE_COPY_PROTECT_CONTROL 0x01 +#define UA20_TE_CONNECTOR_CONTROL 0x02 +#define UA20_TE_OVERLOAD_CONTROL 0x03 +#define UA20_TE_CLUSTER_CONTROL 0x04 +#define UA20_TE_UNDERFLOW_CONTROL 0x05 +#define UA20_TE_OVERFLOW_CONTROL 0x06 +#define UA20_TE_LATENCY_CONTROL 0x07 + +#define UA20_MU_MIXER_CONTROL 0x01 +#define UA20_MU_CLUSTER_CONTROL 0x02 +#define UA20_MU_UNDERFLOW_CONTROL 0x03 +#define UA20_MU_OVERFLOW_CONTROL 0x04 +#define UA20_MU_LATENCY_CONTROL 0x05 + +#define UA20_FMT_PCM (1U << 0) +#define UA20_FMT_PCM8 (1U << 1) +#define UA20_FMT_FLOAT (1U << 2) +#define UA20_FMT_ALAW (1U << 3) +#define UA20_FMT_MULAW (1U << 4) +#define UA20_FMT_RAW (1U << 31) + +#endif /* _UAUDIOREG_H_ */ -- 2.41.0