From patchwork Sat Sep 28 15:08:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814698 Received: from mail-qk1-f177.google.com (mail-qk1-f177.google.com [209.85.222.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A4C3A3C08A; Sat, 28 Sep 2024 15:09:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536162; cv=none; b=DXoVqwAA5qfnukHkfFQRF6LuTRknBrGEp+NAVzhlOpXKwSHdrznv/NiaXlN6DyQAXaFSPlcDtSFR7VAFjwEOhd4+FdO4AvETBSkitaedEf0UA4SGMK7tHPVUofuELt3YXPu/L7QOJ6DLB0K1UBlySpo3PtGNHN+iRZUNY81Xcas= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536162; c=relaxed/simple; bh=Qt6MiSN7hcJhIbF/xI9yzS3PeHn2vFP7W3G9V78dp3w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=l5aysG4oKlUzANOq33cj8AkViEIqX7s5zFRZqNv0vyyhGmv5ZZZmhbqSKYiPtZgDhjODCdtXZZPtMSsyT9ekgnB38h7boeU+hAJdGU3Ov3cSKqr3XYPQ/5cenGjQzO6Y0O9OnyBeUuxkeWihw37lQuVnsbepDrcngpCMqNnRHS4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=IP0/n/QE; arc=none smtp.client-ip=209.85.222.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IP0/n/QE" Received: by mail-qk1-f177.google.com with SMTP id af79cd13be357-7a9a62c6734so26985585a.3; Sat, 28 Sep 2024 08:09:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536159; x=1728140959; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=+92T4M0KkpvzaSGDK4NkdBg9rU8udS1E0OYHQLIDTH8=; b=IP0/n/QE+mDFaReOR9lDSlXjCwhi9IzrUbgCq77mqzZwldsKcv2ktg4zvgcCQokr/p XZLr6w1On6J9LwfMArHL0CY6/q6TvziExkOIabW5qJ4buMtVsEZnEXEVzm0jLVEs27El uRBA0bXB7gm46FXrTyhvJWcQbCJvqjYJ4F8+ZZC/OUjBhHFcwA5oUZNY7YzcEi7yZXko dPq4TosoOhG86QKhAUgYHnWiSEvE896r0dqALbJ1sLaSyRbihDA+eVHHbqtLc+7qbb3i kMLijXJT+TfPZtyuT0I2LvRfR5lQoT7K5vXRV03pj5lu1YGEfnOZtYhHTUWOD6TwOiTJ ms6g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536159; x=1728140959; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+92T4M0KkpvzaSGDK4NkdBg9rU8udS1E0OYHQLIDTH8=; b=Kf04pxcJh44gNP95xJvo6Qd8WjoRNiSha0gbxogIHjtcfo97OMt4hI3M9QsZz+hAHn qplkWRuIjRiKAXcumQt4bX90UevgQwOMt4Mg075Gd7tzF7kAZ2MOB/rR6EewZJMzofjR 0apOwV0Iw+vaoHnjBMfnMOi8bT955VDbMlBJ1XbD2XvvYDZ2+9H3eDOBhO2uiU5SUGWH Y4z9n8C1QorXPWu0QXLrEBlYNtCGRQs0HZc0n/atki9ujLhr2UCj9HTEdAu85v7J14iI sG8xvcDHUB/50g6fSIgBi44ppiMyOBjTgYdbJcose2O97IbmXuM3gQrxXgqT46CnUuHQ Edlw== X-Forwarded-Encrypted: i=1; AJvYcCWRnB1GI2yQ4wDp5oe4ZBfWstH3HcfjHKYthu2SDYS3ahCbFRo+x1Jpl9Df2zYF+tTu8uOMdj4Wk9M=@vger.kernel.org, AJvYcCXmyJvoSGUYpu9IyCO4DQh3g/Hn/CF4ssiZvSfeX1RBqcclfqbUvMDhRxLewj+K+0ttoKegxA57Lqz6udvu@vger.kernel.org X-Gm-Message-State: AOJu0YxuBtgr3iZniaRofwNT7enp5L75ZcOJIGbZaLNlfu6sko990g7j KwOt4YNDa5QPvODruF5kOJourwA0c6zljUJvVZzZczf6v0CajyT7Jv3ZK4rs8wU= X-Google-Smtp-Source: AGHT+IEDd4w4H7Q7DYWVzaJjwpit+wPVgXAuJtL6pnE/ho7t1kqM7y6929uHpfk7tUZZB4lV3GfRHA== X-Received: by 2002:a05:622a:1a0d:b0:458:4126:ec4e with SMTP id d75a77b69052e-45c9f301e26mr44196931cf.11.1727536158662; Sat, 28 Sep 2024 08:09:18 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:17 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 01/14] usb: gadget: f_uac: Refactor common configfs attribute defines used in UAC1/2. Date: Sat, 28 Sep 2024 11:08:52 -0400 Message-ID: <20240928150905.2616313-2-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff Consolidate a bunch of common #defines used by both f_uac1 and f_uac2 to make it easier to maintain. Only the #defines moved and the same static functions get created in each module as before. Formatters are also #define which makes slightly smaller code. Signed-off-by: Chris Wulff --- drivers/usb/gadget/function/f_uac1.c | 148 +-------------- drivers/usb/gadget/function/f_uac2.c | 207 +-------------------- drivers/usb/gadget/function/u_uac_utils.h | 217 ++++++++++++++++++++++ 3 files changed, 235 insertions(+), 337 deletions(-) create mode 100644 drivers/usb/gadget/function/u_uac_utils.h diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index c87e74afc881..eeedcfa61fa1 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -19,6 +19,7 @@ #include "u_audio.h" #include "u_uac1.h" +#include "u_uac_utils.h" /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ #define UAC1_CHANNEL_MASK 0x0FFF @@ -1516,151 +1517,18 @@ static struct configfs_item_operations f_uac1_item_ops = { .release = f_uac1_attr_release, }; -#define uac1_kstrtou32 kstrtou32 -#define uac1_kstrtos16 kstrtos16 -#define uac1_kstrtobool(s, base, res) kstrtobool((s), (res)) - -static const char *u32_fmt = "%u\n"; -static const char *s16_fmt = "%hd\n"; -static const char *bool_fmt = "%u\n"; - +#define UAC1_ATTR_TO_OPTS struct f_uac1_opts *opts = to_f_uac1_opts(item) #define UAC1_ATTRIBUTE(type, name) \ -static ssize_t f_uac1_opts_##name##_show( \ - struct config_item *item, \ - char *page) \ -{ \ - struct f_uac1_opts *opts = to_f_uac1_opts(item); \ - int result; \ - \ - mutex_lock(&opts->lock); \ - result = sprintf(page, type##_fmt, opts->name); \ - mutex_unlock(&opts->lock); \ - \ - return result; \ -} \ - \ -static ssize_t f_uac1_opts_##name##_store( \ - struct config_item *item, \ - const char *page, size_t len) \ -{ \ - struct f_uac1_opts *opts = to_f_uac1_opts(item); \ - int ret; \ - type num; \ - \ - mutex_lock(&opts->lock); \ - if (opts->refcnt) { \ - ret = -EBUSY; \ - goto end; \ - } \ - \ - ret = uac1_kstrto##type(page, 0, &num); \ - if (ret) \ - goto end; \ - \ - opts->name = num; \ - ret = len; \ - \ -end: \ - mutex_unlock(&opts->lock); \ - return ret; \ -} \ - \ -CONFIGFS_ATTR(f_uac1_opts_, name) + UAC_ATTRIBUTE(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts, \ + opts->lock, opts->refcnt, type, name) #define UAC1_RATE_ATTRIBUTE(name) \ -static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ - char *page) \ -{ \ - struct f_uac1_opts *opts = to_f_uac1_opts(item); \ - int result = 0; \ - int i; \ - \ - mutex_lock(&opts->lock); \ - page[0] = '\0'; \ - for (i = 0; i < UAC_MAX_RATES; i++) { \ - if (opts->name##s[i] == 0) \ - break; \ - result += sprintf(page + strlen(page), "%u,", \ - opts->name##s[i]); \ - } \ - if (strlen(page) > 0) \ - page[strlen(page) - 1] = '\n'; \ - mutex_unlock(&opts->lock); \ - \ - return result; \ -} \ - \ -static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \ - const char *page, size_t len) \ -{ \ - struct f_uac1_opts *opts = to_f_uac1_opts(item); \ - char *split_page = NULL; \ - int ret = -EINVAL; \ - char *token; \ - u32 num; \ - int i; \ - \ - mutex_lock(&opts->lock); \ - if (opts->refcnt) { \ - ret = -EBUSY; \ - goto end; \ - } \ - \ - i = 0; \ - memset(opts->name##s, 0x00, sizeof(opts->name##s)); \ - split_page = kstrdup(page, GFP_KERNEL); \ - while ((token = strsep(&split_page, ",")) != NULL) { \ - ret = kstrtou32(token, 0, &num); \ - if (ret) \ - goto end; \ - \ - opts->name##s[i++] = num; \ - ret = len; \ - }; \ - \ -end: \ - kfree(split_page); \ - mutex_unlock(&opts->lock); \ - return ret; \ -} \ - \ -CONFIGFS_ATTR(f_uac1_opts_, name) + UAC_RATE_ATTRIBUTE(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts, \ + opts->lock, opts->refcnt, name) #define UAC1_ATTRIBUTE_STRING(name) \ -static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ - char *page) \ -{ \ - struct f_uac1_opts *opts = to_f_uac1_opts(item); \ - int result; \ - \ - mutex_lock(&opts->lock); \ - result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \ - mutex_unlock(&opts->lock); \ - \ - return result; \ -} \ - \ -static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \ - const char *page, size_t len) \ -{ \ - struct f_uac1_opts *opts = to_f_uac1_opts(item); \ - int ret = 0; \ - \ - mutex_lock(&opts->lock); \ - if (opts->refcnt) { \ - ret = -EBUSY; \ - goto end; \ - } \ - \ - ret = scnprintf(opts->name, min(sizeof(opts->name), len), \ - "%s", page); \ - \ -end: \ - mutex_unlock(&opts->lock); \ - return ret; \ -} \ - \ -CONFIGFS_ATTR(f_uac1_opts_, name) + UAC_ATTRIBUTE_STRING(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts, \ + opts->lock, opts->refcnt, name) UAC1_ATTRIBUTE(u32, c_chmask); UAC1_RATE_ATTRIBUTE(c_srate); diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 1cdda44455b3..2142a0951f71 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -17,6 +17,7 @@ #include "u_audio.h" #include "u_uac2.h" +#include "u_uac_utils.h" /* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */ #define UAC2_CHANNEL_MASK 0x07FFFFFF @@ -1877,210 +1878,22 @@ static struct configfs_item_operations f_uac2_item_ops = { .release = f_uac2_attr_release, }; -#define uac2_kstrtou8 kstrtou8 -#define uac2_kstrtou32 kstrtou32 -#define uac2_kstrtos16 kstrtos16 -#define uac2_kstrtobool(s, base, res) kstrtobool((s), (res)) - -static const char *u8_fmt = "%u\n"; -static const char *u32_fmt = "%u\n"; -static const char *s16_fmt = "%hd\n"; -static const char *bool_fmt = "%u\n"; - +#define UAC2_ATTR_TO_OPTS struct f_uac2_opts *opts = to_f_uac2_opts(item) #define UAC2_ATTRIBUTE(type, name) \ -static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ - char *page) \ -{ \ - struct f_uac2_opts *opts = to_f_uac2_opts(item); \ - int result; \ - \ - mutex_lock(&opts->lock); \ - result = sprintf(page, type##_fmt, opts->name); \ - mutex_unlock(&opts->lock); \ - \ - return result; \ -} \ - \ -static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ - const char *page, size_t len) \ -{ \ - struct f_uac2_opts *opts = to_f_uac2_opts(item); \ - int ret; \ - type num; \ - \ - mutex_lock(&opts->lock); \ - if (opts->refcnt) { \ - ret = -EBUSY; \ - goto end; \ - } \ - \ - ret = uac2_kstrto##type(page, 0, &num); \ - if (ret) \ - goto end; \ - \ - opts->name = num; \ - ret = len; \ - \ -end: \ - mutex_unlock(&opts->lock); \ - return ret; \ -} \ - \ -CONFIGFS_ATTR(f_uac2_opts_, name) + UAC_ATTRIBUTE(f_uac2_opts, UAC2_ATTR_TO_OPTS, opts, \ + opts->lock, opts->refcnt, type, name) #define UAC2_ATTRIBUTE_SYNC(name) \ -static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ - char *page) \ -{ \ - struct f_uac2_opts *opts = to_f_uac2_opts(item); \ - int result; \ - char *str; \ - \ - mutex_lock(&opts->lock); \ - switch (opts->name) { \ - case USB_ENDPOINT_SYNC_ASYNC: \ - str = "async"; \ - break; \ - case USB_ENDPOINT_SYNC_ADAPTIVE: \ - str = "adaptive"; \ - break; \ - default: \ - str = "unknown"; \ - break; \ - } \ - result = sprintf(page, "%s\n", str); \ - mutex_unlock(&opts->lock); \ - \ - return result; \ -} \ - \ -static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ - const char *page, size_t len) \ -{ \ - struct f_uac2_opts *opts = to_f_uac2_opts(item); \ - int ret = 0; \ - \ - mutex_lock(&opts->lock); \ - if (opts->refcnt) { \ - ret = -EBUSY; \ - goto end; \ - } \ - \ - if (!strncmp(page, "async", 5)) \ - opts->name = USB_ENDPOINT_SYNC_ASYNC; \ - else if (!strncmp(page, "adaptive", 8)) \ - opts->name = USB_ENDPOINT_SYNC_ADAPTIVE; \ - else { \ - ret = -EINVAL; \ - goto end; \ - } \ - \ - ret = len; \ - \ -end: \ - mutex_unlock(&opts->lock); \ - return ret; \ -} \ - \ -CONFIGFS_ATTR(f_uac2_opts_, name) + UAC_ATTRIBUTE_SYNC(f_uac2_opts, UAC2_ATTR_TO_OPTS, opts, \ + opts->lock, opts->refcnt, name) #define UAC2_RATE_ATTRIBUTE(name) \ -static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ - char *page) \ -{ \ - struct f_uac2_opts *opts = to_f_uac2_opts(item); \ - int result = 0; \ - int i; \ - \ - mutex_lock(&opts->lock); \ - page[0] = '\0'; \ - for (i = 0; i < UAC_MAX_RATES; i++) { \ - if (opts->name##s[i] == 0) \ - break; \ - result += sprintf(page + strlen(page), "%u,", \ - opts->name##s[i]); \ - } \ - if (strlen(page) > 0) \ - page[strlen(page) - 1] = '\n'; \ - mutex_unlock(&opts->lock); \ - \ - return result; \ -} \ - \ -static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ - const char *page, size_t len) \ -{ \ - struct f_uac2_opts *opts = to_f_uac2_opts(item); \ - char *split_page = NULL; \ - int ret = -EINVAL; \ - char *token; \ - u32 num; \ - int i; \ - \ - mutex_lock(&opts->lock); \ - if (opts->refcnt) { \ - ret = -EBUSY; \ - goto end; \ - } \ - \ - i = 0; \ - memset(opts->name##s, 0x00, sizeof(opts->name##s)); \ - split_page = kstrdup(page, GFP_KERNEL); \ - while ((token = strsep(&split_page, ",")) != NULL) { \ - ret = kstrtou32(token, 0, &num); \ - if (ret) \ - goto end; \ - \ - opts->name##s[i++] = num; \ - ret = len; \ - }; \ - \ -end: \ - kfree(split_page); \ - mutex_unlock(&opts->lock); \ - return ret; \ -} \ - \ -CONFIGFS_ATTR(f_uac2_opts_, name) + UAC_RATE_ATTRIBUTE(f_uac2_opts, UAC2_ATTR_TO_OPTS, opts, \ + opts->lock, opts->refcnt, name) #define UAC2_ATTRIBUTE_STRING(name) \ -static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ - char *page) \ -{ \ - struct f_uac2_opts *opts = to_f_uac2_opts(item); \ - int result; \ - \ - mutex_lock(&opts->lock); \ - result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \ - mutex_unlock(&opts->lock); \ - \ - return result; \ -} \ - \ -static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ - const char *page, size_t len) \ -{ \ - struct f_uac2_opts *opts = to_f_uac2_opts(item); \ - int ret = 0; \ - \ - mutex_lock(&opts->lock); \ - if (opts->refcnt) { \ - ret = -EBUSY; \ - goto end; \ - } \ - \ - if (len && page[len - 1] == '\n') \ - len--; \ - \ - ret = scnprintf(opts->name, min(sizeof(opts->name), len + 1), \ - "%s", page); \ - \ -end: \ - mutex_unlock(&opts->lock); \ - return ret; \ -} \ - \ -CONFIGFS_ATTR(f_uac2_opts_, name) + UAC_ATTRIBUTE_STRING(f_uac2_opts, UAC2_ATTR_TO_OPTS, opts, \ + opts->lock, opts->refcnt, name) UAC2_ATTRIBUTE(u32, p_chmask); UAC2_RATE_ATTRIBUTE(p_srate); diff --git a/drivers/usb/gadget/function/u_uac_utils.h b/drivers/usb/gadget/function/u_uac_utils.h new file mode 100644 index 000000000000..5f88e31103dd --- /dev/null +++ b/drivers/usb/gadget/function/u_uac_utils.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * u_uac_utils.h -- Utilities for UAC1/2 function driver + * + * Copyright (C) 2024 + * Author: Chris Wulff + */ + +#ifndef __U_UAC_UTILS_H +#define __U_UAC_UTILS_H + +#define uac_kstrtou8 kstrtou8 +#define uac_kstrtos16 kstrtos16 +#define uac_kstrtou32 kstrtou32 +#define uac_kstrtobool(s, base, res) kstrtobool((s), (res)) + +#define u8_FMT "%u\n" +#define u32_FMT "%u\n" +#define s16_FMT "%hd\n" +#define bool_FMT "%u\n" + +#define UAC_ATTRIBUTE(prefix, to_struct, var, lock, refcnt, type, name) \ +static ssize_t prefix##_##name##_show(struct config_item *item, \ + char *page) \ +{ \ + to_struct; \ + int result; \ + \ + mutex_lock(&lock); \ + result = sprintf(page, type##_FMT, var->name); \ + mutex_unlock(&lock); \ + \ + return result; \ +} \ + \ +static ssize_t prefix##_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + to_struct; \ + int ret; \ + type num; \ + \ + mutex_lock(&lock); \ + if (refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = uac_kstrto##type(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + var->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(prefix##_, name) + +#define UAC_RATE_ATTRIBUTE(prefix, to_struct, var, lock, refcnt, name) \ +static ssize_t prefix##_##name##_show(struct config_item *item, \ + char *page) \ +{ \ + to_struct; \ + int result = 0; \ + int i; \ + \ + mutex_lock(&lock); \ + page[0] = '\0'; \ + for (i = 0; i < UAC_MAX_RATES; i++) { \ + if (var->name##s[i] == 0) \ + break; \ + result += sprintf(page + strlen(page), "%u,", \ + var->name##s[i]); \ + } \ + if (strlen(page) > 0) \ + page[strlen(page) - 1] = '\n'; \ + mutex_unlock(&lock); \ + \ + return result; \ +} \ + \ +static ssize_t prefix##_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + to_struct; \ + char *split_page = NULL; \ + int ret = -EINVAL; \ + char *token; \ + u32 num; \ + int i; \ + \ + mutex_lock(&lock); \ + if (refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + i = 0; \ + memset(var->name##s, 0x00, sizeof(var->name##s)); \ + split_page = kstrdup(page, GFP_KERNEL); \ + while ((token = strsep(&split_page, ",")) != NULL) { \ + ret = kstrtou32(token, 0, &num); \ + if (ret) \ + goto end; \ + \ + var->name##s[i++] = num; \ + ret = len; \ + }; \ + \ +end: \ + kfree(split_page); \ + mutex_unlock(&lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(prefix##_, name) + +#define UAC_ATTRIBUTE_STRING(prefix, to_struct, var, lock, refcnt, name) \ +static ssize_t prefix##_##name##_show(struct config_item *item, \ + char *page) \ +{ \ + to_struct; \ + int result; \ + \ + mutex_lock(&lock); \ + result = scnprintf(page, sizeof(var->name), "%s", var->name); \ + mutex_unlock(&lock); \ + \ + return result; \ +} \ + \ +static ssize_t prefix##_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + to_struct; \ + int ret = 0; \ + \ + mutex_lock(&lock); \ + if (refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + if (len && page[len - 1] == '\n') \ + len--; \ + \ + ret = scnprintf(var->name, min(sizeof(var->name), len + 1), \ + "%s", page); \ + \ +end: \ + mutex_unlock(&lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(prefix##_, name) + +#define UAC_ATTRIBUTE_SYNC(prefix, to_struct, var, lock, refcnt, name) \ +static ssize_t prefix##_##name##_show(struct config_item *item, \ + char *page) \ +{ \ + to_struct; \ + int result; \ + char *str; \ + \ + mutex_lock(&lock); \ + switch (var->name) { \ + case USB_ENDPOINT_SYNC_ASYNC: \ + str = "async"; \ + break; \ + case USB_ENDPOINT_SYNC_ADAPTIVE: \ + str = "adaptive"; \ + break; \ + default: \ + str = "unknown"; \ + break; \ + } \ + result = sprintf(page, "%s\n", str); \ + mutex_unlock(&lock); \ + \ + return result; \ +} \ + \ +static ssize_t prefix##_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + to_struct; \ + int ret = 0; \ + \ + mutex_lock(&lock); \ + if (refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + if (!strncmp(page, "async", 5)) \ + var->name = USB_ENDPOINT_SYNC_ASYNC; \ + else if (!strncmp(page, "adaptive", 8)) \ + var->name = USB_ENDPOINT_SYNC_ADAPTIVE; \ + else { \ + ret = -EINVAL; \ + goto end; \ + } \ + \ + ret = len; \ + \ +end: \ + mutex_unlock(&lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(prefix##_, name) + +#endif /* __U_UAC_UTILS_H */ From patchwork Sat Sep 28 15:08:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814699 Received: from mail-qk1-f181.google.com (mail-qk1-f181.google.com [209.85.222.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0FEEB61FCE; Sat, 28 Sep 2024 15:09:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536165; cv=none; b=EGgcjHMLx1tG9MdjHOKYG813GFdvEqnknwB1dO4aWId5lQ5hMBQuQZ5KYn/IP22gtA0fa81cM1LAvH4UUazqw0iVDCle3qngJ9rLOES5dQIE2TCPQIweTp9EGmDkDBvZPW5mjMyOh34sCE48pDPFDgVQxdfF54ODRwtlzYreU2U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536165; c=relaxed/simple; bh=IyJz+iqPMoLhHut/jqmGwsV7ix4D1Ize338TAuomV9c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oncPWToX7X+hFqLQrq8/xKxnxTk3R+YOlGxYqM5CIQ+TsQY2ncO7Tv0QBnDazm7k9XhX7cXX/34nwWR+cQJegH5c6lAOpcWURiSM5kBJDc1uh8HVEObBfEqKy1+3cHc6JpQQHezOVFfU+ZAjbmZ47d4zDqffC/VCPWhdD5M+Yes= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=kF6EOpYd; arc=none smtp.client-ip=209.85.222.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="kF6EOpYd" Received: by mail-qk1-f181.google.com with SMTP id af79cd13be357-7a9dbdc8baaso26970585a.0; Sat, 28 Sep 2024 08:09:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536161; x=1728140961; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=AN8bBg4/BtNlz+HQauYfU+FQJfwrHCBrf2UGuGo3/5w=; b=kF6EOpYd7wx0AdrKCjz5EzhT++kDrnTmatktHVqnMSiPyfShfLbP1wzv3YNQgyq1rv vn8zga076WyxgtQydYw6qtjyJpz+6rlp6JLc/mNjTcdAZ8tmmhKFfKGsvQzn7IaopVgJ 7f/YJ4B/HsuJ08woLFPOLIofPJrpQU6IvTasx4FNVxFzRZIuQEnFBkBqe6r25idJ3tHY j2ICAT+RYYg8gYUijoCIEuksZM7MSrDcQ729Jt1lstFSnTj4tkTfWdBSlOwf7+X8EgqT PklSICLdRGa3imhNxGyP9pT5vcQd0mdeX6ug7xbCh8nEcQnVnPFlZ8W34A+b3/PBx/B5 dJOw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536161; x=1728140961; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=AN8bBg4/BtNlz+HQauYfU+FQJfwrHCBrf2UGuGo3/5w=; b=Cw7gY3gcmo6+7GE2hF09b68U0YNlLtW7OaVeoeZ0+KEGyrt5KKpD+HHcdLwOwN+hyc HjvX3RQfiXHOs0VGYTCwu+kyR4Yt5RkEkoPH884EfdNbKaaf81p1Ok3j3gKcl9k7RiQq DtBUsfVmTOHzvePvE4pwZctAo4mEOShA8TF61Lu0j5eZqwDSKZE0eG2pJN/2PINK8lwn dHH/PlYgVZKYX2hS4j/tCdBhQ6Jhb9NjSSsOQoRF8MflZYqYsd0xgrsC9ge5cYzcR+kW n+bUb0n9KfLRpSXDbDiQ+MviXCYFKpw0d+y9GMKyQ/njPipW2NEnASjIUNtuPo/sWdd2 LjZA== X-Forwarded-Encrypted: i=1; AJvYcCVdUDAxSSauDYwTgLyLPvALPBYDzA+j+8RtEKY9UpudKHdUC4SkaHv+6NPIC/izSEQeW77X09n6TAC3E8mD@vger.kernel.org, AJvYcCXvCHWWCZ9RUzDHodowdm/geGzNeeWolbcBc1mJcPbbzp+OeJ5UH1W8KXgodYJ4YT4N9mdvifi/XDE=@vger.kernel.org X-Gm-Message-State: AOJu0Yy64wVNa/9XKA3XMuLGRJKM7Sy6KfDkfQCc+WEQRcYXcsXH9+WH cU3kYxT1qfXSHAqIZX6XagJjSkHh9KQI421bzjfsTA6n40ZN3obZ4anh7MVcYzQ= X-Google-Smtp-Source: AGHT+IHaLZrCTIbzpzEWmpbBnoNblHkLEWA5bgRcxrdSy2w810sJF+7rN+mbLSf9Pn6NvPRdvGHCaQ== X-Received: by 2002:a05:622a:609:b0:458:3297:806f with SMTP id d75a77b69052e-45c9f300aa3mr44139931cf.10.1727536161405; Sat, 28 Sep 2024 08:09:21 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:20 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 02/14] usb: gadget: f_uac1: Fix fs/hs/ss descriptors to have correct values Date: Sat, 28 Sep 2024 11:08:53 -0400 Message-ID: <20240928150905.2616313-3-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff This fixes two problems with the UAC1 descriptors. bInterval for full-speed is now set to 1. Prior to this fix all speeds were set to 4. Super-speed descriptors are now built dynamically the same way as the other speeds. The prior implementation had a fixed set of descriptors and didn't take the presence of volume function units into account. Both of these changes need the refactoring of setup_descriptor to have a separate setup_header which is called for each speed. This implementation closely follows what was done in f_uac2. Fixes: b8fb6db6cb04 ("usb: f_uac1: adds support for SS and SSP") Signed-off-by: Chris Wulff --- drivers/usb/gadget/function/f_uac1.c | 248 ++++++++++++++++++++++----- 1 file changed, 202 insertions(+), 46 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index eeedcfa61fa1..f68d444d1961 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -128,7 +128,25 @@ static struct uac_feature_unit_descriptor *in_feature_unit_desc; static struct uac_feature_unit_descriptor *out_feature_unit_desc; /* AC IN Interrupt Endpoint */ -static struct usb_endpoint_descriptor ac_int_ep_desc = { +static struct usb_endpoint_descriptor fs_ac_int_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 1, +}; + +static struct usb_endpoint_descriptor hs_ac_int_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 4, +}; + +static struct usb_endpoint_descriptor ss_ac_int_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -137,6 +155,14 @@ static struct usb_endpoint_descriptor ac_int_ep_desc = { .bInterval = 4, }; +static struct usb_ss_ep_comp_descriptor ss_ac_int_ep_desc_comp = { + .bLength = sizeof(ss_ac_int_ep_desc_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(2), +}; + /* B.4.1 Standard AS Interface Descriptor */ static struct usb_interface_descriptor as_out_interface_alt_0_desc = { .bLength = USB_DT_INTERFACE_SIZE, @@ -208,7 +234,17 @@ static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = { }; /* Standard ISO OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor as_out_ep_desc = { +static struct usb_endpoint_descriptor fs_as_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + .bInterval = 1, +}; + +static struct usb_endpoint_descriptor hs_as_out_ep_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, @@ -238,8 +274,18 @@ static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = { .bSamFreqType = 0, /* filled on rate setup */ }; -/* Standard ISO OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor as_in_ep_desc = { +/* Standard ISO IN Endpoint Descriptor */ +static struct usb_endpoint_descriptor fs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + .bInterval = 1, +}; + +static struct usb_endpoint_descriptor hs_as_in_ep_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -249,7 +295,7 @@ static struct usb_endpoint_descriptor as_in_ep_desc = { .bInterval = 4, }; -/* Class-specific AS ISO OUT Endpoint Descriptor */ +/* Class-specific AS ISO IN Endpoint Descriptor */ static struct uac_iso_endpoint_descriptor as_iso_in_desc = { .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, @@ -259,7 +305,41 @@ static struct uac_iso_endpoint_descriptor as_iso_in_desc = { .wLockDelay = 0, }; -static struct usb_descriptor_header *f_audio_desc[] = { +static struct usb_descriptor_header *f_audio_fs_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&io_out_ot_desc, + (struct usb_descriptor_header *)&out_feature_unit_desc, + + (struct usb_descriptor_header *)&io_in_it_desc, + (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&in_feature_unit_desc, + + (struct usb_descriptor_header *)&fs_ac_int_ep_desc, + + (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_out_header_desc, + + (struct usb_descriptor_header *)&as_out_type_i_desc, + + (struct usb_descriptor_header *)&fs_as_out_ep_desc, + (struct usb_descriptor_header *)&as_iso_out_desc, + + (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_in_header_desc, + + (struct usb_descriptor_header *)&as_in_type_i_desc, + + (struct usb_descriptor_header *)&fs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct usb_descriptor_header *f_audio_hs_desc[] = { (struct usb_descriptor_header *)&ac_interface_desc, (struct usb_descriptor_header *)&ac_header_desc, @@ -271,7 +351,7 @@ static struct usb_descriptor_header *f_audio_desc[] = { (struct usb_descriptor_header *)&usb_in_ot_desc, (struct usb_descriptor_header *)&in_feature_unit_desc, - (struct usb_descriptor_header *)&ac_int_ep_desc, + (struct usb_descriptor_header *)&hs_ac_int_ep_desc, (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, @@ -279,7 +359,7 @@ static struct usb_descriptor_header *f_audio_desc[] = { (struct usb_descriptor_header *)&as_out_type_i_desc, - (struct usb_descriptor_header *)&as_out_ep_desc, + (struct usb_descriptor_header *)&hs_as_out_ep_desc, (struct usb_descriptor_header *)&as_iso_out_desc, (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, @@ -288,7 +368,7 @@ static struct usb_descriptor_header *f_audio_desc[] = { (struct usb_descriptor_header *)&as_in_type_i_desc, - (struct usb_descriptor_header *)&as_in_ep_desc, + (struct usb_descriptor_header *)&hs_as_in_ep_desc, (struct usb_descriptor_header *)&as_iso_in_desc, NULL, }; @@ -312,7 +392,7 @@ static struct usb_ss_ep_comp_descriptor ss_as_out_ep_desc_comp = { /* wBytesPerInterval = DYNAMIC */ }; -/* Standard ISO OUT Endpoint Descriptor */ +/* Standard ISO IN Endpoint Descriptor */ static struct usb_endpoint_descriptor ss_as_in_ep_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -337,8 +417,14 @@ static struct usb_descriptor_header *f_audio_ss_desc[] = { (struct usb_descriptor_header *)&usb_out_it_desc, (struct usb_descriptor_header *)&io_out_ot_desc, + (struct usb_descriptor_header *)&out_feature_unit_desc, + (struct usb_descriptor_header *)&io_in_it_desc, (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&in_feature_unit_desc, + + (struct usb_descriptor_header *)&ss_ac_int_ep_desc, + (struct usb_descriptor_header *)&ss_ac_int_ep_desc_comp, (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, @@ -346,7 +432,6 @@ static struct usb_descriptor_header *f_audio_ss_desc[] = { (struct usb_descriptor_header *)&as_out_type_i_desc, - //(struct usb_descriptor_header *)&as_out_ep_desc, (struct usb_descriptor_header *)&ss_as_out_ep_desc, (struct usb_descriptor_header *)&ss_as_out_ep_desc_comp, (struct usb_descriptor_header *)&as_iso_out_desc, @@ -357,7 +442,6 @@ static struct usb_descriptor_header *f_audio_ss_desc[] = { (struct usb_descriptor_header *)&as_in_type_i_desc, - //(struct usb_descriptor_header *)&as_in_ep_desc, (struct usb_descriptor_header *)&ss_as_in_ep_desc, (struct usb_descriptor_header *)&ss_as_in_ep_desc_comp, (struct usb_descriptor_header *)&as_iso_in_desc, @@ -1082,6 +1166,10 @@ uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts) /* Use macro to overcome line length limitation */ #define USBDHDR(p) (struct usb_descriptor_header *)(p) +static void setup_headers(struct f_uac1_opts *opts, + struct usb_descriptor_header **headers, + enum usb_device_speed speed); + static void setup_descriptor(struct f_uac1_opts *opts) { /* patch descriptors */ @@ -1137,44 +1225,90 @@ static void setup_descriptor(struct f_uac1_opts *opts) ac_header_desc->wTotalLength = cpu_to_le16(len); } + setup_headers(opts, f_audio_fs_desc, USB_SPEED_FULL); + setup_headers(opts, f_audio_hs_desc, USB_SPEED_HIGH); + setup_headers(opts, f_audio_ss_desc, USB_SPEED_SUPER); +} + +static void setup_headers(struct f_uac1_opts *opts, + struct usb_descriptor_header **headers, + enum usb_device_speed speed) +{ + struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL; + struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL; + struct usb_ss_ep_comp_descriptor *ep_int_desc_comp = NULL; + struct usb_endpoint_descriptor *epout_desc; + struct usb_endpoint_descriptor *epin_desc; + struct usb_endpoint_descriptor *ep_int_desc; + int i; + + switch (speed) { + case USB_SPEED_FULL: + epout_desc = &fs_as_out_ep_desc; + epin_desc = &fs_as_in_ep_desc; + ep_int_desc = &fs_ac_int_ep_desc; + break; + case USB_SPEED_HIGH: + epout_desc = &hs_as_out_ep_desc; + epin_desc = &hs_as_in_ep_desc; + ep_int_desc = &hs_ac_int_ep_desc; + break; + default: + epout_desc = &ss_as_out_ep_desc; + epin_desc = &ss_as_in_ep_desc; + epout_desc_comp = &ss_as_out_ep_desc_comp; + epin_desc_comp = &ss_as_in_ep_desc_comp; + ep_int_desc = &ss_ac_int_ep_desc; + ep_int_desc_comp = &ss_ac_int_ep_desc_comp; + } + i = 0; - f_audio_desc[i++] = USBDHDR(&ac_interface_desc); - f_audio_desc[i++] = USBDHDR(ac_header_desc); + headers[i++] = USBDHDR(&ac_interface_desc); + headers[i++] = USBDHDR(ac_header_desc); if (EPOUT_EN(opts)) { - f_audio_desc[i++] = USBDHDR(&usb_out_it_desc); - f_audio_desc[i++] = USBDHDR(&io_out_ot_desc); + headers[i++] = USBDHDR(&usb_out_it_desc); + headers[i++] = USBDHDR(&io_out_ot_desc); if (FUOUT_EN(opts)) - f_audio_desc[i++] = USBDHDR(out_feature_unit_desc); + headers[i++] = USBDHDR(out_feature_unit_desc); } if (EPIN_EN(opts)) { - f_audio_desc[i++] = USBDHDR(&io_in_it_desc); - f_audio_desc[i++] = USBDHDR(&usb_in_ot_desc); + headers[i++] = USBDHDR(&io_in_it_desc); + headers[i++] = USBDHDR(&usb_in_ot_desc); if (FUIN_EN(opts)) - f_audio_desc[i++] = USBDHDR(in_feature_unit_desc); + headers[i++] = USBDHDR(in_feature_unit_desc); } - if (FUOUT_EN(opts) || FUIN_EN(opts)) - f_audio_desc[i++] = USBDHDR(&ac_int_ep_desc); + if (FUOUT_EN(opts) || FUIN_EN(opts)) { + headers[i++] = USBDHDR(ep_int_desc); + if (ep_int_desc_comp) + headers[i++] = USBDHDR(ep_int_desc_comp); + } if (EPOUT_EN(opts)) { - f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_0_desc); - f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_1_desc); - f_audio_desc[i++] = USBDHDR(&as_out_header_desc); - f_audio_desc[i++] = USBDHDR(&as_out_type_i_desc); - f_audio_desc[i++] = USBDHDR(&as_out_ep_desc); - f_audio_desc[i++] = USBDHDR(&as_iso_out_desc); + headers[i++] = USBDHDR(&as_out_interface_alt_0_desc); + headers[i++] = USBDHDR(&as_out_interface_alt_1_desc); + headers[i++] = USBDHDR(&as_out_header_desc); + headers[i++] = USBDHDR(&as_out_type_i_desc); + headers[i++] = USBDHDR(epout_desc); + if (epout_desc_comp) + headers[i++] = USBDHDR(epout_desc_comp); + + headers[i++] = USBDHDR(&as_iso_out_desc); } if (EPIN_EN(opts)) { - f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_0_desc); - f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_1_desc); - f_audio_desc[i++] = USBDHDR(&as_in_header_desc); - f_audio_desc[i++] = USBDHDR(&as_in_type_i_desc); - f_audio_desc[i++] = USBDHDR(&as_in_ep_desc); - f_audio_desc[i++] = USBDHDR(&as_iso_in_desc); + headers[i++] = USBDHDR(&as_in_interface_alt_0_desc); + headers[i++] = USBDHDR(&as_in_interface_alt_1_desc); + headers[i++] = USBDHDR(&as_in_header_desc); + headers[i++] = USBDHDR(&as_in_type_i_desc); + headers[i++] = USBDHDR(epin_desc); + if (epin_desc_comp) + headers[i++] = USBDHDR(epin_desc_comp); + + headers[i++] = USBDHDR(&as_iso_in_desc); } - f_audio_desc[i] = NULL; + headers[i] = NULL; } static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) @@ -1410,44 +1544,66 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) /* allocate AC interrupt endpoint */ if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) { - ep = usb_ep_autoconfig(cdev->gadget, &ac_int_ep_desc); + ep = usb_ep_autoconfig(cdev->gadget, &fs_ac_int_ep_desc); if (!ep) goto err_free_fu; + + hs_ac_int_ep_desc.bEndpointAddress = fs_ac_int_ep_desc.bEndpointAddress; + ss_ac_int_ep_desc.bEndpointAddress = fs_ac_int_ep_desc.bEndpointAddress; + uac1->int_ep = ep; - uac1->int_ep->desc = &ac_int_ep_desc; + uac1->int_ep->desc = &fs_ac_int_ep_desc; ac_interface_desc.bNumEndpoints = 1; } /* allocate instance-specific endpoints */ if (EPOUT_EN(audio_opts)) { - ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); + ep = usb_ep_autoconfig(cdev->gadget, &fs_as_out_ep_desc); if (!ep) goto err_free_fu; - ss_as_out_ep_desc.bEndpointAddress = as_out_ep_desc.bEndpointAddress; + + hs_as_out_ep_desc.bEndpointAddress = fs_as_out_ep_desc.bEndpointAddress; + ss_as_out_ep_desc.bEndpointAddress = fs_as_out_ep_desc.bEndpointAddress; + ss_as_out_ep_desc_comp.wBytesPerInterval = ss_as_out_ep_desc.wMaxPacketSize; + audio->out_ep = ep; - audio->out_ep->desc = &as_out_ep_desc; + audio->out_ep->desc = &fs_as_out_ep_desc; } if (EPIN_EN(audio_opts)) { - ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc); + ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); if (!ep) goto err_free_fu; - ss_as_in_ep_desc.bEndpointAddress = as_in_ep_desc.bEndpointAddress; + + hs_as_in_ep_desc.bEndpointAddress = fs_as_in_ep_desc.bEndpointAddress; + ss_as_in_ep_desc.bEndpointAddress = fs_as_in_ep_desc.bEndpointAddress; + ss_as_in_ep_desc_comp.wBytesPerInterval = ss_as_in_ep_desc.wMaxPacketSize; + audio->in_ep = ep; - audio->in_ep->desc = &as_in_ep_desc; + audio->in_ep->desc = &fs_as_in_ep_desc; } setup_descriptor(audio_opts); /* copy descriptors, and track endpoint copies */ - status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, f_audio_ss_desc, + status = usb_assign_descriptors(f, f_audio_fs_desc, f_audio_hs_desc, f_audio_ss_desc, f_audio_ss_desc); if (status) goto err_free_fu; - audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize); - audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize); + audio->in_ep_maxpsize = max_t(u16, + le16_to_cpu(fs_as_in_ep_desc.wMaxPacketSize), + le16_to_cpu(hs_as_in_ep_desc.wMaxPacketSize)); + audio->out_ep_maxpsize = max_t(u16, + le16_to_cpu(fs_as_out_ep_desc.wMaxPacketSize), + le16_to_cpu(hs_as_out_ep_desc.wMaxPacketSize)); + + audio->in_ep_maxpsize = max_t(u16, audio->in_ep_maxpsize, + le16_to_cpu(ss_as_in_ep_desc.wMaxPacketSize)); + audio->out_ep_maxpsize = max_t(u16, audio->out_ep_maxpsize, + le16_to_cpu(ss_as_out_ep_desc.wMaxPacketSize)); + audio->params.c_chmask = audio_opts->c_chmask; memcpy(audio->params.c_srates, audio_opts->c_srates, sizeof(audio->params.c_srates)); From patchwork Sat Sep 28 15:08:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814700 Received: from mail-qv1-f43.google.com (mail-qv1-f43.google.com [209.85.219.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DDDE1136352; Sat, 28 Sep 2024 15:09:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536166; cv=none; b=nyKnnqo+2/8M8LGXhsteyl2OuS2PRKX81iTK4v0WrgrZXfpTHUoQyMQkRYTM3eKZ7m29+aAYsu/1OXGjBvxVo9R54KfdnWg5a/R/7FSo0Ox6JTp/hbIy+LDRdjZ03rtp8mZd2wzbF+2QS2RQ8kOXwzRsGNBckInblqpFTTIqxPQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536166; c=relaxed/simple; bh=z0X2xVY0aS8zlwN/lvqnVJjE5yAtz9tjufvdhNlOvIQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FbMByOu+lJC0Rqs/WQQOvWclgZgYKOIanHFL5VboIqzN23lHT7Nm2NeFDG/dIlB8y+gj/ne1oMJmUUeDbh9zpljnSJWMA3bmZC48O0Zwfo13RApGWoJrfdXPIHfEn5U0FRxRUNHu6S/X0GF0BpGMZZohEt0evUxWu9nC0HMgLr4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=QNieBaWR; arc=none smtp.client-ip=209.85.219.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QNieBaWR" Received: by mail-qv1-f43.google.com with SMTP id 6a1803df08f44-6cb48e77b16so1927136d6.0; Sat, 28 Sep 2024 08:09:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536163; x=1728140963; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SuzSoXaQeZoL4jPbk74ZB4LmK4V9Zc/pikziXjh7WQk=; b=QNieBaWRQ/pmz8cZQy/5Zb5/4K21I4mhsvJ8h/a3LVw0RfmvuAZQaWZZKkwNYPKANe EF8VIlBr4lNY4GoZFjIfCcEP42kiDbN6DLWfiCwIiCZYjoIGBURSiJV1TMdZtCxAaOln Unb8n9oUWKdTz2I8b7AvO9gttAK06JxmzlbAJhoSgNx8Bgem+4sY7ylT47+bRicSPqRP IuAsq/Qw+s/gEf+Gad4Qyu8uONIf2WOb6fO03ld3o64PYAoHbFJ12AUsmS9U/wXKHmDY ldUfQMhDsz7E5LokI5QIB7Az//CuRcbfPC+hFqF/lPRQ/lS3NlgyodobzocE9BTcHaG0 dNcg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536163; x=1728140963; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SuzSoXaQeZoL4jPbk74ZB4LmK4V9Zc/pikziXjh7WQk=; b=kiY1lJaVx1jwfVLo4O9lk/c+k6FwqlpgDH3BBkLbMrVommJVSMvguKuoFl1X/vIJD7 IxjO6Mh7AAoGKAwo5xWwqcpRECgzHjF/kHqzxUQPiW3mslQZJUteNGK9etzxQnSaJ3RO whmtEm++MYaMiuzDqvM6Oufnhv1KFqhMFOiGowyLmNil2pBOAXtK0nmVmdv30uS5eEwt PtZqKLzeh9p5PMJQOTABxBuLDzf93ICwO9J9wuKy0PCVeyTRWAXRlLfUtRUa6QdQ4DOp IXQo0vlr5M68d/eaajAr7P37mUpxphYO+Z0ISNsWeYJrKTf4btXWGa3ibk7vDOgudtg2 kUxg== X-Forwarded-Encrypted: i=1; AJvYcCVnB3anNB71Z8BKptDewksp1PV3ptFcMLwq74SHYfND8/Ydh5puY8QShvlsyZC2XWZJn9YHNvGAbqlrB6Lq@vger.kernel.org, AJvYcCWwXvSo/r5eHlow5Xxk4WVtsNw54x0qXVWsrUaOO7yCtpU8Lq3jYQnFXwA8d/6EMwzs13C/0CfEWN0=@vger.kernel.org X-Gm-Message-State: AOJu0YxBmT2YLuDBd+gr3uhLh6cM8AlnbsBdCkWLSOkRxebVDi2EmVal FesKIJYVV0NMn3t/Vfk89MmG4S8rF3uSf5Vf1/r3OzcU0LtCdmEEv48STCKepdg= X-Google-Smtp-Source: AGHT+IGCpZW7Ox6nf5NyXR6WfoiIZbJSap5/P8lAF7P/FMsLF0WFMXqPfAqFn1hG+F9BEOIQhAMpjQ== X-Received: by 2002:ac8:5a41:0:b0:458:31ee:74f6 with SMTP id d75a77b69052e-45cadcd22a5mr19607421cf.11.1727536163383; Sat, 28 Sep 2024 08:09:23 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:22 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 03/14] usb: gadget: f_uac1: Add adaptive sync support for capture Date: Sat, 28 Sep 2024 11:08:54 -0400 Message-ID: <20240928150905.2616313-4-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff Allow the user to configure async or adaptive mode for data from the host. Enabling async mode will include the feedback endpoint. This functions the same as UAC2. Signed-off-by: Chris Wulff --- .../ABI/testing/configfs-usb-gadget-uac1 | 3 + Documentation/usb/gadget-testing.rst | 2 + drivers/usb/gadget/function/f_uac1.c | 101 +++++++++++++++++- drivers/usb/gadget/function/u_uac1.h | 3 + 4 files changed, 108 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1 index 64188a85592b..758b8c9a988a 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac1 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1 @@ -8,6 +8,8 @@ Description: c_chmask capture channel mask c_srate list of capture sampling rates (comma-separated) c_ssize capture sample size (bytes) + c_sync capture synchronization type + (async/adaptive) c_mute_present capture mute control enable c_volume_present capture volume control enable c_volume_min capture volume control min value @@ -16,6 +18,7 @@ Description: (in 1/256 dB) c_volume_res capture volume control resolution (in 1/256 dB) + fb_max maximum extra bandwidth in async mode p_chmask playback channel mask p_srate list of playback sampling rates (comma-separated) p_ssize playback sample size (bytes) diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index bf555c2270f5..68fc0011b388 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -952,11 +952,13 @@ The uac1 function provides these attributes in its function directory: c_chmask capture channel mask c_srate list of capture sampling rates (comma-separated) c_ssize capture sample size (bytes) + c_sync capture synchronization type (async/adaptive) c_mute_present capture mute control enable c_volume_present capture volume control enable c_volume_min capture volume control min value (in 1/256 dB) c_volume_max capture volume control max value (in 1/256 dB) c_volume_res capture volume control resolution (in 1/256 dB) + fb_max maximum extra bandwidth in async mode p_chmask playback channel mask p_srate list of playback sampling rates (comma-separated) p_ssize playback sample size (bytes) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index f68d444d1961..84423d9a8bd7 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -33,6 +33,7 @@ || (_opts)->p_volume_present) #define FUOUT_EN(_opts) ((_opts)->c_mute_present \ || (_opts)->c_volume_present) +#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC) struct f_uac1 { struct g_audio g_audio; @@ -305,6 +306,48 @@ static struct uac_iso_endpoint_descriptor as_iso_in_desc = { .wLockDelay = 0, }; +/* STD AS ISO IN Feedback Endpoint */ +static struct usb_endpoint_descriptor fs_as_in_fback_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK, + .wMaxPacketSize = cpu_to_le16(3), + .bInterval = 1, + .bRefresh = 0, + .bSynchAddress = 0, +}; + +static struct usb_endpoint_descriptor hs_as_in_fback_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK, + .wMaxPacketSize = cpu_to_le16(4), + .bInterval = 4, + .bRefresh = 0, + .bSynchAddress = 0, +}; + +static struct usb_endpoint_descriptor ss_as_in_fback_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK, + .wMaxPacketSize = cpu_to_le16(4), + .bInterval = 4, + .bRefresh = 0, + .bSynchAddress = 0, +}; + +static struct usb_ss_ep_comp_descriptor ss_as_in_fback_desc_comp = { + .bLength = sizeof(ss_as_in_fback_desc_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(4), +}; + static struct usb_descriptor_header *f_audio_fs_desc[] = { (struct usb_descriptor_header *)&ac_interface_desc, (struct usb_descriptor_header *)&ac_header_desc, @@ -327,6 +370,7 @@ static struct usb_descriptor_header *f_audio_fs_desc[] = { (struct usb_descriptor_header *)&fs_as_out_ep_desc, (struct usb_descriptor_header *)&as_iso_out_desc, + (struct usb_descriptor_header *)&fs_as_in_fback_desc, (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, @@ -361,6 +405,7 @@ static struct usb_descriptor_header *f_audio_hs_desc[] = { (struct usb_descriptor_header *)&hs_as_out_ep_desc, (struct usb_descriptor_header *)&as_iso_out_desc, + (struct usb_descriptor_header *)&hs_as_in_fback_desc, (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, @@ -435,6 +480,8 @@ static struct usb_descriptor_header *f_audio_ss_desc[] = { (struct usb_descriptor_header *)&ss_as_out_ep_desc, (struct usb_descriptor_header *)&ss_as_out_ep_desc_comp, (struct usb_descriptor_header *)&as_iso_out_desc, + (struct usb_descriptor_header *)&ss_as_in_fback_desc, + (struct usb_descriptor_header *)&ss_as_in_fback_desc_comp, (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, @@ -1236,9 +1283,11 @@ static void setup_headers(struct f_uac1_opts *opts, { struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL; struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL; + struct usb_ss_ep_comp_descriptor *epin_fback_desc_comp = NULL; struct usb_ss_ep_comp_descriptor *ep_int_desc_comp = NULL; struct usb_endpoint_descriptor *epout_desc; struct usb_endpoint_descriptor *epin_desc; + struct usb_endpoint_descriptor *epin_fback_desc; struct usb_endpoint_descriptor *ep_int_desc; int i; @@ -1246,11 +1295,13 @@ static void setup_headers(struct f_uac1_opts *opts, case USB_SPEED_FULL: epout_desc = &fs_as_out_ep_desc; epin_desc = &fs_as_in_ep_desc; + epin_fback_desc = &fs_as_in_fback_desc; ep_int_desc = &fs_ac_int_ep_desc; break; case USB_SPEED_HIGH: epout_desc = &hs_as_out_ep_desc; epin_desc = &hs_as_in_ep_desc; + epin_fback_desc = &hs_as_in_fback_desc; ep_int_desc = &hs_ac_int_ep_desc; break; default: @@ -1258,6 +1309,8 @@ static void setup_headers(struct f_uac1_opts *opts, epin_desc = &ss_as_in_ep_desc; epout_desc_comp = &ss_as_out_ep_desc_comp; epin_desc_comp = &ss_as_in_ep_desc_comp; + epin_fback_desc = &ss_as_in_fback_desc; + epin_fback_desc_comp = &ss_as_in_fback_desc_comp; ep_int_desc = &ss_ac_int_ep_desc; ep_int_desc_comp = &ss_ac_int_ep_desc_comp; } @@ -1296,6 +1349,12 @@ static void setup_headers(struct f_uac1_opts *opts, headers[i++] = USBDHDR(epout_desc_comp); headers[i++] = USBDHDR(&as_iso_out_desc); + + if (EPOUT_FBACK_IN_EN(opts)) { + headers[i++] = USBDHDR(epin_fback_desc); + if (epin_fback_desc_comp) + headers[i++] = USBDHDR(epin_fback_desc_comp); + } } if (EPIN_EN(opts)) { headers[i++] = USBDHDR(&as_in_interface_alt_0_desc); @@ -1509,6 +1568,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) if (status < 0) goto err_free_fu; ac_interface_desc.bInterfaceNumber = status; + ac_interface_desc.bNumEndpoints = 1; uac1->ac_intf = status; uac1->ac_alt = 0; @@ -1523,6 +1583,23 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) ac_header_desc->baInterfaceNr[ba_iface_id++] = status; uac1->as_out_intf = status; uac1->as_out_alt = 0; + + if (EPOUT_FBACK_IN_EN(audio_opts)) { + fs_as_out_ep_desc.bmAttributes = + USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC; + hs_as_out_ep_desc.bmAttributes = + USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC; + ss_as_out_ep_desc.bmAttributes = + USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC; + ac_interface_desc.bNumEndpoints++; + } else { + fs_as_out_ep_desc.bmAttributes = + USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; + hs_as_out_ep_desc.bmAttributes = + USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; + ss_as_out_ep_desc.bmAttributes = + USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; + } } if (EPIN_EN(audio_opts)) { @@ -1569,6 +1646,17 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio->out_ep = ep; audio->out_ep->desc = &fs_as_out_ep_desc; + + if (EPOUT_FBACK_IN_EN(audio_opts)) { + ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_fback_desc); + if (!ep) + goto err_free_fu; + + hs_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress; + ss_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress; + + audio->in_ep_fback = ep; + } } if (EPIN_EN(audio_opts)) { @@ -1631,7 +1719,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio->params.c_fu.volume_res = audio_opts->c_volume_res; } audio->params.req_number = audio_opts->req_number; - audio->params.fb_max = FBACK_FAST_MAX; + audio->params.fb_max = audio_opts->fb_max; if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) audio->notify = audio_notify; @@ -1678,6 +1766,10 @@ static struct configfs_item_operations f_uac1_item_ops = { UAC_ATTRIBUTE(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts, \ opts->lock, opts->refcnt, type, name) +#define UAC1_ATTRIBUTE_SYNC(name) \ + UAC_ATTRIBUTE_SYNC(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts, \ + opts->lock, opts->refcnt, name) + #define UAC1_RATE_ATTRIBUTE(name) \ UAC_RATE_ATTRIBUTE(f_uac1_opts, UAC1_ATTR_TO_OPTS, opts, \ opts->lock, opts->refcnt, name) @@ -1688,6 +1780,7 @@ static struct configfs_item_operations f_uac1_item_ops = { UAC1_ATTRIBUTE(u32, c_chmask); UAC1_RATE_ATTRIBUTE(c_srate); +UAC1_ATTRIBUTE_SYNC(c_sync); UAC1_ATTRIBUTE(u32, c_ssize); UAC1_ATTRIBUTE(u32, p_chmask); UAC1_RATE_ATTRIBUTE(p_srate); @@ -1706,6 +1799,8 @@ UAC1_ATTRIBUTE(s16, c_volume_min); UAC1_ATTRIBUTE(s16, c_volume_max); UAC1_ATTRIBUTE(s16, c_volume_res); +UAC1_ATTRIBUTE(u32, fb_max); + UAC1_ATTRIBUTE_STRING(function_name); UAC1_ATTRIBUTE_STRING(p_it_name); @@ -1721,11 +1816,13 @@ UAC1_ATTRIBUTE_STRING(c_fu_vol_name); static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_c_chmask, &f_uac1_opts_attr_c_srate, + &f_uac1_opts_attr_c_sync, &f_uac1_opts_attr_c_ssize, &f_uac1_opts_attr_p_chmask, &f_uac1_opts_attr_p_srate, &f_uac1_opts_attr_p_ssize, &f_uac1_opts_attr_req_number, + &f_uac1_opts_attr_fb_max, &f_uac1_opts_attr_p_mute_present, &f_uac1_opts_attr_p_volume_present, @@ -1784,6 +1881,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void) opts->c_chmask = UAC1_DEF_CCHMASK; opts->c_srates[0] = UAC1_DEF_CSRATE; + opts->c_sync = UAC1_DEF_CSYNC; opts->c_ssize = UAC1_DEF_CSSIZE; opts->p_chmask = UAC1_DEF_PCHMASK; opts->p_srates[0] = UAC1_DEF_PSRATE; @@ -1802,6 +1900,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void) opts->c_volume_res = UAC1_DEF_RES_DB; opts->req_number = UAC1_DEF_REQ_NUM; + opts->fb_max = FBACK_FAST_MAX; scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface"); diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index feb6eb76462f..59eac5ca8114 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -14,6 +14,7 @@ #define UAC1_OUT_EP_MAX_PACKET_SIZE 200 #define UAC1_DEF_CCHMASK 0x3 #define UAC1_DEF_CSRATE 48000 +#define UAC1_DEF_CSYNC USB_ENDPOINT_SYNC_ADAPTIVE #define UAC1_DEF_CSSIZE 2 #define UAC1_DEF_PCHMASK 0x3 #define UAC1_DEF_PSRATE 48000 @@ -32,6 +33,7 @@ struct f_uac1_opts { struct usb_function_instance func_inst; int c_chmask; int c_srates[UAC_MAX_RATES]; + int c_sync; int c_ssize; int p_chmask; int p_srates[UAC_MAX_RATES]; @@ -50,6 +52,7 @@ struct f_uac1_opts { s16 c_volume_res; int req_number; + int fb_max; unsigned bound:1; char function_name[USB_MAX_STRING_LEN]; From patchwork Sat Sep 28 15:08:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814701 Received: from mail-qk1-f181.google.com (mail-qk1-f181.google.com [209.85.222.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9FDC31474D8; Sat, 28 Sep 2024 15:09:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536169; cv=none; b=lsKuS9yJcTtXS3HtHPTSGCWs8jTKAHpMSW36dH6JoiBcfb32+7Trfk0PVd4lv6pXC7FjZ095GkTJMDHPIU6ZQ6lwOW7F6ZcfbJ1F7neOGCzPIOwb+bFvMTf/eNRM4P7RgfSDzFdbJfeT1pbjyY9R9OEMpzrrdiuYDFRtZcxz3lk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536169; c=relaxed/simple; bh=DIC2P8HkVB9qMMyxjKjxHU/L50YrnmCLbRnLGFH7WeA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C6cKrazNwouKhRJ2EaCFqoVeowNpX4CC6BTkXwN3+X2YpIj16sr8OXB4yUsXINZR9TgaeBjJSjkpDHYPnKRYdyASpGdc8bMo3Zly2vM2tNlmA59gCkWjDXrKeYZOFx/mU+wD0uwuGNf9PKAl0mbp7XhmEr/VuJUYsO4P2CmLm5o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=arW4pOmJ; arc=none smtp.client-ip=209.85.222.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="arW4pOmJ" Received: by mail-qk1-f181.google.com with SMTP id af79cd13be357-7a9a62c6734so26988085a.3; Sat, 28 Sep 2024 08:09:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536166; x=1728140966; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5MX5tEqh6uhy30KPRHM69PdB+zn/Kf6NCsv4Yh97r1Q=; b=arW4pOmJREFLScse3Q/KVVMbB0KMOz5YWoaV4SieBwOKGdhWZrvN1Uw8UujmyBArSX qZnaeyee3G0cMZMnXvNr5PS+BRbqjj5A3jmha97yKatJ/sAHaPwvgR74FvODHWd82sT6 BehmWDW6SOqbqKfChkJXHngxP8QdUgG0E7BH/0MQstuimjb2oez4818Fwr3byswGLbce WlvHlUwCwUaDeZjTQ6uqrhHmyWnwJRgpd+zvg7clhmGDUXTH0wgFu85ngiAaMqiQ/CJ+ 224fmGXU8doGwcMKidsYrYHgZuH/pTWp0cORhAVVVgaZH57QC8UkuiEL1gsT5TwYwxqE g+YQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536166; x=1728140966; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5MX5tEqh6uhy30KPRHM69PdB+zn/Kf6NCsv4Yh97r1Q=; b=nmpx+SufzYNNFJgzydKI1XhI3nn8yiT15ME6eyJD9SYLbt44x4sXc4VPkBFQJ7YaFs vMn93edqq/ClUSTPa7J3gd9Kil8Tr9c4lYfxz4lr80MVP4kYIkKPLM61yT9bRDcksBaL +0FXdKQrxqxPugPKsr4WoO8pCjrBK1dKPfexzaRW0HM28BmTiyuNClBKtJOONG2tsRwl HRleEUVUY88qMsN9y2PBMj8ndj40r0CufX9qUhQUImoed5bZ1EEDkSjkTFVoUtpREVmJ zeXa30jAEsYWdc2oWxopZtPTBLty0guj7diiJsowuiJLs4q1clriFJJ8uzgnLKQGt16O YFsQ== X-Forwarded-Encrypted: i=1; AJvYcCU8J68VV9YuWFLGRNwMHhO1WPWbx5Hn2Ld9tmeBoHWksJFJnWyCyCsMtvc8yMRd6lvqGQmAroGb2pmxzhE7@vger.kernel.org, AJvYcCXWXQ/sVvwn5/O7mU5jGORl/Lero3XTfR8kYrJPOo3c8CjdKOwRaVaDAn34JUsxEhZQtA3N54owLbE=@vger.kernel.org X-Gm-Message-State: AOJu0Yw5RxZcniwVj1c5wsisy3fThIaKDuo46sorP94pytwr9DSzLjaf pIcTHObcFZeU61Md4Avynm89j3iGjJrQoE28LrQamtC+HB0tsO0MToCVZVKWcHg= X-Google-Smtp-Source: AGHT+IEm0WTpxVpC7oDmcnf2jckEfckfCGS8eSpW94cEArO98OIGPoSDXUlQGXhzgJPxFCR8YXBrdQ== X-Received: by 2002:a05:622a:c8:b0:458:21b3:63f with SMTP id d75a77b69052e-45c9f317f50mr47042161cf.13.1727536166087; Sat, 28 Sep 2024 08:09:26 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:25 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 04/14] usb: gadget: f_uac2: Move max packet size code to a common header Date: Sat, 28 Sep 2024 11:08:55 -0400 Message-ID: <20240928150905.2616313-5-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff Moving this to allow it to be used by f_uac1 in the future. Signed-off-by: Chris Wulff --- drivers/usb/gadget/function/f_uac2.c | 116 ++-------------------- drivers/usb/gadget/function/u_uac_utils.h | 111 +++++++++++++++++++++ 2 files changed, 122 insertions(+), 105 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 2142a0951f71..050789d2d3c9 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -112,16 +112,6 @@ enum { static struct usb_string strings_fn[NUM_STR_DESCRIPTORS + 1] = {}; -static const char *const speed_names[] = { - [USB_SPEED_UNKNOWN] = "UNKNOWN", - [USB_SPEED_LOW] = "LS", - [USB_SPEED_FULL] = "FS", - [USB_SPEED_HIGH] = "HS", - [USB_SPEED_WIRELESS] = "W", - [USB_SPEED_SUPER] = "SS", - [USB_SPEED_SUPER_PLUS] = "SS+", -}; - static struct usb_gadget_strings str_fn = { .language = 0x0409, /* en-us */ .strings = strings_fn, @@ -656,113 +646,29 @@ struct cntrl_subrange_lay3 { DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES); -static int get_max_srate(const int *srates) -{ - int i, max_srate = 0; - - for (i = 0; i < UAC_MAX_RATES; i++) { - if (srates[i] == 0) - break; - if (srates[i] > max_srate) - max_srate = srates[i]; - } - return max_srate; -} - -static int get_max_bw_for_bint(const struct f_uac2_opts *uac2_opts, - u8 bint, unsigned int factor, bool is_playback) +static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_opts *uac2_opts, + struct usb_endpoint_descriptor *ep_desc, + enum usb_device_speed speed, bool is_playback) { - int chmask, srate, ssize; - u16 max_size_bw; + int chmask, srate, ssize, hs_bint, sync; if (is_playback) { chmask = uac2_opts->p_chmask; srate = get_max_srate(uac2_opts->p_srates); ssize = uac2_opts->p_ssize; + hs_bint = uac2_opts->p_hs_bint; + sync = USB_ENDPOINT_SYNC_ASYNC; } else { chmask = uac2_opts->c_chmask; srate = get_max_srate(uac2_opts->c_srates); ssize = uac2_opts->c_ssize; + hs_bint = uac2_opts->c_hs_bint; + sync = uac2_opts->c_sync; } - if (is_playback || (uac2_opts->c_sync == USB_ENDPOINT_SYNC_ASYNC)) { - // playback is always async, capture only when configured - // Win10 requires max packet size + 1 frame - srate = srate * (1000 + uac2_opts->fb_max) / 1000; - // updated srate is always bigger, therefore DIV_ROUND_UP always yields +1 - max_size_bw = num_channels(chmask) * ssize * - (DIV_ROUND_UP(srate, factor / (1 << (bint - 1)))); - } else { - // adding 1 frame provision for Win10 - max_size_bw = num_channels(chmask) * ssize * - (DIV_ROUND_UP(srate, factor / (1 << (bint - 1))) + 1); - } - return max_size_bw; -} - -static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_opts *uac2_opts, - struct usb_endpoint_descriptor *ep_desc, - enum usb_device_speed speed, bool is_playback) -{ - u16 max_size_bw, max_size_ep; - u8 bint, opts_bint; - char *dir; - - switch (speed) { - case USB_SPEED_FULL: - max_size_ep = 1023; - // fixed - bint = ep_desc->bInterval; - max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 1000, is_playback); - break; - - case USB_SPEED_HIGH: - case USB_SPEED_SUPER: - max_size_ep = 1024; - if (is_playback) - opts_bint = uac2_opts->p_hs_bint; - else - opts_bint = uac2_opts->c_hs_bint; - - if (opts_bint > 0) { - /* fixed bint */ - bint = opts_bint; - max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 8000, is_playback); - } else { - /* checking bInterval from 4 to 1 whether the required bandwidth fits */ - for (bint = 4; bint > 0; --bint) { - max_size_bw = get_max_bw_for_bint( - uac2_opts, bint, 8000, is_playback); - if (max_size_bw <= max_size_ep) - break; - } - } - break; - - default: - return -EINVAL; - } - - if (is_playback) - dir = "Playback"; - else - dir = "Capture"; - - if (max_size_bw <= max_size_ep) - dev_dbg(dev, - "%s %s: Would use wMaxPacketSize %d and bInterval %d\n", - speed_names[speed], dir, max_size_bw, bint); - else { - dev_warn(dev, - "%s %s: Req. wMaxPacketSize %d at bInterval %d > max ISOC %d, may drop data!\n", - speed_names[speed], dir, max_size_bw, bint, max_size_ep); - max_size_bw = max_size_ep; - } - - ep_desc->wMaxPacketSize = cpu_to_le16(max_size_bw); - ep_desc->bInterval = bint; - - return 0; + return uac_set_ep_max_packet_size_bint( + dev, ep_desc, speed, is_playback, hs_bint, chmask, + srate, ssize, sync, uac2_opts->fb_max); } static struct uac2_feature_unit_descriptor *build_fu_desc(int chmask) diff --git a/drivers/usb/gadget/function/u_uac_utils.h b/drivers/usb/gadget/function/u_uac_utils.h index 5f88e31103dd..7ef9f699657c 100644 --- a/drivers/usb/gadget/function/u_uac_utils.h +++ b/drivers/usb/gadget/function/u_uac_utils.h @@ -214,4 +214,115 @@ end: \ \ CONFIGFS_ATTR(prefix##_, name) +/* + * Functions for EP interval and max packet size + */ + +static const char *const speed_names[] = { + [USB_SPEED_UNKNOWN] = "UNKNOWN", + [USB_SPEED_LOW] = "LS", + [USB_SPEED_FULL] = "FS", + [USB_SPEED_HIGH] = "HS", + [USB_SPEED_WIRELESS] = "W", + [USB_SPEED_SUPER] = "SS", + [USB_SPEED_SUPER_PLUS] = "SS+", +}; + +static int get_max_srate(const int *srates) +{ + int i, max_srate = 0; + + for (i = 0; i < UAC_MAX_RATES; i++) { + if (srates[i] == 0) + break; + if (srates[i] > max_srate) + max_srate = srates[i]; + } + return max_srate; +} + +static int get_max_bw_for_bint(u8 bint, unsigned int factor, int chmask, + int srate, int ssize, int sync, int fb_max) +{ + u16 max_size_bw; + + if (sync == USB_ENDPOINT_SYNC_ASYNC) { + // playback is always async, capture only when configured + // Win10 requires max packet size + 1 frame + srate = srate * (1000 + fb_max) / 1000; + // updated srate is always bigger, therefore DIV_ROUND_UP always yields +1 + max_size_bw = num_channels(chmask) * ssize * + (DIV_ROUND_UP(srate, factor / (1 << (bint - 1)))); + } else { + // adding 1 frame provision for Win10 + max_size_bw = num_channels(chmask) * ssize * + (DIV_ROUND_UP(srate, factor / (1 << (bint - 1))) + 1); + } + return max_size_bw; +} + +static int uac_set_ep_max_packet_size_bint(struct device *dev, + struct usb_endpoint_descriptor *ep_desc, + enum usb_device_speed speed, bool is_playback, int hs_bint, + int chmask, int srate, int ssize, int sync, int fb_max) +{ + u16 max_size_bw, max_size_ep; + u8 bint; + char *dir; + + switch (speed) { + case USB_SPEED_FULL: + max_size_ep = 1023; + // fixed + bint = ep_desc->bInterval; + max_size_bw = get_max_bw_for_bint(bint, 1000, chmask, srate, + ssize, sync, fb_max); + break; + + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + max_size_ep = 1024; + if (hs_bint > 0) { + /* fixed bint */ + bint = hs_bint; + max_size_bw = get_max_bw_for_bint(bint, 8000, chmask, srate, + ssize, sync, fb_max); + } else { + /* checking bInterval from 4 to 1 whether the required bandwidth fits */ + for (bint = 4; bint > 0; --bint) { + max_size_bw = get_max_bw_for_bint( + bint, 8000, chmask, srate, + ssize, sync, fb_max); + if (max_size_bw <= max_size_ep) + break; + } + } + break; + + default: + return -EINVAL; + } + + if (is_playback) + dir = "Playback"; + else + dir = "Capture"; + + if (max_size_bw <= max_size_ep) + dev_dbg(dev, + "%s %s: Would use wMaxPacketSize %d and bInterval %d\n", + speed_names[speed], dir, max_size_bw, bint); + else { + dev_warn(dev, + "%s %s: Req. wMaxPacketSize %d at bInterval %d > max ISOC %d, may drop data!\n", + speed_names[speed], dir, max_size_bw, bint, max_size_ep); + max_size_bw = max_size_ep; + } + + ep_desc->wMaxPacketSize = cpu_to_le16(max_size_bw); + ep_desc->bInterval = bint; + + return 0; +} + #endif /* __U_UAC_UTILS_H */ From patchwork Sat Sep 28 15:08:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814702 Received: from mail-qv1-f42.google.com (mail-qv1-f42.google.com [209.85.219.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1F884169AE6; Sat, 28 Sep 2024 15:09:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536171; cv=none; b=XX0tOyKst4PIqx6PIF0vHL2WZItJRJTV06iY/n/dkz+ghB5dcFrCPzJg3MvDQu4FfZkOAnI2tXfBf8W/KN0CnziY/hdzDEN1jPTDK68S3iwNZNd+RNgG4/Rc4jzRfler1lQ+YZ4F6K1X8ExyTWq6S4XH/i2y94sYiqTTJlDM3j4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536171; c=relaxed/simple; bh=AoBAcBCN9g5FrQCnEpiLYuw/boCK8ant09G0B3P6MYg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VnMONyCozA80pA9Q84SY/enFzmTDSZnNIa161XCG6wHaw+3xR/JgSU5p8X4QQadcDgkrJm4LB9/bCNFPrJ5jb56FOEvn5bvrKPuL+k+BvuxVT1HYgwReWL8mlIjqwCDNVgES7Llwmzfo/kmmGqBpTnqQ9jovFk6Ka0BDWBJqQ9U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HkseW/4W; arc=none smtp.client-ip=209.85.219.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HkseW/4W" Received: by mail-qv1-f42.google.com with SMTP id 6a1803df08f44-6cb3d95ce6eso2590726d6.1; Sat, 28 Sep 2024 08:09:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536169; x=1728140969; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=n8IRxut/74qoISFME/SEWZ1q6xKA0k5dTsizuBxIsU0=; b=HkseW/4Wr27U47qE1DslFqx+cLwrqwdgJi4znK5XXUx5ELGnKC0UBr7dP/enOsu5S8 ImEwgY4EGplPCVb2fEbpfqpnZKhQrFHos+zPbc5D7/gxnvKQuWAb8wJYOLLiSy2pJTka 1Vb0sOZJMVFvuyH17k9tQ92NY8ty8LtAuwDhXHD8G+WEf1g5xU6v1gh//F30XfUQHOY7 Dk86lzRYYCdEdBjZyDJDvjOFK6gT4+Lpt8DuDWwQgANaROkAaQtQItTBRZjSzJh0z0AS /esbDe74i8+CyyZjyq6qFR23HxKspVW5iAWe34p6WTQZC1ghNHjUsOeqO9xMNAzb5dcp RGCQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536169; x=1728140969; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=n8IRxut/74qoISFME/SEWZ1q6xKA0k5dTsizuBxIsU0=; b=Lu7+Jq1RQ4DF37tLeRnQRUJg7LUXLqvOBGArMDCY4DqQhHRcHkCle7lVP2+ykIowOd WiF7dbboWm1vuyiOALcP4cxOGl1Y0Ppfr+xU72hTXeC7uHATJdJzcjGkJ/HV9AJjzcaR 8eR03RGM+70lpB3AmyBGO9/Eru96KFFqmpfxGLQtLr4sOQh7p67DjLY7qTmFErQpF191 oXW23DCKk7ME3Cb275g1UOIBSnL/fHhiFK07gLLj/gzk1xlIvfzAMKsMfLsBAQ9uYtrM kSQ+9aKrEJHMg2ZfXOKrW7D/O0pqlOVvqJ5oqPnRSklXmwpeBxa73P99p9BCn2Jk6fiE bSJw== X-Forwarded-Encrypted: i=1; AJvYcCUDgQmXzCCxNSj5Jq80Ei+FgakXfl3/xLdrKpY1MNC9Rh5tY5juF6dckyV+cYi4ggXxq2xUiSaLd4Q=@vger.kernel.org, AJvYcCXW/LmJZj6q/FUYzWu+NJRJsykklsPJMtr6a6iB0BMWR3F0+ELXo9TU7ZLGR9dR1NDfJDXK1VnVwYmoovf1@vger.kernel.org X-Gm-Message-State: AOJu0YwcKDekfw7vPH7Ayh3tFhl8ki4TizwzIFTQfNZKxQlqfjq9DoYK 5wD9PICQpKVakj++lmc+jb52QKL7Tyfy9cb/AkIdMOwnUyay1l2BBcP/HxnWeM8= X-Google-Smtp-Source: AGHT+IHqGQU6r8XhBlMDKXXQv6v91Izp4drHo7CON+oRNDupU4Qo0F1r/aOXjVdIbCA/JhLRKO3/bg== X-Received: by 2002:a05:622a:1998:b0:458:2860:c8f4 with SMTP id d75a77b69052e-45cadb3829emr22131471cf.2.1727536168595; Sat, 28 Sep 2024 08:09:28 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:27 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 05/14] usb: gadget: f_uac1: Add hs_bint to configfs. Date: Sat, 28 Sep 2024 11:08:56 -0400 Message-ID: <20240928150905.2616313-6-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff This matches options from f_uac2. This only adds the options but using it requires additional descriptors added later. Signed-off-by: Chris Wulff --- .../ABI/testing/configfs-usb-gadget-uac1 | 2 + Documentation/usb/gadget-testing.rst | 2 + drivers/usb/gadget/function/f_uac1.c | 89 +++++++++++++++++++ drivers/usb/gadget/function/u_uac1.h | 4 + 4 files changed, 97 insertions(+) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1 index 758b8c9a988a..fed8567b10ec 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac1 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1 @@ -8,6 +8,7 @@ Description: c_chmask capture channel mask c_srate list of capture sampling rates (comma-separated) c_ssize capture sample size (bytes) + c_hs_bint capture bInterval for HS/SS (1-4: fixed, 0: auto) c_sync capture synchronization type (async/adaptive) c_mute_present capture mute control enable @@ -22,6 +23,7 @@ Description: p_chmask playback channel mask p_srate list of playback sampling rates (comma-separated) p_ssize playback sample size (bytes) + p_hs_bint playback bInterval for HS/SS (1-4: fixed, 0: auto) p_mute_present playback mute control enable p_volume_present playback volume control enable p_volume_min playback volume control min value diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 68fc0011b388..bdb82b58b260 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -958,6 +958,7 @@ The uac1 function provides these attributes in its function directory: c_volume_min capture volume control min value (in 1/256 dB) c_volume_max capture volume control max value (in 1/256 dB) c_volume_res capture volume control resolution (in 1/256 dB) + c_hs_bint capture bInterval for HS/SS (1-4: fixed, 0: auto) fb_max maximum extra bandwidth in async mode p_chmask playback channel mask p_srate list of playback sampling rates (comma-separated) @@ -967,6 +968,7 @@ The uac1 function provides these attributes in its function directory: p_volume_min playback volume control min value (in 1/256 dB) p_volume_max playback volume control max value (in 1/256 dB) p_volume_res playback volume control resolution (in 1/256 dB) + p_hs_bint playback bInterval for HS/SS (1-4: fixed, 0: auto) req_number the number of pre-allocated requests for both capture and playback function_name name of the interface diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 84423d9a8bd7..861e6219552e 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -1159,6 +1159,32 @@ f_audio_suspend(struct usb_function *f) } /*-------------------------------------------------------------------------*/ + +static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_opts *opts, + struct usb_endpoint_descriptor *ep_desc, + enum usb_device_speed speed, bool is_playback) +{ + int chmask, srate, ssize, hs_bint, sync; + + if (is_playback) { + chmask = opts->p_chmask; + srate = get_max_srate(opts->p_srates); + ssize = opts->p_ssize; + hs_bint = opts->p_hs_bint; + sync = USB_ENDPOINT_SYNC_ASYNC; + } else { + chmask = opts->c_chmask; + srate = get_max_srate(opts->c_srates); + ssize = opts->c_ssize; + hs_bint = opts->c_hs_bint; + sync = opts->c_sync; + } + + return uac_set_ep_max_packet_size_bint( + dev, ep_desc, speed, is_playback, hs_bint, chmask, + srate, ssize, sync, opts->fb_max); +} + static struct uac_feature_unit_descriptor *build_fu_desc(int chmask) { struct uac_feature_unit_descriptor *fu_desc; @@ -1419,6 +1445,15 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) return -EINVAL; } + if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4)) { + dev_err(dev, "Error: incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)\n"); + return -EINVAL; + } + if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4)) { + dev_err(dev, "Error: incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)\n"); + return -EINVAL; + } + return 0; } @@ -1613,6 +1648,54 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) uac1->as_in_alt = 0; } + hs_as_in_ep_desc.bInterval = audio_opts->p_hs_bint; + ss_as_in_ep_desc.bInterval = audio_opts->p_hs_bint; + hs_as_out_ep_desc.bInterval = audio_opts->c_hs_bint; + ss_as_out_ep_desc.bInterval = audio_opts->c_hs_bint; + + /* Calculate wMaxPacketSize according to audio bandwidth */ + status = set_ep_max_packet_size_bint(dev, audio_opts, &fs_as_in_ep_desc, + USB_SPEED_FULL, true); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto err_free_fu; + } + + status = set_ep_max_packet_size_bint(dev, audio_opts, &fs_as_out_ep_desc, + USB_SPEED_FULL, false); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto err_free_fu; + } + + status = set_ep_max_packet_size_bint(dev, audio_opts, &hs_as_in_ep_desc, + USB_SPEED_HIGH, true); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto err_free_fu; + } + + status = set_ep_max_packet_size_bint(dev, audio_opts, &hs_as_out_ep_desc, + USB_SPEED_HIGH, false); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto err_free_fu; + } + + status = set_ep_max_packet_size_bint(dev, audio_opts, &ss_as_in_ep_desc, + USB_SPEED_SUPER, true); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto err_free_fu; + } + + status = set_ep_max_packet_size_bint(dev, audio_opts, &hs_as_out_ep_desc, + USB_SPEED_SUPER, false); + if (status < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + goto err_free_fu; + } + audio->gadget = gadget; status = -ENODEV; @@ -1782,9 +1865,11 @@ UAC1_ATTRIBUTE(u32, c_chmask); UAC1_RATE_ATTRIBUTE(c_srate); UAC1_ATTRIBUTE_SYNC(c_sync); UAC1_ATTRIBUTE(u32, c_ssize); +UAC1_ATTRIBUTE(u8, c_hs_bint); UAC1_ATTRIBUTE(u32, p_chmask); UAC1_RATE_ATTRIBUTE(p_srate); UAC1_ATTRIBUTE(u32, p_ssize); +UAC1_ATTRIBUTE(u8, p_hs_bint); UAC1_ATTRIBUTE(u32, req_number); UAC1_ATTRIBUTE(bool, p_mute_present); @@ -1818,9 +1903,11 @@ static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_c_srate, &f_uac1_opts_attr_c_sync, &f_uac1_opts_attr_c_ssize, + &f_uac1_opts_attr_c_hs_bint, &f_uac1_opts_attr_p_chmask, &f_uac1_opts_attr_p_srate, &f_uac1_opts_attr_p_ssize, + &f_uac1_opts_attr_p_hs_bint, &f_uac1_opts_attr_req_number, &f_uac1_opts_attr_fb_max, @@ -1883,9 +1970,11 @@ static struct usb_function_instance *f_audio_alloc_inst(void) opts->c_srates[0] = UAC1_DEF_CSRATE; opts->c_sync = UAC1_DEF_CSYNC; opts->c_ssize = UAC1_DEF_CSSIZE; + opts->c_hs_bint = UAC1_DEF_CHSBINT; opts->p_chmask = UAC1_DEF_PCHMASK; opts->p_srates[0] = UAC1_DEF_PSRATE; opts->p_ssize = UAC1_DEF_PSSIZE; + opts->p_hs_bint = UAC1_DEF_PHSBINT; opts->p_mute_present = UAC1_DEF_MUTE_PRESENT; opts->p_volume_present = UAC1_DEF_VOLUME_PRESENT; diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 59eac5ca8114..c7e7480bf71f 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -16,9 +16,11 @@ #define UAC1_DEF_CSRATE 48000 #define UAC1_DEF_CSYNC USB_ENDPOINT_SYNC_ADAPTIVE #define UAC1_DEF_CSSIZE 2 +#define UAC1_DEF_CHSBINT 0 #define UAC1_DEF_PCHMASK 0x3 #define UAC1_DEF_PSRATE 48000 #define UAC1_DEF_PSSIZE 2 +#define UAC1_DEF_PHSBINT 0 #define UAC1_DEF_REQ_NUM 2 #define UAC1_DEF_INT_REQ_NUM 10 @@ -35,9 +37,11 @@ struct f_uac1_opts { int c_srates[UAC_MAX_RATES]; int c_sync; int c_ssize; + u8 c_hs_bint; int p_chmask; int p_srates[UAC_MAX_RATES]; int p_ssize; + u8 p_hs_bint; bool p_mute_present; bool p_volume_present; From patchwork Sat Sep 28 15:08:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814703 Received: from mail-qv1-f47.google.com (mail-qv1-f47.google.com [209.85.219.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E5EAC18660A; Sat, 28 Sep 2024 15:09:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536174; cv=none; b=E2qbIKV9NDFgR2IikFdjcgBDR1drWk79TxCHoNRaYduE6uWCXcsqhoxytOub1yqaHPuuP1B5KbF9qF6UtEfxJK6aEC+RNXWKH63RMyAk1JvKZNK9D56Zp06sdTxLr8ugkcvO6Xe1P+H99MngYa+pn69oQNjshFYlSeHeDKTHm9Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536174; c=relaxed/simple; bh=jR0aK5J1Ve9PA8YKuivBGsnUOqKXfycl/5IT7QgZFCg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MVHUzcgV42cr/IM+VQMipPPA1jkdKpp8sjz2O3xusWCSnX5fTByT55qdKyjd6Qs5j9wFVzJfNUSwv4MCU7Sj29gfOKPtDD7/3QZ5rIMOhRAA39izZnPbMp9HN/w5vNXZmyPEZVFdv7dcWJJXBa4PS0OLv4RuxBs4pimwtlYvj+s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=cKGGF1Wa; arc=none smtp.client-ip=209.85.219.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cKGGF1Wa" Received: by mail-qv1-f47.google.com with SMTP id 6a1803df08f44-6cb3eb30527so3001406d6.0; Sat, 28 Sep 2024 08:09:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536171; x=1728140971; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=v00na+4yIcf48dJU0rMJdnHmwBuRKAUkLcZ85/QIWus=; b=cKGGF1Wa5Z7zzkK3KHx/1Nv0MRgAqhMT3hW0UUz7HEixtm7efMgCBsjA2LMTTvEK80 WxhkHi0An+iCBQg0itBwSGL8A5kKWaJ7W7OmWHaLFTD0d7sE4+1hsFuejdrDjeZLzCYl iVe8twicu0TajCFhunfw/UmabaIevWFhahAJLZWopO0lw1vz4s/fbHyVvYYGAJa+/n6q J077Me2lHFLksWG5FueQxBtWXDAjYlTq25ni6H8oWZp34Lb4bbi/w+GhAnvVUX4//sVD OLFbPIZ54aINALZ6G89jF1jfaJ26ex1yUvq0mxtXZfhWMjvBfRDYSnm0qaGy4TEz+H+i TwEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536171; x=1728140971; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=v00na+4yIcf48dJU0rMJdnHmwBuRKAUkLcZ85/QIWus=; b=a9tNSZrOAVc3wCBSgahAdqRa3Jps6833wE6JrxXS1glql4Hw5KU6cJ7N6+X/HsanJ9 5/DbSQ5TXOF2UAPMalM79k0Dkm2jrQHrcQZYWPEkgN0vkMPga3S4mLJ2S5gBl0qsi9Hs Z+N5gtQX2/Agod+ayCLGCAeHONVCxgBLwne3R3KoxB2BcVpACm5/QTRm4zz5LfJHd2Vp fAsVmTydOjCCytxxCdQNGp0/Pk8diqZcHQqB5sJ152MM6yCrB1PoTZ+jYxH34TreHDDY Z9WEIwuK4sHRZu96m93C6tjQoqmbBwP/BvHvOJu07Z6ni4LZccBkBWX8zHAvZDRfRUX2 8nSQ== X-Forwarded-Encrypted: i=1; AJvYcCWjENW5dNPxMXobYEB8DAxt1FMH9C9DlBO2wR5gCVDOJV7qIxOB7hXA5bYy/myhO7ro7jilWJyXH0KixZMx@vger.kernel.org, AJvYcCXgFz8egtCF2ffXJUbkIMIoNJ3Ak2T+2QJzJdH/V7MZ9LJg5Ts/FDZ9szuc+dwcrKidwroEbn8Zt9M=@vger.kernel.org X-Gm-Message-State: AOJu0YwNMDYPu+c5AxbTsWLFIo/gtJo5t92xRaY7UYsTaXaxvmRPo/wd VTS1h2XyWcA+TtT7I5QxSzph4KJqwzCfTM1JSnsSQnF6qGBNFOSlAh97LHPy3wE= X-Google-Smtp-Source: AGHT+IEGXo8oDTDDvCOXGJv7xM790EWRNg5JyvjBDWF0utQ1CmZw1BQOEKAWgQs6J/erPDo4oQ6iGQ== X-Received: by 2002:a05:622a:24e:b0:458:21b3:284 with SMTP id d75a77b69052e-45cadd4006bmr16664361cf.15.1727536171346; Sat, 28 Sep 2024 08:09:31 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:30 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 06/14] usb: gadget: f_uac1: Add terminal type attributes Date: Sat, 28 Sep 2024 11:08:57 -0400 Message-ID: <20240928150905.2616313-7-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff Allow the user to set the terminal types. This matches the options in f_uac2. Signed-off-by: Chris Wulff --- Documentation/ABI/testing/configfs-usb-gadget-uac1 | 2 ++ Documentation/usb/gadget-testing.rst | 2 ++ drivers/usb/gadget/function/f_uac1.c | 11 +++++++++++ drivers/usb/gadget/function/u_uac1.h | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1 index fed8567b10ec..9f7f626329e7 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac1 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1 @@ -43,4 +43,6 @@ Description: c_it_ch_name capture channels name c_ot_name capture output terminal name c_fu_vol_name capture mute/volume functional unit name + c_terminal_type code of the capture terminal type + p_terminal_type code of the playback terminal type ===================== ======================================= diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index bdb82b58b260..53c2518dbf50 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -980,6 +980,8 @@ The uac1 function provides these attributes in its function directory: c_it_ch_name capture channels name c_ot_name capture output terminal name c_fu_vol_name capture mute/volume functional unit name + c_terminal_type code of the capture terminal type + p_terminal_type code of the playback terminal type ================ ==================================================== The attributes have sane default values. diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 861e6219552e..5813fe3e7146 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -1277,6 +1277,8 @@ static void setup_descriptor(struct f_uac1_opts *opts) as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID; as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID; + io_in_it_desc.wTerminalType = cpu_to_le16(opts->c_terminal_type); + io_out_ot_desc.wTerminalType = cpu_to_le16(opts->p_terminal_type); ac_header_desc->wTotalLength = cpu_to_le16(ac_header_desc->bLength); if (EPIN_EN(opts)) { @@ -1898,6 +1900,9 @@ UAC1_ATTRIBUTE_STRING(c_it_ch_name); UAC1_ATTRIBUTE_STRING(c_ot_name); UAC1_ATTRIBUTE_STRING(c_fu_vol_name); +UAC1_ATTRIBUTE(s16, p_terminal_type); +UAC1_ATTRIBUTE(s16, c_terminal_type); + static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_c_chmask, &f_uac1_opts_attr_c_srate, @@ -1935,6 +1940,9 @@ static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_c_ot_name, &f_uac1_opts_attr_c_fu_vol_name, + &f_uac1_opts_attr_p_terminal_type, + &f_uac1_opts_attr_c_terminal_type, + NULL, }; @@ -2003,6 +2011,9 @@ static struct usb_function_instance *f_audio_alloc_inst(void) scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "Playback Output terminal"); scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume"); + opts->p_terminal_type = UAC1_DEF_P_TERM_TYPE; + opts->c_terminal_type = UAC1_DEF_C_TERM_TYPE; + return &opts->func_inst; } diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index c7e7480bf71f..df29018096d3 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -30,6 +30,8 @@ #define UAC1_DEF_MAX_DB 0 /* 0 dB */ #define UAC1_DEF_RES_DB (1*256) /* 1 dB */ +#define UAC1_DEF_P_TERM_TYPE UAC_INPUT_TERMINAL_MICROPHONE +#define UAC1_DEF_C_TERM_TYPE UAC_OUTPUT_TERMINAL_SPEAKER struct f_uac1_opts { struct usb_function_instance func_inst; @@ -71,6 +73,9 @@ struct f_uac1_opts { char c_ot_name[USB_MAX_STRING_LEN]; char c_fu_vol_name[USB_MAX_STRING_LEN]; + s16 p_terminal_type; + s16 c_terminal_type; + struct mutex lock; int refcnt; }; From patchwork Sat Sep 28 15:08:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814704 Received: from mail-qv1-f44.google.com (mail-qv1-f44.google.com [209.85.219.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BF63818786C; Sat, 28 Sep 2024 15:09:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536177; cv=none; b=iBD4gdov0NLSEogYgkVB831b2wdUArtXJr/zUwSf/X2SzmIqpIwdvYWqp5ojzlKnA6F47K0N7gey10F2tKXcJkw+U2y2Z9QkQn7ZxJjVw0Jeso8BDwsGrhBynxyslasWNkZ8l3SVPuWkvXxRVTT807LJbADGIwFxkPmED+JWYTY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536177; c=relaxed/simple; bh=OdQWnJdTMpXJN8De8O2WDiJv+DfKAtTFaJYHxL0kmrk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QaEfzA7jqOoT5TscrEdBM6SiPEsxP7m3B824k3vlTd9sqN0ulg9ep3LH7M+8BfAaVKZoSL2DPGTGwbcWYL7JGfxzqX4Czrw2kDy4pb76E/++M4k8RSf/Lm7z0GUFD2xmYN/ybQq14uFxOZ7tTX5R/qRhemPGZT/SA5tgs06frXg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=mwkH0Oe5; arc=none smtp.client-ip=209.85.219.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mwkH0Oe5" Received: by mail-qv1-f44.google.com with SMTP id 6a1803df08f44-6cb313711a9so4452156d6.0; Sat, 28 Sep 2024 08:09:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536173; x=1728140973; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=F3ZmJY6RwEUv236Or6gK4E9hSSjCqNV2s31MqAFEavs=; b=mwkH0Oe5zFreR7iyyplPusYg/sEDAmghSF734vTWZTkllq1AM35dlRQs4fyxg34Hoa hV8SPo+clKadksT8uG5CKm4LCEvqFTiWAVwhugTdi9VchUta4B2bmIXVo1Yz9eTzXz9M btyCQ+FmDUk0lc5AGOOu/wCmfZjQt8WqyULgGRLluKkE5po7ugtr0lbgbJF8eRXONf6W xrTRaXLbBQLGW6hGKhUnRFdRRww33AG3FRnkapIbs0P7A/LjVdxVpk1RUaEOj/tSz6LH EwnjZPy9m2nVCmXpCRdRz4N4VlqRRaRUboASBEQrJAhpqtlxUNgorB/+xVZPT3pyl37U HfsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536173; x=1728140973; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=F3ZmJY6RwEUv236Or6gK4E9hSSjCqNV2s31MqAFEavs=; b=DNRLp9n+OmXBvOio+WPXqWJwgR2pVCr3YK909C5HGE806KsFIOFxr5tIrZOtj79JpB kMUPWrkkw9F5YLDKNtigKl7OVSiQ0AcruagL3ku7N76ICtD8Q9Vib+cttVC4hAkQP1+j 35PQi95q6Xq1jDFE8fK9/iN3jXF0417U1sMWvWbU4Y6jqOIFOTI8FEB07DjGgS6tFpPC CjawzsRF9ZvZxyTFqe7nJMuebCzIj4RJqPWNjXw0QQbf4LbjYDirGVGxc4cPr3YXebQt wfBZQBRVYx9CZxzcUW5aF7RgnPgn4FXV7gNl+Phj2AQhEOEQ6LP2zobnGQ2j8Xt6UXzm s/kA== X-Forwarded-Encrypted: i=1; AJvYcCVE2brB6DaWu6QTKnHzvUl1OhhSWizJkOyZbnAfexs67iZ0lN8nqTtDbTHJaattv6XIsYaISyTZvhgAI3zN@vger.kernel.org, AJvYcCXUyXTobv8RZepxApyvz5DEhLpVb2CE9whhxpnqo76p0wIAN1GFhtfLccBb0pzuSsCMzOMEHVQIGh8=@vger.kernel.org X-Gm-Message-State: AOJu0YzgS2PFmwxvpitnG+JugBiuJQcu9QsiHpj2ULs7zrEmBNyU+Iru o7UCBhB9Y8rl5i4BtDqA106knBapbkeCeECm4d5aEi88J3T4NEfdP9NzMQg3GRc= X-Google-Smtp-Source: AGHT+IGNdUzIqzXX/P+/cQtiq1Gf916Wi79LL2yrUhiS4EdsJN5gw2YgjBvbbIgk24AUpv2NaPLnXg== X-Received: by 2002:ac8:5787:0:b0:458:2c28:ebac with SMTP id d75a77b69052e-45cadb6922amr19064901cf.4.1727536172814; Sat, 28 Sep 2024 08:09:32 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:32 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 07/14] usb: gadget: f_uac1: Add alt mode settings interface Date: Sat, 28 Sep 2024 11:08:58 -0400 Message-ID: <20240928150905.2616313-8-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff Add the ability to create c_alt.x and p_alt.x directories to have different configurations for channels/sample size/sync mode. This patch only implements the user interface and does not yet alter the behavior of the function. Initial values for the alt mode settings are copied from the main settings at the time the alt mode directory is created. Signed-off-by: Chris Wulff --- Documentation/usb/gadget-testing.rst | 19 ++ drivers/usb/gadget/function/f_uac1.c | 311 ++++++++++++++++++++++++--- drivers/usb/gadget/function/u_uac1.h | 79 +++++-- 3 files changed, 367 insertions(+), 42 deletions(-) diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 53c2518dbf50..5aaf03cf8ebf 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -982,10 +982,29 @@ The uac1 function provides these attributes in its function directory: c_fu_vol_name capture mute/volume functional unit name c_terminal_type code of the capture terminal type p_terminal_type code of the playback terminal type + c_alt.x/ alternate capture setting x (0-255) + p_alt.x/ alternate playback setting x (0-255) ================ ==================================================== The attributes have sane default values. +Alternate settings have these attributes settable. Defaults are copied +from the associated function-wide settings. Alternate setting 0 only +has a name and no other settings. If p/c_alt.1 doesn't exist +function-wide settings will be used for alternate setting 1. + + ================ ==================================================== + name alternate setting name + chmask channel mask + ssize sample size (bytes) + sync synchronization type (async/adaptive) *capture only* + hs_bint bInterval for HS/SS (1-4: fixed, 0: auto) + it_name input terminal name + it_ch_name first input channel name + ot_name output terminal name + fu_vol_name mute/volume functional unit name + ================ ==================================================== + Testing the UAC1 function ------------------------- diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 5813fe3e7146..e7bfc32387bf 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -21,6 +21,9 @@ #include "u_uac1.h" #include "u_uac_utils.h" +#define HOST_TO_DEVICE 0 +#define DEVICE_TO_HOST 1 + /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ #define UAC1_CHANNEL_MASK 0x0FFF @@ -1160,29 +1163,14 @@ f_audio_suspend(struct usb_function *f) /*-------------------------------------------------------------------------*/ -static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_opts *opts, - struct usb_endpoint_descriptor *ep_desc, - enum usb_device_speed speed, bool is_playback) +static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_alt_opts *alt_opts, + struct usb_endpoint_descriptor *ep_desc, + enum usb_device_speed speed, bool is_playback) { - int chmask, srate, ssize, hs_bint, sync; - - if (is_playback) { - chmask = opts->p_chmask; - srate = get_max_srate(opts->p_srates); - ssize = opts->p_ssize; - hs_bint = opts->p_hs_bint; - sync = USB_ENDPOINT_SYNC_ASYNC; - } else { - chmask = opts->c_chmask; - srate = get_max_srate(opts->c_srates); - ssize = opts->c_ssize; - hs_bint = opts->c_hs_bint; - sync = opts->c_sync; - } - return uac_set_ep_max_packet_size_bint( - dev, ep_desc, speed, is_playback, hs_bint, chmask, - srate, ssize, sync, opts->fb_max); + dev, ep_desc, speed, is_playback, alt_opts->hs_bint, + alt_opts->chmask, get_max_srate(alt_opts->srates), + alt_opts->ssize, alt_opts->sync, alt_opts->c.opts->fb_max); } static struct uac_feature_unit_descriptor *build_fu_desc(int chmask) @@ -1459,6 +1447,52 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) return 0; } +/*-------------------------------------------------------------------------*/ + +/* + * Configfs alt mode handling + */ + +static void init_alt_0_opts(struct f_uac1_alt_0_opts *alt_0_opts, + struct f_uac1_opts *opts, int playback) +{ + alt_0_opts->c.opts = opts; + alt_0_opts->c.alt_num = 0; + + // Note: Strings are from the host perspective, opt prefixes are from the device perspective + scnprintf(alt_0_opts->name, sizeof(alt_0_opts->name), + (!playback) ? "Playback Inactive" : "Capture Inactive"); +} + +static void init_alt_opts(struct f_uac1_alt_opts *alt_opts, struct f_uac1_opts *opts, + int alt_num, int playback) +{ + alt_opts->c.opts = opts; + alt_opts->c.alt_num = alt_num; + + INIT_LIST_HEAD(&alt_opts->list); + + // Note: Strings are from the host perspective, opt prefixes are from the device perspective + scnprintf(alt_opts->name, sizeof(alt_opts->name), + (!playback) ? "Playback Active" : "Capture Active"); + strscpy(alt_opts->it_name, (playback) ? opts->p_it_name : opts->c_it_name, + sizeof(alt_opts->it_name)); + strscpy(alt_opts->it_ch_name, (playback) ? opts->p_it_ch_name : opts->c_it_ch_name, + sizeof(alt_opts->it_ch_name)); + strscpy(alt_opts->ot_name, (playback) ? opts->p_ot_name : opts->c_ot_name, + sizeof(alt_opts->ot_name)); + strscpy(alt_opts->fu_vol_name, (playback) ? opts->p_fu_vol_name : opts->c_fu_vol_name, + sizeof(alt_opts->fu_vol_name)); + + /* Copy default options from the main opts */ + alt_opts->chmask = (playback) ? opts->p_chmask : opts->c_chmask; + alt_opts->ssize = (playback) ? opts->p_ssize : opts->c_ssize; + alt_opts->hs_bint = (playback) ? opts->p_hs_bint : opts->c_hs_bint; + alt_opts->srates = (playback) ? opts->p_srates : opts->c_srates; + alt_opts->sync = (playback) ? 0 : opts->c_sync; + alt_opts->terminal_type = (playback) ? opts->p_terminal_type : opts->c_terminal_type; +} + /* audio function driver setup/binding */ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) { @@ -1474,12 +1508,24 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) int status; int idx, i; + audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); + + /* Copy main options to alt modes 0/1 if the groups don't exist + * before validation since they will be checked. + */ + if (!audio_opts->c_alt_0_opts.c.group.cg_item.ci_name) + init_alt_0_opts(&audio_opts->c_alt_0_opts, audio_opts, HOST_TO_DEVICE); + if (!audio_opts->p_alt_0_opts.c.group.cg_item.ci_name) + init_alt_0_opts(&audio_opts->p_alt_0_opts, audio_opts, DEVICE_TO_HOST); + if (!audio_opts->c_alt_1_opts.c.group.cg_item.ci_name) + init_alt_opts(&audio_opts->c_alt_1_opts, audio_opts, 1, HOST_TO_DEVICE); + if (!audio_opts->p_alt_1_opts.c.group.cg_item.ci_name) + init_alt_opts(&audio_opts->p_alt_1_opts, audio_opts, 1, DEVICE_TO_HOST); + status = f_audio_validate_opts(audio, dev); if (status) return status; - audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); - strings_uac1[STR_AC_IF].s = audio_opts->function_name; strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name; @@ -1656,42 +1702,42 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) ss_as_out_ep_desc.bInterval = audio_opts->c_hs_bint; /* Calculate wMaxPacketSize according to audio bandwidth */ - status = set_ep_max_packet_size_bint(dev, audio_opts, &fs_as_in_ep_desc, + status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &fs_as_in_ep_desc, USB_SPEED_FULL, true); if (status < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err_free_fu; } - status = set_ep_max_packet_size_bint(dev, audio_opts, &fs_as_out_ep_desc, + status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &fs_as_out_ep_desc, USB_SPEED_FULL, false); if (status < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err_free_fu; } - status = set_ep_max_packet_size_bint(dev, audio_opts, &hs_as_in_ep_desc, + status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &hs_as_in_ep_desc, USB_SPEED_HIGH, true); if (status < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err_free_fu; } - status = set_ep_max_packet_size_bint(dev, audio_opts, &hs_as_out_ep_desc, + status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &hs_as_out_ep_desc, USB_SPEED_HIGH, false); if (status < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err_free_fu; } - status = set_ep_max_packet_size_bint(dev, audio_opts, &ss_as_in_ep_desc, + status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &ss_as_in_ep_desc, USB_SPEED_SUPER, true); if (status < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err_free_fu; } - status = set_ep_max_packet_size_bint(dev, audio_opts, &hs_as_out_ep_desc, + status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &hs_as_out_ep_desc, USB_SPEED_SUPER, false); if (status < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); @@ -1835,6 +1881,108 @@ static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item) func_inst.group); } +static inline struct f_uac1_alt_opts *to_f_uac1_alt_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uac1_alt_opts, + c.group); +} + +#define UAC1_ALT_ATTR_TO_OPTS struct f_uac1_alt_opts *alt_opts = to_f_uac1_alt_opts(item) +#define UAC1_ALT_ATTRIBUTE(type, name) \ + UAC_ATTRIBUTE(f_uac1_alt_opts, UAC1_ALT_ATTR_TO_OPTS, alt_opts, \ + alt_opts->c.opts->lock, alt_opts->c.opts->refcnt, \ + type, name) + +#define UAC1_ALT_ATTRIBUTE_SYNC(name) \ + UAC_ATTRIBUTE_SYNC(f_uac1_alt_opts, UAC1_ALT_ATTR_TO_OPTS, \ + alt_opts, alt_opts->c.opts->lock, \ + alt_opts->c.opts->refcnt, name) + +#define UAC1_ALT_ATTRIBUTE_STRING(name) \ + UAC_ATTRIBUTE_STRING(f_uac1_alt_opts, UAC1_ALT_ATTR_TO_OPTS, \ + alt_opts, alt_opts->c.opts->lock, \ + alt_opts->c.opts->refcnt, name) + + +UAC1_ALT_ATTRIBUTE_STRING(name); +UAC1_ALT_ATTRIBUTE_STRING(it_name); +UAC1_ALT_ATTRIBUTE_STRING(it_ch_name); +UAC1_ALT_ATTRIBUTE_STRING(ot_name); +UAC1_ALT_ATTRIBUTE_STRING(fu_vol_name); + +UAC1_ALT_ATTRIBUTE(u32, ssize); +UAC1_ALT_ATTRIBUTE(u8, hs_bint); +UAC1_ALT_ATTRIBUTE(u32, chmask); +UAC1_ALT_ATTRIBUTE_SYNC(sync); +UAC1_ALT_ATTRIBUTE(s16, terminal_type); + +static struct configfs_attribute *f_uac1_alt_0_attrs[] = { + &f_uac1_alt_opts_attr_name, + + NULL, +}; + +static const struct config_item_type alt_mode_0_type = { + .ct_attrs = f_uac1_alt_0_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_attribute *f_uac1_alt_attrs_c[] = { + &f_uac1_alt_opts_attr_name, + &f_uac1_alt_opts_attr_it_name, + &f_uac1_alt_opts_attr_it_ch_name, + &f_uac1_alt_opts_attr_ot_name, + &f_uac1_alt_opts_attr_fu_vol_name, + &f_uac1_alt_opts_attr_ssize, + &f_uac1_alt_opts_attr_hs_bint, + &f_uac1_alt_opts_attr_chmask, + &f_uac1_alt_opts_attr_sync, + &f_uac1_alt_opts_attr_terminal_type, + + NULL, +}; + +static struct configfs_attribute *f_uac1_alt_attrs_p[] = { + &f_uac1_alt_opts_attr_name, + &f_uac1_alt_opts_attr_it_name, + &f_uac1_alt_opts_attr_it_ch_name, + &f_uac1_alt_opts_attr_ot_name, + &f_uac1_alt_opts_attr_fu_vol_name, + &f_uac1_alt_opts_attr_ssize, + &f_uac1_alt_opts_attr_hs_bint, + &f_uac1_alt_opts_attr_chmask, + &f_uac1_alt_opts_attr_terminal_type, + + NULL, +}; + +static void f_uac1_alt_attr_release(struct config_item *item) +{ + struct f_uac1_alt_opts *alt_opts = to_f_uac1_alt_opts(item); + + /* Opts 0 and 1 are fixed structures, 2+ are kzalloc'd */ + if (alt_opts->c.alt_num > 1) + kfree(alt_opts); +} + +static struct configfs_item_operations f_uac1_alt_item_ops = { + .release = f_uac1_alt_attr_release, +}; + +static const struct config_item_type alt_mode_c_type = { + .ct_item_ops = &f_uac1_alt_item_ops, + .ct_attrs = f_uac1_alt_attrs_c, + .ct_owner = THIS_MODULE, +}; + +static const struct config_item_type alt_mode_p_type = { + .ct_item_ops = &f_uac1_alt_item_ops, + .ct_attrs = f_uac1_alt_attrs_p, + .ct_owner = THIS_MODULE, +}; + +/*-------------------------------------------------------------------------*/ + static void f_uac1_attr_release(struct config_item *item) { struct f_uac1_opts *opts = to_f_uac1_opts(item); @@ -1946,8 +2094,109 @@ static struct configfs_attribute *f_uac1_attrs[] = { NULL, }; +static struct config_group *f_uac1_group_make( + struct config_group *group, + const char *name) +{ + struct f_uac1_opts *opts = to_f_uac1_opts(&group->cg_item); + struct f_uac1_alt_opts *alt_opts; + struct f_uac1_alt_opts *pos; + struct config_group *ret; + unsigned int alt_num; + int playback = 0; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = ERR_PTR(-EBUSY); + goto end; + } + + if (sscanf(name, "c_alt.%u", &alt_num) != 1) { + playback = 1; + if (sscanf(name, "p_alt.%u", &alt_num) != 1) { + ret = ERR_PTR(-EINVAL); + goto end; + } + } + + if (alt_num > 255) { + ret = ERR_PTR(-EINVAL); + goto end; + } + + /* Alt mode 0 has less properties */ + if (alt_num == 0) { + struct f_uac1_alt_0_opts *alt_0_opts = (playback) ? &opts->p_alt_0_opts + : &opts->c_alt_0_opts; + init_alt_0_opts(alt_0_opts, opts, playback); + config_group_init_type_name(&alt_0_opts->c.group, name, &alt_mode_0_type); + ret = &alt_0_opts->c.group; + goto end; + } + + if (alt_num == 1) { + /* Alt mode 1 always exists */ + alt_opts = (playback) ? &opts->p_alt_1_opts : &opts->c_alt_1_opts; + } else { + /* Allocate a structure for alt mode 2+ */ + alt_opts = kzalloc(sizeof(*alt_opts), GFP_KERNEL); + if (!alt_opts) { + ret = ERR_PTR(-ENOMEM); + goto end; + } + } + + ret = &alt_opts->c.group; + + config_group_init_type_name(&alt_opts->c.group, name, (playback) ? &alt_mode_p_type + : &alt_mode_c_type); + + init_alt_opts(alt_opts, opts, alt_num, playback); + + /* Alt mode 1 doesn't go in the list. It is handled separately to + * also handle the case where the alt.1 group is not created. + */ + if (alt_num == 1) + goto end; + + /* Insert the new alt mode in the list, sorted by alt_num */ + list_for_each_entry(pos, (playback) ? &opts->p_alt_opts : &opts->c_alt_opts, list) { + if (alt_opts->c.alt_num < pos->c.alt_num) { + list_add_tail(&alt_opts->list, &pos->list); + goto end; + } + } + + list_add_tail(&alt_opts->list, (playback) ? &opts->p_alt_opts : &opts->c_alt_opts); + +end: + mutex_unlock(&opts->lock); + + return ret; +} + +static void f_uac1_group_drop(struct config_group *group, struct config_item *item) +{ + struct f_uac1_alt_opts *alt_opts = to_f_uac1_alt_opts(item); + + /* Alt modes 0 and 1 are preallocated and not included in the list */ + if (alt_opts->c.alt_num > 1) { + mutex_lock(&alt_opts->c.opts->lock); + list_del(&alt_opts->list); + mutex_unlock(&alt_opts->c.opts->lock); + } + + config_item_put(item); +} + +static struct configfs_group_operations f_uac1_group_ops = { + .make_group = &f_uac1_group_make, + .drop_item = &f_uac1_group_drop, +}; + static const struct config_item_type f_uac1_func_type = { .ct_item_ops = &f_uac1_item_ops, + .ct_group_ops = &f_uac1_group_ops, .ct_attrs = f_uac1_attrs, .ct_owner = THIS_MODULE, }; @@ -1971,6 +2220,9 @@ static struct usb_function_instance *f_audio_alloc_inst(void) mutex_init(&opts->lock); opts->func_inst.free_func_inst = f_audio_free_inst; + INIT_LIST_HEAD(&opts->c_alt_opts); + INIT_LIST_HEAD(&opts->p_alt_opts); + config_group_init_type_name(&opts->func_inst.group, "", &f_uac1_func_type); @@ -2001,6 +2253,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void) scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface"); + // Note: Strings are from the host perspective, opt prefixes are from the device perspective scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "Capture Input terminal"); scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels"); scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "Capture Output terminal"); diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index df29018096d3..ae69f1eb872d 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -33,8 +33,61 @@ #define UAC1_DEF_P_TERM_TYPE UAC_INPUT_TERMINAL_MICROPHONE #define UAC1_DEF_C_TERM_TYPE UAC_OUTPUT_TERMINAL_SPEAKER + +struct f_uac1_opts; + +struct f_uac1_alt_opts_common { + struct config_group group; + struct f_uac1_opts *opts; + u8 alt_num; +}; + +/* Alt mode 0 only has a name */ +struct f_uac1_alt_0_opts { + struct f_uac1_alt_opts_common c; + + char name[USB_MAX_STRING_LEN]; +}; + +/* Alt modes 1+ */ +struct f_uac1_alt_opts { + struct f_uac1_alt_opts_common c; + + struct list_head list; + + /* Strings */ + char name[USB_MAX_STRING_LEN]; + char it_name[USB_MAX_STRING_LEN]; + char it_ch_name[USB_MAX_STRING_LEN]; + char ot_name[USB_MAX_STRING_LEN]; + char fu_vol_name[USB_MAX_STRING_LEN]; + + /* Audio options */ + int chmask; + int *srates; /* Reference to p/c_srates in opts */ + int sync; + int ssize; + u8 hs_bint; + s16 terminal_type; + +}; + struct f_uac1_opts { struct usb_function_instance func_inst; + + /* Alt mode 0 options */ + struct f_uac1_alt_0_opts c_alt_0_opts; + struct f_uac1_alt_0_opts p_alt_0_opts; + + /* Alt mode 1 options */ + struct f_uac1_alt_opts c_alt_1_opts; + struct f_uac1_alt_opts p_alt_1_opts; + + /* Alt mode 2+ options */ + struct list_head c_alt_opts; + struct list_head p_alt_opts; + + /* Default options and Alt mode 1 if no c/p_alt.1 created */ int c_chmask; int c_srates[UAC_MAX_RATES]; int c_sync; @@ -45,14 +98,14 @@ struct f_uac1_opts { int p_ssize; u8 p_hs_bint; - bool p_mute_present; - bool p_volume_present; + bool p_mute_present; + bool p_volume_present; s16 p_volume_min; s16 p_volume_max; s16 p_volume_res; - bool c_mute_present; - bool c_volume_present; + bool c_mute_present; + bool c_volume_present; s16 c_volume_min; s16 c_volume_max; s16 c_volume_res; @@ -61,17 +114,17 @@ struct f_uac1_opts { int fb_max; unsigned bound:1; - char function_name[USB_MAX_STRING_LEN]; + char function_name[USB_MAX_STRING_LEN]; - char p_it_name[USB_MAX_STRING_LEN]; - char p_it_ch_name[USB_MAX_STRING_LEN]; - char p_ot_name[USB_MAX_STRING_LEN]; - char p_fu_vol_name[USB_MAX_STRING_LEN]; + char p_it_name[USB_MAX_STRING_LEN]; + char p_it_ch_name[USB_MAX_STRING_LEN]; + char p_ot_name[USB_MAX_STRING_LEN]; + char p_fu_vol_name[USB_MAX_STRING_LEN]; - char c_it_name[USB_MAX_STRING_LEN]; - char c_it_ch_name[USB_MAX_STRING_LEN]; - char c_ot_name[USB_MAX_STRING_LEN]; - char c_fu_vol_name[USB_MAX_STRING_LEN]; + char c_it_name[USB_MAX_STRING_LEN]; + char c_it_ch_name[USB_MAX_STRING_LEN]; + char c_ot_name[USB_MAX_STRING_LEN]; + char c_fu_vol_name[USB_MAX_STRING_LEN]; s16 p_terminal_type; s16 c_terminal_type; From patchwork Sat Sep 28 15:08:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814705 Received: from mail-qv1-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7183C1885B2; Sat, 28 Sep 2024 15:09:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536178; cv=none; b=it8Uj0gXvQ8jYEheJHG1VUsMHNMgfZ1UhpM1r2d+feGAqmnpWyBUkrdg2yEHcG/RNOmbVPr0V2N06TQxmx6aWWJZHZhqAj3RahUyJpYuPFFybxIxkhKiWgYq9WmIYiyGfS0gvFeDbczEeqlFurX7af90rUcU/KlaUtiBB8/fiRI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536178; c=relaxed/simple; bh=hbscq2OOJ0gv/BSvUFXETU2H7thYLLa+UttHXS8QaXA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gEKJgQ0tnHY2xvXoFvKme+ne6PzL9eQ5qpRNVsc/IzYXz10jotm3ajOr3Ov9JSDJbOLd0CL+arlE7skHW/bgdyfDJy8ZZxfwTInvc1toCYQHAo4QeAHIVs/yHd2rQmkEnErxL4XUIbclXWaZi1WEKoQDbi8gMnsclDXZMwXG2Hw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=RJ4uxlDO; arc=none smtp.client-ip=209.85.219.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RJ4uxlDO" Received: by mail-qv1-f50.google.com with SMTP id 6a1803df08f44-6cb316db199so4544986d6.2; Sat, 28 Sep 2024 08:09:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536175; x=1728140975; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=57dniUL3gyaElSNWffcZ2ps14gArsQhpLIP8r97nfis=; b=RJ4uxlDOpbIJ7hdWyQI79yPBbVNl8caxSKNNazgmDooSFpJjMBpp8yCkfImaZ+4StL 7+SHBqjPyOQ8yV72oXVRqoa7y39D9ImNIBb7GUCYTtG2hpzoUw9W83VqK43BamcHDEDG DY9Wypc2F1bZnp44Y7gAcer4mcTnTsgHh90nOeoA5Ws4516fkKBtfJkGl1cJZaet9Bid gADk1+rldNZhShfea/MfrD9yP5PdCINKpSb/6XPGkr38SyD9bdxviddVBQK1kVPKFTYp RuZ36wywFyYxNsF26Eeh9Uo1cpo1BUaB1tYFGB6YFm/w2qNrsfsS3J89+U9ptb964DLZ wL5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536175; x=1728140975; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=57dniUL3gyaElSNWffcZ2ps14gArsQhpLIP8r97nfis=; b=EXUdPAJiW0krHgifVID9mz2DZLtX4J1tnzZX923lQniONhcPeQbJmnNSdRiosBfW8V n4+1drb5GSiPehoXP2dpvcMpZX6wUiEaBDSUAFwBRIoeRcAfDSQEsyTqGjFRw+GWGoWp K9tsoL1fwKmcwloOyaQpSsRvEydMY9jei12/lo7BEFVfEBYy5ay2OqM3ZxXnO3cDy+AE xn0ffi8AGy/74cibaACmGNCmo+j0WPoomb0DYIr94nQoxRFRFM0Hq4KmY2MPILl+HTuE rTMlczgCcFKMPvQAAGTawHu2jrgvdQBcLLlj83pRy2K2Ti342XAKg3Vc83N3sNgXBeM9 OswQ== X-Forwarded-Encrypted: i=1; AJvYcCXtUvJbSdmg/n2n+eQr9OY44q9NTN7JNlcf736ZFbumMGV01pnrfmRG6mhymhFZ12EgRlFvTgmfQAJpapg+@vger.kernel.org, AJvYcCXwKySGaD1ze2+Avla30HGy+4Vg3hAXZOwA/9qpBu/QF1RBbFYTDaLedIVMIVdyqQZoYtFLdeH7d3c=@vger.kernel.org X-Gm-Message-State: AOJu0Yx/EIv6TF2QuBwGB9Zk9RaP3/Usu4ikg/iceihdg3SbY4gSA7X3 9J2T4fDB7jGT3ly/+h+9BY+Mxdlc24bKuJrtCxyomrSLbvPT/HX4CPbHsrwgsOw= X-Google-Smtp-Source: AGHT+IHNk2Y4KS9JOIrcFQ+dHiEueHwBJReggiYVq6sUBxfVRSCa71e+SaAtYNQku+F0dyDrfM/D7Q== X-Received: by 2002:a05:622a:1a99:b0:451:9e79:a100 with SMTP id d75a77b69052e-45cadb4f84amr25136951cf.3.1727536174785; Sat, 28 Sep 2024 08:09:34 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:34 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 08/14] usb: gadget: f_uac2: Add alt mode settings interface Date: Sat, 28 Sep 2024 11:08:59 -0400 Message-ID: <20240928150905.2616313-9-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff Add the ability to create c_alt.x and p_alt.x directories to have different configurations for channels/sample size/sync mode. This patch only implements the user interface and does not yet alter the behavior of the function. Initial values for the alt mode settings are copied from the main settings at the time the alt mode directory is created. Signed-off-by: Chris Wulff --- Documentation/usb/gadget-testing.rst | 21 +- drivers/usb/gadget/function/f_uac2.c | 313 ++++++++++++++++++++++++--- drivers/usb/gadget/function/u_uac2.h | 91 ++++++-- 3 files changed, 379 insertions(+), 46 deletions(-) diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 5aaf03cf8ebf..67cae833f246 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -762,7 +762,7 @@ The uac2 function provides these attributes in its function directory: p_volume_max playback volume control max value (in 1/256 dB) p_volume_res playback volume control resolution (in 1/256 dB) p_hs_bint playback bInterval for HS/SS (1-4: fixed, 0: auto) - req_number the number of pre-allocated request for both capture + req_number the number of pre-allocated requests for both capture and playback function_name name of the interface if_ctrl_name topology control name @@ -778,10 +778,29 @@ The uac2 function provides these attributes in its function directory: c_fu_vol_name capture functional unit name c_terminal_type code of the capture terminal type p_terminal_type code of the playback terminal type + c_alt.x/ alternate capture setting x (0-255) + p_alt.x/ alternate playback setting x (0-255) ================ ==================================================== The attributes have sane default values. +Alternate settings have these attributes settable. Defaults are copied +from the associated function-wide settings. Alternate setting 0 only +has a name and no other settings. If p/c_alt.1 doesn't exist +function-wide settings will be used for alternate setting 1. + + ================ ==================================================== + name alternate setting name + chmask channel mask + ssize sample size (bytes) + sync synchronization type (async/adaptive) *capture only* + hs_bint bInterval for HS/SS (1-4: fixed, 0: auto) + it_name input terminal name + it_ch_name first input channel name + ot_name output terminal name + fu_vol_name mute/volume functional unit name + ================ ==================================================== + Testing the UAC2 function ------------------------- diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 050789d2d3c9..e9f951215c26 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -19,6 +19,9 @@ #include "u_uac2.h" #include "u_uac_utils.h" +#define HOST_TO_DEVICE 0 +#define DEVICE_TO_HOST 1 + /* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */ #define UAC2_CHANNEL_MASK 0x07FFFFFF @@ -646,29 +649,15 @@ struct cntrl_subrange_lay3 { DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES); -static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_opts *uac2_opts, +static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_alt_opts *alt_opts, struct usb_endpoint_descriptor *ep_desc, enum usb_device_speed speed, bool is_playback) { - int chmask, srate, ssize, hs_bint, sync; - - if (is_playback) { - chmask = uac2_opts->p_chmask; - srate = get_max_srate(uac2_opts->p_srates); - ssize = uac2_opts->p_ssize; - hs_bint = uac2_opts->p_hs_bint; - sync = USB_ENDPOINT_SYNC_ASYNC; - } else { - chmask = uac2_opts->c_chmask; - srate = get_max_srate(uac2_opts->c_srates); - ssize = uac2_opts->c_ssize; - hs_bint = uac2_opts->c_hs_bint; - sync = uac2_opts->c_sync; - } - return uac_set_ep_max_packet_size_bint( - dev, ep_desc, speed, is_playback, hs_bint, chmask, - srate, ssize, sync, uac2_opts->fb_max); + dev, ep_desc, speed, is_playback, alt_opts->hs_bint, alt_opts->chmask, + get_max_srate(is_playback ? alt_opts->c.opts->p_srates + : alt_opts->c.opts->c_srates), + alt_opts->ssize, alt_opts->sync, alt_opts->c.opts->fb_max); } static struct uac2_feature_unit_descriptor *build_fu_desc(int chmask) @@ -926,6 +915,57 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev) return 0; } +/*-------------------------------------------------------------------------*/ + +/* + * Configfs alt mode handling + */ + +static void init_alt_0_opts(struct f_uac2_alt_0_opts *alt_0_opts, + struct f_uac2_opts *opts, int playback) +{ + alt_0_opts->c.opts = opts; + alt_0_opts->c.alt_num = 0; + + // Note: Strings are from the host perspective, opt prefixes are from the device perspective + scnprintf(alt_0_opts->name, sizeof(alt_0_opts->name), + (!playback) ? "Playback Inactive" : "Capture Inactive"); +} + +static void init_alt_opts(struct f_uac2_alt_opts *alt_opts, struct f_uac2_opts *opts, + int alt_num, int playback) +{ + alt_opts->c.opts = opts; + alt_opts->c.alt_num = alt_num; + + INIT_LIST_HEAD(&alt_opts->list); + + // Note: Strings are from the host perspective, opt prefixes are from the device perspective + scnprintf(alt_opts->name, sizeof(alt_opts->name), + (!playback) ? "Playback Active" : "Capture Active"); + strscpy(alt_opts->it_name, (playback) ? opts->p_it_name : opts->c_it_name, + sizeof(alt_opts->it_name)); + strscpy(alt_opts->it_ch_name, (playback) ? opts->p_it_ch_name : opts->c_it_ch_name, + sizeof(alt_opts->it_ch_name)); + strscpy(alt_opts->ot_name, (playback) ? opts->p_ot_name : opts->c_ot_name, + sizeof(alt_opts->ot_name)); + strscpy(alt_opts->fu_vol_name, (playback) ? opts->p_fu_vol_name : opts->c_fu_vol_name, + sizeof(alt_opts->fu_vol_name)); + + /* Copy default options from the main opts */ + alt_opts->chmask = (playback) ? opts->p_chmask : opts->c_chmask; + alt_opts->ssize = (playback) ? opts->p_ssize : opts->c_ssize; + alt_opts->sync = (playback) ? USB_ENDPOINT_SYNC_ASYNC : opts->c_sync; /* only for capture */ + alt_opts->hs_bint = (playback) ? opts->p_hs_bint : opts->c_hs_bint; + + /* NOTE: These are backwards with relation to other c_/p_ settings in the existing + * userspace API. Correct terminal type is copied into c/p_alt.x + * (eg p_termial_type == c_alt.x/terminal_type) + */ + alt_opts->terminal_type = (!playback) ? opts->p_terminal_type : opts->c_terminal_type; +} + + static int afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) { @@ -938,6 +978,18 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) struct usb_string *us; int ret; + /* Copy main options to alt modes 0/1 if the groups don't exist + * before validation since they will be checked. + */ + if (!uac2_opts->c_alt_0_opts.c.group.cg_item.ci_name) + init_alt_0_opts(&uac2_opts->c_alt_0_opts, uac2_opts, HOST_TO_DEVICE); + if (!uac2_opts->p_alt_0_opts.c.group.cg_item.ci_name) + init_alt_0_opts(&uac2_opts->p_alt_0_opts, uac2_opts, DEVICE_TO_HOST); + if (!uac2_opts->c_alt_1_opts.c.group.cg_item.ci_name) + init_alt_opts(&uac2_opts->c_alt_1_opts, uac2_opts, 1, HOST_TO_DEVICE); + if (!uac2_opts->p_alt_1_opts.c.group.cg_item.ci_name) + init_alt_opts(&uac2_opts->p_alt_1_opts, uac2_opts, 1, DEVICE_TO_HOST); + ret = afunc_validate_opts(agdev, dev); if (ret) return ret; @@ -1109,42 +1161,42 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ss_epout_desc.bInterval = uac2_opts->c_hs_bint; /* Calculate wMaxPacketSize according to audio bandwidth */ - ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epin_desc, + ret = set_ep_max_packet_size_bint(dev, &uac2_opts->p_alt_1_opts, &fs_epin_desc, USB_SPEED_FULL, true); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } - ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epout_desc, + ret = set_ep_max_packet_size_bint(dev, &uac2_opts->c_alt_1_opts, &fs_epout_desc, USB_SPEED_FULL, false); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } - ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epin_desc, + ret = set_ep_max_packet_size_bint(dev, &uac2_opts->p_alt_1_opts, &hs_epin_desc, USB_SPEED_HIGH, true); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } - ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epout_desc, + ret = set_ep_max_packet_size_bint(dev, &uac2_opts->c_alt_1_opts, &hs_epout_desc, USB_SPEED_HIGH, false); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } - ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epin_desc, + ret = set_ep_max_packet_size_bint(dev, &uac2_opts->p_alt_1_opts, &ss_epin_desc, USB_SPEED_SUPER, true); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } - ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epout_desc, + ret = set_ep_max_packet_size_bint(dev, &uac2_opts->c_alt_1_opts, &ss_epout_desc, USB_SPEED_SUPER, false); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); @@ -1773,6 +1825,110 @@ static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item) func_inst.group); } +static inline struct f_uac2_alt_opts *to_f_uac2_alt_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uac2_alt_opts, + c.group); +} + +#define UAC2_ALT_ATTR_TO_OPTS struct f_uac2_alt_opts *alt_opts = to_f_uac2_alt_opts(item) +#define UAC2_ALT_ATTRIBUTE(type, name) \ + UAC_ATTRIBUTE(f_uac2_alt_opts, UAC2_ALT_ATTR_TO_OPTS, alt_opts, \ + alt_opts->c.opts->lock, alt_opts->c.opts->refcnt, \ + type, name) + +#define UAC2_ALT_ATTRIBUTE_SYNC(name) \ + UAC_ATTRIBUTE_SYNC(f_uac2_alt_opts, UAC2_ALT_ATTR_TO_OPTS, \ + alt_opts, alt_opts->c.opts->lock, \ + alt_opts->c.opts->refcnt, name) + +#define UAC2_ALT_ATTRIBUTE_STRING(name) \ + UAC_ATTRIBUTE_STRING(f_uac2_alt_opts, UAC2_ALT_ATTR_TO_OPTS, \ + alt_opts, alt_opts->c.opts->lock, \ + alt_opts->c.opts->refcnt, name) + + +UAC2_ALT_ATTRIBUTE_STRING(name); +UAC2_ALT_ATTRIBUTE_STRING(it_name); +UAC2_ALT_ATTRIBUTE_STRING(it_ch_name); +UAC2_ALT_ATTRIBUTE_STRING(ot_name); +UAC2_ALT_ATTRIBUTE_STRING(fu_vol_name); + +UAC2_ALT_ATTRIBUTE(u32, ssize); +UAC2_ALT_ATTRIBUTE(u32, chmask); +UAC2_ALT_ATTRIBUTE_SYNC(sync); +UAC2_ALT_ATTRIBUTE(u8, hs_bint); +UAC2_ALT_ATTRIBUTE(s16, terminal_type); + +static struct configfs_attribute *f_uac2_alt_0_attrs[] = { + &f_uac2_alt_opts_attr_name, + + NULL, +}; + +static const struct config_item_type alt_mode_0_type = { + .ct_attrs = f_uac2_alt_0_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_attribute *f_uac2_alt_attrs_c[] = { + &f_uac2_alt_opts_attr_name, + &f_uac2_alt_opts_attr_it_name, + &f_uac2_alt_opts_attr_it_ch_name, + &f_uac2_alt_opts_attr_ot_name, + &f_uac2_alt_opts_attr_fu_vol_name, + &f_uac2_alt_opts_attr_ssize, + &f_uac2_alt_opts_attr_chmask, + &f_uac2_alt_opts_attr_sync, + &f_uac2_alt_opts_attr_hs_bint, + &f_uac2_alt_opts_attr_terminal_type, + + NULL, +}; + +static struct configfs_attribute *f_uac2_alt_attrs_p[] = { + &f_uac2_alt_opts_attr_name, + &f_uac2_alt_opts_attr_it_name, + &f_uac2_alt_opts_attr_it_ch_name, + &f_uac2_alt_opts_attr_ot_name, + &f_uac2_alt_opts_attr_fu_vol_name, + &f_uac2_alt_opts_attr_ssize, + &f_uac2_alt_opts_attr_chmask, + /* Playback does not have sync */ + &f_uac2_alt_opts_attr_hs_bint, + &f_uac2_alt_opts_attr_terminal_type, + + NULL, +}; + + +static void f_uac2_alt_attr_release(struct config_item *item) +{ + struct f_uac2_alt_opts *alt_opts = to_f_uac2_alt_opts(item); + + /* Opts 0 and 1 are fixed structures, 2+ are kzalloc'd */ + if (alt_opts->c.alt_num > 1) + kfree(alt_opts); +} + +static struct configfs_item_operations f_uac2_alt_item_ops = { + .release = f_uac2_alt_attr_release, +}; + +static const struct config_item_type alt_mode_c_type = { + .ct_item_ops = &f_uac2_alt_item_ops, + .ct_attrs = f_uac2_alt_attrs_c, + .ct_owner = THIS_MODULE, +}; + +static const struct config_item_type alt_mode_p_type = { + .ct_item_ops = &f_uac2_alt_item_ops, + .ct_attrs = f_uac2_alt_attrs_p, + .ct_owner = THIS_MODULE, +}; + +/*-------------------------------------------------------------------------*/ + static void f_uac2_attr_release(struct config_item *item) { struct f_uac2_opts *opts = to_f_uac2_opts(item); @@ -1889,8 +2045,111 @@ static struct configfs_attribute *f_uac2_attrs[] = { NULL, }; +static struct config_group *f_uac2_group_make( + struct config_group *group, + const char *name) +{ + struct f_uac2_opts *opts = to_f_uac2_opts(&group->cg_item); + struct f_uac2_alt_opts *alt_opts; + struct f_uac2_alt_opts *pos; + struct config_group *ret; + unsigned int alt_num; + int playback = 0; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = ERR_PTR(-EBUSY); + goto end; + } + + if (sscanf(name, "c_alt.%u", &alt_num) != 1) { + playback = 1; + if (sscanf(name, "p_alt.%u", &alt_num) != 1) { + ret = ERR_PTR(-EINVAL); + goto end; + } + } + + if (alt_num > 255) { + ret = ERR_PTR(-EINVAL); + goto end; + } + + /* Alt mode 0 has less properties */ + if (alt_num == 0) { + struct f_uac2_alt_0_opts *alt_0_opts = (playback) ? &opts->p_alt_0_opts + : &opts->c_alt_0_opts; + + init_alt_0_opts(alt_0_opts, opts, playback); + config_group_init_type_name(&alt_0_opts->c.group, name, &alt_mode_0_type); + ret = &alt_0_opts->c.group; + goto end; + } + + if (alt_num == 1) { + /* Alt mode 1 always exists */ + alt_opts = (playback) ? &opts->p_alt_1_opts : &opts->c_alt_1_opts; + } else { + /* Allocate a structure for alt mode 2+ */ + alt_opts = kzalloc(sizeof(*alt_opts), GFP_KERNEL); + if (!alt_opts) { + ret = ERR_PTR(-ENOMEM); + goto end; + } + } + + ret = &alt_opts->c.group; + + config_group_init_type_name(&alt_opts->c.group, name, (playback) ? &alt_mode_p_type + : &alt_mode_c_type); + + init_alt_opts(alt_opts, opts, alt_num, playback); + + /* Alt mode 1 doesn't go in the list. It is handled separately to + * also handle the case where the alt.1 group is not created. + */ + if (alt_num == 1) + goto end; + + /* Insert the new alt mode in the list, sorted by alt_num */ + list_for_each_entry(pos, (playback) ? &opts->p_alt_opts : &opts->c_alt_opts, list) { + if (alt_opts->c.alt_num < pos->c.alt_num) { + list_add_tail(&alt_opts->list, &pos->list); + mutex_unlock(&opts->lock); + goto end; + } + } + + list_add_tail(&alt_opts->list, (playback) ? &opts->p_alt_opts : &opts->c_alt_opts); + +end: + mutex_unlock(&opts->lock); + + return ret; +} + +static void f_uac2_group_drop(struct config_group *group, struct config_item *item) +{ + struct f_uac2_alt_opts *alt_opts = to_f_uac2_alt_opts(item); + + /* Alt modes 0 and 1 are preallocated and not included in the list */ + if (alt_opts->c.alt_num > 1) { + mutex_lock(&alt_opts->c.opts->lock); + list_del(&alt_opts->list); + mutex_unlock(&alt_opts->c.opts->lock); + } + + config_item_put(item); +} + +static struct configfs_group_operations f_uac2_group_ops = { + .make_group = &f_uac2_group_make, + .drop_item = &f_uac2_group_drop, +}; + static const struct config_item_type f_uac2_func_type = { .ct_item_ops = &f_uac2_item_ops, + .ct_group_ops = &f_uac2_group_ops, .ct_attrs = f_uac2_attrs, .ct_owner = THIS_MODULE, }; @@ -1914,6 +2173,9 @@ static struct usb_function_instance *afunc_alloc_inst(void) mutex_init(&opts->lock); opts->func_inst.free_func_inst = afunc_free_inst; + INIT_LIST_HEAD(&opts->c_alt_opts); + INIT_LIST_HEAD(&opts->p_alt_opts); + config_group_init_type_name(&opts->func_inst.group, "", &f_uac2_func_type); @@ -1947,6 +2209,7 @@ static struct usb_function_instance *afunc_alloc_inst(void) scnprintf(opts->clksrc_in_name, sizeof(opts->clksrc_in_name), "Input Clock"); scnprintf(opts->clksrc_out_name, sizeof(opts->clksrc_out_name), "Output Clock"); + // Note: Strings are from the host perspective, opt prefixes are from the device perspective scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "USBD Out"); scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels"); scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "USBH In"); diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index 0df808289ded..8c061e588324 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -40,8 +40,59 @@ #define UAC2_DEF_C_TERM_TYPE 0x201 /* UAC_INPUT_TERMINAL_MICROPHONE*/ +struct f_uac2_opts; + +struct f_uac2_alt_opts_common { + struct config_group group; + struct f_uac2_opts *opts; + u8 alt_num; +}; + +/* Alt mode 0 only has a name */ +struct f_uac2_alt_0_opts { + struct f_uac2_alt_opts_common c; + + char name[USB_MAX_STRING_LEN]; +}; + +/* Alt modes 1+ */ +struct f_uac2_alt_opts { + struct f_uac2_alt_opts_common c; + + struct list_head list; + + /* Strings */ + char name[USB_MAX_STRING_LEN]; + char it_name[USB_MAX_STRING_LEN]; + char it_ch_name[USB_MAX_STRING_LEN]; + char ot_name[USB_MAX_STRING_LEN]; + char fu_vol_name[USB_MAX_STRING_LEN]; + + /* Audio options */ + int chmask; + int ssize; + int sync; + u8 hs_bint; + s16 terminal_type; + +}; + struct f_uac2_opts { struct usb_function_instance func_inst; + + /* Alt mode 0 options */ + struct f_uac2_alt_0_opts c_alt_0_opts; + struct f_uac2_alt_0_opts p_alt_0_opts; + + /* Alt mode 1 options */ + struct f_uac2_alt_opts c_alt_1_opts; + struct f_uac2_alt_opts p_alt_1_opts; + + /* Alt mode 2+ options */ + struct list_head c_alt_opts; + struct list_head p_alt_opts; + + /* Default options and Alt mode 1 if no c/p_alt.1 created */ int p_chmask; int p_srates[UAC_MAX_RATES]; int p_ssize; @@ -52,36 +103,36 @@ struct f_uac2_opts { int c_sync; u8 c_hs_bint; - bool p_mute_present; - bool p_volume_present; + bool p_mute_present; + bool p_volume_present; s16 p_volume_min; s16 p_volume_max; s16 p_volume_res; - bool c_mute_present; - bool c_volume_present; + bool c_mute_present; + bool c_volume_present; s16 c_volume_min; s16 c_volume_max; s16 c_volume_res; int req_number; int fb_max; - bool bound; - - char function_name[USB_MAX_STRING_LEN]; - char if_ctrl_name[USB_MAX_STRING_LEN]; - char clksrc_in_name[USB_MAX_STRING_LEN]; - char clksrc_out_name[USB_MAX_STRING_LEN]; - - char p_it_name[USB_MAX_STRING_LEN]; - char p_it_ch_name[USB_MAX_STRING_LEN]; - char p_ot_name[USB_MAX_STRING_LEN]; - char p_fu_vol_name[USB_MAX_STRING_LEN]; - - char c_it_name[USB_MAX_STRING_LEN]; - char c_it_ch_name[USB_MAX_STRING_LEN]; - char c_ot_name[USB_MAX_STRING_LEN]; - char c_fu_vol_name[USB_MAX_STRING_LEN]; + bool bound; + + char function_name[USB_MAX_STRING_LEN]; + char if_ctrl_name[USB_MAX_STRING_LEN]; + char clksrc_in_name[USB_MAX_STRING_LEN]; + char clksrc_out_name[USB_MAX_STRING_LEN]; + + char p_it_name[USB_MAX_STRING_LEN]; + char p_it_ch_name[USB_MAX_STRING_LEN]; + char p_ot_name[USB_MAX_STRING_LEN]; + char p_fu_vol_name[USB_MAX_STRING_LEN]; + + char c_it_name[USB_MAX_STRING_LEN]; + char c_it_ch_name[USB_MAX_STRING_LEN]; + char c_ot_name[USB_MAX_STRING_LEN]; + char c_fu_vol_name[USB_MAX_STRING_LEN]; s16 p_terminal_type; s16 c_terminal_type; From patchwork Sat Sep 28 15:09:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814706 Received: from mail-qt1-f171.google.com (mail-qt1-f171.google.com [209.85.160.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 18E6718893A; Sat, 28 Sep 2024 15:09:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536180; cv=none; b=n2FqQ88Fzo1J0blUEdXAQRs74AUeGL95DGux4G5aj6eNXD21dsiGCXoa9KuuIX2uwoQ8Oqa/9scN5VQLqbTI2MtsqBzNrgOaH2vZ7cu3CQ6rRGFkjGaYNsyaVABK93HpBDcIjbOwdQU6FmPSChCKBaRMPGUHjp6svddgsRspV4k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536180; c=relaxed/simple; bh=BbZCf5/FOhIKEJagMoX+Au0eH3BJVRYm+kHMoEfdGKk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eItbiQs/I5HcHZKb7q/IvTmbCuM/PCw5GLxSFj3y0xTqY0dpK/7PSQ5XIPSb9iVG1DvK5nQ94GIIFvImn+GMC2MHCkT/kAl+D4RfoQPQBQwsJQ/76JQqOOzJbAi3ekr1YIWO1XtMH2qcFrTgEuTpijF72frczFJleWGthp23Zgs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=N7OiYr/T; arc=none smtp.client-ip=209.85.160.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="N7OiYr/T" Received: by mail-qt1-f171.google.com with SMTP id d75a77b69052e-45813fcf59aso1577061cf.3; Sat, 28 Sep 2024 08:09:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536178; x=1728140978; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=2BSQWlQuRshKpUj3tpPdY61OirhldWjZ0UVfk07xzic=; b=N7OiYr/TKu2ZvaLWvsONwfnRL5O8B5aG8xnWOd8ChTcagR2rQI6E1vYDG6Jd3cG2ue znrEOtVdiz0eNrPxrTdNi8kLA+i/myNQuW+b3iY1uX7qBWZBnS4R7OGKt7v/OG3fvb5+ QyC12BzHDzu1Ho9nx3CZP/khBQUmeagX9MOKwBpwXM8XOrHgIu1nD51fyaZCWsA7otEV y6n/bO2CAH78/eNkQlHZ0uxRWLMYuWobl96+4PyQF04JcRvyUdHywcMCzqb7QxlOv9J7 Glm0n/HXr0Jtec4YL26+S2lNBpJLd+hE/3YJHAkRq0+4m2oMUO2M40Cut6SRq1Ew/64D Ee1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536178; x=1728140978; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=2BSQWlQuRshKpUj3tpPdY61OirhldWjZ0UVfk07xzic=; b=uDQ3fen5XQIzrqOrnpiKRDNofFKhvs7eU8Y1juYtlcF2gyuAUJD/vsVO5KuuKoq8mh NzAs+UB71mRPyF239FM0j8/nlUkL+nGOdQYRvyZTPLkqGSTCS4H+PyofcX0We3WXCToo CFOTA3vYX1sXRUujfV5bjU5/QZX3qmbtK0f6S9b02VwSyr9p3UG8yIyLdMPMY5+e+6Sp LOIxKrAHolzOMGc+q6HZtCGlNQgfRjuj3ogCI5BVwqZuIDDil71zf9MHNqRfqD07kkgB Fd7xgLjjN6G+Z+/c8RG4jZN4dGw+qrubd35uUR2kfNCJthj4JO00/kkbHMycoZvZXsU0 hrzw== X-Forwarded-Encrypted: i=1; AJvYcCXS9nLROXv6l+9hxFso3xpiX8lSnzNUJ5ohi2ZU6bCuJmeA79BmQhA5mf9NLYQzH1X7V79kh0qQZsWKI/hj@vger.kernel.org, AJvYcCXZ/sZx55+n9sFoQ1OWnmYioJHrkvi44cUaTGdHDZ0xSegD3flmWIDEFXYlp3TMG9eT+j946hVTnOY=@vger.kernel.org X-Gm-Message-State: AOJu0YwwAvn9IepnXlsSaLjAzfXaPKayHNpy5FgQHk7l4s6TKEp1Wtzo CQUgZQdKKWhzpyUXqnQPS8JPEXPqsh1Dhh17r3pM4a4Km2GBJhk0SZSw8hNtyTk= X-Google-Smtp-Source: AGHT+IEUP5LqqEqsLbpNBu89htWKblore6ffKLnWKAR3pLtsmxu7nHapkDkhR7kxZxPc/CNyBrkcaw== X-Received: by 2002:ac8:5fd3:0:b0:458:21b3:640 with SMTP id d75a77b69052e-45c9f31988amr39139511cf.14.1727536177565; Sat, 28 Sep 2024 08:09:37 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:36 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 09/14] usb: gadget: f_uac1: Make string table dynamic with strings from all alt modes Date: Sat, 28 Sep 2024 11:09:00 -0400 Message-ID: <20240928150905.2616313-10-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff The number of strings is now dependent on how many alt modes exist and what their strings are set to. This allows the strings to be dynamically consolidated where alt modes use the same strings, or separate when different strings are configured in some alt modes. Signed-off-by: Chris Wulff --- drivers/usb/gadget/function/f_uac1.c | 168 ++++++++++++++++++--------- 1 file changed, 110 insertions(+), 58 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index e7bfc32387bf..3484aa237354 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -498,35 +498,6 @@ static struct usb_descriptor_header *f_audio_ss_desc[] = { NULL, }; -enum { - STR_AC_IF, - STR_USB_OUT_IT, - STR_USB_OUT_IT_CH_NAMES, - STR_IO_OUT_OT, - STR_IO_IN_IT, - STR_IO_IN_IT_CH_NAMES, - STR_USB_IN_OT, - STR_FU_IN, - STR_FU_OUT, - STR_AS_OUT_IF_ALT0, - STR_AS_OUT_IF_ALT1, - STR_AS_IN_IF_ALT0, - STR_AS_IN_IF_ALT1, - NUM_STR_DESCRIPTORS, -}; - -static struct usb_string strings_uac1[NUM_STR_DESCRIPTORS + 1] = {}; - -static struct usb_gadget_strings str_uac1 = { - .language = 0x0409, /* en-us */ - .strings = strings_uac1, -}; - -static struct usb_gadget_strings *uac1_strings[] = { - &str_uac1, - NULL, -}; - /* * This function is an ALSA sound card following USB Audio Class Spec 1.0. */ @@ -1163,6 +1134,93 @@ f_audio_suspend(struct usb_function *f) /*-------------------------------------------------------------------------*/ +/* + * String handling + */ + +#define MAX_STRINGS 256 + +static int add_string(struct usb_string *strings, const char *s) +{ + int i; + + if (!s || s[0] == '\0') + return 0; + + for (i = 0; i < MAX_STRINGS; i++) { + if (!strings[i].s) { + strings[i].s = s; + return 0; /* IDs aren't allocated yet */ + } + + if (!strcmp(s, strings[i].s)) + return strings[i].id; + } + + return -1; +} + +static void add_alt_strings(struct usb_string *strings, struct f_uac1_alt_opts *alt_opts, bool fu) +{ + add_string(strings, alt_opts->name); + add_string(strings, alt_opts->it_name); + add_string(strings, alt_opts->it_ch_name); + add_string(strings, alt_opts->ot_name); + if (fu) + add_string(strings, alt_opts->fu_vol_name); +} + +static struct usb_string *attach_strings(struct usb_composite_dev *cdev, + struct f_uac1_opts *audio_opts) +{ + struct usb_string *strings = kzalloc(sizeof(struct usb_string) * MAX_STRINGS, + GFP_KERNEL); + struct f_uac1_alt_opts *alt_opts; + struct usb_string *us; + int strings_uac1_length; + + struct usb_gadget_strings str_uac1 = { + .language = 0x0409, /* en-us */ + .strings = strings + }; + + struct usb_gadget_strings *uac1_strings[] = { + &str_uac1, + NULL, + }; + + if (!strings) + return ERR_PTR(-ENOMEM); + + /* Add all the strings from all the alt mode options */ + add_string(strings, audio_opts->function_name); + add_string(strings, audio_opts->c_alt_0_opts.name); + add_string(strings, audio_opts->p_alt_0_opts.name); + add_alt_strings(strings, &audio_opts->c_alt_1_opts, FUOUT_EN(audio_opts)); + add_alt_strings(strings, &audio_opts->p_alt_1_opts, FUIN_EN(audio_opts)); + list_for_each_entry(alt_opts, &audio_opts->c_alt_opts, list) { + add_alt_strings(strings, alt_opts, FUOUT_EN(audio_opts)); + } + list_for_each_entry(alt_opts, &audio_opts->p_alt_opts, list) { + add_alt_strings(strings, alt_opts, FUIN_EN(audio_opts)); + } + + for (strings_uac1_length = 0; strings[strings_uac1_length].s; strings_uac1_length++) + ; + + /* Attach strings to the composite device and get string IDs assigned */ + us = usb_gstrings_attach(cdev, uac1_strings, strings_uac1_length); + + /* Strings are now copied to the composite device and we use the + * copy in "us" going forward, that has all the string IDs. + */ + kfree(strings); + + return us; +} + +/*-------------------------------------------------------------------------*/ + static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_alt_opts *alt_opts, struct usb_endpoint_descriptor *ep_desc, enum usb_device_speed speed, bool is_playback) @@ -1526,23 +1584,11 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) if (status) return status; - strings_uac1[STR_AC_IF].s = audio_opts->function_name; - - strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name; - strings_uac1[STR_USB_OUT_IT_CH_NAMES].s = audio_opts->c_it_ch_name; - strings_uac1[STR_IO_OUT_OT].s = audio_opts->c_ot_name; - strings_uac1[STR_FU_OUT].s = audio_opts->c_fu_vol_name; - strings_uac1[STR_AS_OUT_IF_ALT0].s = "Playback Inactive"; - strings_uac1[STR_AS_OUT_IF_ALT1].s = "Playback Active"; - - strings_uac1[STR_IO_IN_IT].s = audio_opts->p_it_name; - strings_uac1[STR_IO_IN_IT_CH_NAMES].s = audio_opts->p_it_ch_name; - strings_uac1[STR_USB_IN_OT].s = audio_opts->p_ot_name; - strings_uac1[STR_FU_IN].s = audio_opts->p_fu_vol_name; - strings_uac1[STR_AS_IN_IF_ALT0].s = "Capture Inactive"; - strings_uac1[STR_AS_IN_IF_ALT1].s = "Capture Active"; + /* Past this point, all settings that apply to an alt mode should + * be used from their alt mode opts. + */ - us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); + us = attach_strings(cdev, audio_opts); if (IS_ERR(us)) return PTR_ERR(us); @@ -1565,31 +1611,37 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) } } - ac_interface_desc.iInterface = us[STR_AC_IF].id; - usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id; - usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id; - io_out_ot_desc.iTerminal = us[STR_IO_OUT_OT].id; - as_out_interface_alt_0_desc.iInterface = us[STR_AS_OUT_IF_ALT0].id; - as_out_interface_alt_1_desc.iInterface = us[STR_AS_OUT_IF_ALT1].id; - io_in_it_desc.iTerminal = us[STR_IO_IN_IT].id; - io_in_it_desc.iChannelNames = us[STR_IO_IN_IT_CH_NAMES].id; - usb_in_ot_desc.iTerminal = us[STR_USB_IN_OT].id; - as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id; - as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id; + ac_interface_desc.iInterface = add_string(us, audio_opts->function_name); + usb_out_it_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.it_name); + usb_out_it_desc.iChannelNames = add_string(us, audio_opts->c_alt_1_opts.it_ch_name); + io_out_ot_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.ot_name); + as_out_interface_alt_0_desc.iInterface = add_string(us, audio_opts->c_alt_0_opts.name); + as_out_interface_alt_1_desc.iInterface = add_string(us, audio_opts->c_alt_1_opts.name); + io_in_it_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.it_name); + io_in_it_desc.iChannelNames = add_string(us, audio_opts->p_alt_1_opts.it_ch_name); + usb_in_ot_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.ot_name); + as_in_interface_alt_0_desc.iInterface = add_string(us, audio_opts->p_alt_0_opts.name); + as_in_interface_alt_1_desc.iInterface = add_string(us, audio_opts->p_alt_1_opts.name); if (FUOUT_EN(audio_opts)) { u8 *i_feature; i_feature = (u8 *)out_feature_unit_desc + out_feature_unit_desc->bLength - 1; - *i_feature = us[STR_FU_OUT].id; + *i_feature = add_string(us, audio_opts->c_alt_1_opts.fu_vol_name); } if (FUIN_EN(audio_opts)) { u8 *i_feature; i_feature = (u8 *)in_feature_unit_desc + in_feature_unit_desc->bLength - 1; - *i_feature = us[STR_FU_IN].id; + *i_feature = add_string(us, audio_opts->p_alt_1_opts.fu_vol_name); + } + + us = attach_strings(cdev, audio_opts); + if (IS_ERR(us)) { + status = PTR_ERR(us); + goto fail; } /* Set channel numbers */ From patchwork Sat Sep 28 15:09:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814707 Received: from mail-qv1-f53.google.com (mail-qv1-f53.google.com [209.85.219.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1A303188CD8; Sat, 28 Sep 2024 15:09:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536183; cv=none; b=mHv04QgwACHw+WnLdI/54f2GbRSUy0wI45HxVEFT1yOnvCMtSJQQHWtkhTA+tpq5KJtwZeINyXyEw8tOwSbnJUrT4opT1svBkY0FE7kkgZHEoee6hkTwrXlTe/+plZhKyp4Osjnbe8xDUmz8zhbUJIt7Gir8sZLc4+3ADoOAscY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536183; c=relaxed/simple; bh=fPcMh6PG41cvs/Q1a1dSMWkRJEIvelySKraIvjb9LPk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AXae/xdAyIlqrTX4CCGFkXx5OedhKBB9ZS7CEnQDRvXe5MesW2MJGtusB0Jz8e052LSJjBJ8eHCPcD7RFbEpB5ye1vHgqDL0yo2dsMPE0KnXe4Wz35TUVQOpbveSLCSFqz5u01uLvXBXg2VN10fkXw2GaKaoGt8UFaLGaDnLgLY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GN4h8o89; arc=none smtp.client-ip=209.85.219.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GN4h8o89" Received: by mail-qv1-f53.google.com with SMTP id 6a1803df08f44-6cb48e77b16so1927546d6.0; Sat, 28 Sep 2024 08:09:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536180; x=1728140980; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ZJ2/UCKUW8V+Esq59seZMzO5ku0C95dG3qrFNZ1EHjI=; b=GN4h8o89zqQIplofN6SrlCcncXRgR/hc511dAHoKAwC61WGPI/xN2lAwjrTPSU6GIf 6sp+kSE2jlxbxxYzO7JDGqyh91xDDfZJSyUM8LW4xJSTXfm6sDpmFz/2Vpu2rPA8JXtx cwzklzjBjn9jwulD7TTyJCEHYy8Gmsooo55jPFpFhhATr8APmiFBCcIUFeG6K8LvEZW0 LjoaD7AZGzwrluHAQ+uizrTxPWIokbz1TdeN8elujxpqmUpX7JvnlKs8DxnIYox5shLj GwptwPl8A3DD6dkDy/fZWxfHfsgVPBl/iWtqSQkQyl2MLGihAOUpelotl1CmIaZk3tXT X7Pg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536180; x=1728140980; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ZJ2/UCKUW8V+Esq59seZMzO5ku0C95dG3qrFNZ1EHjI=; b=emC/UfB75HbN1J7PVmJUlUKBcK6IHTjj3LE90svFYdK3Q1VvW9zIr/VrsSYsBr5FBb 2duUTbHCRvymebVS3tyfIPaZBNXiZp5v9A18HhETo3LKjQWqjh596tiolzF2+0kriYLm ICF4JCriFqBWn187zPUgBzXlVyjPOf0GmOgwaXTQpzd0CtsyZSTjyzwL1n8DVOq1ENsa fjCAv77G1vkCOpFMdbciEScvZn1dhEG/tSAk9LHHEQ+HtMC4TtgD4s4eYyhXqvaW0lZc yCdQIhuTjJg1b8J4N+EV8F1bCZxHmxsFuNiB/sB8ijT/UlH4wdGYo8x4pwou9QYaxJJ3 5fwA== X-Forwarded-Encrypted: i=1; AJvYcCVymSqSR+K0jJRvyF7+pmH8ErJLrjLmJzVjJsaweFBt/uOKUoZlDwiBRb5rxO2xiw0DXNmu7eyZ0MA=@vger.kernel.org, AJvYcCWPr3xr8Os/54cYM41Ss379dqYPPM3kvERCZ1KlO+WHWwFrXpjYzVZv5B8lQt3UMZjXOBGY43DfrmDVNKRD@vger.kernel.org X-Gm-Message-State: AOJu0Ywhfzf5dnZ1kVgRWyUA1Bd7mBLbzGKsdY/bnYrQO3+ohffVl8IC NdZyH+KacNf3OWo1VT2vbJ7LeKxaifD5BN1aFm4J4ugefdsOpBQlEXpJVBIPksE= X-Google-Smtp-Source: AGHT+IELuNqfcY3qT32fQoVJsBKpciKW9lWJLuuLAfyyxPgCEAHfHbGvdp2T4yp6i3+kfNe6r7JK5A== X-Received: by 2002:a05:622a:1486:b0:458:2021:6148 with SMTP id d75a77b69052e-45cadcc99femr19198791cf.9.1727536180143; Sat, 28 Sep 2024 08:09:40 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:38 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 10/14] usb: gadget: f_uac2: Make string table dynamic with strings from all alt modes Date: Sat, 28 Sep 2024 11:09:01 -0400 Message-ID: <20240928150905.2616313-11-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff The number of strings is now dependent on how many alt modes exist and what their strings are set to. This allows the strings to be dynamically consolidated where alt modes use the same strings, or separate when different strings are configured in some alt modes. Signed-off-by: Chris Wulff --- drivers/usb/gadget/function/f_uac2.c | 181 +++++++++++++++++---------- 1 file changed, 113 insertions(+), 68 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index e9f951215c26..54702888855d 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -93,38 +93,6 @@ static int afunc_notify(struct g_audio *agdev, int unit_id, int cs); /* --------- USB Function Interface ------------- */ -enum { - STR_ASSOC, - STR_IF_CTRL, - STR_CLKSRC_IN, - STR_CLKSRC_OUT, - STR_USB_IT, - STR_USB_IT_CH, - STR_IO_IT, - STR_IO_IT_CH, - STR_USB_OT, - STR_IO_OT, - STR_FU_IN, - STR_FU_OUT, - STR_AS_OUT_ALT0, - STR_AS_OUT_ALT1, - STR_AS_IN_ALT0, - STR_AS_IN_ALT1, - NUM_STR_DESCRIPTORS, -}; - -static struct usb_string strings_fn[NUM_STR_DESCRIPTORS + 1] = {}; - -static struct usb_gadget_strings str_fn = { - .language = 0x0409, /* en-us */ - .strings = strings_fn, -}; - -static struct usb_gadget_strings *fn_strings[] = { - &str_fn, - NULL, -}; - static struct usb_interface_assoc_descriptor iad_desc = { .bLength = sizeof iad_desc, .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, @@ -649,6 +617,98 @@ struct cntrl_subrange_lay3 { DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES); +/*-------------------------------------------------------------------------*/ + +/* + * String handling + */ + +#define MAX_STRINGS 256 + +static int add_string(struct usb_string *strings, const char *s) +{ + int i; + + if (!s || s[0] == '\0') + return 0; + + for (i = 0; i < MAX_STRINGS; i++) { + if (!strings[i].s) { + strings[i].s = s; + return 0; /* IDs aren't allocated yet */ + } + + if (!strcmp(s, strings[i].s)) + return strings[i].id; + } + + return -1; +} + +static void add_alt_strings(struct usb_string *strings, struct f_uac2_alt_opts *alt_opts, bool fu) +{ + add_string(strings, alt_opts->name); + add_string(strings, alt_opts->it_name); + add_string(strings, alt_opts->it_ch_name); + add_string(strings, alt_opts->ot_name); + if (fu) + add_string(strings, alt_opts->fu_vol_name); +} + +static struct usb_string *attach_strings(struct usb_composite_dev *cdev, + struct f_uac2_opts *audio_opts) +{ + struct usb_string *strings = kzalloc(sizeof(struct usb_string) * MAX_STRINGS, + GFP_KERNEL); + struct f_uac2_alt_opts *alt_opts; + struct usb_string *us; + int strings_fn_length; + + struct usb_gadget_strings strings_fn = { + .language = 0x0409, /* en-us */ + .strings = strings + }; + + struct usb_gadget_strings *fn_strings[] = { + &strings_fn, + NULL, + }; + + if (!strings) + return ERR_PTR(-ENOMEM); + + /* Add all the strings from all the alt mode options */ + add_string(strings, audio_opts->function_name); + add_string(strings, audio_opts->if_ctrl_name); + add_string(strings, audio_opts->clksrc_in_name); + add_string(strings, audio_opts->clksrc_out_name); + add_string(strings, audio_opts->c_alt_0_opts.name); + add_string(strings, audio_opts->p_alt_0_opts.name); + add_alt_strings(strings, &audio_opts->c_alt_1_opts, FUOUT_EN(audio_opts)); + add_alt_strings(strings, &audio_opts->p_alt_1_opts, FUIN_EN(audio_opts)); + list_for_each_entry(alt_opts, &audio_opts->c_alt_opts, list) { + add_alt_strings(strings, alt_opts, FUOUT_EN(audio_opts)); + } + list_for_each_entry(alt_opts, &audio_opts->p_alt_opts, list) { + add_alt_strings(strings, alt_opts, FUIN_EN(audio_opts)); + } + + for (strings_fn_length = 0; strings[strings_fn_length].s; strings_fn_length++) + ; + + /* Attach strings to the composite device and get string IDs assigned */ + us = usb_gstrings_attach(cdev, fn_strings, strings_fn_length); + + /* Strings are now copied to the composite device and we use the + * copy in "us" going forward, that has all the string IDs. + */ + kfree(strings); + + return us; +} + +/*-------------------------------------------------------------------------*/ + static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_alt_opts *alt_opts, struct usb_endpoint_descriptor *ep_desc, enum usb_device_speed speed, bool is_playback) @@ -994,26 +1054,11 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) if (ret) return ret; - strings_fn[STR_ASSOC].s = uac2_opts->function_name; - strings_fn[STR_IF_CTRL].s = uac2_opts->if_ctrl_name; - strings_fn[STR_CLKSRC_IN].s = uac2_opts->clksrc_in_name; - strings_fn[STR_CLKSRC_OUT].s = uac2_opts->clksrc_out_name; - - strings_fn[STR_USB_IT].s = uac2_opts->c_it_name; - strings_fn[STR_USB_IT_CH].s = uac2_opts->c_it_ch_name; - strings_fn[STR_IO_OT].s = uac2_opts->c_ot_name; - strings_fn[STR_FU_OUT].s = uac2_opts->c_fu_vol_name; - strings_fn[STR_AS_OUT_ALT0].s = "Playback Inactive"; - strings_fn[STR_AS_OUT_ALT1].s = "Playback Active"; - - strings_fn[STR_IO_IT].s = uac2_opts->p_it_name; - strings_fn[STR_IO_IT_CH].s = uac2_opts->p_it_ch_name; - strings_fn[STR_USB_OT].s = uac2_opts->p_ot_name; - strings_fn[STR_FU_IN].s = uac2_opts->p_fu_vol_name; - strings_fn[STR_AS_IN_ALT0].s = "Capture Inactive"; - strings_fn[STR_AS_IN_ALT1].s = "Capture Active"; - - us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); + /* Past this point, all settings that apply to an alt mode should + * be used from their alt mode opts. + */ + + us = attach_strings(cdev, uac2_opts); if (IS_ERR(us)) return PTR_ERR(us); @@ -1030,30 +1075,30 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) } } - iad_desc.iFunction = us[STR_ASSOC].id; - std_ac_if_desc.iInterface = us[STR_IF_CTRL].id; - in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id; - out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id; - usb_out_it_desc.iTerminal = us[STR_USB_IT].id; - usb_out_it_desc.iChannelNames = us[STR_USB_IT_CH].id; - io_in_it_desc.iTerminal = us[STR_IO_IT].id; - io_in_it_desc.iChannelNames = us[STR_IO_IT_CH].id; - usb_in_ot_desc.iTerminal = us[STR_USB_OT].id; - io_out_ot_desc.iTerminal = us[STR_IO_OT].id; - std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id; - std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id; - std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id; - std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id; + iad_desc.iFunction = add_string(us, uac2_opts->function_name); + std_ac_if_desc.iInterface = add_string(us, uac2_opts->if_ctrl_name); + in_clk_src_desc.iClockSource = add_string(us, uac2_opts->clksrc_in_name); + out_clk_src_desc.iClockSource = add_string(us, uac2_opts->clksrc_out_name); + usb_out_it_desc.iTerminal = add_string(us, uac2_opts->c_alt_1_opts.it_name); + usb_out_it_desc.iChannelNames = add_string(us, uac2_opts->c_alt_1_opts.it_ch_name); + io_in_it_desc.iTerminal = add_string(us, uac2_opts->p_alt_1_opts.it_name); + io_in_it_desc.iChannelNames = add_string(us, uac2_opts->p_alt_1_opts.it_ch_name); + usb_in_ot_desc.iTerminal = add_string(us, uac2_opts->p_alt_1_opts.ot_name); + io_out_ot_desc.iTerminal = add_string(us, uac2_opts->c_alt_1_opts.ot_name); + std_as_out_if0_desc.iInterface = add_string(us, uac2_opts->c_alt_0_opts.name); + std_as_out_if1_desc.iInterface = add_string(us, uac2_opts->c_alt_1_opts.name); + std_as_in_if0_desc.iInterface = add_string(us, uac2_opts->p_alt_0_opts.name); + std_as_in_if1_desc.iInterface = add_string(us, uac2_opts->p_alt_0_opts.name); if (FUOUT_EN(uac2_opts)) { u8 *i_feature = (u8 *)out_feature_unit_desc + out_feature_unit_desc->bLength - 1; - *i_feature = us[STR_FU_OUT].id; + *i_feature = add_string(us, uac2_opts->c_alt_1_opts.fu_vol_name); } if (FUIN_EN(uac2_opts)) { u8 *i_feature = (u8 *)in_feature_unit_desc + in_feature_unit_desc->bLength - 1; - *i_feature = us[STR_FU_IN].id; + *i_feature = add_string(us, uac2_opts->p_alt_1_opts.fu_vol_name); } From patchwork Sat Sep 28 15:09:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814708 Received: from mail-qt1-f171.google.com (mail-qt1-f171.google.com [209.85.160.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A0EBF1898E9; Sat, 28 Sep 2024 15:09:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536187; cv=none; b=QQzBOu0Z31neIYNRv5YLx0br2hLeLKUZW13s/HVGOMU0pVXZVqW132uoq+pW3PgtkusRlojb9nMmhtxjUnoB64HcWuwRXOJbuppM4AjNN0p4FMS9OUoEvsQ8ikmtxuGljjM2yYpJkpiJ6vchzxgUIy2KDkuY0aw0yd1PAkVDDEc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536187; c=relaxed/simple; bh=yNV8sLxNXxiZeJ6Rgy7TXgUmJwPPlKIVSQBfX5iLMZo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Gd2x1ax3lzCqN1H5Ggp6GrIwZN8jZ78sXpNDVP5ialy4FXPNIPE8LaQdck3rlLfifWOtFDN3tDw60xqp/axdOqAieRRhuW7O17TjduoBsLUc+VWe0QinHBkH3KDJbvR2DAs+ldO5ht5U1CxWkphNbRML7RLCslATzL+Oze4+d7I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=JIuSy7wZ; arc=none smtp.client-ip=209.85.160.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="JIuSy7wZ" Received: by mail-qt1-f171.google.com with SMTP id d75a77b69052e-45817098ad5so1346811cf.2; Sat, 28 Sep 2024 08:09:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536182; x=1728140982; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=xLZJIs/hfIYLMWubtMu+GJHJ1qhaAzZe28T/ApzhN7o=; b=JIuSy7wZ1nKrkGC+atF5nMZukolbw8cV9qiYw4VwmlEVNcMY6ZNzvrVJnCjnGCe1Ch 2bEXgwqgRROE5y0bbdvp+RgvIjry2NLRATMGhzkKEvaEu7QIrLJhmeWer+khBX7F0GGp L/zaketg1v9sqBjBbnB6eBaUxdNVNjXfAQ/8+e2BvzgIFPwOyI170dmYCMhGZRbHL+vb /BWeLQ6ZYMKvCCho2NeqMwkl0YCyQUCVTW5SaJth4FiXYleW6Jq+6up+gj77gnvnVV4y R59mPXc5uLlvg7TyyGYwfz6XX0BrUb9fVEC6kXyZ8Y7V4HlU+7gyBAL+Ep5TxU3sbq+r ld7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536182; x=1728140982; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=xLZJIs/hfIYLMWubtMu+GJHJ1qhaAzZe28T/ApzhN7o=; b=UTwp/ifYdujEpk1XMAtzPwlwKj1ZDEXcs/QLvxzX/0SZig8VLyiw/uSIZgikVkOaLO 9fPmRe4iyOYnmonfWZ/be5KMS4Hqh/0Chjd2Q15PcDPpfeUq8nG2uf4SxVjtNq7Z7qIb Obi9i9vQV6mlXei7a3rWZfN0awIaC4weLZwuCuKdmJ47la0jFTDN+11/Egki5OaEyKeL Wgo5v3te4h6GJr+zwhT+XDo/STQz5LaChHkKGiSWVDiHL+nWEcnBfUTYGWvft23vCTuH D3PXPzUUHVRs1N6UVC7xQk8QThitVr+vZbiUGjlHth0Aimq+qviwcwrfmktmXO+Gxsrb FU4Q== X-Forwarded-Encrypted: i=1; AJvYcCWdW9E6LBj+f7FjCSQEjU8E9uMp7Hq1NYynG14bC/DNKKO+NM19KPfByAVCIQC8AZKs7kjWy0L8JXA=@vger.kernel.org, AJvYcCWr+MQAdbookbDbcjaoKqid3k2pJk8dCQftUiYI3EQbxK9xghZFA2MT7QBQRjP+byPxXV9RY7uL4sQpuv4w@vger.kernel.org X-Gm-Message-State: AOJu0YyB+vS0yTXqpW0CxDTtVYAxh/eaKmIL+af8MhHdzG/JcvFU1KPW 0/hIXSCr86UvWMX0Tnv0gzPbb76oiys8dCMKpzjDJysMrdY50kWI2gMXiVnjVFc= X-Google-Smtp-Source: AGHT+IEWXfM7gW6UXczhVXuinhDrqcsOpyJqewD2J3HLA9GnrrPKnuPdyMZum6oLB0Qc2Z8Z5Cnddw== X-Received: by 2002:a05:622a:c8:b0:458:21b3:63f with SMTP id d75a77b69052e-45c9f317f50mr47047581cf.13.1727536181653; Sat, 28 Sep 2024 08:09:41 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:41 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 11/14] usb: gadget: f_uac1: Generate dynamic descriptors based on alt opts Date: Sat, 28 Sep 2024 11:09:02 -0400 Message-ID: <20240928150905.2616313-12-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff Descriptors are now generated based on what alt modes are configured. The lifetime of allocations has changed a bit with this such that we deallocate our copy of the descriptors as soon as they've been registered. Many of the descriptors that were static are now attached to their alt mode opts and initialized with a function. Signed-off-by: Chris Wulff --- drivers/usb/gadget/function/f_uac1.c | 1390 +++++++++++++------------- drivers/usb/gadget/function/u_uac1.h | 38 + 2 files changed, 709 insertions(+), 719 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 3484aa237354..7803957e4f82 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -27,16 +27,36 @@ /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ #define UAC1_CHANNEL_MASK 0x0FFF -#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID) -#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID) +#define USB_OUT_FU_ID(_opts) (_opts->c_alt_1_opts.fu_id) +#define USB_IN_FU_ID(_opts) (_opts->p_alt_1_opts.fu_id) -#define EPIN_EN(_opts) ((_opts)->p_chmask != 0) -#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) +#define EP_EN(_alt_opts) ((_alt_opts) && ((_alt_opts)->chmask != 0)) #define FUIN_EN(_opts) ((_opts)->p_mute_present \ || (_opts)->p_volume_present) #define FUOUT_EN(_opts) ((_opts)->c_mute_present \ || (_opts)->c_volume_present) -#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC) +#define EPOUT_FBACK_IN_EN(_alt_opts) ((_alt_opts)->sync == USB_ENDPOINT_SYNC_ASYNC) + +/* Check if any alt mode has option enabled */ +#define EN_ANY(single, fn, cp) \ +static int fn(struct f_uac1_opts *opts) \ +{ \ + struct f_uac1_alt_opts *alt_opts; \ + \ + if (single(&opts->cp##_alt_1_opts)) \ + return 1; \ + \ + list_for_each_entry(alt_opts, &opts->cp##_alt_opts, list) { \ + if (single(alt_opts)) \ + return 1; \ + } \ + \ + return 0; \ +} + +EN_ANY(EP_EN, epout_en_any, c) +EN_ANY(EP_EN, epin_en_any, p) +EN_ANY(EPOUT_FBACK_IN_EN, epout_fback_in_en_any, p) struct f_uac1 { struct g_audio g_audio; @@ -88,49 +108,6 @@ static struct usb_interface_descriptor ac_interface_desc = { /* B.3.2 Class-Specific AC Interface Descriptor */ static struct uac1_ac_header_descriptor *ac_header_desc; -static struct uac_input_terminal_descriptor usb_out_it_desc = { - .bLength = UAC_DT_INPUT_TERMINAL_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_INPUT_TERMINAL, - /* .bTerminalID = DYNAMIC */ - .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), - .bAssocTerminal = 0, - .wChannelConfig = cpu_to_le16(0x3), -}; - -static struct uac1_output_terminal_descriptor io_out_ot_desc = { - .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - /* .bTerminalID = DYNAMIC */ - .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER), - .bAssocTerminal = 0, - /* .bSourceID = DYNAMIC */ -}; - -static struct uac_input_terminal_descriptor io_in_it_desc = { - .bLength = UAC_DT_INPUT_TERMINAL_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_INPUT_TERMINAL, - /* .bTerminalID = DYNAMIC */ - .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE), - .bAssocTerminal = 0, - .wChannelConfig = cpu_to_le16(0x3), -}; - -static struct uac1_output_terminal_descriptor usb_in_ot_desc = { - .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - /* .bTerminalID = DYNAMIC */ - .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), - .bAssocTerminal = 0, - /* .bSourceID = DYNAMIC */ -}; - -static struct uac_feature_unit_descriptor *in_feature_unit_desc; -static struct uac_feature_unit_descriptor *out_feature_unit_desc; - /* AC IN Interrupt Endpoint */ static struct usb_endpoint_descriptor fs_ac_int_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, @@ -167,97 +144,6 @@ static struct usb_ss_ep_comp_descriptor ss_ac_int_ep_desc_comp = { .wBytesPerInterval = cpu_to_le16(2), }; -/* B.4.1 Standard AS Interface Descriptor */ -static struct usb_interface_descriptor as_out_interface_alt_0_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, -}; - -static struct usb_interface_descriptor as_out_interface_alt_1_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bAlternateSetting = 1, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, -}; - -static struct usb_interface_descriptor as_in_interface_alt_0_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, -}; - -static struct usb_interface_descriptor as_in_interface_alt_1_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bAlternateSetting = 1, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, -}; - -/* B.4.2 Class-Specific AS Interface Descriptor */ -static struct uac1_as_header_descriptor as_out_header_desc = { - .bLength = UAC_DT_AS_HEADER_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_AS_GENERAL, - /* .bTerminalLink = DYNAMIC */ - .bDelay = 1, - .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), -}; - -static struct uac1_as_header_descriptor as_in_header_desc = { - .bLength = UAC_DT_AS_HEADER_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_AS_GENERAL, - /* .bTerminalLink = DYNAMIC */ - .bDelay = 1, - .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), -}; - -DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES); -#define uac_format_type_i_discrete_descriptor \ - uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES - -static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = { - .bLength = 0, /* filled on rate setup */ - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_FORMAT_TYPE, - .bFormatType = UAC_FORMAT_TYPE_I, - .bSubframeSize = 2, - .bBitResolution = 16, - .bSamFreqType = 0, /* filled on rate setup */ -}; - -/* Standard ISO OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor fs_as_out_ep_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), - .bInterval = 1, -}; - -static struct usb_endpoint_descriptor hs_as_out_ep_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), - .bInterval = 4, -}; - /* Class-specific AS ISO OUT Endpoint Descriptor */ static struct uac_iso_endpoint_descriptor as_iso_out_desc = { .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, @@ -268,37 +154,6 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = { .wLockDelay = cpu_to_le16(1), }; -static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = { - .bLength = 0, /* filled on rate setup */ - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_FORMAT_TYPE, - .bFormatType = UAC_FORMAT_TYPE_I, - .bSubframeSize = 2, - .bBitResolution = 16, - .bSamFreqType = 0, /* filled on rate setup */ -}; - -/* Standard ISO IN Endpoint Descriptor */ -static struct usb_endpoint_descriptor fs_as_in_ep_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_SYNC_ASYNC - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), - .bInterval = 1, -}; - -static struct usb_endpoint_descriptor hs_as_in_ep_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_SYNC_ASYNC - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), - .bInterval = 4, -}; - /* Class-specific AS ISO IN Endpoint Descriptor */ static struct uac_iso_endpoint_descriptor as_iso_in_desc = { .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, @@ -351,153 +206,6 @@ static struct usb_ss_ep_comp_descriptor ss_as_in_fback_desc_comp = { .wBytesPerInterval = cpu_to_le16(4), }; -static struct usb_descriptor_header *f_audio_fs_desc[] = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - - (struct usb_descriptor_header *)&usb_out_it_desc, - (struct usb_descriptor_header *)&io_out_ot_desc, - (struct usb_descriptor_header *)&out_feature_unit_desc, - - (struct usb_descriptor_header *)&io_in_it_desc, - (struct usb_descriptor_header *)&usb_in_ot_desc, - (struct usb_descriptor_header *)&in_feature_unit_desc, - - (struct usb_descriptor_header *)&fs_ac_int_ep_desc, - - (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_out_header_desc, - - (struct usb_descriptor_header *)&as_out_type_i_desc, - - (struct usb_descriptor_header *)&fs_as_out_ep_desc, - (struct usb_descriptor_header *)&as_iso_out_desc, - (struct usb_descriptor_header *)&fs_as_in_fback_desc, - - (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_in_header_desc, - - (struct usb_descriptor_header *)&as_in_type_i_desc, - - (struct usb_descriptor_header *)&fs_as_in_ep_desc, - (struct usb_descriptor_header *)&as_iso_in_desc, - NULL, -}; - -static struct usb_descriptor_header *f_audio_hs_desc[] = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - - (struct usb_descriptor_header *)&usb_out_it_desc, - (struct usb_descriptor_header *)&io_out_ot_desc, - (struct usb_descriptor_header *)&out_feature_unit_desc, - - (struct usb_descriptor_header *)&io_in_it_desc, - (struct usb_descriptor_header *)&usb_in_ot_desc, - (struct usb_descriptor_header *)&in_feature_unit_desc, - - (struct usb_descriptor_header *)&hs_ac_int_ep_desc, - - (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_out_header_desc, - - (struct usb_descriptor_header *)&as_out_type_i_desc, - - (struct usb_descriptor_header *)&hs_as_out_ep_desc, - (struct usb_descriptor_header *)&as_iso_out_desc, - (struct usb_descriptor_header *)&hs_as_in_fback_desc, - - (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_in_header_desc, - - (struct usb_descriptor_header *)&as_in_type_i_desc, - - (struct usb_descriptor_header *)&hs_as_in_ep_desc, - (struct usb_descriptor_header *)&as_iso_in_desc, - NULL, -}; - -/* Standard ISO OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor ss_as_out_ep_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), - .bInterval = 4, -}; - -static struct usb_ss_ep_comp_descriptor ss_as_out_ep_desc_comp = { - .bLength = sizeof(ss_as_out_ep_desc_comp), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - .bMaxBurst = 0, - .bmAttributes = 0, - /* wBytesPerInterval = DYNAMIC */ -}; - -/* Standard ISO IN Endpoint Descriptor */ -static struct usb_endpoint_descriptor ss_as_in_ep_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_SYNC_ASYNC - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), - .bInterval = 4, -}; - -static struct usb_ss_ep_comp_descriptor ss_as_in_ep_desc_comp = { - .bLength = sizeof(ss_as_in_ep_desc_comp), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - .bMaxBurst = 0, - .bmAttributes = 0, - /* wBytesPerInterval = DYNAMIC */ -}; - -static struct usb_descriptor_header *f_audio_ss_desc[] = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - - (struct usb_descriptor_header *)&usb_out_it_desc, - (struct usb_descriptor_header *)&io_out_ot_desc, - (struct usb_descriptor_header *)&out_feature_unit_desc, - - (struct usb_descriptor_header *)&io_in_it_desc, - (struct usb_descriptor_header *)&usb_in_ot_desc, - (struct usb_descriptor_header *)&in_feature_unit_desc, - - (struct usb_descriptor_header *)&ss_ac_int_ep_desc, - (struct usb_descriptor_header *)&ss_ac_int_ep_desc_comp, - - (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_out_header_desc, - - (struct usb_descriptor_header *)&as_out_type_i_desc, - - (struct usb_descriptor_header *)&ss_as_out_ep_desc, - (struct usb_descriptor_header *)&ss_as_out_ep_desc_comp, - (struct usb_descriptor_header *)&as_iso_out_desc, - (struct usb_descriptor_header *)&ss_as_in_fback_desc, - (struct usb_descriptor_header *)&ss_as_in_fback_desc_comp, - - (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_in_header_desc, - - (struct usb_descriptor_header *)&as_in_type_i_desc, - - (struct usb_descriptor_header *)&ss_as_in_ep_desc, - (struct usb_descriptor_header *)&ss_as_in_ep_desc_comp, - (struct usb_descriptor_header *)&as_iso_in_desc, - NULL, -}; - /* * This function is an ALSA sound card following USB Audio Class Spec 1.0. */ @@ -602,11 +310,11 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { unsigned int is_playback = 0; - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) is_playback = 1; if (control_selector == UAC_FU_MUTE) { @@ -653,11 +361,11 @@ in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { unsigned int is_playback = 0; - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) is_playback = 1; if (control_selector == UAC_FU_VOLUME) { @@ -700,11 +408,11 @@ in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { unsigned int is_playback = 0; - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) is_playback = 1; if (control_selector == UAC_FU_VOLUME) { @@ -747,11 +455,11 @@ in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { unsigned int is_playback = 0; - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) is_playback = 1; if (control_selector == UAC_FU_VOLUME) { @@ -799,11 +507,11 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) return; } - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { unsigned int is_playback = 0; - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) is_playback = 1; if (control_selector == UAC_FU_MUTE) { @@ -848,8 +556,8 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { memcpy(&uac1->setup_cr, cr, sizeof(*cr)); req->context = audio; req->complete = out_rq_cur_complete; @@ -1221,6 +929,10 @@ static struct usb_string *attach_strings(struct usb_composite_dev *cdev, /*-------------------------------------------------------------------------*/ +/* + * Descriptor building functions + */ + static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_alt_opts *alt_opts, struct usb_endpoint_descriptor *ep_desc, enum usb_device_speed speed, bool is_playback) @@ -1231,11 +943,59 @@ static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_a alt_opts->ssize, alt_opts->sync, alt_opts->c.opts->fb_max); } -static struct uac_feature_unit_descriptor *build_fu_desc(int chmask) +struct path_params { + int dir; + int id; + struct f_uac1_opts *opts; + struct usb_string *strings; +}; + +/* 4.3.2.1 Input Terminal Descriptor */ +static void init_it_desc(struct uac_input_terminal_descriptor *it_desc, + struct f_uac1_alt_opts *alt_opts, + struct path_params *params) +{ + it_desc->bLength = UAC_DT_INPUT_TERMINAL_SIZE; + it_desc->bDescriptorType = USB_DT_CS_INTERFACE; + it_desc->bDescriptorSubtype = UAC_INPUT_TERMINAL; + it_desc->bTerminalID = params->id++; + it_desc->wTerminalType = cpu_to_le16((params->dir == HOST_TO_DEVICE) + ? UAC_TERMINAL_STREAMING + : alt_opts->terminal_type); + it_desc->bAssocTerminal = 0; + it_desc->bNrChannels = num_channels(alt_opts->chmask); + it_desc->wChannelConfig = cpu_to_le16(alt_opts->chmask); + it_desc->iTerminal = add_string(params->strings, alt_opts->it_name); + it_desc->iChannelNames = add_string(params->strings, alt_opts->it_ch_name); +} + +/* 4.3.2.2 Output Terminal Descriptor */ +static void init_ot_desc(struct uac1_output_terminal_descriptor *ot_desc, + struct f_uac1_alt_opts *alt_opts, + struct path_params *params, int src_id) +{ + ot_desc->bLength = UAC_DT_OUTPUT_TERMINAL_SIZE; + ot_desc->bDescriptorType = USB_DT_CS_INTERFACE; + ot_desc->bDescriptorSubtype = UAC_OUTPUT_TERMINAL; + ot_desc->bTerminalID = params->id++; + ot_desc->wTerminalType = cpu_to_le16((params->dir == HOST_TO_DEVICE) + ? alt_opts->terminal_type + : UAC_TERMINAL_STREAMING); + ot_desc->bAssocTerminal = 0; + ot_desc->bSourceID = src_id; + ot_desc->iTerminal = add_string(params->strings, alt_opts->ot_name); +} + +/* 4.3.2.5 Feature Unit Descriptor */ +static struct uac_feature_unit_descriptor *build_fu_desc(struct f_uac1_alt_opts *alt_opts, + struct path_params *params, + int src_id) { struct uac_feature_unit_descriptor *fu_desc; - int channels = num_channels(chmask); + int channels = num_channels(alt_opts->chmask); int fu_desc_size = UAC_DT_FEATURE_UNIT_SIZE(channels); + u32 control = 0; + u8 *i_feature; fu_desc = kzalloc(fu_desc_size, GFP_KERNEL); if (!fu_desc) @@ -1243,29 +1003,46 @@ static struct uac_feature_unit_descriptor *build_fu_desc(int chmask) fu_desc->bLength = fu_desc_size; fu_desc->bDescriptorType = USB_DT_CS_INTERFACE; - fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT; + fu_desc->bUnitID = params->id++; + fu_desc->bSourceID = src_id; fu_desc->bControlSize = 2; - /* bUnitID, bSourceID and bmaControls will be defined later */ + if (params->dir == HOST_TO_DEVICE) { + if (params->opts->c_mute_present) + control |= UAC_FU_MUTE; + if (params->opts->c_volume_present) + control |= UAC_FU_VOLUME; + } + + if (params->dir == DEVICE_TO_HOST) { + if (params->opts->p_mute_present) + control |= UAC_FU_MUTE; + if (params->opts->p_volume_present) + control |= UAC_FU_VOLUME; + } + + /* Only master volume/mute is supported. Per-channel controls are all zero. */ + fu_desc->bmaControls[0] = cpu_to_le16(control); + + /* iFeature is located after all channel controls */ + i_feature = (u8 *)fu_desc + fu_desc->bLength - 1; + *i_feature = add_string(params->strings, alt_opts->fu_vol_name); return fu_desc; } -/* B.3.2 Class-Specific AC Interface Descriptor */ +/* 4.3.2 Class-Specific AC Interface Descriptor */ static struct -uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts) +uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1 *uac1, struct f_uac1_opts *opts) { struct uac1_ac_header_descriptor *ac_desc; int ac_header_desc_size; - int num_ifaces = 0; - - if (EPOUT_EN(opts)) - num_ifaces++; - if (EPIN_EN(opts)) - num_ifaces++; + int capture = epout_en_any(opts); + int playback = epin_en_any(opts); + int ba_iface_id = 0; - ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(num_ifaces); + ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(capture + playback); ac_desc = kzalloc(ac_header_desc_size, GFP_KERNEL); if (!ac_desc) @@ -1275,195 +1052,530 @@ uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts) ac_desc->bDescriptorType = USB_DT_CS_INTERFACE; ac_desc->bDescriptorSubtype = UAC_HEADER; ac_desc->bcdADC = cpu_to_le16(0x0100); - ac_desc->bInCollection = num_ifaces; + ac_desc->bInCollection = capture + playback; + + if (capture) + ac_desc->baInterfaceNr[ba_iface_id++] = uac1->as_out_intf; + + if (playback) + ac_desc->baInterfaceNr[ba_iface_id++] = uac1->as_in_intf; - /* wTotalLength and baInterfaceNr will be defined later */ + /* wTotalLength will be defined later */ return ac_desc; } +/* 4.5.1 Standard AS Interface Descriptor */ +static void init_as_interface_desc(struct usb_interface_descriptor *iface_desc, + u8 ifnum, u8 alt, u8 endpoints, const char *name, + struct usb_string *strings) +{ + iface_desc->bLength = USB_DT_INTERFACE_SIZE; + iface_desc->bDescriptorType = USB_DT_INTERFACE; + iface_desc->bInterfaceNumber = ifnum; + iface_desc->bAlternateSetting = alt; + iface_desc->bNumEndpoints = endpoints; + iface_desc->bInterfaceClass = USB_CLASS_AUDIO; + iface_desc->bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING; + iface_desc->bInterfaceProtocol = 0; + iface_desc->iInterface = add_string(strings, name); +} + +/* 4.5.2 Class-Specific AS Interface Descriptor */ +static void init_as_header_desc(struct uac1_as_header_descriptor *as_header_desc, int terminalId) +{ + as_header_desc->bLength = UAC_DT_AS_HEADER_SIZE; + as_header_desc->bDescriptorType = USB_DT_CS_INTERFACE; + as_header_desc->bDescriptorSubtype = UAC_AS_GENERAL; + as_header_desc->bTerminalLink = terminalId; + as_header_desc->bDelay = 1; + as_header_desc->wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM); +} + +/* 4.5.3 Class-Specific AS Format Type Descriptor */ +static void init_uac_format_type_i_discrete_desc(struct f_uac1_alt_opts *alt_opts) +{ + int idx, i; + + alt_opts->fmt_desc.bDescriptorType = USB_DT_CS_INTERFACE; + alt_opts->fmt_desc.bDescriptorSubtype = UAC_FORMAT_TYPE; + alt_opts->fmt_desc.bFormatType = UAC_FORMAT_TYPE_I; + alt_opts->fmt_desc.bNrChannels = num_channels(alt_opts->chmask); + alt_opts->fmt_desc.bSubframeSize = alt_opts->ssize; + alt_opts->fmt_desc.bBitResolution = alt_opts->ssize * 8; + + /* Set sample rates */ + for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) { + if (alt_opts->srates[i] == 0) + break; + memcpy(alt_opts->fmt_desc.tSamFreq[idx++], + &alt_opts->srates[i], 3); + } + alt_opts->fmt_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx); + alt_opts->fmt_desc.bSamFreqType = idx; +} + +static int init_isoc_ep_descriptor(struct device *dev, struct usb_endpoint_descriptor *ep_desc, + struct f_uac1_alt_opts *alt_opts, int dir, + enum usb_device_speed speed, u8 addr) +{ + ep_desc->bLength = USB_DT_ENDPOINT_AUDIO_SIZE; + ep_desc->bDescriptorType = USB_DT_ENDPOINT; + ep_desc->bEndpointAddress = addr; + ep_desc->bmAttributes = USB_ENDPOINT_XFER_ISOC | + (((dir == HOST_TO_DEVICE) && !EPOUT_FBACK_IN_EN(alt_opts)) + ? USB_ENDPOINT_SYNC_ADAPTIVE + : USB_ENDPOINT_SYNC_ASYNC); + ep_desc->bInterval = 1; /* For FS. For HS/SS, this is set later from hs_bint. */ + ep_desc->bRefresh = 0; + ep_desc->bSynchAddress = EPOUT_FBACK_IN_EN(alt_opts) + ? fs_as_in_fback_desc.bEndpointAddress : 0; + + return set_ep_max_packet_size_bint(dev, alt_opts, ep_desc, speed, (dir == DEVICE_TO_HOST)); +} + +static void init_isoc_ep_descriptor_comp(struct usb_ss_ep_comp_descriptor *ep_desc_comp, + struct usb_endpoint_descriptor *ep_desc) +{ + ep_desc_comp->bLength = sizeof(*ep_desc_comp), + ep_desc_comp->bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + ep_desc_comp->bMaxBurst = 0, + ep_desc_comp->bmAttributes = 0, + ep_desc_comp->wBytesPerInterval = ep_desc->wMaxPacketSize; +} + +static int init_alt_descriptors(struct device *dev, struct f_uac1_alt_opts *alt_opts, int ifnum, + u8 epaddr, int endpoints, int terminalID, int dir, + struct usb_string *strings) +{ + int status = 0; + + init_as_header_desc(&alt_opts->as_header_desc, terminalID); + init_as_interface_desc(&alt_opts->intf_desc, ifnum, alt_opts->c.alt_num, endpoints, + alt_opts->name, strings); + init_uac_format_type_i_discrete_desc(alt_opts); + + status = init_isoc_ep_descriptor(dev, &alt_opts->fs_iso_ep_desc, alt_opts, dir, + USB_SPEED_FULL, epaddr); + if (!status) + status = init_isoc_ep_descriptor(dev, &alt_opts->hs_iso_ep_desc, alt_opts, dir, + USB_SPEED_HIGH, epaddr); + if (!status) + status = init_isoc_ep_descriptor(dev, &alt_opts->ss_iso_ep_desc, alt_opts, dir, + USB_SPEED_SUPER, epaddr); + + init_isoc_ep_descriptor_comp(&alt_opts->ss_iso_ep_desc_comp, &alt_opts->ss_iso_ep_desc); + + return status; +} + +static struct f_uac1_path_descriptors * +build_path_descriptors(struct path_params *params, struct f_uac1_alt_opts *alt_opts) +{ + struct f_uac1_path_descriptors *path_descs; + u8 srcId; + + path_descs = kzalloc(sizeof(*path_descs), GFP_KERNEL); + if (!path_descs) + return NULL; + + path_descs->dir = params->dir; + path_descs->alt_opts = alt_opts; + + init_it_desc(&path_descs->it_desc, alt_opts, params); + srcId = path_descs->it_desc.bTerminalID; + + if (((params->dir == HOST_TO_DEVICE) && FUOUT_EN(params->opts)) || + ((params->dir == DEVICE_TO_HOST) && FUIN_EN(params->opts))) { + path_descs->fu_desc = build_fu_desc(alt_opts, params, + path_descs->it_desc.bTerminalID); + srcId = path_descs->fu_desc->bUnitID; + } + + init_ot_desc(&path_descs->ot_desc, alt_opts, params, srcId); + + return path_descs; +} + +static void free_path_descriptors(struct f_uac1_path_descriptors *path_descs) +{ + kfree(path_descs->fu_desc); + kfree(path_descs); +} + +static struct f_uac1_path_descriptors *find_path_descriptors(struct list_head *list, + struct f_uac1_alt_opts *alt_opts, + int dir) +{ + struct f_uac1_path_descriptors *path_descs; + + list_for_each_entry(path_descs, list, list) { + /* Check that all options used in the path descriptors are the same */ + if ((path_descs->dir == dir) && + (!strncmp(path_descs->alt_opts->name, alt_opts->name, + sizeof(alt_opts->name))) && + (!strncmp(path_descs->alt_opts->it_name, alt_opts->it_name, + sizeof(alt_opts->it_name))) && + (!strncmp(path_descs->alt_opts->it_ch_name, alt_opts->it_ch_name, + sizeof(alt_opts->it_ch_name))) && + (!strncmp(path_descs->alt_opts->ot_name, alt_opts->ot_name, + sizeof(alt_opts->ot_name))) && + (path_descs->alt_opts->chmask == alt_opts->chmask) && + (path_descs->alt_opts->terminal_type == alt_opts->terminal_type)) + return path_descs; + } + return NULL; +} + +static int add_path_descriptors(struct list_head *list, + struct path_params *params, + struct f_uac1_alt_opts *alt_opts) +{ + int len = 0; + struct f_uac1_path_descriptors *path_descs; + + if (!EP_EN(alt_opts)) + return 0; + + path_descs = find_path_descriptors(list, alt_opts, params->dir); + + if (!path_descs) { + path_descs = build_path_descriptors(params, alt_opts); + if (path_descs) { + list_add_tail(&path_descs->list, list); + len += sizeof(path_descs->it_desc); + len += sizeof(path_descs->ot_desc); + if (path_descs->fu_desc) + len += path_descs->fu_desc->bLength; + } + } + + if (path_descs) { + alt_opts->as_header_desc.bTerminalLink = + (params->dir == HOST_TO_DEVICE) ? path_descs->it_desc.bTerminalID + : path_descs->ot_desc.bTerminalID; + alt_opts->it_id = path_descs->it_desc.bTerminalID; + alt_opts->fu_id = path_descs->fu_desc ? path_descs->fu_desc->bUnitID : 0; + alt_opts->ot_id = path_descs->ot_desc.bTerminalID; + } + + return len; +} + /* Use macro to overcome line length limitation */ #define USBDHDR(p) (struct usb_descriptor_header *)(p) -static void setup_headers(struct f_uac1_opts *opts, - struct usb_descriptor_header **headers, - enum usb_device_speed speed); +static int setup_headers(struct usb_descriptor_header **desc_list, + struct f_uac1 *uac1, + struct f_uac1_opts *opts, + struct list_head *path_descs, + enum usb_device_speed speed); -static void setup_descriptor(struct f_uac1_opts *opts) +static int setup_descriptor(struct device *dev, struct f_uac1 *uac1, struct f_uac1_opts *opts, + struct usb_string *strings) { + int status; + struct usb_descriptor_header **fs_desc_list, **hs_desc_list, **ss_ssp_desc_list; + /* patch descriptors */ - int i = 1; /* ID's start with 1 */ - - if (EPOUT_EN(opts)) - usb_out_it_desc.bTerminalID = i++; - if (EPIN_EN(opts)) - io_in_it_desc.bTerminalID = i++; - if (EPOUT_EN(opts)) - io_out_ot_desc.bTerminalID = i++; - if (EPIN_EN(opts)) - usb_in_ot_desc.bTerminalID = i++; - if (FUOUT_EN(opts)) - out_feature_unit_desc->bUnitID = i++; - if (FUIN_EN(opts)) - in_feature_unit_desc->bUnitID = i++; - - if (FUIN_EN(opts)) { - usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID; - in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID; - } else { - usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID; - } - if (FUOUT_EN(opts)) { - io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID; - out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID; - } else { - io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID; + int len; + struct list_head path_descs = LIST_HEAD_INIT(path_descs); + int fs_num, hs_num, ss_ssp_num; + struct f_uac1_alt_opts *alt_opts; + struct list_head *path_desc, *tmp; + struct path_params params; + + params.id = 1; /* ID's start with 1 */ + params.opts = opts; + params.strings = strings; + + ac_header_desc = build_ac_header_desc(uac1, opts); + if (!ac_header_desc) + return -ENOMEM; + + len = ac_header_desc->bLength; + + if (uac1->g_audio.out_ep) { + params.dir = HOST_TO_DEVICE; + init_as_interface_desc(&opts->c_alt_0_opts.intf_desc, uac1->as_out_intf, 0, 0, + opts->c_alt_0_opts.name, strings); + + /* Audio path descriptors (input terminal -> -> output terminal) */ + len += add_path_descriptors(&path_descs, ¶ms, &opts->c_alt_1_opts); + + status = init_alt_descriptors(dev, &opts->c_alt_1_opts, uac1->as_out_intf, + uac1->g_audio.out_ep->address, + EPOUT_FBACK_IN_EN(&opts->c_alt_1_opts) ? 2 : 1, + opts->c_alt_1_opts.it_id, HOST_TO_DEVICE, strings); + if (status) { + dev_err(dev, "Failed to init alt descs for capture alt %d (%d)\n", + 1, status); + goto cleanup; + } + + list_for_each_entry(alt_opts, &opts->c_alt_opts, list) { + len += add_path_descriptors(&path_descs, ¶ms, alt_opts); + + status = init_alt_descriptors(dev, alt_opts, uac1->as_out_intf, + uac1->g_audio.out_ep->address, + EPOUT_FBACK_IN_EN(alt_opts) ? 2 : 1, + alt_opts->it_id, HOST_TO_DEVICE, strings); + if (status) { + dev_err(dev, "Failed to init alt descs for capture alt %d (%d)\n", + alt_opts->c.alt_num, status); + goto cleanup; + } + } } - as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID; - as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID; + if (uac1->g_audio.in_ep) { + params.dir = DEVICE_TO_HOST; + init_as_interface_desc(&opts->p_alt_0_opts.intf_desc, uac1->as_in_intf, 0, 0, + opts->p_alt_0_opts.name, strings); - io_in_it_desc.wTerminalType = cpu_to_le16(opts->c_terminal_type); - io_out_ot_desc.wTerminalType = cpu_to_le16(opts->p_terminal_type); - ac_header_desc->wTotalLength = cpu_to_le16(ac_header_desc->bLength); + /* Audio path descriptors (input terminal -> -> output terminal) */ + len += add_path_descriptors(&path_descs, ¶ms, &opts->p_alt_1_opts); - if (EPIN_EN(opts)) { - u16 len = le16_to_cpu(ac_header_desc->wTotalLength); + status = init_alt_descriptors(dev, &opts->p_alt_1_opts, uac1->as_in_intf, + uac1->g_audio.in_ep->address, 1, + opts->p_alt_1_opts.ot_id, DEVICE_TO_HOST, strings); + if (status) { + dev_err(dev, "Failed to init alt descs for playback alt %d (%d)\n", + 1, status); + goto cleanup; + } - len += sizeof(usb_in_ot_desc); - len += sizeof(io_in_it_desc); - if (FUIN_EN(opts)) - len += in_feature_unit_desc->bLength; - ac_header_desc->wTotalLength = cpu_to_le16(len); + list_for_each_entry(alt_opts, &opts->p_alt_opts, list) { + len += add_path_descriptors(&path_descs, ¶ms, alt_opts); + + status = init_alt_descriptors(dev, alt_opts, uac1->as_in_intf, + uac1->g_audio.in_ep->address, 1, + alt_opts->ot_id, DEVICE_TO_HOST, strings); + if (status) { + dev_err(dev, "Failed to init alt descs for playback alt %d (%d)\n", + alt_opts->c.alt_num, status); + goto cleanup; + } + } } - if (EPOUT_EN(opts)) { - u16 len = le16_to_cpu(ac_header_desc->wTotalLength); - len += sizeof(usb_out_it_desc); - len += sizeof(io_out_ot_desc); - if (FUOUT_EN(opts)) - len += out_feature_unit_desc->bLength; - ac_header_desc->wTotalLength = cpu_to_le16(len); + ac_header_desc->wTotalLength = cpu_to_le16(len); + + /* Count how many descriptors we have and then allocate and populate */ + fs_num = setup_headers(NULL, uac1, opts, &path_descs, USB_SPEED_FULL); + hs_num = setup_headers(NULL, uac1, opts, &path_descs, USB_SPEED_HIGH); + ss_ssp_num = setup_headers(NULL, uac1, opts, &path_descs, USB_SPEED_SUPER); + + fs_desc_list = kzalloc((fs_num + hs_num + ss_ssp_num) * sizeof(*fs_desc_list), GFP_KERNEL); + if (!fs_desc_list) { + status = -ENOMEM; + goto cleanup; } + hs_desc_list = fs_desc_list + fs_num; + ss_ssp_desc_list = hs_desc_list + hs_num; - setup_headers(opts, f_audio_fs_desc, USB_SPEED_FULL); - setup_headers(opts, f_audio_hs_desc, USB_SPEED_HIGH); - setup_headers(opts, f_audio_ss_desc, USB_SPEED_SUPER); + (void) setup_headers(fs_desc_list, uac1, opts, &path_descs, USB_SPEED_FULL); + (void) setup_headers(hs_desc_list, uac1, opts, &path_descs, USB_SPEED_HIGH); + (void) setup_headers(ss_ssp_desc_list, uac1, opts, &path_descs, USB_SPEED_SUPER); + + /* copy descriptors, and track endpoint copies */ + status = usb_assign_descriptors(&uac1->g_audio.func, fs_desc_list, hs_desc_list, + ss_ssp_desc_list, ss_ssp_desc_list); + + if (status) + dev_err(dev, "Failed to assign descriptors (%d)\n", status); + + kfree(fs_desc_list); + +cleanup: + list_for_each_safe(path_desc, tmp, &path_descs) { + free_path_descriptors( + container_of(path_desc, struct f_uac1_path_descriptors, list)); + } + kfree(ac_header_desc); + ac_header_desc = NULL; + + return status; } -static void setup_headers(struct f_uac1_opts *opts, - struct usb_descriptor_header **headers, - enum usb_device_speed speed) +static inline void add_descriptor(int i, struct usb_descriptor_header **desc_list, + struct usb_descriptor_header *desc) +{ + if (desc_list) + desc_list[i] = desc; +} + +static int add_alt_descriptors(int i, struct usb_descriptor_header **desc_list, + struct f_uac1_alt_opts *alt_opts, enum usb_device_speed speed) +{ + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->intf_desc)); + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->as_header_desc)); + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->fmt_desc)); + if (speed == USB_SPEED_FULL) + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->fs_iso_ep_desc)); + else if (speed == USB_SPEED_HIGH) + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->hs_iso_ep_desc)); + else if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) { + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->ss_iso_ep_desc)); + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->ss_iso_ep_desc_comp)); + } + + return i; +} + +static int setup_headers(struct usb_descriptor_header **desc_list, + struct f_uac1 *uac1, + struct f_uac1_opts *opts, + struct list_head *path_descs, + enum usb_device_speed speed) { - struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL; - struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL; struct usb_ss_ep_comp_descriptor *epin_fback_desc_comp = NULL; struct usb_ss_ep_comp_descriptor *ep_int_desc_comp = NULL; - struct usb_endpoint_descriptor *epout_desc; - struct usb_endpoint_descriptor *epin_desc; struct usb_endpoint_descriptor *epin_fback_desc; struct usb_endpoint_descriptor *ep_int_desc; - int i; + + int i = 0; + struct list_head *pos; switch (speed) { case USB_SPEED_FULL: - epout_desc = &fs_as_out_ep_desc; - epin_desc = &fs_as_in_ep_desc; epin_fback_desc = &fs_as_in_fback_desc; ep_int_desc = &fs_ac_int_ep_desc; break; case USB_SPEED_HIGH: - epout_desc = &hs_as_out_ep_desc; - epin_desc = &hs_as_in_ep_desc; epin_fback_desc = &hs_as_in_fback_desc; ep_int_desc = &hs_ac_int_ep_desc; break; default: - epout_desc = &ss_as_out_ep_desc; - epin_desc = &ss_as_in_ep_desc; - epout_desc_comp = &ss_as_out_ep_desc_comp; - epin_desc_comp = &ss_as_in_ep_desc_comp; epin_fback_desc = &ss_as_in_fback_desc; epin_fback_desc_comp = &ss_as_in_fback_desc_comp; ep_int_desc = &ss_ac_int_ep_desc; ep_int_desc_comp = &ss_ac_int_ep_desc_comp; } - i = 0; - headers[i++] = USBDHDR(&ac_interface_desc); - headers[i++] = USBDHDR(ac_header_desc); + add_descriptor(i++, desc_list, USBDHDR(&ac_interface_desc)); + add_descriptor(i++, desc_list, USBDHDR(ac_header_desc)); - if (EPOUT_EN(opts)) { - headers[i++] = USBDHDR(&usb_out_it_desc); - headers[i++] = USBDHDR(&io_out_ot_desc); - if (FUOUT_EN(opts)) - headers[i++] = USBDHDR(out_feature_unit_desc); - } - - if (EPIN_EN(opts)) { - headers[i++] = USBDHDR(&io_in_it_desc); - headers[i++] = USBDHDR(&usb_in_ot_desc); - if (FUIN_EN(opts)) - headers[i++] = USBDHDR(in_feature_unit_desc); + list_for_each(pos, path_descs) { + struct f_uac1_path_descriptors *path_desc = + container_of(pos, struct f_uac1_path_descriptors, list); + add_descriptor(i++, desc_list, USBDHDR(&path_desc->it_desc)); + add_descriptor(i++, desc_list, USBDHDR(&path_desc->ot_desc)); + if (path_desc->fu_desc) + add_descriptor(i++, desc_list, USBDHDR(path_desc->fu_desc)); } + // If any FU exists, add the interrupt endpoint descriptor if (FUOUT_EN(opts) || FUIN_EN(opts)) { - headers[i++] = USBDHDR(ep_int_desc); + add_descriptor(i++, desc_list, USBDHDR(ep_int_desc)); if (ep_int_desc_comp) - headers[i++] = USBDHDR(ep_int_desc_comp); + add_descriptor(i++, desc_list, USBDHDR(ep_int_desc_comp)); } - if (EPOUT_EN(opts)) { - headers[i++] = USBDHDR(&as_out_interface_alt_0_desc); - headers[i++] = USBDHDR(&as_out_interface_alt_1_desc); - headers[i++] = USBDHDR(&as_out_header_desc); - headers[i++] = USBDHDR(&as_out_type_i_desc); - headers[i++] = USBDHDR(epout_desc); - if (epout_desc_comp) - headers[i++] = USBDHDR(epout_desc_comp); + // If any capture interface is active + if (epout_en_any(opts)) { + struct f_uac1_alt_opts *alt_opts; + + add_descriptor(i++, desc_list, USBDHDR(&opts->c_alt_0_opts.intf_desc)); - headers[i++] = USBDHDR(&as_iso_out_desc); + if (EP_EN(&opts->c_alt_1_opts)) { + i = add_alt_descriptors(i, desc_list, &opts->c_alt_1_opts, speed); - if (EPOUT_FBACK_IN_EN(opts)) { - headers[i++] = USBDHDR(epin_fback_desc); - if (epin_fback_desc_comp) - headers[i++] = USBDHDR(epin_fback_desc_comp); + add_descriptor(i++, desc_list, USBDHDR(&as_iso_out_desc)); + if (EPOUT_FBACK_IN_EN(&opts->c_alt_1_opts)) { + add_descriptor(i++, desc_list, USBDHDR(epin_fback_desc)); + if (epin_fback_desc_comp) + add_descriptor(i++, desc_list, + USBDHDR(epin_fback_desc_comp)); + } + } + + list_for_each_entry(alt_opts, &opts->c_alt_opts, list) { + if (EP_EN(alt_opts)) { + i = add_alt_descriptors(i, desc_list, alt_opts, speed); + + add_descriptor(i++, desc_list, USBDHDR(&as_iso_out_desc)); + if (EPOUT_FBACK_IN_EN(alt_opts)) { + add_descriptor(i++, desc_list, USBDHDR(epin_fback_desc)); + if (epin_fback_desc_comp) + add_descriptor(i++, desc_list, + USBDHDR(epin_fback_desc_comp)); + } + } } } - if (EPIN_EN(opts)) { - headers[i++] = USBDHDR(&as_in_interface_alt_0_desc); - headers[i++] = USBDHDR(&as_in_interface_alt_1_desc); - headers[i++] = USBDHDR(&as_in_header_desc); - headers[i++] = USBDHDR(&as_in_type_i_desc); - headers[i++] = USBDHDR(epin_desc); - if (epin_desc_comp) - headers[i++] = USBDHDR(epin_desc_comp); - headers[i++] = USBDHDR(&as_iso_in_desc); + // If any playback interface is active + if (epin_en_any(opts)) { + struct f_uac1_alt_opts *alt_opts; + + add_descriptor(i++, desc_list, USBDHDR(&opts->p_alt_0_opts.intf_desc)); + + if (EP_EN(&opts->p_alt_1_opts)) { + i = add_alt_descriptors(i, desc_list, &opts->p_alt_1_opts, speed); + + add_descriptor(i++, desc_list, USBDHDR(&as_iso_in_desc)); + } + + list_for_each_entry(alt_opts, &opts->p_alt_opts, list) { + if (EP_EN(alt_opts)) { + i = add_alt_descriptors(i, desc_list, alt_opts, speed); + + add_descriptor(i++, desc_list, USBDHDR(&as_iso_in_desc)); + } + } } - headers[i] = NULL; + + add_descriptor(i++, desc_list, NULL); + + return i; } static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) { struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + struct f_uac1_alt_opts *alt_opts; - if (!opts->p_chmask && !opts->c_chmask) { + if (!epin_en_any(opts) && !epout_en_any(opts)) { dev_err(dev, "Error: no playback and capture channels\n"); return -EINVAL; - } else if (opts->p_chmask & ~UAC1_CHANNEL_MASK) { - dev_err(dev, "Error: unsupported playback channels mask\n"); - return -EINVAL; - } else if (opts->c_chmask & ~UAC1_CHANNEL_MASK) { - dev_err(dev, "Error: unsupported capture channels mask\n"); - return -EINVAL; - } else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) { - dev_err(dev, "Error: incorrect playback sample size\n"); - return -EINVAL; - } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) { - dev_err(dev, "Error: incorrect capture sample size\n"); - return -EINVAL; - } else if (!opts->p_srates[0]) { + } + + list_for_each_entry(alt_opts, &opts->p_alt_opts, list) { + if (alt_opts->chmask & ~UAC1_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported playback channels mask for alt %d\n", + alt_opts->c.alt_num); + return -EINVAL; + } else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4)) { + dev_err(dev, "Error: incorrect playback sample size for alt %d\n", + alt_opts->c.alt_num); + return -EINVAL; + } else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4)) { + dev_err(dev, "Error: incorrect playback HS/SS bInterval (1-4: fixed, 0: auto) for alt %d\n", + alt_opts->c.alt_num); + + return -EINVAL; + } + } + + list_for_each_entry(alt_opts, &opts->c_alt_opts, list) { + if (alt_opts->chmask & ~UAC1_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported capture channels mask for alt %d\n", + alt_opts->c.alt_num); + return -EINVAL; + } else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4)) { + dev_err(dev, "Error: incorrect capture sample size for alt %d\n", + alt_opts->c.alt_num); + return -EINVAL; + } else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4)) { + dev_err(dev, "Error: incorrect capture HS/SS bInterval (1-4: fixed, 0: auto) for alt %d\n", + alt_opts->c.alt_num); + + return -EINVAL; + } + } + + if (!opts->p_srates[0]) { dev_err(dev, "Error: incorrect playback sampling rate\n"); return -EINVAL; } else if (!opts->c_srates[0]) { @@ -1493,15 +1605,6 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) return -EINVAL; } - if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4)) { - dev_err(dev, "Error: incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)\n"); - return -EINVAL; - } - if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4)) { - dev_err(dev, "Error: incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)\n"); - return -EINVAL; - } - return 0; } @@ -1551,6 +1654,26 @@ static void init_alt_opts(struct f_uac1_alt_opts *alt_opts, struct f_uac1_opts * alt_opts->terminal_type = (playback) ? opts->p_terminal_type : opts->c_terminal_type; } +static u16 get_max_packet_size(struct f_uac1_alt_opts *alt_opts, struct list_head *list) +{ + u16 max_psize = max_t(u16, + le16_to_cpu(alt_opts->fs_iso_ep_desc.wMaxPacketSize), + le16_to_cpu(alt_opts->hs_iso_ep_desc.wMaxPacketSize)); + max_psize = max_t(u16, max_psize, + le16_to_cpu(alt_opts->ss_iso_ep_desc.wMaxPacketSize)); + + list_for_each_entry(alt_opts, list, list) { + max_psize = max_t(u16, max_psize, + le16_to_cpu(alt_opts->fs_iso_ep_desc.wMaxPacketSize)); + max_psize = max_t(u16, max_psize, + le16_to_cpu(alt_opts->hs_iso_ep_desc.wMaxPacketSize)); + max_psize = max_t(u16, max_psize, + le16_to_cpu(alt_opts->ss_iso_ep_desc.wMaxPacketSize)); + } + + return max_psize; +} + /* audio function driver setup/binding */ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) { @@ -1562,9 +1685,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) struct f_uac1_opts *audio_opts; struct usb_ep *ep = NULL; struct usb_string *us; - int ba_iface_id; int status; - int idx, i; + struct list_head strings = LIST_HEAD_INIT(strings); audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); @@ -1588,214 +1710,41 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) * be used from their alt mode opts. */ - us = attach_strings(cdev, audio_opts); - if (IS_ERR(us)) - return PTR_ERR(us); - - ac_header_desc = build_ac_header_desc(audio_opts); - if (!ac_header_desc) - return -ENOMEM; - - if (FUOUT_EN(audio_opts)) { - out_feature_unit_desc = build_fu_desc(audio_opts->c_chmask); - if (!out_feature_unit_desc) { - status = -ENOMEM; - goto fail; - } - } - if (FUIN_EN(audio_opts)) { - in_feature_unit_desc = build_fu_desc(audio_opts->p_chmask); - if (!in_feature_unit_desc) { - status = -ENOMEM; - goto err_free_fu; - } - } - - ac_interface_desc.iInterface = add_string(us, audio_opts->function_name); - usb_out_it_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.it_name); - usb_out_it_desc.iChannelNames = add_string(us, audio_opts->c_alt_1_opts.it_ch_name); - io_out_ot_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.ot_name); - as_out_interface_alt_0_desc.iInterface = add_string(us, audio_opts->c_alt_0_opts.name); - as_out_interface_alt_1_desc.iInterface = add_string(us, audio_opts->c_alt_1_opts.name); - io_in_it_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.it_name); - io_in_it_desc.iChannelNames = add_string(us, audio_opts->p_alt_1_opts.it_ch_name); - usb_in_ot_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.ot_name); - as_in_interface_alt_0_desc.iInterface = add_string(us, audio_opts->p_alt_0_opts.name); - as_in_interface_alt_1_desc.iInterface = add_string(us, audio_opts->p_alt_1_opts.name); - - if (FUOUT_EN(audio_opts)) { - u8 *i_feature; - - i_feature = (u8 *)out_feature_unit_desc + - out_feature_unit_desc->bLength - 1; - *i_feature = add_string(us, audio_opts->c_alt_1_opts.fu_vol_name); - } - if (FUIN_EN(audio_opts)) { - u8 *i_feature; - - i_feature = (u8 *)in_feature_unit_desc + - in_feature_unit_desc->bLength - 1; - *i_feature = add_string(us, audio_opts->p_alt_1_opts.fu_vol_name); - } - us = attach_strings(cdev, audio_opts); if (IS_ERR(us)) { status = PTR_ERR(us); goto fail; } - /* Set channel numbers */ - usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask); - usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask); - as_out_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask); - as_out_type_i_desc.bSubframeSize = audio_opts->c_ssize; - as_out_type_i_desc.bBitResolution = audio_opts->c_ssize * 8; - io_in_it_desc.bNrChannels = num_channels(audio_opts->p_chmask); - io_in_it_desc.wChannelConfig = cpu_to_le16(audio_opts->p_chmask); - as_in_type_i_desc.bNrChannels = num_channels(audio_opts->p_chmask); - as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize; - as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8; - - if (FUOUT_EN(audio_opts)) { - __le16 *bma = (__le16 *)&out_feature_unit_desc->bmaControls[0]; - u32 control = 0; - - if (audio_opts->c_mute_present) - control |= UAC_FU_MUTE; - if (audio_opts->c_volume_present) - control |= UAC_FU_VOLUME; - *bma = cpu_to_le16(control); - } - if (FUIN_EN(audio_opts)) { - __le16 *bma = (__le16 *)&in_feature_unit_desc->bmaControls[0]; - u32 control = 0; - - if (audio_opts->p_mute_present) - control |= UAC_FU_MUTE; - if (audio_opts->p_volume_present) - control |= UAC_FU_VOLUME; - *bma = cpu_to_le16(control); - } - - /* Set sample rates */ - for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) { - if (audio_opts->c_srates[i] == 0) - break; - memcpy(as_out_type_i_desc.tSamFreq[idx++], - &audio_opts->c_srates[i], 3); - } - as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx); - as_out_type_i_desc.bSamFreqType = idx; + ac_interface_desc.iInterface = add_string(us, audio_opts->function_name); - for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) { - if (audio_opts->p_srates[i] == 0) - break; - memcpy(as_in_type_i_desc.tSamFreq[idx++], - &audio_opts->p_srates[i], 3); - } - as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx); - as_in_type_i_desc.bSamFreqType = idx; uac1->p_srate = audio_opts->p_srates[0]; uac1->c_srate = audio_opts->c_srates[0]; - /* allocate instance-specific interface IDs, and patch descriptors */ + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto err_free_fu; + goto fail; ac_interface_desc.bInterfaceNumber = status; - ac_interface_desc.bNumEndpoints = 1; uac1->ac_intf = status; uac1->ac_alt = 0; - ba_iface_id = 0; - - if (EPOUT_EN(audio_opts)) { + if (epout_en_any(audio_opts)) { status = usb_interface_id(c, f); if (status < 0) - goto err_free_fu; - as_out_interface_alt_0_desc.bInterfaceNumber = status; - as_out_interface_alt_1_desc.bInterfaceNumber = status; - ac_header_desc->baInterfaceNr[ba_iface_id++] = status; + goto fail; uac1->as_out_intf = status; uac1->as_out_alt = 0; - - if (EPOUT_FBACK_IN_EN(audio_opts)) { - fs_as_out_ep_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC; - hs_as_out_ep_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC; - ss_as_out_ep_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC; - ac_interface_desc.bNumEndpoints++; - } else { - fs_as_out_ep_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; - hs_as_out_ep_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; - ss_as_out_ep_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; - } } - if (EPIN_EN(audio_opts)) { + if (epin_en_any(audio_opts)) { status = usb_interface_id(c, f); if (status < 0) - goto err_free_fu; - as_in_interface_alt_0_desc.bInterfaceNumber = status; - as_in_interface_alt_1_desc.bInterfaceNumber = status; - ac_header_desc->baInterfaceNr[ba_iface_id++] = status; + goto fail; uac1->as_in_intf = status; uac1->as_in_alt = 0; } - hs_as_in_ep_desc.bInterval = audio_opts->p_hs_bint; - ss_as_in_ep_desc.bInterval = audio_opts->p_hs_bint; - hs_as_out_ep_desc.bInterval = audio_opts->c_hs_bint; - ss_as_out_ep_desc.bInterval = audio_opts->c_hs_bint; - - /* Calculate wMaxPacketSize according to audio bandwidth */ - status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &fs_as_in_ep_desc, - USB_SPEED_FULL, true); - if (status < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err_free_fu; - } - - status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &fs_as_out_ep_desc, - USB_SPEED_FULL, false); - if (status < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err_free_fu; - } - - status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &hs_as_in_ep_desc, - USB_SPEED_HIGH, true); - if (status < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err_free_fu; - } - - status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &hs_as_out_ep_desc, - USB_SPEED_HIGH, false); - if (status < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err_free_fu; - } - - status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &ss_as_in_ep_desc, - USB_SPEED_SUPER, true); - if (status < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err_free_fu; - } - - status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &hs_as_out_ep_desc, - USB_SPEED_SUPER, false); - if (status < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err_free_fu; - } - audio->gadget = gadget; status = -ENODEV; @@ -1805,36 +1754,49 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) /* allocate AC interrupt endpoint */ if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) { ep = usb_ep_autoconfig(cdev->gadget, &fs_ac_int_ep_desc); - if (!ep) - goto err_free_fu; - + if (!ep) { + status = -EINVAL; + dev_err(dev, "Failed to allocate interrupt endpoint\n"); + goto fail; + } hs_ac_int_ep_desc.bEndpointAddress = fs_ac_int_ep_desc.bEndpointAddress; ss_ac_int_ep_desc.bEndpointAddress = fs_ac_int_ep_desc.bEndpointAddress; uac1->int_ep = ep; - uac1->int_ep->desc = &fs_ac_int_ep_desc; ac_interface_desc.bNumEndpoints = 1; } - /* allocate instance-specific endpoints */ - if (EPOUT_EN(audio_opts)) { - ep = usb_ep_autoconfig(cdev->gadget, &fs_as_out_ep_desc); - if (!ep) - goto err_free_fu; - - hs_as_out_ep_desc.bEndpointAddress = fs_as_out_ep_desc.bEndpointAddress; - ss_as_out_ep_desc.bEndpointAddress = fs_as_out_ep_desc.bEndpointAddress; - ss_as_out_ep_desc_comp.wBytesPerInterval = ss_as_out_ep_desc.wMaxPacketSize; + /* Allocate instance-specific endpoints. These use the FS version for alt mode 1. + * All other alt modes and speeds will be initialized to the same endpoint address + * during the setup_descriptor() call. The u_audio code will update the currently + * selected endpoint descriptor when the alt mode changes. + */ + if (epout_en_any(audio_opts)) { + status = init_isoc_ep_descriptor(dev, &audio_opts->c_alt_1_opts.fs_iso_ep_desc, + &audio_opts->c_alt_1_opts, HOST_TO_DEVICE, + USB_SPEED_FULL, USB_DIR_OUT); + if (status) { + dev_err(dev, "Failed to init FS isoc ep descriptor for capture (%d)\n", + status); + goto fail; + } + ep = usb_ep_autoconfig(cdev->gadget, &audio_opts->c_alt_1_opts.fs_iso_ep_desc); + if (!ep) { + status = -EINVAL; + dev_err(dev, "Failed to allocate isoc endpoint for capture\n"); + goto fail; + } audio->out_ep = ep; - audio->out_ep->desc = &fs_as_out_ep_desc; - if (EPOUT_FBACK_IN_EN(audio_opts)) { + if (epout_fback_in_en_any(audio_opts)) { ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_fback_desc); - if (!ep) - goto err_free_fu; - + if (!ep) { + status = -EINVAL; + dev_err(dev, "Failed to allocate feedback endpoint for capture\n"); + goto fail; + } hs_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress; ss_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress; @@ -1842,45 +1804,44 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) } } - if (EPIN_EN(audio_opts)) { - ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); - if (!ep) - goto err_free_fu; - - hs_as_in_ep_desc.bEndpointAddress = fs_as_in_ep_desc.bEndpointAddress; - ss_as_in_ep_desc.bEndpointAddress = fs_as_in_ep_desc.bEndpointAddress; - ss_as_in_ep_desc_comp.wBytesPerInterval = ss_as_in_ep_desc.wMaxPacketSize; + if (epin_en_any(audio_opts)) { + status = init_isoc_ep_descriptor(dev, &audio_opts->p_alt_1_opts.fs_iso_ep_desc, + &audio_opts->p_alt_1_opts, HOST_TO_DEVICE, + USB_SPEED_FULL, USB_DIR_IN); + if (status) { + dev_err(dev, "Failed to init FS isoc ep descriptor for playback (%d)\n", + status); + goto fail; + } + ep = usb_ep_autoconfig(cdev->gadget, &audio_opts->p_alt_1_opts.fs_iso_ep_desc); + if (!ep) { + status = -EINVAL; + dev_err(dev, "Failed to allocate isoc endpoint for playback\n"); + goto fail; + } audio->in_ep = ep; - audio->in_ep->desc = &fs_as_in_ep_desc; } - setup_descriptor(audio_opts); + status = setup_descriptor(dev, uac1, audio_opts, us); - /* copy descriptors, and track endpoint copies */ - status = usb_assign_descriptors(f, f_audio_fs_desc, f_audio_hs_desc, f_audio_ss_desc, - f_audio_ss_desc); if (status) - goto err_free_fu; - - audio->in_ep_maxpsize = max_t(u16, - le16_to_cpu(fs_as_in_ep_desc.wMaxPacketSize), - le16_to_cpu(hs_as_in_ep_desc.wMaxPacketSize)); - audio->out_ep_maxpsize = max_t(u16, - le16_to_cpu(fs_as_out_ep_desc.wMaxPacketSize), - le16_to_cpu(hs_as_out_ep_desc.wMaxPacketSize)); + goto fail; - audio->in_ep_maxpsize = max_t(u16, audio->in_ep_maxpsize, - le16_to_cpu(ss_as_in_ep_desc.wMaxPacketSize)); - audio->out_ep_maxpsize = max_t(u16, audio->out_ep_maxpsize, - le16_to_cpu(ss_as_out_ep_desc.wMaxPacketSize)); + // Set max packet size for all alt modes. These are used to allocate the buffers in u_audio. + audio->out_ep_maxpsize = get_max_packet_size(&audio_opts->c_alt_1_opts, + &audio_opts->c_alt_opts); + audio->in_ep_maxpsize = get_max_packet_size(&audio_opts->p_alt_1_opts, + &audio_opts->p_alt_opts); + // TODO: This may need some change with the audio params for the current alt mode audio->params.c_chmask = audio_opts->c_chmask; memcpy(audio->params.c_srates, audio_opts->c_srates, sizeof(audio->params.c_srates)); audio->params.c_ssize = audio_opts->c_ssize; + if (FUIN_EN(audio_opts)) { - audio->params.p_fu.id = USB_IN_FU_ID; + audio->params.p_fu.id = USB_IN_FU_ID(audio_opts); audio->params.p_fu.mute_present = audio_opts->p_mute_present; audio->params.p_fu.volume_present = audio_opts->p_volume_present; @@ -1888,12 +1849,15 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio->params.p_fu.volume_max = audio_opts->p_volume_max; audio->params.p_fu.volume_res = audio_opts->p_volume_res; } + + // TODO: This may need some change with the audio params for the current alt mode audio->params.p_chmask = audio_opts->p_chmask; memcpy(audio->params.p_srates, audio_opts->p_srates, sizeof(audio->params.p_srates)); audio->params.p_ssize = audio_opts->p_ssize; + if (FUOUT_EN(audio_opts)) { - audio->params.c_fu.id = USB_OUT_FU_ID; + audio->params.c_fu.id = USB_OUT_FU_ID(audio_opts); audio->params.c_fu.mute_present = audio_opts->c_mute_present; audio->params.c_fu.volume_present = audio_opts->c_volume_present; @@ -1914,14 +1878,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) err_card_register: usb_free_all_descriptors(f); -err_free_fu: - kfree(out_feature_unit_desc); - out_feature_unit_desc = NULL; - kfree(in_feature_unit_desc); - in_feature_unit_desc = NULL; fail: - kfree(ac_header_desc); - ac_header_desc = NULL; return status; } @@ -2342,11 +2299,6 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) g_audio_cleanup(audio); usb_free_all_descriptors(f); - kfree(out_feature_unit_desc); - out_feature_unit_desc = NULL; - kfree(in_feature_unit_desc); - in_feature_unit_desc = NULL; - kfree(ac_header_desc); ac_header_desc = NULL; diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index ae69f1eb872d..e45bd17eb92f 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -9,6 +9,7 @@ #define __U_UAC1_H #include +#include #include "uac_common.h" #define UAC1_OUT_EP_MAX_PACKET_SIZE 200 @@ -47,8 +48,15 @@ struct f_uac1_alt_0_opts { struct f_uac1_alt_opts_common c; char name[USB_MAX_STRING_LEN]; + + /* Descriptors */ + struct usb_interface_descriptor intf_desc; }; +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES); +#define uac_format_type_i_discrete_descriptor \ + uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES + /* Alt modes 1+ */ struct f_uac1_alt_opts { struct f_uac1_alt_opts_common c; @@ -70,6 +78,36 @@ struct f_uac1_alt_opts { u8 hs_bint; s16 terminal_type; + /* Descriptors */ + struct usb_interface_descriptor intf_desc; + struct uac1_as_header_descriptor as_header_desc; + struct uac_format_type_i_discrete_descriptor fmt_desc; + + struct usb_endpoint_descriptor fs_iso_ep_desc; + struct usb_endpoint_descriptor hs_iso_ep_desc; + struct usb_endpoint_descriptor ss_iso_ep_desc; + struct usb_ss_ep_comp_descriptor ss_iso_ep_desc_comp; + + u8 it_id; /* Input Terminal Descriptor bTerminalID */ + u8 fu_id; /* Feature Unit Descriptor bUnitID */ + u8 ot_id; /* Output Terminal Descriptor bTerminalID */ +}; + +#undef uac_format_type_i_discrete_descriptor + +struct f_uac1_path_descriptors { + struct list_head list; + + int dir; /* HOST_TO_DEVICE or DEVICE_TO_HOST */ + + /* Alt mode opts this path descriptor is from */ + struct f_uac1_alt_opts *alt_opts; + + struct uac_input_terminal_descriptor it_desc; + struct uac1_output_terminal_descriptor ot_desc; + + /* Feature unit is optional */ + struct uac_feature_unit_descriptor *fu_desc; }; struct f_uac1_opts { From patchwork Sat Sep 28 15:09:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814709 Received: from mail-qk1-f180.google.com (mail-qk1-f180.google.com [209.85.222.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE61918A921; Sat, 28 Sep 2024 15:09:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536189; cv=none; b=Knmr5S/7ZoKeLw5gKVvvsEZyXocaG6RlTtbKcjVQpINbKyqpmWm1PNbo6yHCOEKPlQrJDanyuficcHcJvZfFTVeNZoPrOT83w5YNxjSOEG0992l+JyKB5imHNegJYt0Re3PU7u4AtnvLkVqZR33tnodhWAvE7sjcSRagKvf/ZwU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536189; c=relaxed/simple; bh=o7XGOokJ+e+sOjGGkTJZ+wTHZqR1vPtWH8P+uQmd+IY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HVm2I067jH7C3lnuyBlXwrIPsgXyiAJXzkoxMzngbdKAxA6exrYFAxx3gccIR1UHniImuVtYJDHlrW4x3p5HMVZfXNcF4I68/tLX4fIVRVT+QlF+cg83L3mcznn7YXTzTlNUa0XfawLq6Lzg57mIn/je9SuPwYnzUk51J6qMhlM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=QCPcXD0N; arc=none smtp.client-ip=209.85.222.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QCPcXD0N" Received: by mail-qk1-f180.google.com with SMTP id af79cd13be357-7a9a62c6734so26991685a.3; Sat, 28 Sep 2024 08:09:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536184; x=1728140984; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=cz6QLjUN8djoup62DDAPYZRS4zR0AtcHy88IlKr36fM=; b=QCPcXD0Nr3JK9Cukco75Dcqk4FcoksSuc4VD4eAIsVbc/ALC5a4+6ldmje8kWp1adp OsdEvfXFr3SoCrkAIkMfxCoQlJJsYlHjfK6TY8M5YvrLoj/PBE+PfEJMpGdqd5sgZv1j Uz+QpuTI4NIC86uyERcsGWTuGpoAPb/aUtSgLOEiTsAyE8RYgOvgmR0Mjg5w1et1iew4 MPFGqG2hPQxsYpdexbnRCuLjK2uzR1GDWh3kY0V5rR9yJfGXbUrt0dIpGj5qs7E8CqGx DOYCs+uRuoVzPegd6AZfG8T+MxMvePxfsW+x63kLhNqYyumLQocWPEULueXfabWfrWkD T+/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536184; x=1728140984; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=cz6QLjUN8djoup62DDAPYZRS4zR0AtcHy88IlKr36fM=; b=GG4oUQflhHXpM8R0K3WVMMzoAdSKHXobURtXuTCpnq5dNawZ+dp2Jc7hIVb4gJclLI mp2ldd2byXhiooQ931HnMohsakS+AhQ2OS979WX2Rgs8b5LwP5meHnZCAH0wyfdSTPQp We3ooA9Fxr9GfXs/zX9nM4/8CXSM8JccewACM8f7lQP8mm+t09eo6v+ymr3oeLeJ7pEo 8/eLWoicxJeBJSOVBjpbUqrhXP3/NxegSpBhnz+JUCkMX3pJVYjSUDxqd6jr65zbX6ax i75zFqFP2dQRtvywoo29y1E9dA3d8DNu0OjrLHC8TlgtfE7iasBQrAYDlgIogkX6p7pN oJNA== X-Forwarded-Encrypted: i=1; AJvYcCUiMUcFusSoICCW6I24sFnSVtDHUPw6aVb9pA/pcZ+pvMvh7pvWZd3BIykAZzTKjGr+xWJ5zv32XgM=@vger.kernel.org, AJvYcCXirBry8pB6RyR5bX4boEP1XtaeR/P3VJidWUi5UNJFfQBmIAAZLZ6hjcjApEuJV6Z8zNZStKx/7n8I2n40@vger.kernel.org X-Gm-Message-State: AOJu0YxFfJPqI5/qb0J8bn2lMAGGyd749ZX3yOBC/Vosga7G+Z58wPCo Ozr8UHBMlDlVIE7mGt7siikBWaH1wjO+VsWaUHyg7+TGzzXp/A8cNoHQJwP/DKc= X-Google-Smtp-Source: AGHT+IGW4JQk4HIYPMoWaFCttCnWvXK9oy4qBu+P1iXTm5F+1j3cswuwN649ZVJisD3Ih3NEm03E3w== X-Received: by 2002:a05:620a:1707:b0:7a9:bf88:7d9a with SMTP id af79cd13be357-7ae378c355dmr419970585a.10.1727536184033; Sat, 28 Sep 2024 08:09:44 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:43 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 12/14] usb: gadget: f_uac2: Generate dynamic descriptors based on alt opts Date: Sat, 28 Sep 2024 11:09:03 -0400 Message-ID: <20240928150905.2616313-13-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff Descriptors are now generated based on what alt modes are configured. The lifetime of allocations has changed a bit with this such that we deallocate our copy of the descriptors as soon as they've been registered. Many of the descriptors that were static are now attached to their alt mode opts and initialized with a function. Signed-off-by: Chris Wulff --- drivers/usb/gadget/function/f_uac2.c | 1322 ++++++++++++-------------- drivers/usb/gadget/function/u_uac2.h | 32 + 2 files changed, 654 insertions(+), 700 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 54702888855d..c30fbd062793 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -35,8 +35,8 @@ */ #define USB_OUT_CLK_ID (out_clk_src_desc.bClockID) #define USB_IN_CLK_ID (in_clk_src_desc.bClockID) -#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID) -#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID) +#define USB_OUT_FU_ID(_opts) (_opts->c_alt_1_opts.fu_id) +#define USB_IN_FU_ID(_opts) (_opts->p_alt_1_opts.fu_id) #define CONTROL_ABSENT 0 #define CONTROL_RDONLY 1 @@ -54,15 +54,36 @@ #define UNFLW_CTRL 8 #define OVFLW_CTRL 10 -#define EPIN_EN(_opts) ((_opts)->p_chmask != 0) -#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) -#define FUIN_EN(_opts) (EPIN_EN(_opts) \ + +#define EP_EN(_alt_opts) ((_alt_opts) && ((_alt_opts)->chmask != 0)) +#define FUIN_EN(_opts) (EP_EN(&_opts->p_alt_1_opts) \ && ((_opts)->p_mute_present \ || (_opts)->p_volume_present)) -#define FUOUT_EN(_opts) (EPOUT_EN(_opts) \ +#define FUOUT_EN(_opts) (EP_EN(&_opts->c_alt_1_opts) \ && ((_opts)->c_mute_present \ || (_opts)->c_volume_present)) -#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC) +#define EPOUT_FBACK_IN_EN(_alt_opts) ((_alt_opts)->sync == USB_ENDPOINT_SYNC_ASYNC) + +/* Check if any alt mode has option enabled */ +#define EN_ANY(single, fn, cp) \ +static int fn(struct f_uac2_opts *opts) \ +{ \ + struct f_uac2_alt_opts *alt_opts; \ + \ + if (single(&opts->cp##_alt_1_opts)) \ + return 1; \ + \ + list_for_each_entry(alt_opts, &opts->cp##_alt_opts, list) { \ + if (single(alt_opts)) \ + return 1; \ + } \ + \ + return 0; \ +} + +EN_ANY(EP_EN, epout_en_any, c) +EN_ANY(EP_EN, epin_en_any, p) +EN_ANY(EPOUT_FBACK_IN_EN, epout_fback_in_en_any, p) struct f_uac2 { struct g_audio g_audio; @@ -94,7 +115,7 @@ static int afunc_notify(struct g_audio *agdev, int unit_id, int cs); /* --------- USB Function Interface ------------- */ static struct usb_interface_assoc_descriptor iad_desc = { - .bLength = sizeof iad_desc, + .bLength = sizeof(iad_desc), .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, .bFirstInterface = 0, @@ -140,65 +161,6 @@ static struct uac_clock_source_descriptor out_clk_src_desc = { .bAssocTerminal = 0, }; -/* Input Terminal for USB_OUT */ -static struct uac2_input_terminal_descriptor usb_out_it_desc = { - .bLength = sizeof usb_out_it_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - - .bDescriptorSubtype = UAC_INPUT_TERMINAL, - /* .bTerminalID = DYNAMIC */ - .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), - .bAssocTerminal = 0, - /* .bCSourceID = DYNAMIC */ - .iChannelNames = 0, - .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), -}; - -/* Input Terminal for I/O-In */ -static struct uac2_input_terminal_descriptor io_in_it_desc = { - .bLength = sizeof io_in_it_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - - .bDescriptorSubtype = UAC_INPUT_TERMINAL, - /* .bTerminalID = DYNAMIC */ - /* .wTerminalType = DYNAMIC */ - .bAssocTerminal = 0, - /* .bCSourceID = DYNAMIC */ - .iChannelNames = 0, - .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), -}; - -/* Ouput Terminal for USB_IN */ -static struct uac2_output_terminal_descriptor usb_in_ot_desc = { - .bLength = sizeof usb_in_ot_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - - .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - /* .bTerminalID = DYNAMIC */ - .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), - .bAssocTerminal = 0, - /* .bSourceID = DYNAMIC */ - /* .bCSourceID = DYNAMIC */ - .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), -}; - -/* Ouput Terminal for I/O-Out */ -static struct uac2_output_terminal_descriptor io_out_ot_desc = { - .bLength = sizeof io_out_ot_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - - .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - /* .bTerminalID = DYNAMIC */ - /* .wTerminalType = DYNAMIC */ - .bAssocTerminal = 0, - /* .bSourceID = DYNAMIC */ - /* .bCSourceID = DYNAMIC */ - .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), -}; - -static struct uac2_feature_unit_descriptor *in_feature_unit_desc; -static struct uac2_feature_unit_descriptor *out_feature_unit_desc; - static struct uac2_ac_header_descriptor ac_hdr_desc = { .bLength = sizeof ac_hdr_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -246,89 +208,6 @@ static struct usb_ss_ep_comp_descriptor ss_ep_int_desc_comp = { .wBytesPerInterval = cpu_to_le16(6), }; -/* Audio Streaming OUT Interface - Alt0 */ -static struct usb_interface_descriptor std_as_out_if0_desc = { - .bLength = sizeof std_as_out_if0_desc, - .bDescriptorType = USB_DT_INTERFACE, - - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, - .bInterfaceProtocol = UAC_VERSION_2, -}; - -/* Audio Streaming OUT Interface - Alt1 */ -static struct usb_interface_descriptor std_as_out_if1_desc = { - .bLength = sizeof std_as_out_if1_desc, - .bDescriptorType = USB_DT_INTERFACE, - - .bAlternateSetting = 1, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, - .bInterfaceProtocol = UAC_VERSION_2, -}; - -/* Audio Stream OUT Intface Desc */ -static struct uac2_as_header_descriptor as_out_hdr_desc = { - .bLength = sizeof as_out_hdr_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - - .bDescriptorSubtype = UAC_AS_GENERAL, - /* .bTerminalLink = DYNAMIC */ - .bmControls = 0, - .bFormatType = UAC_FORMAT_TYPE_I, - .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM), - .iChannelNames = 0, -}; - -/* Audio USB_OUT Format */ -static struct uac2_format_type_i_descriptor as_out_fmt1_desc = { - .bLength = sizeof as_out_fmt1_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_FORMAT_TYPE, - .bFormatType = UAC_FORMAT_TYPE_I, -}; - -/* STD AS ISO OUT Endpoint */ -static struct usb_endpoint_descriptor fs_epout_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - /* .bmAttributes = DYNAMIC */ - /* .wMaxPacketSize = DYNAMIC */ - .bInterval = 1, -}; - -static struct usb_endpoint_descriptor hs_epout_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - /* .bmAttributes = DYNAMIC */ - /* .wMaxPacketSize = DYNAMIC */ - /* .bInterval = DYNAMIC */ -}; - -static struct usb_endpoint_descriptor ss_epout_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - /* .bmAttributes = DYNAMIC */ - /* .wMaxPacketSize = DYNAMIC */ - /* .bInterval = DYNAMIC */ -}; - -static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = { - .bLength = sizeof(ss_epout_desc_comp), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - .bMaxBurst = 0, - .bmAttributes = 0, - /* wBytesPerInterval = DYNAMIC */ -}; - /* CS AS ISO OUT Endpoint */ static struct uac2_iso_endpoint_descriptor as_iso_out_desc = { .bLength = sizeof as_iso_out_desc, @@ -379,90 +258,6 @@ static struct usb_ss_ep_comp_descriptor ss_epin_fback_desc_comp = { .wBytesPerInterval = cpu_to_le16(4), }; - -/* Audio Streaming IN Interface - Alt0 */ -static struct usb_interface_descriptor std_as_in_if0_desc = { - .bLength = sizeof std_as_in_if0_desc, - .bDescriptorType = USB_DT_INTERFACE, - - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, - .bInterfaceProtocol = UAC_VERSION_2, -}; - -/* Audio Streaming IN Interface - Alt1 */ -static struct usb_interface_descriptor std_as_in_if1_desc = { - .bLength = sizeof std_as_in_if1_desc, - .bDescriptorType = USB_DT_INTERFACE, - - .bAlternateSetting = 1, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, - .bInterfaceProtocol = UAC_VERSION_2, -}; - -/* Audio Stream IN Intface Desc */ -static struct uac2_as_header_descriptor as_in_hdr_desc = { - .bLength = sizeof as_in_hdr_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - - .bDescriptorSubtype = UAC_AS_GENERAL, - /* .bTerminalLink = DYNAMIC */ - .bmControls = 0, - .bFormatType = UAC_FORMAT_TYPE_I, - .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM), - .iChannelNames = 0, -}; - -/* Audio USB_IN Format */ -static struct uac2_format_type_i_descriptor as_in_fmt1_desc = { - .bLength = sizeof as_in_fmt1_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_FORMAT_TYPE, - .bFormatType = UAC_FORMAT_TYPE_I, -}; - -/* STD AS ISO IN Endpoint */ -static struct usb_endpoint_descriptor fs_epin_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, - /* .wMaxPacketSize = DYNAMIC */ - .bInterval = 1, -}; - -static struct usb_endpoint_descriptor hs_epin_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, - /* .wMaxPacketSize = DYNAMIC */ - /* .bInterval = DYNAMIC */ -}; - -static struct usb_endpoint_descriptor ss_epin_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, - /* .wMaxPacketSize = DYNAMIC */ - /* .bInterval = DYNAMIC */ -}; - -static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = { - .bLength = sizeof(ss_epin_desc_comp), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - .bMaxBurst = 0, - .bmAttributes = 0, - /* wBytesPerInterval = DYNAMIC */ -}; - /* CS AS ISO IN Endpoint */ static struct uac2_iso_endpoint_descriptor as_iso_in_desc = { .bLength = sizeof as_iso_in_desc, @@ -475,115 +270,6 @@ static struct uac2_iso_endpoint_descriptor as_iso_in_desc = { .wLockDelay = 0, }; -static struct usb_descriptor_header *fs_audio_desc[] = { - (struct usb_descriptor_header *)&iad_desc, - (struct usb_descriptor_header *)&std_ac_if_desc, - - (struct usb_descriptor_header *)&ac_hdr_desc, - (struct usb_descriptor_header *)&in_clk_src_desc, - (struct usb_descriptor_header *)&out_clk_src_desc, - (struct usb_descriptor_header *)&usb_out_it_desc, - (struct usb_descriptor_header *)&out_feature_unit_desc, - (struct usb_descriptor_header *)&io_in_it_desc, - (struct usb_descriptor_header *)&usb_in_ot_desc, - (struct usb_descriptor_header *)&in_feature_unit_desc, - (struct usb_descriptor_header *)&io_out_ot_desc, - - (struct usb_descriptor_header *)&fs_ep_int_desc, - - (struct usb_descriptor_header *)&std_as_out_if0_desc, - (struct usb_descriptor_header *)&std_as_out_if1_desc, - - (struct usb_descriptor_header *)&as_out_hdr_desc, - (struct usb_descriptor_header *)&as_out_fmt1_desc, - (struct usb_descriptor_header *)&fs_epout_desc, - (struct usb_descriptor_header *)&as_iso_out_desc, - (struct usb_descriptor_header *)&fs_epin_fback_desc, - - (struct usb_descriptor_header *)&std_as_in_if0_desc, - (struct usb_descriptor_header *)&std_as_in_if1_desc, - - (struct usb_descriptor_header *)&as_in_hdr_desc, - (struct usb_descriptor_header *)&as_in_fmt1_desc, - (struct usb_descriptor_header *)&fs_epin_desc, - (struct usb_descriptor_header *)&as_iso_in_desc, - NULL, -}; - -static struct usb_descriptor_header *hs_audio_desc[] = { - (struct usb_descriptor_header *)&iad_desc, - (struct usb_descriptor_header *)&std_ac_if_desc, - - (struct usb_descriptor_header *)&ac_hdr_desc, - (struct usb_descriptor_header *)&in_clk_src_desc, - (struct usb_descriptor_header *)&out_clk_src_desc, - (struct usb_descriptor_header *)&usb_out_it_desc, - (struct usb_descriptor_header *)&out_feature_unit_desc, - (struct usb_descriptor_header *)&io_in_it_desc, - (struct usb_descriptor_header *)&usb_in_ot_desc, - (struct usb_descriptor_header *)&in_feature_unit_desc, - (struct usb_descriptor_header *)&io_out_ot_desc, - - (struct usb_descriptor_header *)&hs_ep_int_desc, - - (struct usb_descriptor_header *)&std_as_out_if0_desc, - (struct usb_descriptor_header *)&std_as_out_if1_desc, - - (struct usb_descriptor_header *)&as_out_hdr_desc, - (struct usb_descriptor_header *)&as_out_fmt1_desc, - (struct usb_descriptor_header *)&hs_epout_desc, - (struct usb_descriptor_header *)&as_iso_out_desc, - (struct usb_descriptor_header *)&hs_epin_fback_desc, - - (struct usb_descriptor_header *)&std_as_in_if0_desc, - (struct usb_descriptor_header *)&std_as_in_if1_desc, - - (struct usb_descriptor_header *)&as_in_hdr_desc, - (struct usb_descriptor_header *)&as_in_fmt1_desc, - (struct usb_descriptor_header *)&hs_epin_desc, - (struct usb_descriptor_header *)&as_iso_in_desc, - NULL, -}; - -static struct usb_descriptor_header *ss_audio_desc[] = { - (struct usb_descriptor_header *)&iad_desc, - (struct usb_descriptor_header *)&std_ac_if_desc, - - (struct usb_descriptor_header *)&ac_hdr_desc, - (struct usb_descriptor_header *)&in_clk_src_desc, - (struct usb_descriptor_header *)&out_clk_src_desc, - (struct usb_descriptor_header *)&usb_out_it_desc, - (struct usb_descriptor_header *)&out_feature_unit_desc, - (struct usb_descriptor_header *)&io_in_it_desc, - (struct usb_descriptor_header *)&usb_in_ot_desc, - (struct usb_descriptor_header *)&in_feature_unit_desc, - (struct usb_descriptor_header *)&io_out_ot_desc, - - (struct usb_descriptor_header *)&ss_ep_int_desc, - (struct usb_descriptor_header *)&ss_ep_int_desc_comp, - - (struct usb_descriptor_header *)&std_as_out_if0_desc, - (struct usb_descriptor_header *)&std_as_out_if1_desc, - - (struct usb_descriptor_header *)&as_out_hdr_desc, - (struct usb_descriptor_header *)&as_out_fmt1_desc, - (struct usb_descriptor_header *)&ss_epout_desc, - (struct usb_descriptor_header *)&ss_epout_desc_comp, - (struct usb_descriptor_header *)&as_iso_out_desc, - (struct usb_descriptor_header *)&ss_epin_fback_desc, - (struct usb_descriptor_header *)&ss_epin_fback_desc_comp, - - (struct usb_descriptor_header *)&std_as_in_if0_desc, - (struct usb_descriptor_header *)&std_as_in_if1_desc, - - (struct usb_descriptor_header *)&as_in_hdr_desc, - (struct usb_descriptor_header *)&as_in_fmt1_desc, - (struct usb_descriptor_header *)&ss_epin_desc, - (struct usb_descriptor_header *)&ss_epin_desc_comp, - (struct usb_descriptor_header *)&as_iso_in_desc, - NULL, -}; - struct cntrl_cur_lay2 { __le16 wCUR; }; @@ -720,11 +406,65 @@ static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_a alt_opts->ssize, alt_opts->sync, alt_opts->c.opts->fb_max); } -static struct uac2_feature_unit_descriptor *build_fu_desc(int chmask) +struct path_params { + int dir; + int id; + struct f_uac2_opts *opts; + struct usb_string *strings; +}; + +/* Audio20 4.7.2.4 Input Terminal Descriptor */ +static void init_it_desc(struct uac2_input_terminal_descriptor *it_desc, + struct f_uac2_alt_opts *alt_opts, + struct path_params *params) +{ + it_desc->bLength = sizeof(*it_desc); + it_desc->bDescriptorType = USB_DT_CS_INTERFACE; + it_desc->bDescriptorSubtype = UAC_INPUT_TERMINAL; + it_desc->bTerminalID = params->id++; + it_desc->wTerminalType = cpu_to_le16((params->dir == HOST_TO_DEVICE) ? + UAC_TERMINAL_STREAMING : + alt_opts->terminal_type); + it_desc->bAssocTerminal = 0; + it_desc->bCSourceID = (params->dir == HOST_TO_DEVICE) ? out_clk_src_desc.bClockID + : in_clk_src_desc.bClockID; + it_desc->bNrChannels = num_channels(alt_opts->chmask); + it_desc->bmChannelConfig = cpu_to_le32(alt_opts->chmask); + it_desc->iChannelNames = add_string(params->strings, alt_opts->it_ch_name); + it_desc->bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL); + it_desc->iTerminal = add_string(params->strings, alt_opts->it_name); +} + +/* Audio20 4.7.2.5 Output Terminal Descriptor */ +static void init_ot_desc(struct uac2_output_terminal_descriptor *ot_desc, + struct f_uac2_alt_opts *alt_opts, + struct path_params *params, int src_id) +{ + ot_desc->bLength = sizeof(*ot_desc); + ot_desc->bDescriptorType = USB_DT_CS_INTERFACE; + ot_desc->bDescriptorSubtype = UAC_OUTPUT_TERMINAL; + ot_desc->bTerminalID = params->id++; + ot_desc->wTerminalType = cpu_to_le16((params->dir == HOST_TO_DEVICE) ? + alt_opts->terminal_type : + UAC_TERMINAL_STREAMING); + ot_desc->bAssocTerminal = 0; + ot_desc->bSourceID = src_id; + ot_desc->bCSourceID = (params->dir == HOST_TO_DEVICE) ? out_clk_src_desc.bClockID + : in_clk_src_desc.bClockID; + ot_desc->bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL); + ot_desc->iTerminal = add_string(params->strings, alt_opts->ot_name); +} + +/* Audio20 4.7.2.8 Feature Unit Descriptor */ +static struct uac2_feature_unit_descriptor *build_fu_desc(struct f_uac2_alt_opts *alt_opts, + struct path_params *params, int src_id) { struct uac2_feature_unit_descriptor *fu_desc; - int channels = num_channels(chmask); + int channels = num_channels(alt_opts->chmask); int fu_desc_size = UAC2_DT_FEATURE_UNIT_SIZE(channels); + __le32 *bma; + u32 control = 0; + u8 *i_feature; fu_desc = kzalloc(fu_desc_size, GFP_KERNEL); if (!fu_desc) @@ -732,218 +472,520 @@ static struct uac2_feature_unit_descriptor *build_fu_desc(int chmask) fu_desc->bLength = fu_desc_size; fu_desc->bDescriptorType = USB_DT_CS_INTERFACE; - fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT; + fu_desc->bUnitID = params->id++; + fu_desc->bSourceID = src_id; /* bUnitID, bSourceID and bmaControls will be defined later */ + if (params->dir == HOST_TO_DEVICE) { + if (params->opts->c_mute_present) + control |= CONTROL_RDWR << FU_MUTE_CTRL; + if (params->opts->c_volume_present) + control |= CONTROL_RDWR << FU_VOL_CTRL; + } + + if (params->dir == DEVICE_TO_HOST) { + if (params->opts->p_mute_present) + control |= CONTROL_RDWR << FU_MUTE_CTRL; + if (params->opts->p_volume_present) + control |= CONTROL_RDWR << FU_VOL_CTRL; + } + + /* Only master volume/mute is supported. Per-channel controls are all zero. */ + bma = (__le32 *)&fu_desc->bmaControls[0]; + *bma = cpu_to_le32(control); + + /* iFeature is located after all channel controls */ + i_feature = (u8 *)fu_desc + fu_desc->bLength - 1; + *i_feature = add_string(params->strings, alt_opts->fu_vol_name); return fu_desc; } +/* Audio20 4.9.1 Standard AS Interface Descriptor */ +static void init_as_interface_desc(struct usb_interface_descriptor *iface_desc, + u8 ifnum, u8 alt, u8 endpoints, const char *name, + struct usb_string *strings) +{ + iface_desc->bLength = sizeof(*iface_desc); + iface_desc->bDescriptorType = USB_DT_INTERFACE; + iface_desc->bInterfaceNumber = ifnum; + iface_desc->bAlternateSetting = alt; + iface_desc->bNumEndpoints = endpoints; + iface_desc->bInterfaceClass = USB_CLASS_AUDIO; + iface_desc->bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING; + iface_desc->bInterfaceProtocol = UAC_VERSION_2; + iface_desc->iInterface = add_string(strings, name); +} + +/* Audio20 4.9.2 Class-Specific AS Interface Descriptor */ +static void init_as_header_desc(struct f_uac2_alt_opts *alt_opts, int terminalId) +{ + alt_opts->as_header_desc.bLength = sizeof(alt_opts->as_header_desc); + alt_opts->as_header_desc.bDescriptorType = USB_DT_CS_INTERFACE; + alt_opts->as_header_desc.bDescriptorSubtype = UAC_AS_GENERAL; + alt_opts->as_header_desc.bTerminalLink = terminalId; + alt_opts->as_header_desc.bmControls = 0; + alt_opts->as_header_desc.bFormatType = UAC_FORMAT_TYPE_I; + alt_opts->as_header_desc.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM); + alt_opts->as_header_desc.bNrChannels = num_channels(alt_opts->chmask); + alt_opts->as_header_desc.bmChannelConfig = cpu_to_le32(alt_opts->chmask); + alt_opts->as_header_desc.iChannelNames = 0; +} + +/* Audio20 4.9.3 Class-Specific AS Format Type Descriptor */ +static void init_uac_format_type_i_discrete_desc(struct f_uac2_alt_opts *alt_opts) +{ + alt_opts->fmt_desc.bLength = sizeof(alt_opts->fmt_desc); + alt_opts->fmt_desc.bDescriptorType = USB_DT_CS_INTERFACE; + alt_opts->fmt_desc.bDescriptorSubtype = UAC_FORMAT_TYPE; + alt_opts->fmt_desc.bFormatType = UAC_FORMAT_TYPE_I; + alt_opts->fmt_desc.bSubslotSize = alt_opts->ssize; + alt_opts->fmt_desc.bBitResolution = alt_opts->ssize * 8; +} + +static int init_isoc_ep_descriptor(struct device *dev, struct usb_endpoint_descriptor *ep_desc, + struct f_uac2_alt_opts *alt_opts, int dir, + enum usb_device_speed speed, u8 addr) +{ + ep_desc->bLength = USB_DT_ENDPOINT_SIZE; + ep_desc->bDescriptorType = USB_DT_ENDPOINT; + ep_desc->bEndpointAddress = addr; + ep_desc->bmAttributes = USB_ENDPOINT_XFER_ISOC | + (((dir == HOST_TO_DEVICE) && !EPOUT_FBACK_IN_EN(alt_opts)) + ? USB_ENDPOINT_SYNC_ADAPTIVE + : USB_ENDPOINT_SYNC_ASYNC); + ep_desc->bInterval = 1; /* For FS. For HS/SS, this is set later from hs_bint. */ + + return set_ep_max_packet_size_bint(dev, alt_opts, ep_desc, speed, (dir == DEVICE_TO_HOST)); +} + +static void init_isoc_ep_descriptor_comp(struct usb_ss_ep_comp_descriptor *ep_desc_comp, + struct usb_endpoint_descriptor *ep_desc) +{ + ep_desc_comp->bLength = sizeof(*ep_desc_comp), + ep_desc_comp->bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + ep_desc_comp->bMaxBurst = 0, + ep_desc_comp->bmAttributes = 0, + ep_desc_comp->wBytesPerInterval = ep_desc->wMaxPacketSize; +} + +static int init_alt_descriptors(struct device *dev, struct f_uac2_alt_opts *alt_opts, int ifnum, + u8 epaddr, int endpoints, int terminalID, int dir, + struct usb_string *strings) +{ + int status = 0; + + init_as_header_desc(alt_opts, terminalID); + init_as_interface_desc(&alt_opts->intf_desc, ifnum, alt_opts->c.alt_num, + endpoints, alt_opts->name, strings); + init_uac_format_type_i_discrete_desc(alt_opts); + + status = init_isoc_ep_descriptor(dev, &alt_opts->fs_iso_ep_desc, alt_opts, + dir, USB_SPEED_FULL, epaddr); + if (!status) + status = init_isoc_ep_descriptor(dev, &alt_opts->hs_iso_ep_desc, alt_opts, + dir, USB_SPEED_HIGH, epaddr); + if (!status) + status = init_isoc_ep_descriptor(dev, &alt_opts->ss_iso_ep_desc, alt_opts, + dir, USB_SPEED_SUPER, epaddr); + + init_isoc_ep_descriptor_comp(&alt_opts->ss_iso_ep_desc_comp, &alt_opts->ss_iso_ep_desc); + + return status; +} + +static struct f_uac2_path_descriptors *build_path_descriptors(struct path_params *params, + struct f_uac2_alt_opts *alt_opts) +{ + struct f_uac2_path_descriptors *path_descs; + u8 srcId; + + path_descs = kzalloc(sizeof(*path_descs), GFP_KERNEL); + if (!path_descs) + return NULL; + + path_descs->dir = params->dir; + path_descs->alt_opts = alt_opts; + + init_it_desc(&path_descs->it_desc, alt_opts, params); + srcId = path_descs->it_desc.bTerminalID; + + if (((params->dir == HOST_TO_DEVICE) && FUOUT_EN(params->opts)) || + ((params->dir == DEVICE_TO_HOST) && FUIN_EN(params->opts))) { + path_descs->fu_desc = build_fu_desc(alt_opts, params, + path_descs->it_desc.bTerminalID); + if (!path_descs->fu_desc) { + kfree(path_descs); + return NULL; + } + srcId = path_descs->fu_desc->bUnitID; + } + + init_ot_desc(&path_descs->ot_desc, alt_opts, params, srcId); + + return path_descs; +} + +static void free_path_descriptors(struct f_uac2_path_descriptors *path_descs) +{ + kfree(path_descs->fu_desc); + kfree(path_descs); +} + +static struct f_uac2_path_descriptors *find_path_descriptors(struct list_head *list, + struct f_uac2_alt_opts *alt_opts, + int dir) +{ + struct f_uac2_path_descriptors *path_descs; + + list_for_each_entry(path_descs, list, list) { + /* Check that all options used in the path descriptors are the same */ + if ((path_descs->dir == dir) && + (!strncmp(path_descs->alt_opts->name, alt_opts->name, + sizeof(alt_opts->name))) && + (!strncmp(path_descs->alt_opts->it_name, alt_opts->it_name, + sizeof(alt_opts->it_name))) && + (!strncmp(path_descs->alt_opts->it_ch_name, alt_opts->it_ch_name, + sizeof(alt_opts->it_ch_name))) && + (!strncmp(path_descs->alt_opts->ot_name, alt_opts->ot_name, + sizeof(alt_opts->ot_name))) && + (path_descs->alt_opts->chmask == alt_opts->chmask) && + (path_descs->alt_opts->terminal_type == alt_opts->terminal_type)) + return path_descs; + } + return NULL; +} + +static int add_path_descriptors(struct list_head *list, struct path_params *params, + struct f_uac2_alt_opts *alt_opts) +{ + int len = 0; + struct f_uac2_path_descriptors *path_descs; + + if (!EP_EN(alt_opts)) + return 0; + + path_descs = find_path_descriptors(list, alt_opts, params->dir); + + if (!path_descs) { + path_descs = build_path_descriptors(params, alt_opts); + if (path_descs) { + list_add_tail(&path_descs->list, list); + len += sizeof(path_descs->it_desc); + len += sizeof(path_descs->ot_desc); + if (path_descs->fu_desc) + len += path_descs->fu_desc->bLength; + } + } + + if (path_descs) { + alt_opts->as_header_desc.bTerminalLink = + (params->dir == HOST_TO_DEVICE) ? path_descs->it_desc.bTerminalID + : path_descs->ot_desc.bTerminalID; + alt_opts->it_id = path_descs->it_desc.bTerminalID; + alt_opts->fu_id = path_descs->fu_desc ? path_descs->fu_desc->bUnitID : 0; + alt_opts->ot_id = path_descs->ot_desc.bTerminalID; + } + + return len; +} + /* Use macro to overcome line length limitation */ -#define USBDHDR(p) (struct usb_descriptor_header *)(p) +#define USBDHDR(p) ((struct usb_descriptor_header *)(p)) -static void setup_headers(struct f_uac2_opts *opts, - struct usb_descriptor_header **headers, - enum usb_device_speed speed) +static inline void add_descriptor(int i, struct usb_descriptor_header **desc_list, + struct usb_descriptor_header *desc) +{ + if (desc_list) + desc_list[i] = desc; +} + +static int add_alt_descriptors(int i, struct usb_descriptor_header **desc_list, + struct f_uac2_alt_opts *alt_opts, enum usb_device_speed speed) +{ + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->intf_desc)); + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->as_header_desc)); + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->fmt_desc)); + if (speed == USB_SPEED_FULL) + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->fs_iso_ep_desc)); + else if (speed == USB_SPEED_HIGH) + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->hs_iso_ep_desc)); + else if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) { + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->ss_iso_ep_desc)); + add_descriptor(i++, desc_list, USBDHDR(&alt_opts->ss_iso_ep_desc_comp)); + } + + return i; +} + + +static int setup_headers(struct f_uac2_opts *opts, + struct usb_descriptor_header **headers, + struct list_head *path_descs, + enum usb_device_speed speed) { - struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL; - struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL; struct usb_ss_ep_comp_descriptor *epin_fback_desc_comp = NULL; struct usb_ss_ep_comp_descriptor *ep_int_desc_comp = NULL; - struct usb_endpoint_descriptor *epout_desc; - struct usb_endpoint_descriptor *epin_desc; struct usb_endpoint_descriptor *epin_fback_desc; struct usb_endpoint_descriptor *ep_int_desc; - int i; + + int i = 0; + struct list_head *pos; switch (speed) { case USB_SPEED_FULL: - epout_desc = &fs_epout_desc; - epin_desc = &fs_epin_desc; epin_fback_desc = &fs_epin_fback_desc; ep_int_desc = &fs_ep_int_desc; break; case USB_SPEED_HIGH: - epout_desc = &hs_epout_desc; - epin_desc = &hs_epin_desc; epin_fback_desc = &hs_epin_fback_desc; ep_int_desc = &hs_ep_int_desc; break; default: - epout_desc = &ss_epout_desc; - epin_desc = &ss_epin_desc; - epout_desc_comp = &ss_epout_desc_comp; - epin_desc_comp = &ss_epin_desc_comp; epin_fback_desc = &ss_epin_fback_desc; epin_fback_desc_comp = &ss_epin_fback_desc_comp; ep_int_desc = &ss_ep_int_desc; ep_int_desc_comp = &ss_ep_int_desc_comp; } - i = 0; - headers[i++] = USBDHDR(&iad_desc); - headers[i++] = USBDHDR(&std_ac_if_desc); - headers[i++] = USBDHDR(&ac_hdr_desc); - if (EPIN_EN(opts)) - headers[i++] = USBDHDR(&in_clk_src_desc); - if (EPOUT_EN(opts)) { - headers[i++] = USBDHDR(&out_clk_src_desc); - headers[i++] = USBDHDR(&usb_out_it_desc); - - if (FUOUT_EN(opts)) - headers[i++] = USBDHDR(out_feature_unit_desc); + add_descriptor(i++, headers, USBDHDR(&iad_desc)); + add_descriptor(i++, headers, USBDHDR(&std_ac_if_desc)); + add_descriptor(i++, headers, USBDHDR(&ac_hdr_desc)); + if (epin_en_any(opts)) + add_descriptor(i++, headers, USBDHDR(&in_clk_src_desc)); + if (epout_en_any(opts)) + add_descriptor(i++, headers, USBDHDR(&out_clk_src_desc)); + + list_for_each(pos, path_descs) { + struct f_uac2_path_descriptors *path_desc = + container_of(pos, struct f_uac2_path_descriptors, list); + add_descriptor(i++, headers, USBDHDR(&path_desc->it_desc)); + if (path_desc->fu_desc) + add_descriptor(i++, headers, USBDHDR(path_desc->fu_desc)); + add_descriptor(i++, headers, USBDHDR(&path_desc->ot_desc)); } - if (EPIN_EN(opts)) { - headers[i++] = USBDHDR(&io_in_it_desc); + // If any FU exists, add the interrupt endpoint descriptor + if (FUOUT_EN(opts) || FUIN_EN(opts)) { + add_descriptor(i++, headers, USBDHDR(ep_int_desc)); + if (ep_int_desc_comp) + add_descriptor(i++, headers, USBDHDR(ep_int_desc_comp)); + } - if (FUIN_EN(opts)) - headers[i++] = USBDHDR(in_feature_unit_desc); + // If any capture interface is active + if (epout_en_any(opts)) { + struct f_uac2_alt_opts *alt_opts; - headers[i++] = USBDHDR(&usb_in_ot_desc); - } + add_descriptor(i++, headers, USBDHDR(&opts->c_alt_0_opts.intf_desc)); - if (EPOUT_EN(opts)) - headers[i++] = USBDHDR(&io_out_ot_desc); + if (EP_EN(&opts->c_alt_1_opts)) { + i = add_alt_descriptors(i, headers, &opts->c_alt_1_opts, speed); - if (FUOUT_EN(opts) || FUIN_EN(opts)) { - headers[i++] = USBDHDR(ep_int_desc); - if (ep_int_desc_comp) - headers[i++] = USBDHDR(ep_int_desc_comp); - } + add_descriptor(i++, headers, USBDHDR(&as_iso_out_desc)); + if (EPOUT_FBACK_IN_EN(&opts->c_alt_1_opts)) { + add_descriptor(i++, headers, USBDHDR(epin_fback_desc)); + if (epin_fback_desc_comp) + add_descriptor(i++, headers, USBDHDR(epin_fback_desc_comp)); + } + } - if (EPOUT_EN(opts)) { - headers[i++] = USBDHDR(&std_as_out_if0_desc); - headers[i++] = USBDHDR(&std_as_out_if1_desc); - headers[i++] = USBDHDR(&as_out_hdr_desc); - headers[i++] = USBDHDR(&as_out_fmt1_desc); - headers[i++] = USBDHDR(epout_desc); - if (epout_desc_comp) - headers[i++] = USBDHDR(epout_desc_comp); - - headers[i++] = USBDHDR(&as_iso_out_desc); - - if (EPOUT_FBACK_IN_EN(opts)) { - headers[i++] = USBDHDR(epin_fback_desc); - if (epin_fback_desc_comp) - headers[i++] = USBDHDR(epin_fback_desc_comp); + list_for_each_entry(alt_opts, &opts->c_alt_opts, list) { + if (EP_EN(alt_opts)) { + i = add_alt_descriptors(i, headers, alt_opts, speed); + + add_descriptor(i++, headers, USBDHDR(&as_iso_out_desc)); + if (EPOUT_FBACK_IN_EN(alt_opts)) { + add_descriptor(i++, headers, USBDHDR(epin_fback_desc)); + if (epin_fback_desc_comp) + add_descriptor(i++, headers, + USBDHDR(epin_fback_desc_comp)); + } + } } } - if (EPIN_EN(opts)) { - headers[i++] = USBDHDR(&std_as_in_if0_desc); - headers[i++] = USBDHDR(&std_as_in_if1_desc); - headers[i++] = USBDHDR(&as_in_hdr_desc); - headers[i++] = USBDHDR(&as_in_fmt1_desc); - headers[i++] = USBDHDR(epin_desc); - if (epin_desc_comp) - headers[i++] = USBDHDR(epin_desc_comp); + // If any playback interface is active + if (epin_en_any(opts)) { + struct f_uac2_alt_opts *alt_opts; + + add_descriptor(i++, headers, USBDHDR(&opts->p_alt_0_opts.intf_desc)); + + if (EP_EN(&opts->p_alt_1_opts)) { + i = add_alt_descriptors(i, headers, &opts->p_alt_1_opts, speed); - headers[i++] = USBDHDR(&as_iso_in_desc); + add_descriptor(i++, headers, USBDHDR(&as_iso_in_desc)); + } + + list_for_each_entry(alt_opts, &opts->p_alt_opts, list) { + if (EP_EN(alt_opts)) { + i = add_alt_descriptors(i, headers, alt_opts, speed); + + add_descriptor(i++, headers, USBDHDR(&as_iso_in_desc)); + } + } } - headers[i] = NULL; + + add_descriptor(i++, headers, NULL); + + return i; } -static void setup_descriptor(struct f_uac2_opts *opts) +static int setup_descriptor(struct device *dev, struct f_uac2 *uac2, struct f_uac2_opts *opts, + struct usb_string *strings) { + int status; + struct usb_descriptor_header **fs_desc_list, **hs_desc_list, **ss_ssp_desc_list; + /* patch descriptors */ - int i = 1; /* ID's start with 1 */ - - if (EPOUT_EN(opts)) - usb_out_it_desc.bTerminalID = i++; - if (EPIN_EN(opts)) - io_in_it_desc.bTerminalID = i++; - if (EPOUT_EN(opts)) - io_out_ot_desc.bTerminalID = i++; - if (EPIN_EN(opts)) - usb_in_ot_desc.bTerminalID = i++; - if (FUOUT_EN(opts)) - out_feature_unit_desc->bUnitID = i++; - if (FUIN_EN(opts)) - in_feature_unit_desc->bUnitID = i++; - if (EPOUT_EN(opts)) - out_clk_src_desc.bClockID = i++; - if (EPIN_EN(opts)) - in_clk_src_desc.bClockID = i++; - - usb_out_it_desc.bCSourceID = out_clk_src_desc.bClockID; - - if (FUIN_EN(opts)) { - usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID; - in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID; - } else { - usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID; - } + int len; + struct list_head path_descs = LIST_HEAD_INIT(path_descs); + int fs_num, hs_num, ss_ssp_num; + struct f_uac2_alt_opts *alt_opts; + struct list_head *path_desc, *tmp; + struct path_params params; - usb_in_ot_desc.bCSourceID = in_clk_src_desc.bClockID; - io_in_it_desc.bCSourceID = in_clk_src_desc.bClockID; - io_out_ot_desc.bCSourceID = out_clk_src_desc.bClockID; + params.id = 1; /* ID's start with 1 */ + params.opts = opts; + params.strings = strings; - if (FUOUT_EN(opts)) { - io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID; - out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID; - } else { - io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID; - } + len = sizeof(ac_hdr_desc); - as_out_hdr_desc.bTerminalLink = usb_out_it_desc.bTerminalID; - as_in_hdr_desc.bTerminalLink = usb_in_ot_desc.bTerminalID; + if (uac2->g_audio.out_ep) { + params.dir = HOST_TO_DEVICE; + out_clk_src_desc.bClockID = params.id++; + len += sizeof(out_clk_src_desc); - iad_desc.bInterfaceCount = 1; - ac_hdr_desc.wTotalLength = cpu_to_le16(sizeof(ac_hdr_desc)); + init_as_interface_desc(&opts->c_alt_0_opts.intf_desc, uac2->as_out_intf, 0, 0, + opts->c_alt_0_opts.name, strings); - if (EPIN_EN(opts)) { - u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength); + /* Audio path descriptors (input terminal -> -> output terminal) */ + len += add_path_descriptors(&path_descs, ¶ms, &opts->c_alt_1_opts); + status = init_alt_descriptors(dev, &opts->c_alt_1_opts, uac2->as_out_intf, + uac2->g_audio.out_ep->address, + EPOUT_FBACK_IN_EN(&opts->c_alt_1_opts) ? 2 : 1, + opts->c_alt_1_opts.it_id, HOST_TO_DEVICE, strings); + if (status) { + dev_err(dev, "Failed to init alt descs for capture alt %d (%d)\n", + 1, status); + goto cleanup; + } + + list_for_each_entry(alt_opts, &opts->c_alt_opts, list) { + len += add_path_descriptors(&path_descs, ¶ms, alt_opts); + + status = init_alt_descriptors(dev, alt_opts, uac2->as_out_intf, + uac2->g_audio.out_ep->address, + EPOUT_FBACK_IN_EN(alt_opts) ? 2 : 1, + alt_opts->it_id, HOST_TO_DEVICE, strings); + if (status) { + dev_err(dev, "Failed to init alt descs for capture alt %d (%d)\n", + alt_opts->c.alt_num, status); + goto cleanup; + } + } + } + + if (uac2->g_audio.in_ep) { + params.dir = DEVICE_TO_HOST; + in_clk_src_desc.bClockID = params.id++; len += sizeof(in_clk_src_desc); - len += sizeof(usb_in_ot_desc); - if (FUIN_EN(opts)) - len += in_feature_unit_desc->bLength; + init_as_interface_desc(&opts->p_alt_0_opts.intf_desc, uac2->as_in_intf, 0, 0, + opts->p_alt_0_opts.name, strings); - len += sizeof(io_in_it_desc); - ac_hdr_desc.wTotalLength = cpu_to_le16(len); - iad_desc.bInterfaceCount++; + /* Audio path descriptors (input terminal -> -> output terminal) */ + len += add_path_descriptors(&path_descs, ¶ms, &opts->p_alt_1_opts); + + status = init_alt_descriptors(dev, &opts->p_alt_1_opts, uac2->as_in_intf, + uac2->g_audio.in_ep->address, 1, + opts->p_alt_1_opts.ot_id, DEVICE_TO_HOST, strings); + if (status) { + dev_err(dev, "Failed to init alt descs for playback alt %d (%d)\n", + 1, status); + goto cleanup; + } + + list_for_each_entry(alt_opts, &opts->p_alt_opts, list) { + len += add_path_descriptors(&path_descs, ¶ms, alt_opts); + + status = init_alt_descriptors(dev, alt_opts, uac2->as_in_intf, + uac2->g_audio.in_ep->address, 1, + alt_opts->ot_id, DEVICE_TO_HOST, strings); + if (status) { + dev_err(dev, "Failed to init alt descs for playback alt %d (%d)\n", + alt_opts->c.alt_num, status); + goto cleanup; + } + } } - if (EPOUT_EN(opts)) { - u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength); - len += sizeof(out_clk_src_desc); - len += sizeof(usb_out_it_desc); + ac_hdr_desc.wTotalLength = cpu_to_le16(len); - if (FUOUT_EN(opts)) - len += out_feature_unit_desc->bLength; + /* Count how many descriptors we have and then allocate and populate */ + fs_num = setup_headers(opts, NULL, &path_descs, USB_SPEED_FULL); + hs_num = setup_headers(opts, NULL, &path_descs, USB_SPEED_HIGH); + ss_ssp_num = setup_headers(opts, NULL, &path_descs, USB_SPEED_SUPER); - len += sizeof(io_out_ot_desc); - ac_hdr_desc.wTotalLength = cpu_to_le16(len); - iad_desc.bInterfaceCount++; + fs_desc_list = kzalloc((fs_num + hs_num + ss_ssp_num) * sizeof(*fs_desc_list), GFP_KERNEL); + if (!fs_desc_list) { + status = -ENOMEM; + goto cleanup; } + hs_desc_list = fs_desc_list + fs_num; + ss_ssp_desc_list = hs_desc_list + hs_num; + + (void) setup_headers(opts, fs_desc_list, &path_descs, USB_SPEED_FULL); + (void) setup_headers(opts, hs_desc_list, &path_descs, USB_SPEED_HIGH); + (void) setup_headers(opts, ss_ssp_desc_list, &path_descs, USB_SPEED_SUPER); - io_in_it_desc.wTerminalType = cpu_to_le16(opts->c_terminal_type); - io_out_ot_desc.wTerminalType = cpu_to_le16(opts->p_terminal_type); + /* copy descriptors, and track endpoint copies */ + status = usb_assign_descriptors(&uac2->g_audio.func, fs_desc_list, hs_desc_list, + ss_ssp_desc_list, ss_ssp_desc_list); - setup_headers(opts, fs_audio_desc, USB_SPEED_FULL); - setup_headers(opts, hs_audio_desc, USB_SPEED_HIGH); - setup_headers(opts, ss_audio_desc, USB_SPEED_SUPER); + if (status) + dev_err(dev, "Failed to assign descriptors (%d)\n", status); + + kfree(fs_desc_list); + +cleanup: + list_for_each_safe(path_desc, tmp, &path_descs) { + free_path_descriptors( + container_of(path_desc, struct f_uac2_path_descriptors, list)); + } + + return status; } static int afunc_validate_opts(struct g_audio *agdev, struct device *dev) { struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); + struct f_uac2_alt_opts *alt_opts; const char *msg = NULL; - if (!opts->p_chmask && !opts->c_chmask) + if (!epin_en_any(opts) && !epout_en_any(opts)) msg = "no playback and capture channels"; - else if (opts->p_chmask & ~UAC2_CHANNEL_MASK) - msg = "unsupported playback channels mask"; - else if (opts->c_chmask & ~UAC2_CHANNEL_MASK) - msg = "unsupported capture channels mask"; - else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) - msg = "incorrect playback sample size"; - else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) - msg = "incorrect capture sample size"; - else if (!opts->p_srates[0]) + + list_for_each_entry(alt_opts, &opts->p_alt_opts, list) { + if (alt_opts->chmask & ~UAC2_CHANNEL_MASK) + msg = "unsupported playback channels mask"; + else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4)) + msg = "incorrect playback sample size"; + else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4)) + msg = "incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)"; + } + + list_for_each_entry(alt_opts, &opts->c_alt_opts, list) { + if (alt_opts->chmask & ~UAC2_CHANNEL_MASK) + msg = "unsupported capture channels mask"; + else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4)) + msg = "incorrect capture sample size"; + else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4)) + msg = "incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)"; + } + + if (!opts->p_srates[0]) msg = "incorrect playback sampling rate"; else if (!opts->c_srates[0]) msg = "incorrect capture sampling rate"; @@ -962,11 +1004,6 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev) else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) msg = "incorrect capture volume resolution"; - else if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4)) - msg = "incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)"; - else if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4)) - msg = "incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)"; - if (msg) { dev_err(dev, "Error: %s\n", msg); return -EINVAL; @@ -1025,6 +1062,25 @@ static void init_alt_opts(struct f_uac2_alt_opts *alt_opts, struct f_uac2_opts * alt_opts->terminal_type = (!playback) ? opts->p_terminal_type : opts->c_terminal_type; } +static u16 get_max_packet_size(struct f_uac2_alt_opts *alt_opts, struct list_head *list) +{ + u16 max_psize = max_t(u16, + le16_to_cpu(alt_opts->fs_iso_ep_desc.wMaxPacketSize), + le16_to_cpu(alt_opts->hs_iso_ep_desc.wMaxPacketSize)); + max_psize = max_t(u16, max_psize, + le16_to_cpu(alt_opts->ss_iso_ep_desc.wMaxPacketSize)); + + list_for_each_entry(alt_opts, list, list) { + max_psize = max_t(u16, max_psize, + le16_to_cpu(alt_opts->fs_iso_ep_desc.wMaxPacketSize)); + max_psize = max_t(u16, max_psize, + le16_to_cpu(alt_opts->hs_iso_ep_desc.wMaxPacketSize)); + max_psize = max_t(u16, max_psize, + le16_to_cpu(alt_opts->ss_iso_ep_desc.wMaxPacketSize)); + } + + return max_psize; +} static int afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) @@ -1062,272 +1118,147 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) if (IS_ERR(us)) return PTR_ERR(us); - if (FUOUT_EN(uac2_opts)) { - out_feature_unit_desc = build_fu_desc(uac2_opts->c_chmask); - if (!out_feature_unit_desc) - return -ENOMEM; - } - if (FUIN_EN(uac2_opts)) { - in_feature_unit_desc = build_fu_desc(uac2_opts->p_chmask); - if (!in_feature_unit_desc) { - ret = -ENOMEM; - goto err_free_fu; - } - } - iad_desc.iFunction = add_string(us, uac2_opts->function_name); std_ac_if_desc.iInterface = add_string(us, uac2_opts->if_ctrl_name); in_clk_src_desc.iClockSource = add_string(us, uac2_opts->clksrc_in_name); out_clk_src_desc.iClockSource = add_string(us, uac2_opts->clksrc_out_name); - usb_out_it_desc.iTerminal = add_string(us, uac2_opts->c_alt_1_opts.it_name); - usb_out_it_desc.iChannelNames = add_string(us, uac2_opts->c_alt_1_opts.it_ch_name); - io_in_it_desc.iTerminal = add_string(us, uac2_opts->p_alt_1_opts.it_name); - io_in_it_desc.iChannelNames = add_string(us, uac2_opts->p_alt_1_opts.it_ch_name); - usb_in_ot_desc.iTerminal = add_string(us, uac2_opts->p_alt_1_opts.ot_name); - io_out_ot_desc.iTerminal = add_string(us, uac2_opts->c_alt_1_opts.ot_name); - std_as_out_if0_desc.iInterface = add_string(us, uac2_opts->c_alt_0_opts.name); - std_as_out_if1_desc.iInterface = add_string(us, uac2_opts->c_alt_1_opts.name); - std_as_in_if0_desc.iInterface = add_string(us, uac2_opts->p_alt_0_opts.name); - std_as_in_if1_desc.iInterface = add_string(us, uac2_opts->p_alt_0_opts.name); - - if (FUOUT_EN(uac2_opts)) { - u8 *i_feature = (u8 *)out_feature_unit_desc + - out_feature_unit_desc->bLength - 1; - *i_feature = add_string(us, uac2_opts->c_alt_1_opts.fu_vol_name); - } - if (FUIN_EN(uac2_opts)) { - u8 *i_feature = (u8 *)in_feature_unit_desc + - in_feature_unit_desc->bLength - 1; - *i_feature = add_string(us, uac2_opts->p_alt_1_opts.fu_vol_name); - } - - - /* Initialize the configurable parameters */ - usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask); - usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask); - io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask); - io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask); - as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask); - as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask); - as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask); - as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask); - as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize; - as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8; - as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize; - as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8; - if (FUOUT_EN(uac2_opts)) { - __le32 *bma = (__le32 *)&out_feature_unit_desc->bmaControls[0]; - u32 control = 0; - - if (uac2_opts->c_mute_present) - control |= CONTROL_RDWR << FU_MUTE_CTRL; - if (uac2_opts->c_volume_present) - control |= CONTROL_RDWR << FU_VOL_CTRL; - *bma = cpu_to_le32(control); - } - if (FUIN_EN(uac2_opts)) { - __le32 *bma = (__le32 *)&in_feature_unit_desc->bmaControls[0]; - u32 control = 0; - - if (uac2_opts->p_mute_present) - control |= CONTROL_RDWR << FU_MUTE_CTRL; - if (uac2_opts->p_volume_present) - control |= CONTROL_RDWR << FU_VOL_CTRL; - *bma = cpu_to_le32(control); - } + /* allocate instance-specific interface IDs */ ret = usb_interface_id(cfg, fn); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err_free_fu; + goto fail; } iad_desc.bFirstInterface = ret; + iad_desc.bInterfaceCount = 1; std_ac_if_desc.bInterfaceNumber = ret; uac2->ac_intf = ret; uac2->ac_alt = 0; - if (EPOUT_EN(uac2_opts)) { + if (epout_en_any(uac2_opts)) { ret = usb_interface_id(cfg, fn); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err_free_fu; + goto fail; } - std_as_out_if0_desc.bInterfaceNumber = ret; - std_as_out_if1_desc.bInterfaceNumber = ret; - std_as_out_if1_desc.bNumEndpoints = 1; + + iad_desc.bInterfaceCount++; + uac2->as_out_intf = ret; uac2->as_out_alt = 0; - - if (EPOUT_FBACK_IN_EN(uac2_opts)) { - fs_epout_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC; - hs_epout_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC; - ss_epout_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC; - std_as_out_if1_desc.bNumEndpoints++; - } else { - fs_epout_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; - hs_epout_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; - ss_epout_desc.bmAttributes = - USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; - } } - if (EPIN_EN(uac2_opts)) { + if (epin_en_any(uac2_opts)) { ret = usb_interface_id(cfg, fn); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err_free_fu; + goto fail; } - std_as_in_if0_desc.bInterfaceNumber = ret; - std_as_in_if1_desc.bInterfaceNumber = ret; + + iad_desc.bInterfaceCount++; + uac2->as_in_intf = ret; uac2->as_in_alt = 0; } + /* allocate AC interrupt endpoint */ if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts)) { uac2->int_ep = usb_ep_autoconfig(gadget, &fs_ep_int_desc); if (!uac2->int_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); ret = -ENODEV; - goto err_free_fu; + goto fail; } + hs_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress; + ss_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress; std_ac_if_desc.bNumEndpoints = 1; } - hs_epin_desc.bInterval = uac2_opts->p_hs_bint; - ss_epin_desc.bInterval = uac2_opts->p_hs_bint; - hs_epout_desc.bInterval = uac2_opts->c_hs_bint; - ss_epout_desc.bInterval = uac2_opts->c_hs_bint; - - /* Calculate wMaxPacketSize according to audio bandwidth */ - ret = set_ep_max_packet_size_bint(dev, &uac2_opts->p_alt_1_opts, &fs_epin_desc, - USB_SPEED_FULL, true); - if (ret < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return ret; - } - - ret = set_ep_max_packet_size_bint(dev, &uac2_opts->c_alt_1_opts, &fs_epout_desc, - USB_SPEED_FULL, false); - if (ret < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return ret; - } - - ret = set_ep_max_packet_size_bint(dev, &uac2_opts->p_alt_1_opts, &hs_epin_desc, - USB_SPEED_HIGH, true); - if (ret < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return ret; - } - - ret = set_ep_max_packet_size_bint(dev, &uac2_opts->c_alt_1_opts, &hs_epout_desc, - USB_SPEED_HIGH, false); - if (ret < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return ret; - } - - ret = set_ep_max_packet_size_bint(dev, &uac2_opts->p_alt_1_opts, &ss_epin_desc, - USB_SPEED_SUPER, true); - if (ret < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return ret; - } - - ret = set_ep_max_packet_size_bint(dev, &uac2_opts->c_alt_1_opts, &ss_epout_desc, - USB_SPEED_SUPER, false); - if (ret < 0) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return ret; - } + /* Allocate instance-specific endpoints. These use the FS version for alt mode 1. + * All other alt modes and speeds will be initialized to the same endpoint address + * during the setup_descriptor() call. The u_audio code will update the currently + * selected endpoint descriptor when the alt mode changes. + */ + if (epout_en_any(uac2_opts)) { + ret = init_isoc_ep_descriptor(dev, &uac2_opts->c_alt_1_opts.fs_iso_ep_desc, + &uac2_opts->c_alt_1_opts, HOST_TO_DEVICE, + USB_SPEED_FULL, USB_DIR_OUT); + if (ret) { + dev_err(dev, "Failed to init FS isoc ep desc for capture (%d)\n", ret); + goto fail; + } - if (EPOUT_EN(uac2_opts)) { - agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); + agdev->out_ep = usb_ep_autoconfig(gadget, &uac2_opts->c_alt_1_opts.fs_iso_ep_desc); if (!agdev->out_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); ret = -ENODEV; - goto err_free_fu; + goto fail; } - if (EPOUT_FBACK_IN_EN(uac2_opts)) { + if (epout_fback_in_en_any(uac2_opts)) { agdev->in_ep_fback = usb_ep_autoconfig(gadget, &fs_epin_fback_desc); if (!agdev->in_ep_fback) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); ret = -ENODEV; - goto err_free_fu; + goto fail; } + hs_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress; + ss_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress; } } - if (EPIN_EN(uac2_opts)) { - agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); + if (epin_en_any(uac2_opts)) { + ret = init_isoc_ep_descriptor(dev, &uac2_opts->p_alt_1_opts.fs_iso_ep_desc, + &uac2_opts->p_alt_1_opts, HOST_TO_DEVICE, + USB_SPEED_FULL, USB_DIR_IN); + if (ret) { + dev_err(dev, "Failed to init FS isoc ep desc for playback (%d)\n", ret); + goto fail; + } + + agdev->in_ep = usb_ep_autoconfig(gadget, &uac2_opts->p_alt_1_opts.fs_iso_ep_desc); if (!agdev->in_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); ret = -ENODEV; - goto err_free_fu; + goto fail; } } - agdev->in_ep_maxpsize = max_t(u16, - le16_to_cpu(fs_epin_desc.wMaxPacketSize), - le16_to_cpu(hs_epin_desc.wMaxPacketSize)); - agdev->out_ep_maxpsize = max_t(u16, - le16_to_cpu(fs_epout_desc.wMaxPacketSize), - le16_to_cpu(hs_epout_desc.wMaxPacketSize)); - - agdev->in_ep_maxpsize = max_t(u16, agdev->in_ep_maxpsize, - le16_to_cpu(ss_epin_desc.wMaxPacketSize)); - agdev->out_ep_maxpsize = max_t(u16, agdev->out_ep_maxpsize, - le16_to_cpu(ss_epout_desc.wMaxPacketSize)); - - ss_epin_desc_comp.wBytesPerInterval = ss_epin_desc.wMaxPacketSize; - ss_epout_desc_comp.wBytesPerInterval = ss_epout_desc.wMaxPacketSize; - - // HS and SS endpoint addresses are copied from autoconfigured FS descriptors - hs_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress; - hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; - hs_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress; - hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; - ss_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; - ss_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress; - ss_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; - ss_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress; - - setup_descriptor(uac2_opts); - - ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, ss_audio_desc, - ss_audio_desc); - if (ret) - goto err_free_fu; + agdev->out_ep_maxpsize = get_max_packet_size(&uac2_opts->c_alt_1_opts, + &uac2_opts->c_alt_opts); + agdev->in_ep_maxpsize = get_max_packet_size(&uac2_opts->p_alt_1_opts, + &uac2_opts->p_alt_opts); + + setup_descriptor(dev, uac2, uac2_opts, us); agdev->gadget = gadget; + // TODO: This may need some change with the audio params for the current alt mode agdev->params.p_chmask = uac2_opts->p_chmask; memcpy(agdev->params.p_srates, uac2_opts->p_srates, sizeof(agdev->params.p_srates)); agdev->params.p_ssize = uac2_opts->p_ssize; + if (FUIN_EN(uac2_opts)) { - agdev->params.p_fu.id = USB_IN_FU_ID; + agdev->params.p_fu.id = USB_IN_FU_ID(uac2_opts); agdev->params.p_fu.mute_present = uac2_opts->p_mute_present; agdev->params.p_fu.volume_present = uac2_opts->p_volume_present; agdev->params.p_fu.volume_min = uac2_opts->p_volume_min; agdev->params.p_fu.volume_max = uac2_opts->p_volume_max; agdev->params.p_fu.volume_res = uac2_opts->p_volume_res; } + + // TODO: This may need some change with the audio params for the current alt mode agdev->params.c_chmask = uac2_opts->c_chmask; memcpy(agdev->params.c_srates, uac2_opts->c_srates, sizeof(agdev->params.c_srates)); agdev->params.c_ssize = uac2_opts->c_ssize; + if (FUOUT_EN(uac2_opts)) { - agdev->params.c_fu.id = USB_OUT_FU_ID; + agdev->params.c_fu.id = USB_OUT_FU_ID(uac2_opts); agdev->params.c_fu.mute_present = uac2_opts->c_mute_present; agdev->params.c_fu.volume_present = uac2_opts->c_volume_present; agdev->params.c_fu.volume_min = uac2_opts->c_volume_min; @@ -1338,7 +1269,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->params.fb_max = uac2_opts->fb_max; if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts)) - agdev->notify = afunc_notify; + agdev->notify = afunc_notify; ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget"); if (ret) @@ -1349,11 +1280,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) err_free_descs: usb_free_all_descriptors(fn); agdev->gadget = NULL; -err_free_fu: - kfree(out_feature_unit_desc); - out_feature_unit_desc = NULL; - kfree(in_feature_unit_desc); - in_feature_unit_desc = NULL; +fail: return ret; } @@ -1428,7 +1355,7 @@ afunc_notify(struct g_audio *agdev, int unit_id, int cs) } static int -afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) +afunc_set_alt(struct usb_function *fn, unsigned int intf, unsigned int alt) { struct usb_composite_dev *cdev = fn->config->cdev; struct f_uac2 *uac2 = func_to_uac2(fn); @@ -1483,7 +1410,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) } static int -afunc_get_alt(struct usb_function *fn, unsigned intf) +afunc_get_alt(struct usb_function *fn, unsigned int intf) { struct f_uac2 *uac2 = func_to_uac2(fn); struct g_audio *agdev = func_to_g_audio(fn); @@ -1561,11 +1488,11 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) "%s:%d control_selector=%d TODO!\n", __func__, __LINE__, control_selector); } - } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { unsigned int is_playback = 0; - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) is_playback = 1; if (control_selector == UAC_FU_MUTE) { @@ -1650,11 +1577,11 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) "%s:%d control_selector=%d TODO!\n", __func__, __LINE__, control_selector); } - } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { unsigned int is_playback = 0; - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) is_playback = 1; if (control_selector == UAC_FU_VOLUME) { @@ -1740,11 +1667,11 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) return; } - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { unsigned int is_playback = 0; - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) is_playback = 1; if (control_selector == UAC_FU_MUTE) { @@ -1794,8 +1721,8 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) req->complete = uac2_cs_control_sam_freq; return w_length; } - } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { memcpy(&uac2->setup_cr, cr, sizeof(*cr)); req->context = agdev; req->complete = out_rq_cur_complete; @@ -2292,11 +2219,6 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); agdev->gadget = NULL; - - kfree(out_feature_unit_desc); - out_feature_unit_desc = NULL; - kfree(in_feature_unit_desc); - in_feature_unit_desc = NULL; } static struct usb_function *afunc_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index 8c061e588324..91171c6e493a 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -53,6 +53,9 @@ struct f_uac2_alt_0_opts { struct f_uac2_alt_opts_common c; char name[USB_MAX_STRING_LEN]; + + /* Descriptors */ + struct usb_interface_descriptor intf_desc; }; /* Alt modes 1+ */ @@ -75,6 +78,35 @@ struct f_uac2_alt_opts { u8 hs_bint; s16 terminal_type; + /* Descriptors */ + struct usb_interface_descriptor intf_desc; + struct uac2_as_header_descriptor as_header_desc; + struct uac2_format_type_i_descriptor fmt_desc; + + struct usb_endpoint_descriptor fs_iso_ep_desc; + struct usb_endpoint_descriptor hs_iso_ep_desc; + struct usb_endpoint_descriptor ss_iso_ep_desc; + struct usb_ss_ep_comp_descriptor ss_iso_ep_desc_comp; + + u8 clk_id; /* Clock Source Descriptor bClockID */ + u8 it_id; /* Input Terminal Descriptor bTerminalID */ + u8 fu_id; /* Feature Unit Descriptor bUnitID */ + u8 ot_id; /* Output Terminal Descriptor bTerminalID */ +}; + +struct f_uac2_path_descriptors { + struct list_head list; + + int dir; /* HOST_TO_DEVICE or DEVICE_TO_HOST */ + + /* Alt mode opts this path descriptor is from */ + struct f_uac2_alt_opts *alt_opts; + + struct uac2_input_terminal_descriptor it_desc; + struct uac2_output_terminal_descriptor ot_desc; + + /* Feature unit is optional */ + struct uac2_feature_unit_descriptor *fu_desc; }; struct f_uac2_opts { From patchwork Sat Sep 28 15:09:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814710 Received: from mail-qv1-f52.google.com (mail-qv1-f52.google.com [209.85.219.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3A97218B46E; Sat, 28 Sep 2024 15:09:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536190; cv=none; b=tQYibFSw+FNcYEb61LaCXKfSLceHQgRpw2MSUdnbCfeEp6QxFS1MEnytaasfjzbiB9l8UKHOECsfaDwsiXFgZ6B3Mg8WHLy8RXX2Gy8OZAXjiLltAKQJKuyIzfG3OCxJdBD3cfC8TrGrFDOPxlbYTLJH4dZj9ZKc9DxPRt9/kyU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536190; c=relaxed/simple; bh=ZUHywjTgyAgorv+P4vGuQHmf7hYZIps+djPVQa06mX4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MSJjbgDZwT+HLbAOcvCOLxcf4HxyqGxw3v88cWP4gHEzv7Zq9z+9okCwyeuZsQuTHXtaML1SWXk18wnrF+So5ZUfjzsCktxk/Vt3Y3f+KcBZXswI6UDcE1c8apgKRGWwmMxxfW1cpaKTdr3SuoG+sBothLnKO+4eAW2TwqnPXPo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Yv1JbIDI; arc=none smtp.client-ip=209.85.219.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Yv1JbIDI" Received: by mail-qv1-f52.google.com with SMTP id 6a1803df08f44-6cb45ccf4c5so2035576d6.3; Sat, 28 Sep 2024 08:09:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536186; x=1728140986; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kQT3vCU4G2dOz3SaAnxPwSTdt0kd5jqB6slZV1QyU88=; b=Yv1JbIDIOSGyODmVH8lpfMYXDOSKlD0cegJr/EKwI1GUVfwl9CTps07QVDJcIn78VA WP5/76KNqx0Z4nu3DwZP1aFrCi9jEs0CfKpy7hT/3lDPuBZAfmfe0VFn7SnUTfS5szL4 OSZRBgc9eFPqbtU2axCOktyYawOU2KNuk6ARh5ryjecv/NUvaTrWt249Z39dTI1XCfmM 6EtFDr/8o/Y/sf20dq/zuhfjJfAFv6JbjKvXmgVyE6u0cM7ZMEdU8vk9roZEH1Y7Q7W0 ruIoKPl37/bztWZSGpYHsfWQMxYVOF13UzvBrs/iL5rRgeOBhkGAFcdAIwT3XeLOZZu4 D0vg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536186; x=1728140986; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kQT3vCU4G2dOz3SaAnxPwSTdt0kd5jqB6slZV1QyU88=; b=BxjH8Yu2ZYXjPg8Q1bAJnlWhH1GBoXeEz69fPWGiH2rRtjZh7ZKfzk/JBvTEdVU9Fy wlbGSPkwj7Gs/FmTiS9/GHb269MZcHjl4vNyf7npuqNfosvmGMJ6/ltGlFjpcftoMETr razNPP0N9suIDvNJ0ng0ci6DcEF3QgscKilgF8h6zViceBKmYcU+716PDSS7KovBUrwG wTJ4Htwb8svua7aEUJY+jl+SCc0Nw5LFbsLLuKVvOxWZE+gFBzKn657d5IJ8/4Vq+DON nIMfEXlZf3Q4y6WX91+TOkTImgWrpzMMLKnK2IzSWzwsQ3HlgSPWRTCZOAXdjfMRvoHK T35w== X-Forwarded-Encrypted: i=1; AJvYcCVK11GGKrA7U6RG8x7ByU7SSpL0QLe5uFjG4ePStyllG+Bz9zOOFujfEWk0cp5focddjbIKrssMN64=@vger.kernel.org, AJvYcCXXFOcfyy7WCwNCtVf+veKOmViuI58scbaHmPZE2plLNu/pbYAcsmGA5gTxXHFIIG1halfjJ6jYLtxkGAKW@vger.kernel.org X-Gm-Message-State: AOJu0Yx6c0ZPAx5vupGLlCV7kA+BFrU4OTF6XjpWhsajTLt82Kk0A6S0 6hU4GCWmV27f73bZKh4VFjBM8tYimyGX3k0S1f0wy6Q1maTodIw+TOTCbGY64AY= X-Google-Smtp-Source: AGHT+IFQPNxvBusz5TCd3f3HKa/fc1BQS4PC55vb99SfY9Er2er5hhcHT8wjNS27/JRFfXhMpTHcnw== X-Received: by 2002:a05:620a:191f:b0:7a9:b52a:a6db with SMTP id af79cd13be357-7ae40613946mr196687585a.7.1727536186227; Sat, 28 Sep 2024 08:09:46 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:45 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 13/14] usb: gadget: f_uac1: support ganged volume/mute controls Date: Sat, 28 Sep 2024 11:09:04 -0400 Message-ID: <20240928150905.2616313-14-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff When multiple feature units exist due to differences in other terminal descriptors, they still represent the same volume/mute controls. Signed-off-by: Chris Wulff --- drivers/usb/gadget/function/f_uac1.c | 186 ++++++++++++++++++++------- 1 file changed, 136 insertions(+), 50 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 7803957e4f82..c29cbe4cea14 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -27,8 +27,7 @@ /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ #define UAC1_CHANNEL_MASK 0x0FFF -#define USB_OUT_FU_ID(_opts) (_opts->c_alt_1_opts.fu_id) -#define USB_IN_FU_ID(_opts) (_opts->p_alt_1_opts.fu_id) +#define USB_FU_ID(_alt_opts) ((_alt_opts) ? (_alt_opts)->fu_id : 0) #define EP_EN(_alt_opts) ((_alt_opts) && ((_alt_opts)->chmask != 0)) #define FUIN_EN(_opts) ((_opts)->p_mute_present \ @@ -83,6 +82,25 @@ static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) return container_of(audio->func.fi, struct f_uac1_opts, func_inst); } +static inline struct f_uac1_alt_opts *get_alt_opts(struct f_uac1_opts *opts, int alt, int dir) +{ + struct f_uac1_alt_opts *alt_opts; + + if (alt == 0) + return NULL; + + if (alt == 1) + return (dir == HOST_TO_DEVICE) ? &opts->c_alt_1_opts : &opts->p_alt_1_opts; + + list_for_each_entry(alt_opts, (dir == HOST_TO_DEVICE) ? &opts->c_alt_opts + : &opts->p_alt_opts, list) { + if (alt_opts->c.alt_num == alt) + return alt_opts; + } + + return NULL; +} + /* * DESCRIPTORS ... most are static, but strings and full * configuration descriptors are built on demand. @@ -244,16 +262,53 @@ static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req) usb_ep_free_request(_ep, req); } -static int audio_notify(struct g_audio *audio, int unit_id, int cs) +static int audio_notify_one(struct g_audio *audio, int unit_id, int cs); + +static int audio_notify_multiple(struct g_audio *audio, int unit_id, int cs, int source_id) { struct f_uac1 *uac1 = func_to_uac1(&audio->func); - struct usb_request *req; - struct uac1_status_word *msg; + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); int ret; + struct f_uac1_alt_opts *alt_opts; + struct list_head *alt_opts_list; if (!uac1->int_ep->enabled) return 0; + if (unit_id != source_id) { + ret = audio_notify_one(audio, unit_id, cs); + if (ret) + return ret; + } + + alt_opts_list = (unit_id == USB_FU_ID(&opts->c_alt_1_opts)) ? &opts->c_alt_opts + : &opts->p_alt_opts; + + /* Notify all other ganged controls */ + list_for_each_entry(alt_opts, alt_opts_list, list) { + if ((USB_FU_ID(alt_opts) > unit_id) && (USB_FU_ID(alt_opts) != source_id)) { + unit_id = USB_FU_ID(alt_opts); + ret = audio_notify_one(audio, unit_id, cs); + if (ret) + return ret; + } + } + + return 0; +} + +static int audio_notify(struct g_audio *audio, int unit_id, int cs) +{ + return audio_notify_multiple(audio, unit_id, cs, 0); +} + +static int audio_notify_one(struct g_audio *audio, int unit_id, int cs) +{ + struct f_uac1 *uac1 = func_to_uac1(&audio->func); + struct usb_request *req; + struct uac1_status_word *msg; + int ret; + if (atomic_inc_return(&uac1->int_count) > UAC1_DEF_INT_REQ_NUM) { atomic_dec(&uac1->int_count); return 0; @@ -297,6 +352,40 @@ static int audio_notify(struct g_audio *audio, int unit_id, int cs) return ret; } +static struct f_uac1_alt_opts * +find_feature_unit(struct f_uac1_opts *opts, u8 entity_id, int *is_playback) +{ + struct f_uac1_alt_opts *alt_opts; + + if (FUOUT_EN(opts)) { + if (is_playback) + *is_playback = 0; + + if (entity_id == USB_FU_ID(&opts->c_alt_1_opts)) + return &opts->c_alt_1_opts; + + list_for_each_entry(alt_opts, &opts->c_alt_opts, list) { + if (entity_id == USB_FU_ID(alt_opts)) + return alt_opts; + } + } + + if (FUIN_EN(opts)) { + if (is_playback) + *is_playback = 1; + + if (entity_id == USB_FU_ID(&opts->p_alt_1_opts)) + return &opts->p_alt_1_opts; + + list_for_each_entry(alt_opts, &opts->p_alt_opts, list) { + if (entity_id == USB_FU_ID(alt_opts)) + return alt_opts; + } + } + + return NULL; +} + static int in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) { @@ -309,14 +398,10 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; + unsigned int is_playback = 0; + struct f_uac1_alt_opts *alt_opts = find_feature_unit(opts, entity_id, &is_playback); - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { - unsigned int is_playback = 0; - - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) - is_playback = 1; - + if (alt_opts) { if (control_selector == UAC_FU_MUTE) { unsigned int mute; @@ -360,14 +445,10 @@ in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; + unsigned int is_playback = 0; + struct f_uac1_alt_opts *alt_opts = find_feature_unit(opts, entity_id, &is_playback); - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { - unsigned int is_playback = 0; - - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) - is_playback = 1; - + if (alt_opts) { if (control_selector == UAC_FU_VOLUME) { __le16 r; s16 min_db; @@ -407,14 +488,10 @@ in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; + unsigned int is_playback = 0; + struct f_uac1_alt_opts *alt_opts = find_feature_unit(opts, entity_id, &is_playback); - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { - unsigned int is_playback = 0; - - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) - is_playback = 1; - + if (alt_opts) { if (control_selector == UAC_FU_VOLUME) { __le16 r; s16 max_db; @@ -454,14 +531,10 @@ in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; + unsigned int is_playback = 0; + struct f_uac1_alt_opts *alt_opts = find_feature_unit(opts, entity_id, &is_playback); - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { - unsigned int is_playback = 0; - - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) - is_playback = 1; - + if (alt_opts) { if (control_selector == UAC_FU_VOLUME) { __le16 r; s16 res_db; @@ -501,24 +574,25 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) u16 w_value = le16_to_cpu(cr->wValue); u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; + unsigned int is_playback = 0; + struct f_uac1_alt_opts *alt_opts = find_feature_unit(opts, entity_id, &is_playback); if (req->status != 0) { dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status); return; } - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { - unsigned int is_playback = 0; - - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) - is_playback = 1; - + if (alt_opts) { if (control_selector == UAC_FU_MUTE) { u8 mute = *(u8 *)req->buf; u_audio_set_mute(audio, is_playback, mute); + /* We also need to send notify for ganged controls */ + audio_notify_multiple(audio, USB_FU_ID(is_playback ? &opts->p_alt_1_opts + : &opts->c_alt_1_opts), + control_selector, entity_id); + return; } else if (control_selector == UAC_FU_VOLUME) { __le16 *c = req->buf; @@ -527,6 +601,11 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) volume = le16_to_cpu(*c); u_audio_set_volume(audio, is_playback, volume); + /* We also need to send notify for ganged controls */ + audio_notify_multiple(audio, USB_FU_ID(is_playback ? &opts->p_alt_1_opts + : &opts->c_alt_1_opts), + control_selector, entity_id); + return; } else { dev_err(&audio->gadget->dev, @@ -556,8 +635,7 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { + if (find_feature_unit(opts, entity_id, NULL)) { memcpy(&uac1->setup_cr, cr, sizeof(*cr)); req->context = audio; req->complete = out_rq_cur_complete; @@ -751,14 +829,10 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct device *dev = &gadget->dev; struct g_audio *audio = func_to_g_audio(f); struct f_uac1 *uac1 = func_to_uac1(f); + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + struct f_uac1_alt_opts *alt_opts = NULL; int ret = 0; - /* No i/f has more than 2 alt settings */ - if (alt > 1) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return -EINVAL; - } - if (intf == uac1->ac_intf) { /* Control I/f has only 1 AltSetting - 0 */ if (alt) { @@ -777,6 +851,12 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } if (intf == uac1->as_out_intf) { + alt_opts = get_alt_opts(opts, alt, HOST_TO_DEVICE); + if (alt && !alt_opts) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + uac1->as_out_alt = alt; if (alt) @@ -784,6 +864,12 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) else u_audio_stop_capture(&uac1->g_audio); } else if (intf == uac1->as_in_intf) { + alt_opts = get_alt_opts(opts, alt, DEVICE_TO_HOST); + if (alt && !alt_opts) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + uac1->as_in_alt = alt; if (alt) @@ -1841,7 +1927,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio->params.c_ssize = audio_opts->c_ssize; if (FUIN_EN(audio_opts)) { - audio->params.p_fu.id = USB_IN_FU_ID(audio_opts); + audio->params.p_fu.id = USB_FU_ID(&audio_opts->p_alt_1_opts); audio->params.p_fu.mute_present = audio_opts->p_mute_present; audio->params.p_fu.volume_present = audio_opts->p_volume_present; @@ -1857,7 +1943,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio->params.p_ssize = audio_opts->p_ssize; if (FUOUT_EN(audio_opts)) { - audio->params.c_fu.id = USB_OUT_FU_ID(audio_opts); + audio->params.c_fu.id = USB_FU_ID(&audio_opts->c_alt_1_opts); audio->params.c_fu.mute_present = audio_opts->c_mute_present; audio->params.c_fu.volume_present = audio_opts->c_volume_present; From patchwork Sat Sep 28 15:09:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wulff X-Patchwork-Id: 13814711 Received: from mail-qv1-f43.google.com (mail-qv1-f43.google.com [209.85.219.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 889DA18BC36; Sat, 28 Sep 2024 15:09:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536192; cv=none; b=K1JGq3S/OEBd89d0zVE2aAXhPVvkqL2Qu6PCs7Sih6/9vBfYUlslCmdgqaxqIu76gjQW3yQGB4F1+5Dkmll2DeceuVGx5XhVbr8raLohL7hAoDskG9WTFNHcVtkEAaoVD9n9k1o/YK/sU2RL/VsjcJRxla311PJ6vXRFXFbzbKk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727536192; c=relaxed/simple; bh=Fy6I94nc+1V40lt4e3qFmMV5zy3ba9bIWTfNuWdiaY4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hjzBabNzqhphU+NuK1OqDHJsHStEhncvaDLIw5SQP/0ictRdaSgInvYdP1t/gJpTUbOh+EDBOtBc290oCcsmLlwyOW4K4F48z4FTODgvjIKWa6pgaA4iCEAKBXAPrFxG/e84a7GNUNowQMxbCrctBC6LDJzTQkzLnIGxjUgmnyI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=krL+FYxR; arc=none smtp.client-ip=209.85.219.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="krL+FYxR" Received: by mail-qv1-f43.google.com with SMTP id 6a1803df08f44-6cb3648c894so3835496d6.2; Sat, 28 Sep 2024 08:09:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727536189; x=1728140989; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=38DUzai26Am3YIcMwNP/ofkUCUtRlhDt+jxJLAqbPNA=; b=krL+FYxR5ZOOkyw42Utar2qRTea4uqemJIh7bjMu3CsLdwdVh/te6vjUmehTYlQNgI t/2oyu3HjzMSlBmtXapJBUV1tDRHZsKCHyR2CVFuPTMiWoFhDPsDbZl4nBnd9VwNQjR9 VfZE/H2KRwWBFopOwbMAJ7xXBp7rVUdLaMwrs2nsBHLaEGg+pAn3Dr/K4dHaaRZCWE6A AjQeh4IRMvL/rDDJpr5U3/qa1/5wjAPmkayAJVB4K/7z1eJREAgES7uR5oIF2QF/4i89 WpaRhZczrEjXAy5/PZhzvVUFeDBzxS8uHnuq+crl9M4UupzlBn94mJT947/FEQpNUNZs macw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727536189; x=1728140989; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=38DUzai26Am3YIcMwNP/ofkUCUtRlhDt+jxJLAqbPNA=; b=Wgmor88zv/iptX1AmXDErP3ZaotUjfxo2Vhn0u4KUG4Z1kw6zEGM3PHWw3Dd8+yUH4 vMZXTPxh9QLj8OKH0fCZ0oUEKneVmU0ppUEQ/JCsXOWfCX8Jpcp2h5iL5qCkfUosjvm8 saJ7ZTPo0z4YADxmy6usTLVABXH8CI64kkGKvMcIennKjz94VifrRPpLfWCx+pdwDyDu iqk3twxZVVweR+7XQEeOJly5vIbsmvPpo7mtN/ffUTl18vVYLar4007tIXXBhJ49Y7MU 83CsvZN1i54yjW5xYRC/zC++K5ef1eYXXIpsJjRK9uYU53mUu2mE+JLqOk/wmGtkJ7AM PjtA== X-Forwarded-Encrypted: i=1; AJvYcCW7khrH6+vjs8uZ+LKZoAJcExAVU8UbeOw9d/PtKJdpLBrqYdn/kD5+Ygas2BDz3zMKEAK/7ywStw4=@vger.kernel.org, AJvYcCXyyW4FpZuDkTEe3h+HEyBQ7KYdSl/mjkuwmCIVc4Ol1M72GOwAYbEYie5y97OwZr6GE0G6aPhmOMH6n3cf@vger.kernel.org X-Gm-Message-State: AOJu0YwF7zzBUNRrFdZVIzYQ1IY9OkAbPc03DRHTpdRcSkGlyNoblR45 GRCE7/xhvEeNmRBH9+pdGUxBAToCtEJTuzik0Nixy5ZpzBgwPh3CSBOD+9hCN3k= X-Google-Smtp-Source: AGHT+IGqLS0ZiLea/j1PV95EuAVVUKMwBls8xzYkV1BXHKzipBM57ZI3/j/tBdo2wggi+jpEorORMw== X-Received: by 2002:a05:622a:146:b0:458:21b2:4904 with SMTP id d75a77b69052e-45cadd38a2bmr18009661cf.14.1727536188916; Sat, 28 Sep 2024 08:09:48 -0700 (PDT) Received: from localhost.localdomain (syn-104-229-042-148.res.spectrum.com. [104.229.42.148]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-45c9f35392esm18662881cf.82.2024.09.28.08.09.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 08:09:47 -0700 (PDT) From: crwulff@gmail.com To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Greg Kroah-Hartman , James Gruber , Jeff Johnson , John Keeping , Jonathan Corbet , Lee Jones , Perr Zhang , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Chris Wulff Subject: [PATCH RFC 14/14] usb: gadget: f_uac2: support ganged volume/mute controls Date: Sat, 28 Sep 2024 11:09:05 -0400 Message-ID: <20240928150905.2616313-15-crwulff@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240928150905.2616313-1-crwulff@gmail.com> References: <20240928150905.2616313-1-crwulff@gmail.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Chris Wulff When multiple feature units exist due to differences in other terminal descriptors, they still represent the same volume/mute controls. Signed-off-by: Chris Wulff --- drivers/usb/gadget/function/f_uac2.c | 171 +++++++++++++++++++++------ 1 file changed, 133 insertions(+), 38 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index c30fbd062793..427a95b043db 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -35,8 +35,8 @@ */ #define USB_OUT_CLK_ID (out_clk_src_desc.bClockID) #define USB_IN_CLK_ID (in_clk_src_desc.bClockID) -#define USB_OUT_FU_ID(_opts) (_opts->c_alt_1_opts.fu_id) -#define USB_IN_FU_ID(_opts) (_opts->p_alt_1_opts.fu_id) +#define USB_FU_ID(_alt_opts) ((_alt_opts) ? (_alt_opts)->fu_id : 0) + #define CONTROL_ABSENT 0 #define CONTROL_RDONLY 1 @@ -54,7 +54,6 @@ #define UNFLW_CTRL 8 #define OVFLW_CTRL 10 - #define EP_EN(_alt_opts) ((_alt_opts) && ((_alt_opts)->chmask != 0)) #define FUIN_EN(_opts) (EP_EN(&_opts->p_alt_1_opts) \ && ((_opts)->p_mute_present \ @@ -112,6 +111,59 @@ struct f_uac2_opts *g_audio_to_uac2_opts(struct g_audio *agdev) static int afunc_notify(struct g_audio *agdev, int unit_id, int cs); +static inline struct f_uac2_alt_opts *get_alt_opts(struct f_uac2_opts *opts, int alt, int dir) +{ + struct f_uac2_alt_opts *alt_opts; + + if (alt == 0) + return NULL; + + if (alt == 1) + return (dir == HOST_TO_DEVICE) ? &opts->c_alt_1_opts : &opts->p_alt_1_opts; + + list_for_each_entry(alt_opts, (dir == HOST_TO_DEVICE) ? &opts->c_alt_opts + : &opts->p_alt_opts, list) { + if (alt_opts->c.alt_num == alt) + return alt_opts; + } + + return NULL; +} + +static struct f_uac2_alt_opts * +find_feature_unit(struct f_uac2_opts *opts, u8 entity_id, int *is_playback) +{ + struct f_uac2_alt_opts *alt_opts; + + if (FUOUT_EN(opts)) { + if (is_playback) + *is_playback = 0; + + if (entity_id == USB_FU_ID(&opts->c_alt_1_opts)) + return &opts->c_alt_1_opts; + + list_for_each_entry(alt_opts, &opts->c_alt_opts, list) { + if (entity_id == USB_FU_ID(alt_opts)) + return alt_opts; + } + } + + if (FUIN_EN(opts)) { + if (is_playback) + *is_playback = 1; + + if (entity_id == USB_FU_ID(&opts->p_alt_1_opts)) + return &opts->p_alt_1_opts; + + list_for_each_entry(alt_opts, &opts->p_alt_opts, list) { + if (entity_id == USB_FU_ID(alt_opts)) + return alt_opts; + } + } + + return NULL; +} + /* --------- USB Function Interface ------------- */ static struct usb_interface_assoc_descriptor iad_desc = { @@ -1243,7 +1295,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->params.p_ssize = uac2_opts->p_ssize; if (FUIN_EN(uac2_opts)) { - agdev->params.p_fu.id = USB_IN_FU_ID(uac2_opts); + agdev->params.p_fu.id = USB_FU_ID(&uac2_opts->p_alt_1_opts); agdev->params.p_fu.mute_present = uac2_opts->p_mute_present; agdev->params.p_fu.volume_present = uac2_opts->p_volume_present; agdev->params.p_fu.volume_min = uac2_opts->p_volume_min; @@ -1258,7 +1310,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->params.c_ssize = uac2_opts->c_ssize; if (FUOUT_EN(uac2_opts)) { - agdev->params.c_fu.id = USB_OUT_FU_ID(uac2_opts); + agdev->params.c_fu.id = USB_FU_ID(&uac2_opts->c_alt_1_opts); agdev->params.c_fu.mute_present = uac2_opts->c_mute_present; agdev->params.c_fu.volume_present = uac2_opts->c_volume_present; agdev->params.c_fu.volume_min = uac2_opts->c_volume_min; @@ -1295,8 +1347,49 @@ afunc_notify_complete(struct usb_ep *_ep, struct usb_request *req) usb_ep_free_request(_ep, req); } +static int afunc_notify_one(struct g_audio *agdev, int unit_id, int cs); + +static int afunc_notify_multiple(struct g_audio *agdev, int unit_id, int cs, int source_id) +{ + struct f_uac2 *uac2 = func_to_uac2(&agdev->func); + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); + int ret; + struct f_uac2_alt_opts *alt_opts; + struct list_head *alt_opts_list; + + if (!uac2->int_ep->enabled) + return 0; + + if (unit_id != source_id) { + ret = afunc_notify_one(agdev, unit_id, cs); + if (ret) + return ret; + } + + alt_opts_list = (unit_id == USB_FU_ID(&opts->c_alt_1_opts)) ? &opts->c_alt_opts + : &opts->p_alt_opts; + + /* Notify all other ganged controls */ + list_for_each_entry(alt_opts, alt_opts_list, list) { + if ((USB_FU_ID(alt_opts) > unit_id) && (USB_FU_ID(alt_opts) != source_id)) { + unit_id = USB_FU_ID(alt_opts); + ret = afunc_notify_one(agdev, unit_id, cs); + if (ret) + return ret; + } + } + + return 0; +} + static int afunc_notify(struct g_audio *agdev, int unit_id, int cs) +{ + return afunc_notify_multiple(agdev, unit_id, cs, 0); +} + +static int +afunc_notify_one(struct g_audio *agdev, int unit_id, int cs) { struct f_uac2 *uac2 = func_to_uac2(&agdev->func); struct usb_request *req; @@ -1304,9 +1397,6 @@ afunc_notify(struct g_audio *agdev, int unit_id, int cs) u16 w_index, w_value; int ret; - if (!uac2->int_ep->enabled) - return 0; - if (atomic_inc_return(&uac2->int_count) > UAC2_DEF_INT_REQ_NUM) { atomic_dec(&uac2->int_count); return 0; @@ -1360,16 +1450,12 @@ afunc_set_alt(struct usb_function *fn, unsigned int intf, unsigned int alt) struct usb_composite_dev *cdev = fn->config->cdev; struct f_uac2 *uac2 = func_to_uac2(fn); struct g_audio *agdev = func_to_g_audio(fn); + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); + struct f_uac2_alt_opts *alt_opts = NULL; struct usb_gadget *gadget = cdev->gadget; struct device *dev = &gadget->dev; int ret = 0; - /* No i/f has more than 2 alt settings */ - if (alt > 1) { - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return -EINVAL; - } - if (intf == uac2->ac_intf) { /* Control I/f has only 1 AltSetting - 0 */ if (alt) { @@ -1388,6 +1474,12 @@ afunc_set_alt(struct usb_function *fn, unsigned int intf, unsigned int alt) } if (intf == uac2->as_out_intf) { + alt_opts = get_alt_opts(opts, alt, HOST_TO_DEVICE); + if (alt && !alt_opts) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + uac2->as_out_alt = alt; if (alt) @@ -1395,6 +1487,12 @@ afunc_set_alt(struct usb_function *fn, unsigned int intf, unsigned int alt) else u_audio_stop_capture(&uac2->g_audio); } else if (intf == uac2->as_in_intf) { + alt_opts = get_alt_opts(opts, alt, DEVICE_TO_HOST); + if (alt && !alt_opts) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + uac2->as_in_alt = alt; if (alt) @@ -1463,6 +1561,8 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; u32 p_srate, c_srate; + unsigned int is_playback = 0; + struct f_uac2_alt_opts *alt_opts = find_feature_unit(opts, entity_id, &is_playback); u_audio_get_playback_srate(agdev, &p_srate); u_audio_get_capture_srate(agdev, &c_srate); @@ -1488,13 +1588,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) "%s:%d control_selector=%d TODO!\n", __func__, __LINE__, control_selector); } - } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { - unsigned int is_playback = 0; - - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) - is_playback = 1; - + } else if (alt_opts) { if (control_selector == UAC_FU_MUTE) { unsigned int mute; @@ -1539,6 +1633,8 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; + unsigned int is_playback = 0; + struct f_uac2_alt_opts *alt_opts = find_feature_unit(opts, entity_id, &is_playback); if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) { if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { @@ -1577,13 +1673,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) "%s:%d control_selector=%d TODO!\n", __func__, __LINE__, control_selector); } - } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { - unsigned int is_playback = 0; - - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) - is_playback = 1; - + } else if (alt_opts) { if (control_selector == UAC_FU_VOLUME) { struct cntrl_range_lay2 r; s16 max_db, min_db, res_db; @@ -1654,31 +1744,32 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) { struct g_audio *agdev = req->context; struct usb_composite_dev *cdev = agdev->func.config->cdev; - struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); struct f_uac2 *uac2 = func_to_uac2(&agdev->func); + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); struct usb_ctrlrequest *cr = &uac2->setup_cr; u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; + unsigned int is_playback; + struct f_uac2_alt_opts *alt_opts = find_feature_unit(opts, entity_id, &is_playback); if (req->status != 0) { dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status); return; } - if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { - unsigned int is_playback = 0; - - if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) - is_playback = 1; - + if (alt_opts) { if (control_selector == UAC_FU_MUTE) { u8 mute = *(u8 *)req->buf; u_audio_set_mute(agdev, is_playback, mute); + /* We also need to send notify for ganged controls */ + afunc_notify_multiple(agdev, USB_FU_ID(is_playback ? &opts->p_alt_1_opts + : &opts->c_alt_1_opts), + control_selector, entity_id); + return; } else if (control_selector == UAC_FU_VOLUME) { struct cntrl_cur_lay2 *c = req->buf; @@ -1687,6 +1778,11 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) volume = le16_to_cpu(c->wCUR); u_audio_set_volume(agdev, is_playback, volume); + /* We also need to send notify for ganged controls */ + afunc_notify_multiple(agdev, USB_FU_ID(is_playback ? &opts->p_alt_1_opts + : &opts->c_alt_1_opts), + control_selector, entity_id); + return; } else { dev_err(&agdev->gadget->dev, @@ -1721,8 +1817,7 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) req->complete = uac2_cs_control_sam_freq; return w_length; } - } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) || - (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) { + } else if (find_feature_unit(opts, entity_id, NULL)) { memcpy(&uac2->setup_cr, cr, sizeof(*cr)); req->context = agdev; req->complete = out_rq_cur_complete;