Received: by 10.192.165.148 with SMTP id m20csp214406imm; Thu, 3 May 2018 18:28:23 -0700 (PDT) X-Google-Smtp-Source: AB8JxZo4fwHBpXzh5i7OfnWLApc9AfaREcip9paWMFEZGZhRKM/rSWbwA/OmUcHRLFQxv3mQvRc7 X-Received: by 2002:a63:3385:: with SMTP id z127-v6mr2795791pgz.217.1525397303521; Thu, 03 May 2018 18:28:23 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1525397303; cv=none; d=google.com; s=arc-20160816; b=Qb4BLilgNsF9uNuyc06m5T+A29DAGlV1s871kEtZG2d5tnqcZrRZBNmU2h28rSZwzV SQuDn/jwEVyBBE5wHSu+9jCpOXbkw3cMjCa7gTfp4g7IJqyJdvHZEC2s+yGVaI17uQG/ D2OPoqQ2yHfFv7yWQ617RkUZpt2uuhaUje8KPJehABlwcHYwGh8T5Xo7sy1h/OWypxUq ujOg54/ajCDYptKNoMPrDdToQSKeM6SWShhYOQSKaLst1uZffzvU9vxMFIbhZHJVT7rA fVAouJzeLOpTIW4QxyGYxslGFw7P2/gNQCijWjZy4a/b+CREaNd9/Oe7I7iMiAB1yvw/ 03Pw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=mTDwbCCAgVGuhjKLI1fv8k84Vtt7fiORiHVM5xcC+rU=; b=UHAnn0CIITozo8rJnM+8I1uDB+aqgmGBlMKrMkw5YK/2FNMDLrKRjWxKW8zQ3gIXN/ M5nbvQZbjI1MnrgcBSNVe2RrjmmT5mnkR64KJW6Eep6Uq/l1gITBoerYb+b+bpgfNi4t 3MycuRkihxy2JqlVo0lkA/eUfzWcBUcd/y7xKWBPPBd5X8Uv2B9fGcwvFwK7+5O3bLv/ X6D5WPGS+HdKoJUEueyxJCC8zMhEV1t1OpWmZNRPGx52IBE/u0UDx2qt0d0cAyfzjMKZ zHYb8LR6h2MuZg8+uF2Gu8QjAfDRimFy6hTt7399DlneHNJCRlAfRSmYsNoG/McTValm 2CLw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=Px4a+3K5; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id b89-v6si14761566plb.262.2018.05.03.18.28.09; Thu, 03 May 2018 18:28:23 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=Px4a+3K5; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751785AbeEDB0J (ORCPT + 99 others); Thu, 3 May 2018 21:26:09 -0400 Received: from mail-lf0-f65.google.com ([209.85.215.65]:35249 "EHLO mail-lf0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751432AbeEDBYy (ORCPT ); Thu, 3 May 2018 21:24:54 -0400 Received: by mail-lf0-f65.google.com with SMTP id y72-v6so14901509lfd.2 for ; Thu, 03 May 2018 18:24:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=mTDwbCCAgVGuhjKLI1fv8k84Vtt7fiORiHVM5xcC+rU=; b=Px4a+3K5sXhbdTEI/c1NxJBVzqzUEzcdBA5MEbAgoVeTkTL5uMDy86hl8aCqfuRTVj gxThzTui3e/I004nnxavclGSsaZgJsy5hNwTxKXPMpzVpCZzlig1LZf8LgPpGYUl90I/ dTwQ+gCzwSuaKQJfK7fnibp9XhG9caXYngHVlA7JIFuDQL+R0AtR9eWaEcPPc7ohlrQq oLxVpMsbfap6ArPYjVp45qBMfduKNwsxe8D0zL2sAawSxvJXSsgm1m1tZC3fcgLQDMB5 Ovg/R1Mbu6M/G6dkfEgb1QqywJHncHUL3zwdroq+Hh8rRHfP/a50vKyfF3bdXCGpCSAH oEBQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=mTDwbCCAgVGuhjKLI1fv8k84Vtt7fiORiHVM5xcC+rU=; b=l7jZmRtRMgzGxVFXiPEXV/zfRSx8vaf2y2i3aiik0AavBatbs1Gp3z7ow1ulpbf3+K nssKK9j85Pwh7vzQGCSYa3+W+e68I26cpRD8mMtW/J1KBuIt+C+CvHkwLD0UePwt9Fta jmYITehyqYTlX/vLJSYF2QMzk7OpFZ+m4rx+AywRJfAvN6Da9ZZgLIRdQYCfj08Rm13k JEGIhpgSw9lHbS/iKZlkPcmrFbM6+cAmBYNxo7wsEfdE9/E0tjYKkcgQ3ZkKp8Ofh6hX /spHUvJqqOksEQ2B8Eq/Trakug4P/MF/H0Qo3iygbaOHTWaKnDrDf0h4s3XscMifdPs0 gyIQ== X-Gm-Message-State: ALQs6tD/0PlHZeGy8bwbOjPGP5+peUHozX73rpeVPgDiR+mEvxb3SnQo R8NCFtngpPJ60KpeD+BMRVmZSsxFVdA= X-Received: by 2002:a2e:4082:: with SMTP id r2-v6mr18236860lje.48.1525397092492; Thu, 03 May 2018 18:24:52 -0700 (PDT) Received: from localhost ([62.216.57.27]) by smtp.gmail.com with ESMTPSA id h13-v6sm2263513lfg.20.2018.05.03.18.24.51 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Thu, 03 May 2018 18:24:51 -0700 (PDT) From: Ruslan Bilovol To: Takashi Iwai Cc: Jorge , Andrew Chant , Greg Kroah-Hartman , alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 2/7] ALSA: usb: stream: refactor uac1/2 audio interface parsing Date: Fri, 4 May 2018 04:23:59 +0300 Message-Id: <1525397044-15080-3-git-send-email-ruslan.bilovol@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1525397044-15080-1-git-send-email-ruslan.bilovol@gmail.com> References: <1525397044-15080-1-git-send-email-ruslan.bilovol@gmail.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support. Move class-specific parts of uac1/2 parsing to separate function which now produce audioformat structure that is ready to be fed to snd_usb_add_audio_stream(). This also broke Blue Microphones workaround (which relies on audioformat decoded from previous altsetting) into two parts: prepare quirk flag analyzing previous altsetting then use it with current altsetting. Signed-off-by: Ruslan Bilovol --- sound/usb/stream.c | 333 +++++++++++++++++++++++++++++------------------------ 1 file changed, 185 insertions(+), 148 deletions(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 8ec0a52..3369226 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -656,6 +656,156 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, return fp; } +static struct audioformat * +snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int protocol, int iface_no, int altset_idx, + int altno, int stream, int bm_quirk) +{ + struct usb_device *dev = chip->dev; + struct uac_format_type_i_continuous_descriptor *fmt; + unsigned int num_channels = 0, chconfig = 0; + struct audioformat *fp; + int clock = 0; + u64 format; + + /* get audio formats */ + if (protocol == UAC_VERSION_1) { + struct uac1_as_header_descriptor *as = + snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_AS_GENERAL); + struct uac_input_terminal_descriptor *iterm; + + if (!as) { + dev_err(&dev->dev, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_AS_GENERAL desc\n", + iface_no, altno); + return NULL; + } + + format = le16_to_cpu(as->wFormatTag); /* remember the format value */ + + iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (iterm) { + num_channels = iterm->bNrChannels; + chconfig = le16_to_cpu(iterm->wChannelConfig); + } + } else { /* UAC_VERSION_2 */ + struct uac2_input_terminal_descriptor *input_term; + struct uac2_output_terminal_descriptor *output_term; + struct uac2_as_header_descriptor *as = + snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_AS_GENERAL); + + if (!as) { + dev_err(&dev->dev, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_AS_GENERAL desc\n", + iface_no, altno); + return NULL; + } + + num_channels = as->bNrChannels; + format = le32_to_cpu(as->bmFormats); + chconfig = le32_to_cpu(as->bmChannelConfig); + + /* + * lookup the terminal associated to this interface + * to extract the clock + */ + input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (input_term) { + clock = input_term->bCSourceID; + if (!chconfig && (num_channels == input_term->bNrChannels)) + chconfig = le32_to_cpu(input_term->bmChannelConfig); + goto found_clock; + } + + output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (output_term) { + clock = output_term->bCSourceID; + goto found_clock; + } + + dev_err(&dev->dev, + "%u:%d : bogus bTerminalLink %d\n", + iface_no, altno, as->bTerminalLink); + 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; + + fp = audio_format_alloc_init(chip, alts, protocol, iface_no, + altset_idx, altno, num_channels, clock); + if (!fp) + return ERR_PTR(-ENOMEM); + + fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, + iface_no); + + /* some quirks for attributes here */ + snd_usb_audioformat_attributes_quirk(chip, fp, stream); + + /* ok, let's parse further... */ + if (snd_usb_parse_audio_format(chip, fp, format, + fmt, stream) < 0) { + kfree(fp->rate_table); + kfree(fp); + return NULL; + } + + /* Create chmap */ + if (fp->channels != num_channels) + chconfig = 0; + + fp->chmap = convert_chmap(fp->channels, chconfig, protocol); + + return fp; +} int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) { @@ -663,14 +813,13 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) struct usb_interface *iface; struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; + struct uac3_as_header_descriptor *as = NULL; int i, altno, err, stream; u64 format = 0; unsigned int num_channels = 0; struct audioformat *fp = NULL; int num, protocol, clock = 0; - struct uac_format_type_i_continuous_descriptor *fmt = NULL; struct snd_pcm_chmap_elem *chmap_v3 = NULL; - unsigned int chconfig; dev = chip->dev; @@ -719,98 +868,41 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) protocol <= 2) protocol = UAC_VERSION_1; - chconfig = 0; - /* get audio formats */ switch (protocol) { default: dev_dbg(&dev->dev, "%u:%d: unknown interface protocol %#02x, assuming v1\n", iface_no, altno, protocol); protocol = UAC_VERSION_1; /* fall through */ - - case UAC_VERSION_1: { - struct uac1_as_header_descriptor *as = - snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); - struct uac_input_terminal_descriptor *iterm; - - if (!as) { - dev_err(&dev->dev, - "%u:%d : UAC_AS_GENERAL descriptor not found\n", - iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_AS_GENERAL desc\n", - iface_no, altno); - continue; - } - - format = le16_to_cpu(as->wFormatTag); /* remember the format value */ - - iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (iterm) { - num_channels = iterm->bNrChannels; - chconfig = le16_to_cpu(iterm->wChannelConfig); - } - - break; - } - + case UAC_VERSION_1: + /* fall through */ case UAC_VERSION_2: { - struct uac2_input_terminal_descriptor *input_term; - struct uac2_output_terminal_descriptor *output_term; - struct uac2_as_header_descriptor *as = - snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); - - if (!as) { - dev_err(&dev->dev, - "%u:%d : UAC_AS_GENERAL descriptor not found\n", - iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_AS_GENERAL desc\n", - iface_no, altno); - continue; - } + int bm_quirk = 0; - num_channels = as->bNrChannels; - format = le32_to_cpu(as->bmFormats); - chconfig = le32_to_cpu(as->bmChannelConfig); - - /* lookup the terminal associated to this interface - * to extract the clock */ - input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (input_term) { - clock = input_term->bCSourceID; - if (!chconfig && (num_channels == input_term->bNrChannels)) - chconfig = le32_to_cpu(input_term->bmChannelConfig); - break; - } - - output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (output_term) { - clock = output_term->bCSourceID; - break; - } + /* + * 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; - dev_err(&dev->dev, - "%u:%d : bogus bTerminalLink %d\n", - iface_no, altno, as->bTerminalLink); - continue; + fp = snd_usb_get_audioformat_uac12(chip, alts, protocol, + iface_no, i, altno, + stream, bm_quirk); + break; } - case UAC_VERSION_3: { struct uac3_input_terminal_descriptor *input_term; struct uac3_output_terminal_descriptor *output_term; - struct uac3_as_header_descriptor *as; struct uac3_cluster_header_descriptor *cluster; struct uac3_hc_descriptor_header hc_header; u16 cluster_id, wLength; @@ -923,40 +1015,12 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) } if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { - /* 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); - continue; - } - 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); + if (!fp) continue; - } + else if (IS_ERR(fp)) + return PTR_ERR(fp); - /* - * 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. - */ - if (fmt->bNrChannels == 1 && - fmt->bSubframeSize == 2 && - 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) - continue; + goto skip_uac3; } fp = audio_format_alloc_init(chip, alts, protocol, iface_no, i, @@ -967,45 +1031,18 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); - - /* some quirks for attributes here */ - snd_usb_audioformat_attributes_quirk(chip, fp, stream); + fp->chmap = chmap_v3; /* ok, let's parse further... */ - if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { - if (snd_usb_parse_audio_format(chip, fp, format, - fmt, stream) < 0) { - kfree(fp->rate_table); - kfree(fp); - fp = NULL; - continue; - } - } else { - struct uac3_as_header_descriptor *as; - - as = snd_usb_find_csint_desc(alts->extra, - alts->extralen, - NULL, UAC_AS_GENERAL); - - if (snd_usb_parse_audio_format_v3(chip, fp, as, - stream) < 0) { - kfree(fp->rate_table); - kfree(fp); - fp = NULL; - continue; - } + if (snd_usb_parse_audio_format_v3(chip, fp, as, + stream) < 0) { + kfree(fp->rate_table); + kfree(fp); + fp = NULL; + continue; } - /* Create chmap */ - if (fp->channels != num_channels) - chconfig = 0; - - if (protocol == UAC_VERSION_3) - fp->chmap = chmap_v3; - else - fp->chmap = convert_chmap(fp->channels, chconfig, - protocol); - +skip_uac3: dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { -- 1.9.1