if (!subs->num_formats) return; /* not initialized */
list_for_each_entry_safe(fp, n, &subs->fmt_list, list)
audioformat_free(fp);
kfree(subs->str_pd);
snd_media_stream_delete(subs);
}
/* * free a usb stream instance
*/ staticvoid snd_usb_audio_stream_free(struct snd_usb_stream *stream)
{
free_substream(&stream->substream[0]);
free_substream(&stream->substream[1]);
list_del(&stream->list);
kfree(stream);
}
if (subs->cur_audiofmt)
chmap = subs->cur_audiofmt->chmap; if (chmap) { for (i = 0; i < chmap->channels; i++)
ucontrol->value.integer.value[i] = chmap->map[i];
} for (; i < subs->channels_max; i++)
ucontrol->value.integer.value[i] = 0; return 0;
}
/* create a chmap kctl assigned to the given USB substream */ staticint add_chmap(struct snd_pcm *pcm, int stream, struct snd_usb_substream *subs)
{ struct audioformat *fp; struct snd_pcm_chmap *chmap; struct snd_kcontrol *kctl; int err;
list_for_each_entry(fp, &subs->fmt_list, list) if (fp->chmap) goto ok; /* no chmap is found */ return 0;
/* convert from USB ChannelConfig bits to ALSA chmap element */ staticstruct snd_pcm_chmap_elem *convert_chmap(int channels, unsignedint bits, int protocol)
{ staticconstunsignedint uac1_maps[] = {
SNDRV_CHMAP_FL, /* left front */
SNDRV_CHMAP_FR, /* right front */
SNDRV_CHMAP_FC, /* center front */
SNDRV_CHMAP_LFE, /* LFE */
SNDRV_CHMAP_RL, /* left surround */
SNDRV_CHMAP_RR, /* right surround */
SNDRV_CHMAP_FLC, /* left of center */
SNDRV_CHMAP_FRC, /* right of center */
SNDRV_CHMAP_RC, /* surround */
SNDRV_CHMAP_SL, /* side left */
SNDRV_CHMAP_SR, /* side right */
SNDRV_CHMAP_TC, /* top */
0 /* terminator */
}; staticconstunsignedint uac2_maps[] = {
SNDRV_CHMAP_FL, /* front left */
SNDRV_CHMAP_FR, /* front right */
SNDRV_CHMAP_FC, /* front center */
SNDRV_CHMAP_LFE, /* LFE */
SNDRV_CHMAP_RL, /* back left */
SNDRV_CHMAP_RR, /* back right */
SNDRV_CHMAP_FLC, /* front left of center */
SNDRV_CHMAP_FRC, /* front right of center */
SNDRV_CHMAP_RC, /* back center */
SNDRV_CHMAP_SL, /* side left */
SNDRV_CHMAP_SR, /* side right */
SNDRV_CHMAP_TC, /* top center */
SNDRV_CHMAP_TFL, /* top front left */
SNDRV_CHMAP_TFC, /* top front center */
SNDRV_CHMAP_TFR, /* top front right */
SNDRV_CHMAP_TRL, /* top back left */
SNDRV_CHMAP_TRC, /* top back center */
SNDRV_CHMAP_TRR, /* top back right */
SNDRV_CHMAP_TFLC, /* top front left of center */
SNDRV_CHMAP_TFRC, /* top front right of center */
SNDRV_CHMAP_LLFE, /* left LFE */
SNDRV_CHMAP_RLFE, /* right LFE */
SNDRV_CHMAP_TSL, /* top side left */
SNDRV_CHMAP_TSR, /* top side right */
SNDRV_CHMAP_BC, /* bottom center */
SNDRV_CHMAP_RLC, /* back left of center */
SNDRV_CHMAP_RRC, /* back right of center */
0 /* terminator */
}; struct snd_pcm_chmap_elem *chmap; constunsignedint *maps; int c;
if (channels > ARRAY_SIZE(chmap->map)) return NULL;
chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); if (!chmap) return NULL;
if (bits) { for (; bits && *maps; maps++, bits >>= 1) { if (bits & 1)
chmap->map[c++] = *maps; if (c == chmap->channels) break;
}
} else { /* If we're missing wChannelConfig, then guess something
to make sure the channel map is not skipped entirely */ if (channels == 1)
chmap->map[c++] = SNDRV_CHMAP_MONO; else for (; c < channels && *maps; maps++)
chmap->map[c++] = *maps;
}
for (; c < channels; c++)
chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
return chmap;
}
/* UAC3 device stores channels information in Cluster Descriptors */ staticstruct
snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
*cluster)
{ unsignedint channels = cluster->bNrChannels; struct snd_pcm_chmap_elem *chmap; void *p = cluster; int len, c;
if (channels > ARRAY_SIZE(chmap->map)) return NULL;
chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); if (!chmap) return NULL;
len = le16_to_cpu(cluster->wLength);
c = 0;
p += sizeof(*cluster);
len -= sizeof(*cluster);
/* * TODO: this conversion is not complete, update it * after adding UAC3 values to asound.h
*/ switch (is->bChRelationship) { case UAC3_CH_MONO:
map = SNDRV_CHMAP_MONO; break; case UAC3_CH_LEFT: case UAC3_CH_FRONT_LEFT: case UAC3_CH_HEADPHONE_LEFT:
map = SNDRV_CHMAP_FL; break; case UAC3_CH_RIGHT: case UAC3_CH_FRONT_RIGHT: case UAC3_CH_HEADPHONE_RIGHT:
map = SNDRV_CHMAP_FR; break; case UAC3_CH_FRONT_CENTER:
map = SNDRV_CHMAP_FC; break; case UAC3_CH_FRONT_LEFT_OF_CENTER:
map = SNDRV_CHMAP_FLC; break; case UAC3_CH_FRONT_RIGHT_OF_CENTER:
map = SNDRV_CHMAP_FRC; break; case UAC3_CH_SIDE_LEFT:
map = SNDRV_CHMAP_SL; break; case UAC3_CH_SIDE_RIGHT:
map = SNDRV_CHMAP_SR; break; case UAC3_CH_BACK_LEFT:
map = SNDRV_CHMAP_RL; break; case UAC3_CH_BACK_RIGHT:
map = SNDRV_CHMAP_RR; break; case UAC3_CH_BACK_CENTER:
map = SNDRV_CHMAP_RC; break; case UAC3_CH_BACK_LEFT_OF_CENTER:
map = SNDRV_CHMAP_RLC; break; case UAC3_CH_BACK_RIGHT_OF_CENTER:
map = SNDRV_CHMAP_RRC; break; case UAC3_CH_TOP_CENTER:
map = SNDRV_CHMAP_TC; break; case UAC3_CH_TOP_FRONT_LEFT:
map = SNDRV_CHMAP_TFL; break; case UAC3_CH_TOP_FRONT_RIGHT:
map = SNDRV_CHMAP_TFR; break; case UAC3_CH_TOP_FRONT_CENTER:
map = SNDRV_CHMAP_TFC; break; case UAC3_CH_TOP_FRONT_LOC:
map = SNDRV_CHMAP_TFLC; break; case UAC3_CH_TOP_FRONT_ROC:
map = SNDRV_CHMAP_TFRC; break; case UAC3_CH_TOP_SIDE_LEFT:
map = SNDRV_CHMAP_TSL; break; case UAC3_CH_TOP_SIDE_RIGHT:
map = SNDRV_CHMAP_TSR; break; case UAC3_CH_TOP_BACK_LEFT:
map = SNDRV_CHMAP_TRL; break; case UAC3_CH_TOP_BACK_RIGHT:
map = SNDRV_CHMAP_TRR; break; case UAC3_CH_TOP_BACK_CENTER:
map = SNDRV_CHMAP_TRC; break; case UAC3_CH_BOTTOM_CENTER:
map = SNDRV_CHMAP_BC; break; case UAC3_CH_LOW_FREQUENCY_EFFECTS:
map = SNDRV_CHMAP_LFE; break; case UAC3_CH_LFE_LEFT:
map = SNDRV_CHMAP_LLFE; break; case UAC3_CH_LFE_RIGHT:
map = SNDRV_CHMAP_RLFE; break; case UAC3_CH_RELATIONSHIP_UNDEFINED: default:
map = SNDRV_CHMAP_UNKNOWN; break;
}
chmap->map[c++] = map;
}
p += cs_len;
len -= cs_len;
}
if (channels < c)
pr_err("%s: channel number mismatch\n", __func__);
chmap->channels = channels;
for (; c < channels; c++)
chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
return chmap;
}
/* * add this endpoint to the chip instance. * if a stream with the same endpoint already exists, append to it. * if not, create a new pcm stream. note, fp is added to the substream * fmt_list and will be freed on the chip instance release. do not free * fp or do remove it from the substream fmt_list to avoid double-free.
*/ staticint __snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, struct audioformat *fp, struct snd_usb_power_domain *pd)
/* * Keep using head insertion for M-Audio Audiophile USB (tm) which has a * fix to swap capture stream order in conf/cards/USB-audio.conf
*/ if (chip->usb_id == USB_ID(0x0763, 0x2003))
list_add(&as->list, &chip->pcm_list); else
list_add_tail(&as->list, &chip->pcm_list);
staticint parse_uac_endpoint_attributes(struct snd_usb_audio *chip, struct usb_host_interface *alts, int protocol, int iface_no)
{ /* parsed with a v1 header here. that's ok as we only look at the
* header first which is the same for both versions */ struct uac_iso_endpoint_descriptor *csep; struct usb_interface_descriptor *altsd = get_iface_desc(alts); int attributes = 0;
/* Creamware Noah has this descriptor after the 2nd endpoint */ if (!csep && altsd->bNumEndpoints >= 2)
csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
/* * If we can't locate the USB_DT_CS_ENDPOINT descriptor in the extra * bytes after the first endpoint, go search the entire interface. * Some devices have it directly *before* the standard endpoint.
*/ if (!csep)
csep = snd_usb_find_desc(alts->extra, alts->extralen, NULL, USB_DT_CS_ENDPOINT);
/* emulate the endpoint attributes of a v1 device */ if (csep2->bmControls & UAC2_CONTROL_PITCH)
attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL;
} else { /* UAC_VERSION_3 */ struct uac3_iso_endpoint_descriptor *csep3 =
(struct uac3_iso_endpoint_descriptor *) csep;
if (csep3->bLength < sizeof(*csep3)) goto error; /* emulate the endpoint attributes of a v1 device */ if (le32_to_cpu(csep3->bmControls) & UAC2_CONTROL_PITCH)
attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL;
}
return attributes;
error:
usb_audio_warn(chip, "%u:%d : no or invalid class specific endpoint descriptor\n",
iface_no, altsd->bAlternateSetting); return 0;
}
/* find an input terminal descriptor (either UAC1 or UAC2) with the given * terminal id
*/ staticvoid *
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, int terminal_id, int protocol)
{ struct uac2_input_terminal_descriptor *term = NULL;
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_INPUT_TERMINAL))) { if (!snd_usb_validate_audio_desc(term, protocol)) continue; if (term->bTerminalID == terminal_id) return term;
}
return NULL;
}
staticvoid *
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, int terminal_id, int protocol)
{ /* OK to use with both UAC2 and UAC3 */ struct uac2_output_terminal_descriptor *term = NULL;
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_OUTPUT_TERMINAL))) { if (!snd_usb_validate_audio_desc(term, protocol)) continue; if (term->bTerminalID == terminal_id) return term;
}
return NULL;
}
staticstruct audioformat *
audio_format_alloc_init(struct snd_usb_audio *chip, struct usb_host_interface *alts, int protocol, int iface_no, int altset_idx, int altno, int num_channels, int clock)
{ struct audioformat *fp;
fp = kzalloc(sizeof(*fp), GFP_KERNEL); if (!fp) return NULL;
found_clock: /* get format type */
fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen,
NULL, UAC_FORMAT_TYPE); if (!fmt) {
dev_err(&dev->dev, "%u:%d : no UAC_FORMAT_TYPE desc\n",
iface_no, altno); return NULL;
} if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8))
|| ((protocol == UAC_VERSION_2) &&
(fmt->bLength < 6))) {
dev_err(&dev->dev, "%u:%d : invalid UAC_FORMAT_TYPE desc\n",
iface_no, altno); return NULL;
}
/* * Blue Microphones workaround: The last altsetting is * identical with the previous one, except for a larger * packet size, but is actually a mislabeled two-channel * setting; ignore it. * * Part 2: analyze quirk flag and format
*/ if (bm_quirk && fmt->bNrChannels == 1 && fmt->bSubframeSize == 2) return NULL;
cluster_id = le16_to_cpu(as->wClusterDescrID); if (!cluster_id) {
dev_err(&dev->dev, "%u:%d : no cluster descriptor\n",
iface_no, altno); return NULL;
}
/* * Get number of channels and channel map through * High Capability Cluster Descriptor * * First step: get High Capability header and * read size of Cluster Descriptor
*/
err = snd_usb_ctl_msg(chip->dev,
usb_rcvctrlpipe(chip->dev, 0),
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
cluster_id,
snd_usb_ctrl_intf(ctrl_intf),
&hc_header, sizeof(hc_header)); if (err < 0) return ERR_PTR(err); elseif (err != sizeof(hc_header)) {
dev_err(&dev->dev, "%u:%d : can't get High Capability descriptor\n",
iface_no, altno); return ERR_PTR(-EIO);
}
/* * lookup the terminal associated to this interface * to extract the clock
*/
input_term = snd_usb_find_input_terminal_descriptor(ctrl_intf,
as->bTerminalLink,
UAC_VERSION_3); if (input_term) {
clock = input_term->bCSourceID; goto found_clock;
}
/* parse the interface's altsettings */
iface = usb_ifnum_to_if(dev, iface_no);
num = iface->num_altsetting;
/* * Dallas DS4201 workaround: It presents 5 altsettings, but the last * one misses syncpipe, and does not produce any sound.
*/ if (chip->usb_id == USB_ID(0x04fa, 0x4201) && num >= 4)
num = 4;
for (i = 0; i < num; i++) {
alts = &iface->altsetting[i];
altsd = get_iface_desc(alts);
protocol = altsd->bInterfaceProtocol; /* skip invalid one */ if (((altsd->bInterfaceClass != USB_CLASS_AUDIO ||
(altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC)) &&
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
altsd->bNumEndpoints < 1 ||
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0) continue; /* must be isochronous */ if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
USB_ENDPOINT_XFER_ISOC) continue; /* check direction */
stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
altno = altsd->bAlternateSetting;
if (snd_usb_apply_interface_quirk(chip, iface_no, altno)) continue;
/* * Roland audio streaming interfaces are marked with protocols * 0/1/2, but are UAC 1 compatible.
*/ if (USB_ID_VENDOR(chip->usb_id) == 0x0582 &&
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
protocol <= 2)
protocol = UAC_VERSION_1;
switch (protocol) { default:
dev_dbg(&dev->dev, "%u:%d: unknown interface protocol %#02x, assuming v1\n",
iface_no, altno, protocol);
protocol = UAC_VERSION_1;
fallthrough; case UAC_VERSION_1: case UAC_VERSION_2: { int bm_quirk = 0;
/* * Blue Microphones workaround: The last altsetting is * identical with the previous one, except for a larger * packet size, but is actually a mislabeled two-channel * setting; ignore it. * * Part 1: prepare quirk flag
*/ if (altno == 2 && num == 3 &&
fp && fp->altsetting == 1 && fp->channels == 1 &&
fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
protocol == UAC_VERSION_1 &&
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
fp->maxpacksize * 2)
bm_quirk = 1;
fp = snd_usb_get_audioformat_uac12(chip, alts, protocol,
iface_no, i, altno,
stream, bm_quirk); break;
} case UAC_VERSION_3:
fp = snd_usb_get_audioformat_uac3(chip, alts, &pd,
iface_no, i, altno, stream); break;
}
if (!fp) continue; elseif (IS_ERR(fp)) return PTR_ERR(fp);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.