svn commit: r242822 - in stable/9/sys/dev: sound/usb usb usb/quirk
Hans Petter Selasky
hselasky at FreeBSD.org
Fri Nov 9 07:29:11 UTC 2012
Author: hselasky
Date: Fri Nov 9 07:29:11 2012
New Revision: 242822
URL: http://svnweb.freebsd.org/changeset/base/242822
Log:
MFC r242127, r240078, r240609, r241988, r242129, r242223, r242438,
r242453, r242455 and r242458:
Add full support for Fast Track Ultra 8R from M-audio.
Implement support for USB Audio v2.0.
Remove some redundant USB audio v1.0 debug data. Use lsusb instead.
Implement support for the so-called USB feedback endpoint for USB
audio devices.
Export all mixer nodes into dev.pcm.X.mixer.Y sysctl nodes.
PR: usb/171254
Modified:
stable/9/sys/dev/sound/usb/uaudio.c
stable/9/sys/dev/sound/usb/uaudioreg.h
stable/9/sys/dev/usb/quirk/usb_quirk.c
stable/9/sys/dev/usb/usbdevs
Directory Properties:
stable/9/sys/ (props changed)
stable/9/sys/dev/ (props changed)
Modified: stable/9/sys/dev/sound/usb/uaudio.c
==============================================================================
--- stable/9/sys/dev/sound/usb/uaudio.c Fri Nov 9 07:12:31 2012 (r242821)
+++ stable/9/sys/dev/sound/usb/uaudio.c Fri Nov 9 07:29:11 2012 (r242822)
@@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_request.h>
#define USB_DEBUG_VAR uaudio_debug
#include <dev/usb/usb_debug.h>
@@ -115,22 +116,39 @@ SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, def
#endif
#define UAUDIO_NFRAMES 64 /* must be factor of 8 due HS-USB */
-#define UAUDIO_NCHANBUFS 2 /* number of outstanding request */
-#define UAUDIO_RECURSE_LIMIT 24 /* rounds */
+#define UAUDIO_NCHANBUFS 2 /* number of outstanding request */
+#define UAUDIO_RECURSE_LIMIT 255 /* rounds */
#define MAKE_WORD(h,l) (((h) << 8) | (l))
#define BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1)
#define UAUDIO_MAX_CHAN(x) (x)
+union uaudio_asid {
+ const struct usb_audio_streaming_interface_descriptor *v1;
+ const struct usb_audio20_streaming_interface_descriptor *v2;
+};
+
+union uaudio_asf1d {
+ const struct usb_audio_streaming_type1_descriptor *v1;
+ const struct usb_audio20_streaming_type1_descriptor *v2;
+};
+
+union uaudio_sed {
+ const struct usb_audio_streaming_endpoint_descriptor *v1;
+ const struct usb_audio20_streaming_endpoint_descriptor *v2;
+};
+
struct uaudio_mixer_node {
+ const char *name;
+
int32_t minval;
int32_t maxval;
-#define MIX_MAX_CHAN 8
+#define MIX_MAX_CHAN 16
int32_t wValue[MIX_MAX_CHAN]; /* using nchan */
uint32_t mul;
uint32_t ctl;
- uint16_t wData[MIX_MAX_CHAN]; /* using nchan */
+ int wData[MIX_MAX_CHAN]; /* using nchan */
uint16_t wIndex;
uint8_t update[(MIX_MAX_CHAN + 7) / 8];
@@ -149,6 +167,9 @@ struct uaudio_mixer_node {
#define MAX_SELECTOR_INPUT_PIN 256
uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN];
uint8_t class;
+ uint8_t val_default;
+
+ uint8_t desc[64];
struct uaudio_mixer_node *next;
};
@@ -161,12 +182,10 @@ struct uaudio_chan {
struct mtx *pcm_mtx; /* lock protecting this structure */
struct uaudio_softc *priv_sc;
struct pcm_channel *pcm_ch;
- struct usb_xfer *xfer[UAUDIO_NCHANBUFS];
- const struct usb_audio_streaming_interface_descriptor *p_asid;
- const struct usb_audio_streaming_type1_descriptor *p_asf1d;
- const struct usb_audio_streaming_endpoint_descriptor *p_sed;
+ struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1];
+ union uaudio_asf1d p_asf1d;
+ union uaudio_sed p_sed;
const usb_endpoint_descriptor_audio_t *p_ed1;
- const usb_endpoint_descriptor_audio_t *p_ed2;
const struct uaudio_format *p_fmt;
uint8_t *buf; /* pointer to buffer */
@@ -192,6 +211,13 @@ struct uaudio_chan {
uint8_t valid;
uint8_t iface_index;
uint8_t iface_alt_index;
+ uint8_t channels;
+
+ uint8_t last_sync_time;
+ uint8_t last_sync_state;
+#define UAUDIO_SYNC_NONE 0
+#define UAUDIO_SYNC_MORE 1
+#define UAUDIO_SYNC_LESS 2
};
#define UMIDI_CABLES_MAX 16 /* units */
@@ -242,13 +268,23 @@ struct umidi_chan {
uint8_t single_command;
};
+struct uaudio_search_result {
+ uint8_t bit_input[(256 + 7) / 8];
+ uint8_t bit_output[(256 + 7) / 8];
+ uint8_t recurse_level;
+ uint8_t id_max;
+ uint8_t is_input;
+};
+
struct uaudio_softc {
struct sbuf sc_sndstat;
struct sndcard_func sc_sndcard_func;
struct uaudio_chan sc_rec_chan;
struct uaudio_chan sc_play_chan;
struct umidi_chan sc_midi_chan;
+ struct uaudio_search_result sc_mixer_clocks;
+ struct mtx *sc_mixer_lock;
struct usb_device *sc_udev;
struct usb_xfer *sc_mixer_xfer[1];
struct uaudio_mixer_node *sc_mixer_root;
@@ -273,24 +309,28 @@ struct uaudio_softc {
uint8_t sc_uq_au_vendor_class:1;
};
-struct uaudio_search_result {
- uint8_t bit_input[(256 + 7) / 8];
- uint8_t bit_output[(256 + 7) / 8];
- uint8_t bit_visited[(256 + 7) / 8];
- uint8_t recurse_level;
- uint8_t id_max;
-};
-
struct uaudio_terminal_node {
union {
const struct usb_descriptor *desc;
- const struct usb_audio_input_terminal *it;
- const struct usb_audio_output_terminal *ot;
- const struct usb_audio_mixer_unit_0 *mu;
- const struct usb_audio_selector_unit *su;
- const struct usb_audio_feature_unit *fu;
- const struct usb_audio_processing_unit_0 *pu;
- const struct usb_audio_extension_unit_0 *eu;
+ const struct usb_audio_input_terminal *it_v1;
+ const struct usb_audio_output_terminal *ot_v1;
+ const struct usb_audio_mixer_unit_0 *mu_v1;
+ const struct usb_audio_selector_unit *su_v1;
+ const struct usb_audio_feature_unit *fu_v1;
+ const struct usb_audio_processing_unit_0 *pu_v1;
+ const struct usb_audio_extension_unit_0 *eu_v1;
+ const struct usb_audio20_clock_source_unit *csrc_v2;
+ const struct usb_audio20_clock_selector_unit_0 *csel_v2;
+ const struct usb_audio20_clock_multiplier_unit *cmul_v2;
+ const struct usb_audio20_input_terminal *it_v2;
+ const struct usb_audio20_output_terminal *ot_v2;
+ const struct usb_audio20_mixer_unit_0 *mu_v2;
+ const struct usb_audio20_selector_unit *su_v2;
+ const struct usb_audio20_feature_unit *fu_v2;
+ const struct usb_audio20_sample_rate_unit *ru_v2;
+ const struct usb_audio20_processing_unit_0 *pu_v2;
+ const struct usb_audio20_extension_unit_0 *eu_v2;
+ const struct usb_audio20_effect_unit *ef_v2;
} u;
struct uaudio_search_result usr;
struct uaudio_terminal_node *root;
@@ -303,7 +343,7 @@ struct uaudio_format {
const char *description;
};
-static const struct uaudio_format uaudio_formats[] = {
+static const struct uaudio_format uaudio10_formats[] = {
{UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
{UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
@@ -321,6 +361,24 @@ static const struct uaudio_format uaudio
{0, 0, 0, NULL}
};
+static const struct uaudio_format uaudio20_formats[] = {
+
+ {UA20_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"},
+ {UA20_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"},
+ {UA20_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"},
+ {UA20_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"},
+
+ {UA20_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
+ {UA20_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
+ {UA20_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"},
+ {UA20_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"},
+
+ {UA20_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"},
+ {UA20_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"},
+
+ {0, 0, 0, NULL}
+};
+
#define UAC_OUTPUT 0
#define UAC_INPUT 1
#define UAC_EQUAL 2
@@ -341,23 +399,23 @@ static device_attach_t uaudio_attach;
static device_detach_t uaudio_detach;
static usb_callback_t uaudio_chan_play_callback;
+static usb_callback_t uaudio_chan_play_sync_callback;
static usb_callback_t uaudio_chan_record_callback;
+static usb_callback_t uaudio_chan_record_sync_callback;
static usb_callback_t uaudio_mixer_write_cfg_callback;
static usb_callback_t umidi_bulk_read_callback;
static usb_callback_t umidi_bulk_write_callback;
-static void uaudio_chan_fill_info_sub(struct uaudio_softc *,
- struct usb_device *, uint32_t, uint8_t, uint8_t);
-static void uaudio_chan_fill_info(struct uaudio_softc *,
- struct usb_device *);
-static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *,
- struct uaudio_mixer_node *);
-static void uaudio_mixer_add_ctl(struct uaudio_softc *,
- struct uaudio_mixer_node *);
-static void uaudio_mixer_add_input(struct uaudio_softc *,
- const struct uaudio_terminal_node *, int);
-static void uaudio_mixer_add_output(struct uaudio_softc *,
- const struct uaudio_terminal_node *, int);
+/* ==== USB mixer ==== */
+
+static int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS);
+static void uaudio_mixer_ctl_free(struct uaudio_softc *);
+static void uaudio_mixer_register_sysctl(struct uaudio_softc *, device_t);
+static void uaudio_mixer_reload_all(struct uaudio_softc *);
+static void uaudio_mixer_controls_create_ftu(struct uaudio_softc *);
+
+/* ==== USB audio v1.0 ==== */
+
static void uaudio_mixer_add_mixer(struct uaudio_softc *,
const struct uaudio_terminal_node *, int);
static void uaudio_mixer_add_selector(struct uaudio_softc *,
@@ -378,25 +436,56 @@ static uint16_t uaudio_mixer_determine_c
struct uaudio_mixer_node *);
static uint16_t uaudio_mixer_feature_name(const struct uaudio_terminal_node *,
struct uaudio_mixer_node *);
-static const struct uaudio_terminal_node *uaudio_mixer_get_input(
- const struct uaudio_terminal_node *, uint8_t);
-static const struct uaudio_terminal_node *uaudio_mixer_get_output(
- const struct uaudio_terminal_node *, uint8_t);
static void uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *,
const uint8_t *, uint8_t, struct uaudio_search_result *);
-static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *,
- uint8_t, uint8_t, struct uaudio_search_result *);
+static const void *uaudio_mixer_verify_desc(const void *, uint32_t);
+static usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t);
+static int uaudio_mixer_get(struct usb_device *, uint16_t, uint8_t,
+ struct uaudio_mixer_node *);
+
+/* ==== USB audio v2.0 ==== */
+
+static void uaudio20_mixer_add_mixer(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static void uaudio20_mixer_add_selector(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static void uaudio20_mixer_add_feature(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static struct usb_audio20_cluster uaudio20_mixer_get_cluster(uint8_t,
+ const struct uaudio_terminal_node *);
+static uint16_t uaudio20_mixer_determine_class(const struct uaudio_terminal_node *,
+ struct uaudio_mixer_node *);
+static uint16_t uaudio20_mixer_feature_name(const struct uaudio_terminal_node *,
+ struct uaudio_mixer_node *);
+static void uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *,
+ const uint8_t *, uint8_t, struct uaudio_search_result *);
+static const void *uaudio20_mixer_verify_desc(const void *, uint32_t);
+static usb_error_t uaudio20_set_speed(struct usb_device *, uint8_t,
+ uint8_t, uint32_t);
+
+/* USB audio v1.0 and v2.0 */
+
+static void uaudio_chan_fill_info_sub(struct uaudio_softc *,
+ struct usb_device *, uint32_t, uint8_t, uint8_t);
+static void uaudio_chan_fill_info(struct uaudio_softc *,
+ struct usb_device *);
+static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *,
+ struct uaudio_mixer_node *);
+static void uaudio_mixer_add_ctl(struct uaudio_softc *,
+ struct uaudio_mixer_node *);
static void uaudio_mixer_fill_info(struct uaudio_softc *,
struct usb_device *, void *);
-static uint16_t uaudio_mixer_get(struct usb_device *, uint8_t,
- struct uaudio_mixer_node *);
static void uaudio_mixer_ctl_set(struct uaudio_softc *,
struct uaudio_mixer_node *, uint8_t, int32_t val);
-static usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t);
static int uaudio_mixer_signext(uint8_t, int);
static int uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val);
-static const void *uaudio_mixer_verify_desc(const void *, uint32_t);
static void uaudio_mixer_init(struct uaudio_softc *);
+static const struct uaudio_terminal_node *uaudio_mixer_get_input(
+ const struct uaudio_terminal_node *, uint8_t);
+static const struct uaudio_terminal_node *uaudio_mixer_get_output(
+ const struct uaudio_terminal_node *, uint8_t);
+static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *,
+ uint8_t, uint8_t, struct uaudio_search_result *);
static uint8_t umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t);
static struct umidi_sub_chan *umidi_sub_by_fifo(struct usb_fifo *);
static void umidi_start_read(struct usb_fifo *);
@@ -413,13 +502,10 @@ static int umidi_detach(device_t dev);
#ifdef USB_DEBUG
static void uaudio_chan_dump_ep_desc(
const usb_endpoint_descriptor_audio_t *);
-static void uaudio_mixer_dump_cluster(uint8_t,
- const struct uaudio_terminal_node *);
-static const char *uaudio_mixer_get_terminal_name(uint16_t);
#endif
static const struct usb_config
- uaudio_cfg_record[UAUDIO_NCHANBUFS] = {
+ uaudio_cfg_record[UAUDIO_NCHANBUFS + 1] = {
[0] = {
.type = UE_ISOCHRONOUS,
.endpoint = UE_ADDR_ANY,
@@ -439,10 +525,20 @@ static const struct usb_config
.flags = {.short_xfer_ok = 1,},
.callback = &uaudio_chan_record_callback,
},
+
+ [2] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .frames = 1,
+ .flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
+ .callback = &uaudio_chan_record_sync_callback,
+ },
};
static const struct usb_config
- uaudio_cfg_play[UAUDIO_NCHANBUFS] = {
+ uaudio_cfg_play[UAUDIO_NCHANBUFS + 1] = {
[0] = {
.type = UE_ISOCHRONOUS,
.endpoint = UE_ADDR_ANY,
@@ -462,6 +558,16 @@ static const struct usb_config
.flags = {.short_xfer_ok = 1,},
.callback = &uaudio_chan_play_callback,
},
+
+ [2] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .frames = 1,
+ .flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
+ .callback = &uaudio_chan_play_sync_callback,
+ },
};
static const struct usb_config
@@ -555,7 +661,8 @@ uaudio_probe(device_t dev)
/* lookup non-standard device */
if (uaa->info.bInterfaceClass != UICLASS_AUDIO) {
- if (usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0)
+ if (uaa->info.bInterfaceClass != UICLASS_VENDOR ||
+ usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0)
return (ENXIO);
}
@@ -614,30 +721,43 @@ uaudio_attach(device_t dev)
id = usbd_get_interface_descriptor(uaa->iface);
- uaudio_chan_fill_info(sc, uaa->device);
-
+ /* must fill mixer info before channel info */
uaudio_mixer_fill_info(sc, uaa->device, id);
+ /* fill channel info */
+ uaudio_chan_fill_info(sc, uaa->device);
+
DPRINTF("audio rev %d.%02x\n",
sc->sc_audio_rev >> 8,
sc->sc_audio_rev & 0xff);
+ if (sc->sc_mixer_count == 0) {
+ if (uaa->info.idVendor == USB_VENDOR_MAUDIO &&
+ (uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA ||
+ uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA8R)) {
+ DPRINTF("Generating mixer descriptors\n");
+ uaudio_mixer_controls_create_ftu(sc);
+ }
+ }
+
DPRINTF("%d mixer controls\n",
sc->sc_mixer_count);
if (sc->sc_play_chan.valid) {
- device_printf(dev, "Play: %d Hz, %d ch, %s format.\n",
+ device_printf(dev, "Play: %d Hz, %d ch, %s format, "
+ "2x8ms buffer.\n",
sc->sc_play_chan.sample_rate,
- sc->sc_play_chan.p_asf1d->bNrChannels,
+ sc->sc_play_chan.channels,
sc->sc_play_chan.p_fmt->description);
} else {
device_printf(dev, "No playback.\n");
}
if (sc->sc_rec_chan.valid) {
- device_printf(dev, "Record: %d Hz, %d ch, %s format.\n",
+ device_printf(dev, "Record: %d Hz, %d ch, %s format, "
+ "2x8ms buffer.\n",
sc->sc_rec_chan.sample_rate,
- sc->sc_rec_chan.p_asf1d->bNrChannels,
+ sc->sc_rec_chan.channels,
sc->sc_rec_chan.p_fmt->description);
} else {
device_printf(dev, "No recording.\n");
@@ -679,6 +799,10 @@ uaudio_attach(device_t dev)
DPRINTF("child attach failed\n");
goto detach;
}
+
+ /* reload all mixer settings */
+ uaudio_mixer_reload_all(sc);
+
return (0); /* success */
detach:
@@ -714,9 +838,8 @@ uaudio_attach_sub(device_t dev, kobj_cla
*/
uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL);
}
- if (mixer_init(dev, mixer_class, sc)) {
+ if (mixer_init(dev, mixer_class, sc))
goto detach;
- }
sc->sc_mixer_init = 1;
snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
@@ -738,6 +861,8 @@ uaudio_attach_sub(device_t dev, kobj_cla
}
pcm_setstatus(dev, status);
+ uaudio_mixer_register_sysctl(sc, dev);
+
return (0); /* success */
detach:
@@ -779,9 +904,9 @@ uaudio_detach(device_t dev)
* any.
*/
if (sc->sc_play_chan.valid)
- usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS);
+ usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1);
if (sc->sc_rec_chan.valid)
- usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS);
+ usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1);
if (bus_generic_detach(dev) != 0) {
DPRINTF("detach failed!\n");
@@ -791,6 +916,10 @@ uaudio_detach(device_t dev)
umidi_detach(dev);
+ /* free mixer data */
+
+ uaudio_mixer_ctl_free(sc);
+
return (0);
}
@@ -857,28 +986,88 @@ uaudio_record_fix_fs(usb_endpoint_descri
}
}
+static usb_error_t
+uaudio20_check_rate(struct usb_device *udev, uint8_t iface_no,
+ uint8_t clockid, uint32_t rate)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+ uint8_t data[255];
+ uint16_t actlen;
+ uint16_t rates;
+ uint16_t x;
+
+ DPRINTFN(6, "ifaceno=%d clockid=%d rate=%u\n",
+ iface_no, clockid, rate);
+
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UA20_CS_RANGE;
+ USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0);
+ USETW2(req.wIndex, clockid, iface_no);
+ USETW(req.wLength, 255);
+
+ error = usbd_do_request_flags(udev, NULL, &req, data,
+ USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT);
+
+ if (error != 0 || actlen < 2)
+ return (USB_ERR_INVAL);
+
+ rates = data[0] | (data[1] << 8);
+ actlen = (actlen - 2) / 12;
+
+ if (rates > actlen) {
+ DPRINTF("Too many rates\n");
+ rates = actlen;
+ }
+
+ for (x = 0; x != rates; x++) {
+ uint32_t min = UGETDW(data + 2 + (12 * x));
+ uint32_t max = UGETDW(data + 6 + (12 * x));
+ uint32_t res = UGETDW(data + 10 + (12 * x));
+
+ if (res == 0) {
+ DPRINTF("Zero residue\n");
+ res = 1;
+ }
+
+ if (min > max) {
+ DPRINTF("Swapped max and min\n");
+ uint32_t temp;
+ temp = min;
+ min = max;
+ max = temp;
+ }
+
+ if (rate >= min && rate <= max &&
+ (((rate - min) % res) == 0)) {
+ return (0);
+ }
+ }
+ return (USB_ERR_INVAL);
+}
+
static void
uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
uint32_t rate, uint8_t channels, uint8_t bit_resolution)
{
struct usb_descriptor *desc = NULL;
- const struct usb_audio_streaming_interface_descriptor *asid = NULL;
- const struct usb_audio_streaming_type1_descriptor *asf1d = NULL;
- const struct usb_audio_streaming_endpoint_descriptor *sed = NULL;
+ union uaudio_asid asid = { NULL };
+ union uaudio_asf1d asf1d = { NULL };
+ union uaudio_sed sed = { NULL };
usb_endpoint_descriptor_audio_t *ed1 = NULL;
- const usb_endpoint_descriptor_audio_t *ed2 = NULL;
+ const struct usb_audio_control_descriptor *acdp = NULL;
struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
struct usb_interface_descriptor *id;
- const struct uaudio_format *p_fmt;
+ const struct uaudio_format *p_fmt = NULL;
struct uaudio_chan *chan;
uint16_t curidx = 0xFFFF;
uint16_t lastidx = 0xFFFF;
uint16_t alt_index = 0;
- uint16_t wFormat;
+ uint16_t audio_rev = 0;
+ uint16_t x;
uint8_t ep_dir;
uint8_t bChannels;
uint8_t bBitResolution;
- uint8_t x;
uint8_t audio_if = 0;
uint8_t uma_if_class;
@@ -923,171 +1112,264 @@ uaudio_chan_fill_info_sub(struct uaudio_
sc->sc_midi_chan.valid = 1;
}
}
- asid = NULL;
- asf1d = NULL;
+ asid.v1 = NULL;
+ asf1d.v1 = NULL;
ed1 = NULL;
- ed2 = NULL;
- sed = NULL;
+ sed.v1 = NULL;
}
- if ((desc->bDescriptorType == UDESC_CS_INTERFACE) &&
+
+ if (audio_if == 0) {
+ if ((acdp == NULL) &&
+ (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
+ (desc->bDescriptorSubtype == UDESCSUB_AC_HEADER) &&
+ (desc->bLength >= sizeof(*acdp))) {
+ acdp = (void *)desc;
+ audio_rev = UGETW(acdp->bcdADC);
+ }
+
+ /*
+ * Don't collect any USB audio descriptors if
+ * this is not an USB audio stream interface.
+ */
+ continue;
+ }
+
+ if ((acdp != NULL) &&
+ (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
(desc->bDescriptorSubtype == AS_GENERAL) &&
- (desc->bLength >= sizeof(*asid))) {
- if (asid == NULL) {
- asid = (void *)desc;
+ (asid.v1 == NULL)) {
+ if (audio_rev >= UAUDIO_VERSION_30) {
+ /* FALLTHROUGH */
+ } else if (audio_rev >= UAUDIO_VERSION_20) {
+ if (desc->bLength >= sizeof(*asid.v2)) {
+ asid.v2 = (void *)desc;
+ }
+ } else {
+ if (desc->bLength >= sizeof(*asid.v1)) {
+ asid.v1 = (void *)desc;
+ }
}
}
- if ((desc->bDescriptorType == UDESC_CS_INTERFACE) &&
+ if ((acdp != NULL) &&
+ (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
(desc->bDescriptorSubtype == FORMAT_TYPE) &&
- (desc->bLength >= sizeof(*asf1d))) {
- if (asf1d == NULL) {
- asf1d = (void *)desc;
- if (asf1d->bFormatType != FORMAT_TYPE_I) {
- DPRINTFN(11, "ignored bFormatType = %d\n",
- asf1d->bFormatType);
- asf1d = NULL;
- continue;
- }
- if (asf1d->bLength < (sizeof(*asf1d) +
- ((asf1d->bSamFreqType == 0) ? 6 :
- (asf1d->bSamFreqType * 3)))) {
- DPRINTFN(11, "'asf1d' descriptor is too short\n");
- asf1d = NULL;
- continue;
+ (asf1d.v1 == NULL)) {
+ if (audio_rev >= UAUDIO_VERSION_30) {
+ /* FALLTHROUGH */
+ } else if (audio_rev >= UAUDIO_VERSION_20) {
+ if (desc->bLength >= sizeof(*asf1d.v2))
+ asf1d.v2 = (void *)desc;
+ } else {
+ if (desc->bLength >= sizeof(*asf1d.v1)) {
+ asf1d.v1 = (void *)desc;
+
+ if (asf1d.v1->bFormatType != FORMAT_TYPE_I) {
+ DPRINTFN(11, "ignored bFormatType = %d\n",
+ asf1d.v1->bFormatType);
+ asf1d.v1 = NULL;
+ continue;
+ }
+ if (desc->bLength < (sizeof(*asf1d.v1) +
+ ((asf1d.v1->bSamFreqType == 0) ? 6 :
+ (asf1d.v1->bSamFreqType * 3)))) {
+ DPRINTFN(11, "invalid descriptor, "
+ "too short\n");
+ asf1d.v1 = NULL;
+ continue;
+ }
}
}
}
if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
- (desc->bLength >= UEP_MINSIZE)) {
- if (ed1 == NULL) {
- ed1 = (void *)desc;
- if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) {
- ed1 = NULL;
- }
+ (desc->bLength >= UEP_MINSIZE) &&
+ (ed1 == NULL)) {
+ ed1 = (void *)desc;
+ if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) {
+ ed1 = NULL;
+ continue;
}
}
- if ((desc->bDescriptorType == UDESC_CS_ENDPOINT) &&
+ if ((acdp != NULL) &&
+ (desc->bDescriptorType == UDESC_CS_ENDPOINT) &&
(desc->bDescriptorSubtype == AS_GENERAL) &&
- (desc->bLength >= sizeof(*sed))) {
- if (sed == NULL) {
- sed = (void *)desc;
+ (sed.v1 == NULL)) {
+ if (audio_rev >= UAUDIO_VERSION_30) {
+ /* FALLTHROUGH */
+ } else if (audio_rev >= UAUDIO_VERSION_20) {
+ if (desc->bLength >= sizeof(*sed.v2))
+ sed.v2 = (void *)desc;
+ } else {
+ if (desc->bLength >= sizeof(*sed.v1))
+ sed.v1 = (void *)desc;
}
}
- if (audio_if && asid && asf1d && ed1 && sed) {
+ if (asid.v1 == NULL || asf1d.v1 == NULL ||
+ ed1 == NULL || sed.v1 == NULL) {
+ /* need more descriptors */
+ continue;
+ }
+
+ ep_dir = UE_GET_DIR(ed1->bEndpointAddress);
- ep_dir = UE_GET_DIR(ed1->bEndpointAddress);
+ /* We ignore sync endpoint information until further. */
- /* We ignore sync endpoint information until further. */
+ if (audio_rev >= UAUDIO_VERSION_30) {
+ goto next_ep;
+ } else if (audio_rev >= UAUDIO_VERSION_20) {
- wFormat = UGETW(asid->wFormatTag);
- bChannels = UAUDIO_MAX_CHAN(asf1d->bNrChannels);
- bBitResolution = asf1d->bBitResolution;
+ uint32_t dwFormat;
+ uint8_t bSubslotSize;
- if (asf1d->bSamFreqType == 0) {
+ dwFormat = UGETDW(asid.v2->bmFormats);
+ bChannels = asid.v2->bNrChannels;
+ bBitResolution = asf1d.v2->bBitResolution;
+ bSubslotSize = asf1d.v2->bSubslotSize;
+
+ if (bBitResolution != (bSubslotSize * 8)) {
+ DPRINTF("Invalid bSubslotSize\n");
+ goto next_ep;
+ }
+
+ if ((bChannels != channels) ||
+ (bBitResolution != bit_resolution)) {
+ DPRINTF("Wrong number of channels\n");
+ goto next_ep;
+ }
+
+ for (p_fmt = uaudio20_formats;
+ p_fmt->wFormat != 0; p_fmt++) {
+ if ((p_fmt->wFormat & dwFormat) &&
+ (p_fmt->bPrecision == bBitResolution))
+ break;
+ }
+
+ if (p_fmt->wFormat == 0) {
+ DPRINTF("Unsupported audio format\n");
+ goto next_ep;
+ }
+
+ for (x = 0; x != 256; x++) {
+ if (ep_dir == UE_DIR_OUT) {
+ if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
+ (1 << (x % 8)))) {
+ continue;
+ }
+ } else {
+ if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
+ (1 << (x % 8)))) {
+ continue;
+ }
+ }
+
+ DPRINTF("Checking clock ID=%d\n", x);
+
+ if (uaudio20_check_rate(udev,
+ sc->sc_mixer_iface_no, x, rate)) {
+ DPRINTF("Unsupported sampling "
+ "rate, id=%d\n", x);
+ goto next_ep;
+ }
+ }
+ } else {
+ uint16_t wFormat;
+
+ wFormat = UGETW(asid.v1->wFormatTag);
+ bChannels = UAUDIO_MAX_CHAN(asf1d.v1->bNrChannels);
+ bBitResolution = asf1d.v1->bBitResolution;
+
+ if (asf1d.v1->bSamFreqType == 0) {
DPRINTFN(16, "Sample rate: %d-%dHz\n",
- UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+ UA_SAMP_LO(asf1d.v1),
+ UA_SAMP_HI(asf1d.v1));
- if ((rate >= UA_SAMP_LO(asf1d)) &&
- (rate <= UA_SAMP_HI(asf1d))) {
+ if ((rate >= UA_SAMP_LO(asf1d.v1)) &&
+ (rate <= UA_SAMP_HI(asf1d.v1)))
goto found_rate;
- }
} else {
- for (x = 0; x < asf1d->bSamFreqType; x++) {
+ for (x = 0; x < asf1d.v1->bSamFreqType; x++) {
DPRINTFN(16, "Sample rate = %dHz\n",
- UA_GETSAMP(asf1d, x));
+ UA_GETSAMP(asf1d.v1, x));
- if (rate == UA_GETSAMP(asf1d, x)) {
+ if (rate == UA_GETSAMP(asf1d.v1, x))
goto found_rate;
- }
}
}
-
- audio_if = 0;
- continue;
+ goto next_ep;
found_rate:
-
- for (p_fmt = uaudio_formats;
- p_fmt->wFormat;
- p_fmt++) {
+ for (p_fmt = uaudio10_formats;
+ p_fmt->wFormat != 0; p_fmt++) {
if ((p_fmt->wFormat == wFormat) &&
- (p_fmt->bPrecision == bBitResolution)) {
- goto found_format;
- }
+ (p_fmt->bPrecision == bBitResolution))
+ break;
+ }
+ if (p_fmt->wFormat == 0) {
+ DPRINTF("Unsupported audio format\n");
+ goto next_ep;
}
- audio_if = 0;
- continue;
-
- found_format:
-
- if ((bChannels == channels) &&
- (bBitResolution == bit_resolution)) {
+ if ((bChannels != channels) ||
+ (bBitResolution != bit_resolution)) {
+ DPRINTF("Wrong number of channels\n");
+ goto next_ep;
+ }
+ }
- chan = (ep_dir == UE_DIR_IN) ?
- &sc->sc_rec_chan :
- &sc->sc_play_chan;
+ chan = (ep_dir == UE_DIR_IN) ?
+ &sc->sc_rec_chan : &sc->sc_play_chan;
- if ((chan->valid == 0) && usbd_get_iface(udev, curidx)) {
+ if (chan->valid != 0 ||
+ usbd_get_iface(udev, curidx) == NULL) {
+ DPRINTF("Channel already exists or "
+ "interface is not valid\n");
+ goto next_ep;
+ }
- chan->valid = 1;
+ chan->valid = 1;
#ifdef USB_DEBUG
- uaudio_chan_dump_ep_desc(ed1);
- uaudio_chan_dump_ep_desc(ed2);
-
- if (sed->bmAttributes & UA_SED_FREQ_CONTROL) {
- DPRINTFN(2, "FREQ_CONTROL\n");
- }
- if (sed->bmAttributes & UA_SED_PITCH_CONTROL) {
- DPRINTFN(2, "PITCH_CONTROL\n");
- }
+ uaudio_chan_dump_ep_desc(ed1);
#endif
- DPRINTF("Sample rate = %dHz, channels = %d, "
- "bits = %d, format = %s\n", rate, channels,
- bit_resolution, p_fmt->description);
-
- chan->sample_rate = rate;
- chan->p_asid = asid;
- chan->p_asf1d = asf1d;
- chan->p_ed1 = ed1;
- chan->p_ed2 = ed2;
- chan->p_fmt = p_fmt;
- chan->p_sed = sed;
- chan->iface_index = curidx;
- chan->iface_alt_index = alt_index;
-
- if (ep_dir == UE_DIR_IN)
- chan->usb_cfg =
- uaudio_cfg_record;
- else
- chan->usb_cfg =
- uaudio_cfg_play;
-
- chan->sample_size = ((
- UAUDIO_MAX_CHAN(chan->p_asf1d->bNrChannels) *
- chan->p_asf1d->bBitResolution) / 8);
-
- if (ep_dir == UE_DIR_IN &&
- usbd_get_speed(udev) == USB_SPEED_FULL) {
- uaudio_record_fix_fs(ed1,
- chan->sample_size * (rate / 1000),
- chan->sample_size * (rate / 4000));
- }
+ DPRINTF("Sample rate = %dHz, channels = %d, "
+ "bits = %d, format = %s\n", rate, channels,
+ bit_resolution, p_fmt->description);
+
+ chan->sample_rate = rate;
+ chan->p_asf1d = asf1d;
+ chan->p_ed1 = ed1;
+ chan->p_fmt = p_fmt;
+ chan->p_sed = sed;
+ chan->iface_index = curidx;
+ chan->iface_alt_index = alt_index;
- if (sc->sc_sndstat_valid) {
- sbuf_printf(&sc->sc_sndstat, "\n\t"
- "mode %d.%d:(%s) %dch, %d/%dbit, %s, %dHz",
- curidx, alt_index,
- (ep_dir == UE_DIR_IN) ? "input" : "output",
- asf1d->bNrChannels, asf1d->bBitResolution,
- asf1d->bSubFrameSize * 8,
- p_fmt->description, rate);
- }
- }
- }
- audio_if = 0;
- continue;
- }
+ if (ep_dir == UE_DIR_IN)
+ chan->usb_cfg = uaudio_cfg_record;
+ else
+ chan->usb_cfg = uaudio_cfg_play;
+
+ chan->sample_size = (UAUDIO_MAX_CHAN(channels) *
+ p_fmt->bPrecision) / 8;
+ chan->channels = channels;
+
+ if (ep_dir == UE_DIR_IN &&
+ usbd_get_speed(udev) == USB_SPEED_FULL) {
+ uaudio_record_fix_fs(ed1,
+ chan->sample_size * (rate / 1000),
+ chan->sample_size * (rate / 4000));
+ }
+
+ if (sc->sc_sndstat_valid != 0) {
+ sbuf_printf(&sc->sc_sndstat, "\n\t"
+ "mode %d.%d:(%s) %dch, %dbit, %s, %dHz",
+ curidx, alt_index,
+ (ep_dir == UE_DIR_IN) ? "input" : "output",
+ channels, p_fmt->bPrecision,
+ p_fmt->description, rate);
+ }
+
+ next_ep:
+ sed.v1 = NULL;
+ ed1 = NULL;
}
}
@@ -1137,7 +1419,7 @@ uaudio_chan_fill_info(struct uaudio_soft
* disable surround setups on FULL-speed USB
* by default
*/
- channels = 2;
+ channels = 4;
break;
default:
channels = 16;
@@ -1177,72 +1459,186 @@ done:
}
static void
-uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
+uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct uaudio_chan *ch = usbd_xfer_softc(xfer);
struct usb_page_cache *pc;
- uint32_t total;
- uint32_t blockcount;
- uint32_t n;
- uint32_t offset;
+ uint8_t buf[4];
+ uint64_t temp;
+ int len;
int actlen;
- int sumlen;
-
- usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
+ int nframes;
- if (ch->end == ch->start) {
- DPRINTF("no buffer!\n");
- return;
- }
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
-tr_transferred:
- if (actlen < sumlen) {
- DPRINTF("short transfer, "
- "%d of %d bytes\n", actlen, sumlen);
- }
- chn_intr(ch->pcm_ch);
- case USB_ST_SETUP:
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-stable-9
mailing list