ALSA: usbaudio: implement basic set of class v2.0 parser
This adds a number of parsers for audio class v2.0. In particular, the following internals are different and now handled by the code: * the number of streaming interfaces is now reported by an interface association descriptor. The old approach using a proprietary descriptor is deprecated. * The number of channels per interface is now stored in the AS_GENERAL descriptor (used to be part of the FORMAT_TYPE descriptor). * The list of supported sample rates is no longer stored in a variable length appendix of the format_type descriptor but is retrieved from the device using a class specific GET_RANGE command. * Supported sample formats are now reported as 32bit bitmap rather than a fixed value. For now, this is worked around by choosing just one of them. * A devices needs to have at least one CLOCK_SOURCE descriptor which denotes a clockID that is needed im the class request command. * Many descriptors (format_type, ...) have changed their layout. Handle this by casting the descriptors to the appropriate structs. Signed-off-by: Daniel Mack <daniel@caiaq.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
8fee4aff8c
commit
53ee98fe8a
|
@ -2422,17 +2422,53 @@ static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *
|
|||
* @format: the format tag (wFormatTag)
|
||||
* @fmt: the format type descriptor
|
||||
*/
|
||||
static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audioformat *fp,
|
||||
int format, void *fmt_raw)
|
||||
static int parse_audio_format_i_type(struct snd_usb_audio *chip,
|
||||
struct audioformat *fp,
|
||||
int format, void *_fmt,
|
||||
int protocol)
|
||||
{
|
||||
int pcm_format;
|
||||
int pcm_format, i;
|
||||
int sample_width, sample_bytes;
|
||||
struct uac_format_type_i_discrete_descriptor *fmt = fmt_raw;
|
||||
|
||||
switch (protocol) {
|
||||
case UAC_VERSION_1: {
|
||||
struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
|
||||
sample_width = fmt->bBitResolution;
|
||||
sample_bytes = fmt->bSubframeSize;
|
||||
break;
|
||||
}
|
||||
|
||||
case UAC_VERSION_2: {
|
||||
struct uac_format_type_i_ext_descriptor *fmt = _fmt;
|
||||
sample_width = fmt->bBitResolution;
|
||||
sample_bytes = fmt->bSubslotSize;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
* USB audio class v2 devices specify a bitmap of possible
|
||||
* audio formats rather than one fix value. For now, we just
|
||||
* pick one of them and report that as the only possible
|
||||
* value for this setting.
|
||||
* The bit allocation map is in fact compatible to the
|
||||
* wFormatTag of the v1 AS streaming descriptors, which is why
|
||||
* we can simply map the matrix.
|
||||
*/
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
if (format & (1UL << i)) {
|
||||
format = i + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME: correct endianess and sign? */
|
||||
pcm_format = -1;
|
||||
sample_width = fmt->bBitResolution;
|
||||
sample_bytes = fmt->bSubframeSize;
|
||||
|
||||
switch (format) {
|
||||
case 0: /* some devices don't define this correctly... */
|
||||
|
@ -2446,6 +2482,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor
|
|||
sample_width, sample_bytes);
|
||||
}
|
||||
/* check the format byte size */
|
||||
printk(" XXXXX SAMPLE BYTES %d\n", sample_bytes);
|
||||
switch (sample_bytes) {
|
||||
case 1:
|
||||
pcm_format = SNDRV_PCM_FORMAT_S8;
|
||||
|
@ -2500,7 +2537,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor
|
|||
|
||||
/*
|
||||
* parse the format descriptor and stores the possible sample rates
|
||||
* on the audioformat table.
|
||||
* on the audioformat table (audio class v1).
|
||||
*
|
||||
* @dev: usb device
|
||||
* @fp: audioformat record
|
||||
|
@ -2508,8 +2545,8 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor
|
|||
* @offset: the start offset of descriptor pointing the rate type
|
||||
* (7 for type I and II, 8 for type II)
|
||||
*/
|
||||
static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioformat *fp,
|
||||
unsigned char *fmt, int offset)
|
||||
static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audioformat *fp,
|
||||
unsigned char *fmt, int offset)
|
||||
{
|
||||
int nr_rates = fmt[offset];
|
||||
|
||||
|
@ -2564,14 +2601,86 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse the format descriptor and stores the possible sample rates
|
||||
* on the audioformat table (audio class v2).
|
||||
*/
|
||||
static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
|
||||
struct audioformat *fp,
|
||||
struct usb_host_interface *iface)
|
||||
{
|
||||
struct usb_device *dev = chip->dev;
|
||||
unsigned char tmp[2], *data;
|
||||
int i, nr_rates, data_size, ret = 0;
|
||||
|
||||
/* get the number of sample rates first by only fetching 2 bytes */
|
||||
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), CS_RANGE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
0x0100, chip->clock_id << 8, tmp, sizeof(tmp), 1000);
|
||||
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
nr_rates = (tmp[1] << 8) | tmp[0];
|
||||
data_size = 2 + 12 * nr_rates;
|
||||
data = kzalloc(data_size, GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* now get the full information */
|
||||
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), CS_RANGE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
0x0100, chip->clock_id << 8, data, data_size, 1000);
|
||||
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
|
||||
if (!fp->rate_table) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
fp->nr_rates = 0;
|
||||
fp->rate_min = fp->rate_max = 0;
|
||||
|
||||
for (i = 0; i < nr_rates; i++) {
|
||||
int rate = combine_quad(&data[2 + 12 * i]);
|
||||
|
||||
fp->rate_table[fp->nr_rates] = rate;
|
||||
if (!fp->rate_min || rate < fp->rate_min)
|
||||
fp->rate_min = rate;
|
||||
if (!fp->rate_max || rate > fp->rate_max)
|
||||
fp->rate_max = rate;
|
||||
fp->rates |= snd_pcm_rate_to_rate_bit(rate);
|
||||
fp->nr_rates++;
|
||||
}
|
||||
|
||||
err_free:
|
||||
kfree(data);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse the format type I and III descriptors
|
||||
*/
|
||||
static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *fp,
|
||||
int format, void *fmt_raw)
|
||||
static int parse_audio_format_i(struct snd_usb_audio *chip,
|
||||
struct audioformat *fp,
|
||||
int format, void *_fmt,
|
||||
struct usb_host_interface *iface)
|
||||
{
|
||||
int pcm_format;
|
||||
struct uac_format_type_i_discrete_descriptor *fmt = fmt_raw;
|
||||
struct usb_interface_descriptor *altsd = get_iface_desc(iface);
|
||||
struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
|
||||
int protocol = altsd->bInterfaceProtocol;
|
||||
int pcm_format, ret;
|
||||
|
||||
if (fmt->bFormatType == USB_FORMAT_TYPE_III) {
|
||||
/* FIXME: the format type is really IECxxx
|
||||
|
@ -2591,30 +2700,49 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *
|
|||
pcm_format = SNDRV_PCM_FORMAT_S16_LE;
|
||||
}
|
||||
} else {
|
||||
pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
|
||||
pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol);
|
||||
if (pcm_format < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
fp->format = pcm_format;
|
||||
fp->channels = fmt->bNrChannels;
|
||||
|
||||
/* gather possible sample rates */
|
||||
/* audio class v1 reports possible sample rates as part of the
|
||||
* proprietary class specific descriptor.
|
||||
* audio class v2 uses class specific EP0 range requests for that.
|
||||
*/
|
||||
switch (protocol) {
|
||||
case UAC_VERSION_1:
|
||||
fp->channels = fmt->bNrChannels;
|
||||
ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7);
|
||||
break;
|
||||
case UAC_VERSION_2:
|
||||
/* fp->channels is already set in this case */
|
||||
ret = parse_audio_format_rates_v2(chip, fp, iface);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fp->channels < 1) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
|
||||
chip->dev->devnum, fp->iface, fp->altsetting, fp->channels);
|
||||
return -1;
|
||||
}
|
||||
return parse_audio_format_rates(chip, fp, fmt_raw, 7);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse the format type II descriptor
|
||||
*/
|
||||
static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat *fp,
|
||||
int format, void *fmt_raw)
|
||||
static int parse_audio_format_ii(struct snd_usb_audio *chip,
|
||||
struct audioformat *fp,
|
||||
int format, void *_fmt,
|
||||
struct usb_host_interface *iface)
|
||||
{
|
||||
int brate, framesize;
|
||||
struct uac_format_type_ii_discrete_descriptor *fmt = fmt_raw;
|
||||
int brate, framesize, ret;
|
||||
struct usb_interface_descriptor *altsd = get_iface_desc(iface);
|
||||
int protocol = altsd->bInterfaceProtocol;
|
||||
|
||||
switch (format) {
|
||||
case USB_AUDIO_FORMAT_AC3:
|
||||
|
@ -2634,35 +2762,50 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat
|
|||
|
||||
fp->channels = 1;
|
||||
|
||||
brate = le16_to_cpu(fmt->wMaxBitRate);
|
||||
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
|
||||
snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
|
||||
fp->frame_size = framesize;
|
||||
return parse_audio_format_rates(chip, fp, fmt_raw, 8); /* fmt[8..] sample rates */
|
||||
switch (protocol) {
|
||||
case UAC_VERSION_1: {
|
||||
struct uac_format_type_ii_discrete_descriptor *fmt = _fmt;
|
||||
brate = le16_to_cpu(fmt->wMaxBitRate);
|
||||
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
|
||||
snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
|
||||
fp->frame_size = framesize;
|
||||
ret = parse_audio_format_rates_v1(chip, fp, _fmt, 8); /* fmt[8..] sample rates */
|
||||
break;
|
||||
}
|
||||
case UAC_VERSION_2: {
|
||||
struct uac_format_type_ii_ext_descriptor *fmt = _fmt;
|
||||
brate = le16_to_cpu(fmt->wMaxBitRate);
|
||||
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
|
||||
snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
|
||||
fp->frame_size = framesize;
|
||||
ret = parse_audio_format_rates_v2(chip, fp, iface);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
|
||||
int format, void *fmt_raw, int stream)
|
||||
int format, unsigned char *fmt, int stream,
|
||||
struct usb_host_interface *iface)
|
||||
{
|
||||
int err;
|
||||
/* we only parse the common header of all format types here,
|
||||
* so it is safe to take a type_i struct */
|
||||
struct uac_format_type_i_discrete_descriptor *fmt = fmt_raw;
|
||||
|
||||
switch (fmt->bFormatType) {
|
||||
switch (fmt[3]) {
|
||||
case USB_FORMAT_TYPE_I:
|
||||
case USB_FORMAT_TYPE_III:
|
||||
err = parse_audio_format_i(chip, fp, format, fmt);
|
||||
err = parse_audio_format_i(chip, fp, format, fmt, iface);
|
||||
break;
|
||||
case USB_FORMAT_TYPE_II:
|
||||
err = parse_audio_format_ii(chip, fp, format, fmt);
|
||||
err = parse_audio_format_ii(chip, fp, format, fmt, iface);
|
||||
break;
|
||||
default:
|
||||
snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
|
||||
chip->dev->devnum, fp->iface, fp->altsetting, fmt->bFormatType);
|
||||
chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]);
|
||||
return -1;
|
||||
}
|
||||
fp->fmt_type = fmt->bFormatType;
|
||||
fp->fmt_type = fmt[3];
|
||||
if (err < 0)
|
||||
return err;
|
||||
#if 1
|
||||
|
@ -2673,7 +2816,7 @@ static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp
|
|||
if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
|
||||
chip->usb_id == USB_ID(0x041e, 0x3020) ||
|
||||
chip->usb_id == USB_ID(0x041e, 0x3061)) {
|
||||
if (fmt->bFormatType == USB_FORMAT_TYPE_I &&
|
||||
if (fmt[3] == USB_FORMAT_TYPE_I &&
|
||||
fp->rates != SNDRV_PCM_RATE_48000 &&
|
||||
fp->rates != SNDRV_PCM_RATE_96000)
|
||||
return -1;
|
||||
|
@ -2702,10 +2845,10 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
struct usb_host_interface *alts;
|
||||
struct usb_interface_descriptor *altsd;
|
||||
int i, altno, err, stream;
|
||||
int format;
|
||||
int format = 0, num_channels = 0;
|
||||
struct audioformat *fp = NULL;
|
||||
unsigned char *fmt, *csep;
|
||||
int num;
|
||||
int num, protocol;
|
||||
|
||||
dev = chip->dev;
|
||||
|
||||
|
@ -2722,10 +2865,9 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
num = 4;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct uac_as_header_descriptor_v1 *as;
|
||||
|
||||
alts = &iface->altsetting[i];
|
||||
altsd = get_iface_desc(alts);
|
||||
protocol = altsd->bInterfaceProtocol;
|
||||
/* skip invalid one */
|
||||
if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
|
||||
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
|
||||
|
@ -2742,7 +2884,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
|
||||
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
||||
altno = altsd->bAlternateSetting;
|
||||
|
||||
|
||||
/* audiophile usb: skip altsets incompatible with device_setup
|
||||
*/
|
||||
if (chip->usb_id == USB_ID(0x0763, 0x2003) &&
|
||||
|
@ -2750,21 +2892,54 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
continue;
|
||||
|
||||
/* get audio formats */
|
||||
as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, AS_GENERAL);
|
||||
switch (protocol) {
|
||||
case UAC_VERSION_1: {
|
||||
struct uac_as_header_descriptor_v1 *as =
|
||||
snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, AS_GENERAL);
|
||||
|
||||
if (!as) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
if (!as) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (as->bLength < sizeof(*as)) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
format = le16_to_cpu(as->wFormatTag); /* remember the format value */
|
||||
break;
|
||||
}
|
||||
|
||||
if (as->bLength < sizeof(*as)) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
case UAC_VERSION_2: {
|
||||
struct uac_as_header_descriptor_v2 *as =
|
||||
snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, AS_GENERAL);
|
||||
|
||||
if (!as) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (as->bLength < sizeof(*as)) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
num_channels = as->bNrChannels;
|
||||
format = le32_to_cpu(as->bmFormats);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
format = le16_to_cpu(as->wFormatTag); /* remember the format value */
|
||||
default:
|
||||
snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n",
|
||||
dev->devnum, iface_no, altno, protocol);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* get format type */
|
||||
fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, FORMAT_TYPE);
|
||||
|
@ -2773,7 +2948,8 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
if (fmt[0] < 8) {
|
||||
if (((protocol == UAC_VERSION_1) && (fmt[0] < 8)) ||
|
||||
((protocol == UAC_VERSION_2) && (fmt[0] != 6))) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
|
@ -2787,6 +2963,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 &&
|
||||
fp && fp->altsetting == 1 && fp->channels == 1 &&
|
||||
fp->format == SNDRV_PCM_FORMAT_S16_LE &&
|
||||
protocol == UAC_VERSION_1 &&
|
||||
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
|
||||
fp->maxpacksize * 2)
|
||||
continue;
|
||||
|
@ -2815,6 +2992,8 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
|
||||
fp->datainterval = parse_datainterval(chip, alts);
|
||||
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
|
||||
/* num_channels is only set for v2 interfaces */
|
||||
fp->channels = num_channels;
|
||||
if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
|
||||
fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
|
||||
* (fp->maxpacksize & 0x7ff);
|
||||
|
@ -2850,7 +3029,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||
}
|
||||
|
||||
/* ok, let's parse further... */
|
||||
if (parse_audio_format(chip, fp, format, fmt, stream) < 0) {
|
||||
if (parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
|
||||
kfree(fp->rate_table);
|
||||
kfree(fp);
|
||||
continue;
|
||||
|
@ -2958,35 +3137,82 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
|
|||
{
|
||||
struct usb_device *dev = chip->dev;
|
||||
struct usb_host_interface *host_iface;
|
||||
struct uac_ac_header_descriptor_v1 *h1;
|
||||
struct usb_interface_descriptor *altsd;
|
||||
void *control_header;
|
||||
int i;
|
||||
int i, protocol;
|
||||
|
||||
/* find audiocontrol interface */
|
||||
host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
|
||||
control_header = snd_usb_find_csint_desc(host_iface->extra,
|
||||
host_iface->extralen,
|
||||
NULL, HEADER);
|
||||
altsd = get_iface_desc(host_iface);
|
||||
protocol = altsd->bInterfaceProtocol;
|
||||
|
||||
if (!control_header) {
|
||||
snd_printk(KERN_ERR "cannot find HEADER\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
h1 = control_header;
|
||||
switch (protocol) {
|
||||
case UAC_VERSION_1: {
|
||||
struct uac_ac_header_descriptor_v1 *h1 = control_header;
|
||||
|
||||
if (!h1->bInCollection) {
|
||||
snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
|
||||
return -EINVAL;
|
||||
if (!h1->bInCollection) {
|
||||
snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
|
||||
snd_printk(KERN_ERR "invalid HEADER (v1)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < h1->bInCollection; i++)
|
||||
snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
|
||||
snd_printk(KERN_ERR "invalid HEADER (v1)\n");
|
||||
return -EINVAL;
|
||||
case UAC_VERSION_2: {
|
||||
struct uac_clock_source_descriptor *cs;
|
||||
struct usb_interface_assoc_descriptor *assoc =
|
||||
usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
|
||||
|
||||
if (!assoc) {
|
||||
snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME: for now, we expect there is at least one clock source
|
||||
* descriptor and we always take the first one.
|
||||
* We should properly support devices with multiple clock sources,
|
||||
* clock selectors and sample rate conversion units. */
|
||||
|
||||
cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
|
||||
NULL, CLOCK_SOURCE);
|
||||
|
||||
if (!cs) {
|
||||
snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->clock_id = cs->bClockID;
|
||||
|
||||
for (i = 0; i < assoc->bInterfaceCount; i++) {
|
||||
int intf = assoc->bFirstInterface + i;
|
||||
|
||||
if (intf != ctrlif)
|
||||
snd_usb_create_stream(chip, ctrlif, intf);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < h1->bInCollection; i++)
|
||||
snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);
|
||||
default:
|
||||
snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -142,6 +142,9 @@ struct snd_usb_audio {
|
|||
int num_interfaces;
|
||||
int num_suspended_intf;
|
||||
|
||||
/* for audio class v2 */
|
||||
int clock_id;
|
||||
|
||||
struct list_head pcm_list; /* list of pcm streams */
|
||||
int pcm_devs;
|
||||
|
||||
|
|
Loading…
Reference in New Issue