From patchwork Mon Dec 20 08:25:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687317 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CA8FBC433EF for ; Mon, 20 Dec 2021 08:25:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230408AbhLTIZv (ORCPT ); Mon, 20 Dec 2021 03:25:51 -0500 Received: from cable.insite.cz ([84.242.75.189]:40833 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229827AbhLTIZv (ORCPT ); Mon, 20 Dec 2021 03:25:51 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 1FF8FA1A3D40C; Mon, 20 Dec 2021 09:25:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988749; bh=4OReWYNWtGWkkIdlPU08sx5H+R+qLS85r9ZPvLOz62Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lRNaX3LmT+QkgYBmjLu8gQIdmLNQNcMmOXprUEzEJco6KNpv7YWN155ANZDIZ+94K UHZyYbJ+0gLP6IgE3UX09ak4s0Jh4tQTzCRml9Bj5B1PuvGr8xynvdzFOCqxcJKw8c l9xUN/DBWs4s3NoPDakFYwBcDoeNwTE1buRS9VfQ= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id d68Mj07ll8-k; Mon, 20 Dec 2021 09:25:44 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id C42AFA1A3D401; Mon, 20 Dec 2021 09:25:43 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988744; bh=4OReWYNWtGWkkIdlPU08sx5H+R+qLS85r9ZPvLOz62Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PovdHsbs0q+D2K3mD4uqqbBXkyt6QbVXCrI+jyRiXRHeUWLgC4wLe48a4ry2/6c5t EK3nME9hNyRvttiX4HEK/DieF6BoPVQFulvwms4xf57LofIiLwfc6tWQKSXULrErCc doWy/6Ye8BlJkhFAg8DPgoFBMXhEr2W8D6+JXa+I= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Date: Mon, 20 Dec 2021 09:25:32 +0100 Message-Id: <20211220082542.13750-2-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Both capture and playback alsa devices use subdevice 0. Yet capture-side ctls are defined for subdevice 1. The patch sets subdevice 0 for them. Signed-off-by: Pavel Hofman --- drivers/usb/gadget/function/u_audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index c46400be5464..4f6c0049c534 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -1145,7 +1145,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, } kctl->id.device = pcm->device; - kctl->id.subdevice = i; + kctl->id.subdevice = 0; err = snd_ctl_add(card, kctl); if (err < 0) @@ -1168,7 +1168,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, } kctl->id.device = pcm->device; - kctl->id.subdevice = i; + kctl->id.subdevice = 0; kctl->tlv.c = u_audio_volume_tlv; From patchwork Mon Dec 20 08:25:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687319 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6D87AC433FE for ; Mon, 20 Dec 2021 08:25:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231372AbhLTIZw (ORCPT ); Mon, 20 Dec 2021 03:25:52 -0500 Received: from cable.insite.cz ([84.242.75.189]:60365 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229874AbhLTIZv (ORCPT ); Mon, 20 Dec 2021 03:25:51 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id EFB56A1A3D400; Mon, 20 Dec 2021 09:25:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988749; bh=ahBtijJ5iunVsC4K1uKqZgjRN0ieDvzhhXBg11RqPzo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FoOt5YBpI+zjmhwt5Mfk/57yjnorkhbAhYhA4b7o1vjHFy2z3k7XUTCt1I3O4+tN4 m5c5nQ4GqpheUB+fOEk8rx5WtP91qYap7zf07PcbWM5WIoec5DSJrdkXPN4qlfmVXr P1Rbzo0m82ylq9Usj1XkIfcocnzhF1BDOrLOJ1GI= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id HaflhKrTUy7z; Mon, 20 Dec 2021 09:25:44 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 26CFEA1A3D402; Mon, 20 Dec 2021 09:25:44 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988744; bh=ahBtijJ5iunVsC4K1uKqZgjRN0ieDvzhhXBg11RqPzo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nbBhQuPLjpa/6XK4hwMlyuEXLHz+byNuw7y/FhSKrWyHkztRJcN+A1G4AQVJ/AYAN kLqNprCgRLWKLkA9mwFv7Gv4WEAkXWrLkFb4L4rOZD+qPD/5O1yWR2SqFYBBmHDpHr f6X5vb3MdZ19mAzmdpzuuGcNJE5ATg/a+dgAg448= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 02/11] usb: gadget: u_audio: Support multiple sampling rates Date: Mon, 20 Dec 2021 09:25:33 +0100 Message-Id: <20211220082542.13750-3-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org From: Julian Scheel Implement support for multiple sampling rates in u_audio part of the audio gadget. The currently configured rates are exposed through read-only amixer controls 'Capture Rate' and 'Playback Rate'. Signed-off-by: Julian Scheel Signed-off-by: Pavel Hofman --- drivers/usb/gadget/function/f_uac1.c | 2 + drivers/usb/gadget/function/f_uac2.c | 2 + drivers/usb/gadget/function/u_audio.c | 135 +++++++++++++++++++++++ drivers/usb/gadget/function/u_audio.h | 10 +- drivers/usb/gadget/function/uac_common.h | 9 ++ 5 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/gadget/function/uac_common.h diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 03f50643fbba..ccb0e4f41e5d 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -1298,6 +1298,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize); audio->params.c_chmask = audio_opts->c_chmask; audio->params.c_srate = audio_opts->c_srate; + audio->params.c_srates[0] = audio_opts->c_srate; audio->params.c_ssize = audio_opts->c_ssize; if (FUIN_EN(audio_opts)) { audio->params.p_fu.id = USB_IN_FU_ID; @@ -1310,6 +1311,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) } audio->params.p_chmask = audio_opts->p_chmask; audio->params.p_srate = audio_opts->p_srate; + audio->params.p_srates[0] = audio_opts->p_srate; audio->params.p_ssize = audio_opts->p_ssize; if (FUOUT_EN(audio_opts)) { audio->params.c_fu.id = USB_OUT_FU_ID; diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index f8c1f406f19b..1d6e426e5078 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1210,6 +1210,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->params.p_chmask = uac2_opts->p_chmask; agdev->params.p_srate = uac2_opts->p_srate; + agdev->params.p_srates[0] = uac2_opts->p_srate; agdev->params.p_ssize = uac2_opts->p_ssize; if (FUIN_EN(uac2_opts)) { agdev->params.p_fu.id = USB_IN_FU_ID; @@ -1221,6 +1222,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) } agdev->params.c_chmask = uac2_opts->c_chmask; agdev->params.c_srate = uac2_opts->c_srate; + agdev->params.c_srates[0] = uac2_opts->c_srate; agdev->params.c_ssize = uac2_opts->c_ssize; if (FUOUT_EN(uac2_opts)) { agdev->params.c_fu.id = USB_OUT_FU_ID; diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 4f6c0049c534..e737a104156d 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -32,6 +32,7 @@ enum { UAC_P_PITCH_CTRL, UAC_MUTE_CTRL, UAC_VOLUME_CTRL, + UAC_RATE_CTRL, }; /* Runtime data params for one stream */ @@ -62,6 +63,8 @@ struct uac_rtd_params { s16 volume; int mute; + struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */ + spinlock_t lock; /* lock for control transfers */ }; @@ -490,6 +493,48 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep) dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); } +int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate) +{ + struct uac_params *params = &audio_dev->params; + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm = &uac->c_prm; + int i; + + dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); + for (i = 0; i < UAC_MAX_RATES; i++) { + if (params->c_srates[i] == srate) { + params->c_srate = srate; + return 0; + } + if (params->c_srates[i] == 0) + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(u_audio_set_capture_srate); + +int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm = &uac->p_prm; + struct uac_params *params = &audio_dev->params; + int i; + + dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); + for (i = 0; i < UAC_MAX_RATES; i++) { + if (params->p_srates[i] == srate) { + params->p_srate = srate; + return 0; + } + if (params->p_srates[i] == 0) + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(u_audio_set_playback_srate); + int u_audio_start_capture(struct g_audio *audio_dev) { struct snd_uac_chip *uac = audio_dev->uac; @@ -501,6 +546,7 @@ int u_audio_start_capture(struct g_audio *audio_dev) struct uac_params *params = &audio_dev->params; int req_len, i; + dev_dbg(dev, "start capture with rate %d\n", params->c_srate); ep = audio_dev->out_ep; prm = &uac->c_prm; config_ep_by_speed(gadget, &audio_dev->func, ep); @@ -593,6 +639,7 @@ int u_audio_start_playback(struct g_audio *audio_dev) int req_len, i; unsigned int p_interval, p_pktsize; + dev_dbg(dev, "start playback with rate %d\n", params->p_srate); ep = audio_dev->in_ep; prm = &uac->p_prm; config_ep_by_speed(gadget, &audio_dev->func, ep); @@ -941,6 +988,68 @@ static int u_audio_volume_put(struct snd_kcontrol *kcontrol, return change; } +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_min_srate(const int *srates) +{ + int i, min_srate = INT_MAX; + + for (i = 0; i < UAC_MAX_RATES; i++) { + if (srates[i] == 0) + break; + if (srates[i] < min_srate) + min_srate = srates[i]; + } + return min_srate; +} + +static int uac_pcm_rate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + const int *srates; + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + struct uac_params *params = &audio_dev->params; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + + if (prm == &uac->c_prm) + srates = params->c_srates; + else + srates = params->p_srates; + uinfo->value.integer.min = get_min_srate(srates); + uinfo->value.integer.max = get_max_srate(srates); + return 0; +} + +static int uac_pcm_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + struct uac_params *params = &audio_dev->params; + + if (prm == &uac->c_prm) + ucontrol->value.integer.value[0] = params->c_srate; + else + ucontrol->value.integer.value[0] = params->p_srate; + + return 0; +} static struct snd_kcontrol_new u_audio_controls[] = { [UAC_FBACK_CTRL] { @@ -971,6 +1080,13 @@ static struct snd_kcontrol_new u_audio_controls[] = { .get = u_audio_volume_get, .put = u_audio_volume_put, }, + [UAC_RATE_CTRL] { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "", /* will be filled later */ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = uac_pcm_rate_info, + .get = uac_pcm_rate_get, + }, }; int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, @@ -1184,6 +1300,25 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, prm->volume_min = fu->volume_min; prm->volume_res = fu->volume_res; } + + /* Add rate control */ + snprintf(ctrl_name, sizeof(ctrl_name), + "%s Rate", direction); + u_audio_controls[UAC_RATE_CTRL].name = ctrl_name; + + kctl = snd_ctl_new1(&u_audio_controls[UAC_RATE_CTRL], prm); + if (!kctl) { + err = -ENOMEM; + goto snd_fail; + } + + kctl->id.device = pcm->device; + kctl->id.subdevice = 0; + + err = snd_ctl_add(card, kctl); + if (err < 0) + goto snd_fail; + prm->snd_kctl_rate = kctl; } strscpy(card->driver, card_name, sizeof(card->driver)); diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h index 8dfdae1721cd..76b5b8169444 100644 --- a/drivers/usb/gadget/function/u_audio.h +++ b/drivers/usb/gadget/function/u_audio.h @@ -10,6 +10,7 @@ #define __U_AUDIO_H #include +#include "uac_common.h" /* * Same maximum frequency deviation on the slower side as in @@ -40,13 +41,15 @@ struct uac_fu_params { struct uac_params { /* playback */ int p_chmask; /* channel mask */ - int p_srate; /* rate in Hz */ + int p_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */ + int p_srate; /* selected rate in Hz */ int p_ssize; /* sample size */ struct uac_fu_params p_fu; /* Feature Unit parameters */ /* capture */ int c_chmask; /* channel mask */ - int c_srate; /* rate in Hz */ + int c_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */ + int c_srate; /* selected rate in Hz */ int c_ssize; /* sample size */ struct uac_fu_params c_fu; /* Feature Unit parameters */ @@ -117,6 +120,9 @@ void u_audio_stop_capture(struct g_audio *g_audio); int u_audio_start_playback(struct g_audio *g_audio); void u_audio_stop_playback(struct g_audio *g_audio); +int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate); +int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate); + int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val); int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val); int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val); diff --git a/drivers/usb/gadget/function/uac_common.h b/drivers/usb/gadget/function/uac_common.h new file mode 100644 index 000000000000..3ecf89d6e814 --- /dev/null +++ b/drivers/usb/gadget/function/uac_common.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + */ + +#ifndef UAC_COMMON_H +#define UAC_COMMON_H + +#define UAC_MAX_RATES 10 /* maximum number of rates configurable by f_uac1/2 */ +#endif From patchwork Mon Dec 20 08:25:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687321 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2DB23C43217 for ; Mon, 20 Dec 2021 08:25:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231472AbhLTIZx (ORCPT ); Mon, 20 Dec 2021 03:25:53 -0500 Received: from cable.insite.cz ([84.242.75.189]:39351 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229632AbhLTIZw (ORCPT ); Mon, 20 Dec 2021 03:25:52 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 8533EA1A3D402; Mon, 20 Dec 2021 09:25:50 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988750; bh=Xne0b8n0z/LWfnow/J1Xm6QDToOHuC/GquzUfE+nK6Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=onZZiXE07S71d7ie8ElhrD9KVL+wR4/xXxAnNhZlT5BmeyyPS1kqE1+m5I+3L9NwL sh4asGTJtnxputQ+uRvigH7IDkB75N2dp8XOCMIgFcDX5KZQFqxDl7EQd21z1Xco5o K95ZAUKGJgqhpZyWu3JxGPNkXnUeisrGYiKffqjc= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id L4ypqtJedGoj; Mon, 20 Dec 2021 09:25:44 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 78203A1A3D403; Mon, 20 Dec 2021 09:25:44 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988744; bh=Xne0b8n0z/LWfnow/J1Xm6QDToOHuC/GquzUfE+nK6Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AcGtNdC/bsghbjcD2r1XLUQ99hzJWLkKEvYVlcdWXVVN3JrWSUwwExwLPNsPNf/JA uDDUd2HzZlyuHXIP9A6Scx3o4wIfADUAx1A+QiPy8wYjyY0+91VjAkHp7A+V1mg+vM eYizvuM8UcBgSAPgAcZ7/fncZSPUEWm4EY1Zk164= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 03/11] usb: gadget: f_uac2: Support multiple sampling rates Date: Mon, 20 Dec 2021 09:25:34 +0100 Message-Id: <20211220082542.13750-4-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org From: Julian Scheel A list of sampling rates can be specified via configfs. All enabled sampling rates are sent to the USB host on request. When the host selects a sampling rate the internal active rate is updated. Config strings with single value stay compatible with the previous version. Multiple samplerates passed as configuration arrays to g_audio module when built for f_uac2. Signed-off-by: Julian Scheel Signed-off-by: Pavel Hofman --- .../ABI/testing/configfs-usb-gadget-uac2 | 4 +- Documentation/usb/gadget-testing.rst | 4 +- drivers/usb/gadget/function/f_uac2.c | 118 ++++++++++++++---- drivers/usb/gadget/function/u_uac2.h | 62 +++++++++ drivers/usb/gadget/legacy/audio.c | 28 +++-- 5 files changed, 177 insertions(+), 39 deletions(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2 index 244d96650123..b20b0471d48a 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac2 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2 @@ -6,7 +6,7 @@ Description: ===================== ======================================= c_chmask capture channel mask - c_srate capture sampling rate + c_srate list of capture sampling rates (comma-separated) c_ssize capture sample size (bytes) c_sync capture synchronization type (async/adaptive) @@ -20,7 +20,7 @@ Description: (in 1/256 dB) fb_max maximum extra bandwidth in async mode p_chmask playback channel mask - p_srate playback sampling rate + p_srate list of playback sampling rates (comma-separated) p_ssize playback sample size (bytes) p_mute_present playback mute control enable p_volume_present playback volume control enable diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index c18113077889..928f60a31544 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -726,7 +726,7 @@ The uac2 function provides these attributes in its function directory: ================ ==================================================== c_chmask capture channel mask - c_srate capture sampling rate + 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 @@ -736,7 +736,7 @@ The uac2 function provides these attributes in its function directory: 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 playback sampling rate + p_srate list of playback sampling rates (comma-separated) p_ssize playback sample size (bytes) p_mute_present playback mute control enable p_volume_present playback volume control enable diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 1d6e426e5078..74e32bb146c7 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -70,6 +70,7 @@ struct f_uac2 { /* Interrupt IN endpoint of AC interface */ struct usb_ep *int_ep; atomic_t int_count; + int ctl_id; }; static inline struct f_uac2 *func_to_uac2(struct usb_function *f) @@ -166,7 +167,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = { .bDescriptorSubtype = UAC2_CLOCK_SOURCE, /* .bClockID = DYNAMIC */ .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED, - .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL), + .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL), .bAssocTerminal = 0, }; @@ -178,7 +179,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = { .bDescriptorSubtype = UAC2_CLOCK_SOURCE, /* .bClockID = DYNAMIC */ .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED, - .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL), + .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL), .bAssocTerminal = 0, }; @@ -635,12 +636,32 @@ struct cntrl_cur_lay3 { }; struct cntrl_range_lay3 { - __le16 wNumSubRanges; __le32 dMIN; __le32 dMAX; __le32 dRES; } __packed; +#define ranges_size(c) (sizeof(c.wNumSubRanges) + c.wNumSubRanges \ + * sizeof(struct cntrl_ranges_lay3)) + +struct cntrl_ranges_lay3 { + __u16 wNumSubRanges; + struct cntrl_range_lay3 r[UAC_MAX_RATES]; +} __packed; + +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 set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, struct usb_endpoint_descriptor *ep_desc, enum usb_device_speed speed, bool is_playback) @@ -667,11 +688,11 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, if (is_playback) { chmask = uac2_opts->p_chmask; - srate = uac2_opts->p_srate; + srate = get_max_srate(uac2_opts->p_srates); ssize = uac2_opts->p_ssize; } else { chmask = uac2_opts->c_chmask; - srate = uac2_opts->c_srate; + srate = get_max_srate(uac2_opts->c_srates); ssize = uac2_opts->c_ssize; } @@ -912,10 +933,10 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev) } 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_srate) { + } else if (!opts->p_srates[0]) { dev_err(dev, "Error: incorrect playback sampling rate\n"); return -EINVAL; - } else if (!opts->c_srate) { + } else if (!opts->c_srates[0]) { dev_err(dev, "Error: incorrect capture sampling rate\n"); return -EINVAL; } @@ -1210,7 +1231,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->params.p_chmask = uac2_opts->p_chmask; agdev->params.p_srate = uac2_opts->p_srate; - agdev->params.p_srates[0] = uac2_opts->p_srate; + 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; @@ -1222,7 +1244,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) } agdev->params.c_chmask = uac2_opts->c_chmask; agdev->params.c_srate = uac2_opts->c_srate; - agdev->params.c_srates[0] = uac2_opts->c_srate; + 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; @@ -1502,28 +1525,39 @@ 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; - int p_srate, c_srate; - - p_srate = opts->p_srate; - c_srate = opts->c_srate; if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) { if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { - struct cntrl_range_lay3 r; + struct cntrl_ranges_lay3 rs; + int i; + int wNumSubRanges = 0; + int srate; + int *srates; if (entity_id == USB_IN_CLK_ID) - r.dMIN = cpu_to_le32(p_srate); + srates = opts->p_srates; else if (entity_id == USB_OUT_CLK_ID) - r.dMIN = cpu_to_le32(c_srate); + srates = opts->c_srates; else return -EOPNOTSUPP; - - r.dMAX = r.dMIN; - r.dRES = 0; - r.wNumSubRanges = cpu_to_le16(1); - - value = min_t(unsigned int, w_length, sizeof(r)); - memcpy(req->buf, &r, value); + for (i = 0; i < UAC_MAX_RATES; i++) { + srate = srates[i]; + if (srate == 0) + break; + + rs.r[wNumSubRanges].dMIN = cpu_to_le32(srate); + rs.r[wNumSubRanges].dMAX = cpu_to_le32(srate); + rs.r[wNumSubRanges].dRES = 0; + wNumSubRanges++; + dev_dbg(&agdev->gadget->dev, + "%s(): clk %d: rate ID %d: %d\n", + __func__, entity_id, wNumSubRanges, srate); + } + rs.wNumSubRanges = cpu_to_le16(wNumSubRanges); + value = min_t(unsigned int, w_length, ranges_size(rs)); + dev_dbg(&agdev->gadget->dev, "%s(): sending %d rates, size %d\n", + __func__, rs.wNumSubRanges, value); + memcpy(req->buf, &rs, value); } else { dev_err(&agdev->gadget->dev, "%s:%d control_selector=%d TODO!\n", @@ -1582,6 +1616,28 @@ ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr) return -EOPNOTSUPP; } +static void uac2_cs_control_sam_freq(struct usb_ep *ep, struct usb_request *req) +{ + struct usb_function *fn = ep->driver_data; + struct g_audio *agdev = func_to_g_audio(fn); + struct f_uac2 *uac2 = func_to_uac2(fn); + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); + u32 val; + + if (req->actual != 4) + return; + + val = le32_to_cpu(*((u32 *)req->buf)); + dev_dbg(&agdev->gadget->dev, "%s val: %d.\n", __func__, val); + if (uac2->ctl_id == USB_IN_CLK_ID) { + opts->p_srate = val; + u_audio_set_playback_srate(agdev, opts->p_srate); + } else if (uac2->ctl_id == USB_OUT_CLK_ID) { + opts->c_srate = val; + u_audio_set_capture_srate(agdev, opts->c_srate); + } +} + static void out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) { @@ -1633,6 +1689,7 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) static int out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) { + struct usb_composite_dev *cdev = fn->config->cdev; struct usb_request *req = fn->config->cdev->req; struct g_audio *agdev = func_to_g_audio(fn); struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); @@ -1642,10 +1699,17 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) u16 w_value = le16_to_cpu(cr->wValue); u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; + u8 clock_id = w_index >> 8; if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) { - if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { + dev_dbg(&agdev->gadget->dev, + "control_selector UAC2_CS_CONTROL_SAM_FREQ, clock: %d\n", clock_id); + cdev->gadget->ep0->driver_data = fn; + uac2->ctl_id = clock_id; + 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))) { memcpy(&uac2->setup_cr, cr, sizeof(*cr)); @@ -1839,10 +1903,10 @@ end: \ CONFIGFS_ATTR(f_uac2_opts_, name) UAC2_ATTRIBUTE(u32, p_chmask); -UAC2_ATTRIBUTE(u32, p_srate); +UAC_RATE2_ATTRIBUTE(p_srate); UAC2_ATTRIBUTE(u32, p_ssize); UAC2_ATTRIBUTE(u32, c_chmask); -UAC2_ATTRIBUTE(u32, c_srate); +UAC_RATE2_ATTRIBUTE(c_srate); UAC2_ATTRIBUTE_SYNC(c_sync); UAC2_ATTRIBUTE(u32, c_ssize); UAC2_ATTRIBUTE(u32, req_number); @@ -1915,9 +1979,11 @@ static struct usb_function_instance *afunc_alloc_inst(void) &f_uac2_func_type); opts->p_chmask = UAC2_DEF_PCHMASK; + opts->p_srates[0] = UAC2_DEF_PSRATE; opts->p_srate = UAC2_DEF_PSRATE; opts->p_ssize = UAC2_DEF_PSSIZE; opts->c_chmask = UAC2_DEF_CCHMASK; + opts->c_srates[0] = UAC2_DEF_CSRATE; opts->c_srate = UAC2_DEF_CSRATE; opts->c_ssize = UAC2_DEF_CSSIZE; opts->c_sync = UAC2_DEF_CSYNC; diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index e0c8e3513bfd..8058217322f8 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -14,6 +14,7 @@ #define U_UAC2_H #include +#include "uac_common.h" #define UAC2_DEF_PCHMASK 0x3 #define UAC2_DEF_PSRATE 48000 @@ -35,9 +36,11 @@ struct f_uac2_opts { struct usb_function_instance func_inst; int p_chmask; + int p_srates[UAC_MAX_RATES]; int p_srate; int p_ssize; int c_chmask; + int c_srates[UAC_MAX_RATES]; int c_srate; int c_ssize; int c_sync; @@ -62,4 +65,63 @@ struct f_uac2_opts { int refcnt; }; +#define UAC_RATE2_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; \ + opts->name = num; \ + ret = len; \ + }; \ + \ +end: \ + kfree(split_page); \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(f_uac2_opts_, name) #endif diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index a748ed0842e8..58bcb26c7854 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -26,9 +26,10 @@ module_param(p_chmask, uint, S_IRUGO); MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); /* Playback Default 48 KHz */ -static int p_srate = UAC2_DEF_PSRATE; -module_param(p_srate, uint, S_IRUGO); -MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); +static int p_srates[UAC_MAX_RATES] = {UAC2_DEF_PSRATE}; +static int p_srates_cnt = 1; +module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, S_IRUGO); +MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)"); /* Playback Default 16bits/sample */ static int p_ssize = UAC2_DEF_PSSIZE; @@ -41,9 +42,10 @@ module_param(c_chmask, uint, S_IRUGO); MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); /* Capture Default 64 KHz */ -static int c_srate = UAC2_DEF_CSRATE; -module_param(c_srate, uint, S_IRUGO); -MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); +static int c_srates[UAC_MAX_RATES] = {UAC2_DEF_CSRATE}; +static int c_srates_cnt = 1; +module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, S_IRUGO); +MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)"); /* Capture Default 16bits/sample */ static int c_ssize = UAC2_DEF_CSSIZE; @@ -244,7 +246,7 @@ static int audio_bind(struct usb_composite_dev *cdev) struct f_uac1_legacy_opts *uac1_opts; #endif #endif - int status; + int status, i; #ifndef CONFIG_GADGET_UAC1 fi_uac2 = usb_get_function_instance("uac2"); @@ -263,10 +265,18 @@ static int audio_bind(struct usb_composite_dev *cdev) #ifndef CONFIG_GADGET_UAC1 uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst); uac2_opts->p_chmask = p_chmask; - uac2_opts->p_srate = p_srate; + + for (i = 0; i < p_srates_cnt; ++i) + uac2_opts->p_srates[i] = p_srates[i]; + uac2_opts->p_srate = p_srates[0]; + uac2_opts->p_ssize = p_ssize; uac2_opts->c_chmask = c_chmask; - uac2_opts->c_srate = c_srate; + + for (i = 0; i < c_srates_cnt; ++i) + uac2_opts->c_srates[i] = c_srates[i]; + uac2_opts->c_srate = c_srates[0]; + uac2_opts->c_ssize = c_ssize; uac2_opts->req_number = UAC2_DEF_REQ_NUM; #else From patchwork Mon Dec 20 08:25:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687323 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B7C6EC433F5 for ; Mon, 20 Dec 2021 08:25:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231761AbhLTIZx (ORCPT ); Mon, 20 Dec 2021 03:25:53 -0500 Received: from cable.insite.cz ([84.242.75.189]:42268 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231318AbhLTIZx (ORCPT ); Mon, 20 Dec 2021 03:25:53 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id D1F3DA1A3D40D; Mon, 20 Dec 2021 09:25:50 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988750; bh=AACdxCNFxKZ9SsL+82wfjvg8ae4J1WzucBE+EIVGt1M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TOn15HGBiGwzn13aGdd1rzS71o+J9ty8UihkDGb9Op3XeSiJjPrpMoCFhdeE4mMWn R5msOKehvxYzTPugjAfHA+EadUYSGrSYPqy0dHBy0JU/OPIahEXpUWcTsl/Lb42uGM gYJJMI7rwfXBsAnAYMvANADtIuTBJZy/w35PcOyw= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id N6IIEoQm0_21; Mon, 20 Dec 2021 09:25:45 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id DE453A1A3D404; Mon, 20 Dec 2021 09:25:44 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988745; bh=AACdxCNFxKZ9SsL+82wfjvg8ae4J1WzucBE+EIVGt1M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iy+lOBGhOWq0FmIcKJir5L09beT0GzSwiXZeBeSpWajkAEtHCVwUfqsG515aP+Bax Dd5agiXjQbKLUUti+p+1PaA/RwRpfH4cxhn92JlxQp20im+4QW7vrR1SLkSV0oj5TM /t/7an/LB2BZDphqqVOxU3RqKq6h1ipEgvfc/COw= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 04/11] usb: gadget: f_uac1: Support multiple sampling rates Date: Mon, 20 Dec 2021 09:25:35 +0100 Message-Id: <20211220082542.13750-5-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org From: Julian Scheel A list of sampling rates can be specified via configfs. All enabled sampling rates are sent to the USB host on request. When the host selects a sampling rate the internal active rate is updated. Config strings with single value stay compatible with the previous version. Multiple samplerates passed as configuration arrays to g_audio module when built for f_uac1. Signed-off-by: Julian Scheel Signed-off-by: Pavel Hofman Reported-by: kernel test robot --- .../ABI/testing/configfs-usb-gadget-uac1 | 4 +- Documentation/usb/gadget-testing.rst | 4 +- drivers/usb/gadget/function/f_uac1.c | 114 ++++++++++++++---- drivers/usb/gadget/function/u_uac1.h | 63 +++++++++- drivers/usb/gadget/legacy/audio.c | 12 +- 5 files changed, 168 insertions(+), 29 deletions(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1 index b576b3d6ea6d..a8ecf17f688b 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac1 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1 @@ -6,7 +6,7 @@ Description: ===================== ======================================= c_chmask capture channel mask - c_srate capture sampling rate + c_srate list of capture sampling rates (comma-separated) c_ssize capture sample size (bytes) c_mute_present capture mute control enable c_volume_present capture volume control enable @@ -17,7 +17,7 @@ Description: c_volume_res capture volume control resolution (in 1/256 dB) p_chmask playback channel mask - p_srate playback sampling rate + p_srate list of playback sampling rates (comma-separated) p_ssize playback sample size (bytes) p_mute_present playback mute control enable p_volume_present playback volume control enable diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 928f60a31544..f21cc21d2d7b 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -916,7 +916,7 @@ The uac1 function provides these attributes in its function directory: ================ ==================================================== c_chmask capture channel mask - c_srate capture sampling rate + c_srate list of capture sampling rates (comma-separated) c_ssize capture sample size (bytes) c_mute_present capture mute control enable c_volume_present capture volume control enable @@ -924,7 +924,7 @@ The uac1 function provides these attributes in its function directory: c_volume_max capture volume control max value (in 1/256 dB) c_volume_res capture volume control resolution (in 1/256 dB) p_chmask playback channel mask - p_srate playback sampling rate + p_srate list of playback sampling rates (comma-separated) p_ssize playback sample size (bytes) p_mute_present playback mute control enable p_volume_present playback volume control enable diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index ccb0e4f41e5d..7fd2b5580374 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -3,6 +3,7 @@ * f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API) * * Copyright (C) 2016 Ruslan Bilovol + * Copyright (C) 2021 Julian Scheel * * This driver doesn't expect any real Audio codec to be present * on the device - the audio streams are simply sinked to and @@ -42,6 +43,7 @@ struct f_uac1 { /* Interrupt IN endpoint of AC interface */ struct usb_ep *int_ep; atomic_t int_count; + int ctl_id; }; static inline struct f_uac1 *func_to_uac1(struct usb_function *f) @@ -188,16 +190,18 @@ static struct uac1_as_header_descriptor as_in_header_desc = { .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), }; -DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); +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_1 as_out_type_i_desc = { - .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), +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 = 1, + .bSamFreqType = 0, /* filled on rate setup */ }; /* Standard ISO OUT Endpoint Descriptor */ @@ -221,14 +225,14 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = { .wLockDelay = cpu_to_le16(1), }; -static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = { - .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(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 = 1, + .bSamFreqType = 0, /* filled on rate setup */ }; /* Standard ISO OUT Endpoint Descriptor */ @@ -333,6 +337,31 @@ static struct usb_gadget_strings *uac1_strings[] = { * This function is an ALSA sound card following USB Audio Class Spec 1.0. */ +static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req) +{ + struct usb_function *fn = ep->driver_data; + struct usb_composite_dev *cdev = fn->config->cdev; + struct g_audio *agdev = func_to_g_audio(fn); + struct f_uac1 *uac1 = func_to_uac1(fn); + struct f_uac1_opts *opts = g_audio_to_uac1_opts(agdev); + u8 *buf = (u8 *)req->buf; + u32 val = 0; + + if (req->actual != 3) { + WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n"); + return; + } + + val = buf[0] | (buf[1] << 8) | (buf[2] << 16); + if (uac1->ctl_id == (USB_DIR_IN | 2)) { + opts->p_srate = val; + u_audio_set_playback_srate(agdev, opts->p_srate); + } else if (uac1->ctl_id == (USB_DIR_OUT | 1)) { + opts->c_srate = val; + u_audio_set_capture_srate(agdev, opts->c_srate); + } +} + static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req) { struct g_audio *audio = req->context; @@ -707,18 +736,27 @@ static int audio_set_endpoint_req(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = f->config->cdev->req; + struct f_uac1 *uac1 = func_to_uac1(f); int value = -EOPNOTSUPP; u16 ep = le16_to_cpu(ctrl->wIndex); u16 len = le16_to_cpu(ctrl->wLength); u16 w_value = le16_to_cpu(ctrl->wValue); + u8 cs = w_value >> 8; DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", ctrl->bRequest, w_value, len, ep); switch (ctrl->bRequest) { - case UAC_SET_CUR: + case UAC_SET_CUR: { + if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) { + cdev->gadget->ep0->driver_data = f; + uac1->ctl_id = ep; + req->complete = uac_cs_attr_sample_rate; + } value = len; break; + } case UAC_SET_MIN: break; @@ -743,16 +781,34 @@ static int audio_get_endpoint_req(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = f->config->cdev->req; + struct g_audio *agdev = func_to_g_audio(f); + struct f_uac1_opts *opts = g_audio_to_uac1_opts(agdev); + u8 *buf = (u8 *)req->buf; int value = -EOPNOTSUPP; - u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u8 ep = le16_to_cpu(ctrl->wIndex); u16 len = le16_to_cpu(ctrl->wLength); u16 w_value = le16_to_cpu(ctrl->wValue); + u8 cs = w_value >> 8; + u32 val = 0; DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", ctrl->bRequest, w_value, len, ep); switch (ctrl->bRequest) { - case UAC_GET_CUR: + case UAC_GET_CUR: { + if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) { + if (ep == (USB_DIR_IN | 2)) + val = opts->p_srate; + else if (ep == (USB_DIR_OUT | 1)) + val = opts->c_srate; + buf[2] = (val >> 16) & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[0] = val & 0xff; + } + value = len; + break; + } case UAC_GET_MIN: case UAC_GET_MAX: case UAC_GET_RES: @@ -1118,10 +1174,9 @@ 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; - u8 *sam_freq; - int rate; int ba_iface_id; int status; + int idx, i; status = f_audio_validate_opts(audio, dev); if (status) @@ -1213,12 +1268,23 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) } /* Set sample rates */ - rate = audio_opts->c_srate; - sam_freq = as_out_type_i_desc.tSamFreq[0]; - memcpy(sam_freq, &rate, 3); - rate = audio_opts->p_srate; - sam_freq = as_in_type_i_desc.tSamFreq[0]; - memcpy(sam_freq, &rate, 3); + 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; + + 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; /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); @@ -1298,7 +1364,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize); audio->params.c_chmask = audio_opts->c_chmask; audio->params.c_srate = audio_opts->c_srate; - audio->params.c_srates[0] = audio_opts->c_srate; + 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; @@ -1311,7 +1378,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) } audio->params.p_chmask = audio_opts->p_chmask; audio->params.p_srate = audio_opts->p_srate; - audio->params.p_srates[0] = audio_opts->p_srate; + 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; @@ -1417,10 +1485,10 @@ end: \ CONFIGFS_ATTR(f_uac1_opts_, name) UAC1_ATTRIBUTE(u32, c_chmask); -UAC1_ATTRIBUTE(u32, c_srate); +UAC_RATE1_ATTRIBUTE(c_srate); UAC1_ATTRIBUTE(u32, c_ssize); UAC1_ATTRIBUTE(u32, p_chmask); -UAC1_ATTRIBUTE(u32, p_srate); +UAC_RATE1_ATTRIBUTE(p_srate); UAC1_ATTRIBUTE(u32, p_ssize); UAC1_ATTRIBUTE(u32, req_number); @@ -1490,9 +1558,11 @@ static struct usb_function_instance *f_audio_alloc_inst(void) opts->c_chmask = UAC1_DEF_CCHMASK; opts->c_srate = UAC1_DEF_CSRATE; + opts->c_srates[0] = UAC1_DEF_CSRATE; opts->c_ssize = UAC1_DEF_CSSIZE; opts->p_chmask = UAC1_DEF_PCHMASK; opts->p_srate = UAC1_DEF_PSRATE; + opts->p_srates[0] = UAC1_DEF_PSRATE; opts->p_ssize = UAC1_DEF_PSSIZE; opts->p_mute_present = UAC1_DEF_MUTE_PRESENT; diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 589fae861141..57dce469b46d 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 "uac_common.h" #define UAC1_OUT_EP_MAX_PACKET_SIZE 200 #define UAC1_DEF_CCHMASK 0x3 @@ -30,9 +31,11 @@ struct f_uac1_opts { struct usb_function_instance func_inst; int c_chmask; + int c_srates[UAC_MAX_RATES]; int c_srate; int c_ssize; int p_chmask; + int p_srates[UAC_MAX_RATES]; int p_srate; int p_ssize; @@ -54,5 +57,63 @@ struct f_uac1_opts { struct mutex lock; int refcnt; }; - +#define UAC_RATE1_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; \ + opts->name = num; \ + ret = len; \ + }; \ + \ +end: \ + kfree(split_page); \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(f_uac1_opts_, name) #endif /* __U_UAC1_H */ diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index 58bcb26c7854..f3cae72bcd4c 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -283,10 +283,18 @@ static int audio_bind(struct usb_composite_dev *cdev) #ifndef CONFIG_GADGET_UAC1_LEGACY uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst); uac1_opts->p_chmask = p_chmask; - uac1_opts->p_srate = p_srate; + + for (i = 0; i < p_srates_cnt; ++i) + uac1_opts->p_srates[i] = p_srates[i]; + uac1_opts->p_srate = p_srates[0]; + uac1_opts->p_ssize = p_ssize; uac1_opts->c_chmask = c_chmask; - uac1_opts->c_srate = c_srate; + + for (i = 0; i < c_srates_cnt; ++i) + uac1_opts->c_srates[i] = c_srates[i]; + uac1_opts->c_srate = c_srates[0]; + uac1_opts->c_ssize = c_ssize; uac1_opts->req_number = UAC1_DEF_REQ_NUM; #else /* CONFIG_GADGET_UAC1_LEGACY */ From patchwork Mon Dec 20 08:25:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687325 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8B9FEC433EF for ; Mon, 20 Dec 2021 08:26:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234522AbhLTI0A (ORCPT ); Mon, 20 Dec 2021 03:26:00 -0500 Received: from cable.insite.cz ([84.242.75.189]:41714 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231934AbhLTIZz (ORCPT ); Mon, 20 Dec 2021 03:25:55 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 6197BA1A3D401; Mon, 20 Dec 2021 09:25:54 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988754; bh=5WV1/LEF/ad9TH3t2JcjjaLGwjbTNb/Fnr9SQbHJyLo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m1qpIYOg9oYE9qqneLaHCB87pmOaMQBb9EtkQ54G414WAu4mQpXu1JMYOV+IDE77Z kLArUNJiYf0xPbW6fN2tny6Q8Jw9z/f1ge6KpxPrRm7a/1GILvoncjYihHMyyr27Ku PIGpRxf9boTgFCwBfacRrAgLscUPoo7M1yRJiuz4= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id d4TH05kYS7UZ; Mon, 20 Dec 2021 09:25:49 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 3B467A1A3D405; Mon, 20 Dec 2021 09:25:45 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988745; bh=5WV1/LEF/ad9TH3t2JcjjaLGwjbTNb/Fnr9SQbHJyLo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iH8bWasZ3HfIb3Sq290FiCuvcJUBavqN/FWaN6fi8cYuAfOpxZ65WjVqd3nDGGbs3 NgORUiMYcoZ/bgaq4A3QMz8RxnJz+QaKBYHJ4xMzDu1lYnMVgTb12aSpnu2C+h0MN2 GwnRgD5qIzjvdT2onLvla1swCJmErVj7InsiBZqg= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names Date: Mon, 20 Dec 2021 09:25:36 +0100 Message-Id: <20211220082542.13750-6-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org From: Julian Scheel The gadget no longer supports only one frequency. Therefore USB strings corresponding to the clock sources are renamed from specific Hz value to general names Input clock/Output clock. Signed-off-by: Julian Scheel Signed-off-by: Pavel Hofman --- drivers/usb/gadget/function/f_uac2.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 74e32bb146c7..ef8e39e80523 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -105,14 +105,11 @@ enum { STR_AS_IN_ALT1, }; -static char clksrc_in[8]; -static char clksrc_out[8]; - static struct usb_string strings_fn[] = { [STR_ASSOC].s = "Source/Sink", [STR_IF_CTRL].s = "Topology Control", - [STR_CLKSRC_IN].s = clksrc_in, - [STR_CLKSRC_OUT].s = clksrc_out, + [STR_CLKSRC_IN].s = "Input clock", + [STR_CLKSRC_OUT].s = "Output clock", [STR_USB_IT].s = "USBH Out", [STR_IO_IT].s = "USBD Out", [STR_USB_OT].s = "USBH In", @@ -1058,9 +1055,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) *bma = cpu_to_le32(control); } - snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate); - snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate); - ret = usb_interface_id(cfg, fn); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); From patchwork Mon Dec 20 08:25:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687331 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 952B8C433EF for ; Mon, 20 Dec 2021 08:26:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232506AbhLTI0C (ORCPT ); Mon, 20 Dec 2021 03:26:02 -0500 Received: from cable.insite.cz ([84.242.75.189]:55537 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232470AbhLTIZ7 (ORCPT ); Mon, 20 Dec 2021 03:25:59 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id D2AE5A1A3D413; Mon, 20 Dec 2021 09:25:54 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988754; bh=Miww34CI1HM+vFrrFdUYBLjj1bjD5ruYmQEzP4ZkXpU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jTXAZSPTSIOplEAiPtH2AP1PnDj51auQ32Q0wQfMFgpDrrmOm9bnKi7UYVpPMsDIu 4PwnOnXvRnvLrl+AHE2Fi9Yz0okbC4NlUgZxwMTa7C8aOHz5eaVFXTLx6bh5p2ZzWG nm9srf2uAeX6k0juE2BqmmnKngE3v9/FNyf17+/g= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Brdj_ezlOKxN; Mon, 20 Dec 2021 09:25:49 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 875A9A1A3D406; Mon, 20 Dec 2021 09:25:45 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988745; bh=Miww34CI1HM+vFrrFdUYBLjj1bjD5ruYmQEzP4ZkXpU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IjRMrdWykXTbuzI5cpGx+4+zDk3Z7vlk+SBH1QeYdTS+AXi7xor3hszlRHqoMMBbE yXxRLkuFbpGEzWAE1DOoBBXTpgDkS9HhFGK1kh8Q/KZnbMSAan86urHEU8PNBz62wp aKg/PJKED8O+GDng7SjCFp2pGzlD8VUUwVBpd66A= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Date: Mon, 20 Dec 2021 09:25:37 +0100 Message-Id: <20211220082542.13750-7-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org The Playback/Capture ctl currently reports rate value set by USB control selector UAC2_CS_CONTROL_SAM_FREQ (fixed for UAC1). When the host has stopped playback/capture, the reported value does not change. The gadget side has no information whether the host has started/stopped capture/playback. This patch sets the value reported by the respective rate ctl to zero when the host side has stopped playback/capture. Also, it calls snd_ctl_notify when start/stop occurs, so that a subscribed client can act appropriately. Tests have confirmed that USB hosts change UAC2_CS_CONTROL_SAM_FREQ before switching altsetting to activate playback/capture, resulting in correct order (params->c/p_srate is set to requested rate before u_audio_start_capture/playback is called). The gadget rate notifications are used by user-space audio gadget controller gaudio_ctl https://github.com/pavhofman/gaudio_ctl. Signed-off-by: Pavel Hofman --- drivers/usb/gadget/function/u_audio.c | 46 ++++++++++++--------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index e737a104156d..a6293415c071 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -64,6 +64,7 @@ struct uac_rtd_params { int mute; struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */ + int rep_srate; /* srate reported by snd_kctl_rate */ spinlock_t lock; /* lock for control transfers */ @@ -496,8 +497,6 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep) int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate) { struct uac_params *params = &audio_dev->params; - struct snd_uac_chip *uac = audio_dev->uac; - struct uac_rtd_params *prm = &uac->c_prm; int i; dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); @@ -516,8 +515,6 @@ EXPORT_SYMBOL_GPL(u_audio_set_capture_srate); int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate) { - struct snd_uac_chip *uac = audio_dev->uac; - struct uac_rtd_params *prm = &uac->p_prm; struct uac_params *params = &audio_dev->params; int i; @@ -535,6 +532,18 @@ int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate) } EXPORT_SYMBOL_GPL(u_audio_set_playback_srate); +static void set_reported_srate(struct uac_rtd_params *prm, int srate) +{ + struct snd_kcontrol *kctl = prm->snd_kctl_rate; + + if (prm->rep_srate != srate) { + prm->rep_srate = srate; + snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE, + &kctl->id); + pr_debug("Setting '%s' to %d", kctl->id.name, srate); + } +} + int u_audio_start_capture(struct g_audio *audio_dev) { struct snd_uac_chip *uac = audio_dev->uac; @@ -574,6 +583,8 @@ int u_audio_start_capture(struct g_audio *audio_dev) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); } + set_reported_srate(&uac->c_prm, params->c_srate); + ep_fback = audio_dev->in_ep_fback; if (!ep_fback) return 0; @@ -619,6 +630,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev) { struct snd_uac_chip *uac = audio_dev->uac; + set_reported_srate(&uac->c_prm, 0); if (audio_dev->in_ep_fback) free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); free_ep(&uac->c_prm, audio_dev->out_ep); @@ -691,6 +703,8 @@ int u_audio_start_playback(struct g_audio *audio_dev) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); } + set_reported_srate(&uac->p_prm, params->p_srate); + return 0; } EXPORT_SYMBOL_GPL(u_audio_start_playback); @@ -699,6 +713,7 @@ void u_audio_stop_playback(struct g_audio *audio_dev) { struct snd_uac_chip *uac = audio_dev->uac; + set_reported_srate(&uac->p_prm, 0); free_ep(&uac->p_prm, audio_dev->in_ep); } EXPORT_SYMBOL_GPL(u_audio_stop_playback); @@ -1001,19 +1016,6 @@ static int get_max_srate(const int *srates) return max_srate; } -static int get_min_srate(const int *srates) -{ - int i, min_srate = INT_MAX; - - for (i = 0; i < UAC_MAX_RATES; i++) { - if (srates[i] == 0) - break; - if (srates[i] < min_srate) - min_srate = srates[i]; - } - return min_srate; -} - static int uac_pcm_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1030,7 +1032,7 @@ static int uac_pcm_rate_info(struct snd_kcontrol *kcontrol, srates = params->c_srates; else srates = params->p_srates; - uinfo->value.integer.min = get_min_srate(srates); + uinfo->value.integer.min = 0; uinfo->value.integer.max = get_max_srate(srates); return 0; } @@ -1039,14 +1041,8 @@ static int uac_pcm_rate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); - struct snd_uac_chip *uac = prm->uac; - struct g_audio *audio_dev = uac->audio_dev; - struct uac_params *params = &audio_dev->params; - if (prm == &uac->c_prm) - ucontrol->value.integer.value[0] = params->c_srate; - else - ucontrol->value.integer.value[0] = params->p_srate; + ucontrol->value.integer.value[0] = prm->rep_srate; return 0; } From patchwork Mon Dec 20 08:25:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687327 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4AF24C433FE for ; Mon, 20 Dec 2021 08:26:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232475AbhLTI0B (ORCPT ); Mon, 20 Dec 2021 03:26:01 -0500 Received: from cable.insite.cz ([84.242.75.189]:60155 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232390AbhLTIZ4 (ORCPT ); Mon, 20 Dec 2021 03:25:56 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 8AF85A1A3D404; Mon, 20 Dec 2021 09:25:54 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988754; bh=4cg2A1gETsfLPgX1bNxerCaugEwwxGd1dUXYkPhu0uI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dOJUE4M8tEP8Vm+UmqcXlSzHH140B8isFDBDJ32tG+vaVDY6XLQwSFAPdc9v0aljs V4DNsRH+eginqkas8VVXfKGuKlvXXDlNa9r3P9/WQHJbZk608y37nCmH8AyZG86yhO 1kbGa6YmXbUkvds1KJjhQP43tz/3nqxf9GMaAtdA= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id IEpcfxYyrLLF; Mon, 20 Dec 2021 09:25:49 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id D829FA1A3D407; Mon, 20 Dec 2021 09:25:45 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988746; bh=4cg2A1gETsfLPgX1bNxerCaugEwwxGd1dUXYkPhu0uI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VhJJFjy97dHyxWuOhDo6W2Y+F/7+6gxJb0SEuNqDRAaAQ5719qlnQiEVtAqR1KS93 zIrB23EmioHeb72QPNJ3nz0FsLPF7LZaj0hhsd06GxZSveE2x1hqwNDMdBprJEYKOb QXF9ZTxeewmBXz0gi9TPNArFOidqqYrmq1uWPOKQ= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop Date: Mon, 20 Dec 2021 09:25:38 +0100 Message-Id: <20211220082542.13750-8-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org When the USB host stops capture/playback, the corresponding PCM substream (if activated and running) is stopped and drained. Signed-off-by: Pavel Hofman --- drivers/usb/gadget/function/u_audio.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index a6293415c071..9dbce51c2eb7 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -544,6 +544,20 @@ static void set_reported_srate(struct uac_rtd_params *prm, int srate) } } +static void stop_substream(struct uac_rtd_params *prm) +{ + unsigned long _flags; + struct snd_pcm_substream *substream; + + substream = prm->ss; + if (substream) { + snd_pcm_stream_lock_irqsave(substream, _flags); + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_DRAINING); + snd_pcm_stream_unlock_irqrestore(substream, _flags); + } +} + int u_audio_start_capture(struct g_audio *audio_dev) { struct snd_uac_chip *uac = audio_dev->uac; @@ -630,6 +644,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev) { struct snd_uac_chip *uac = audio_dev->uac; + stop_substream(&uac->c_prm); set_reported_srate(&uac->c_prm, 0); if (audio_dev->in_ep_fback) free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); @@ -713,6 +728,7 @@ void u_audio_stop_playback(struct g_audio *audio_dev) { struct snd_uac_chip *uac = audio_dev->uac; + stop_substream(&uac->p_prm); set_reported_srate(&uac->p_prm, 0); free_ep(&uac->p_prm, audio_dev->in_ep); } From patchwork Mon Dec 20 08:25:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687329 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D72FCC433F5 for ; Mon, 20 Dec 2021 08:26:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231318AbhLTI0C (ORCPT ); Mon, 20 Dec 2021 03:26:02 -0500 Received: from cable.insite.cz ([84.242.75.189]:41707 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232447AbhLTIZ7 (ORCPT ); Mon, 20 Dec 2021 03:25:59 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 9670DA1A3D410; Mon, 20 Dec 2021 09:25:54 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988754; bh=FIayO3i7Y2XcnMYAxSNxAfnmPI914JgdzjyviD40hgA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mMSToaOhvZyWpYqWEb0GhndrE7YdmylQJMoV8gNFe+MvJxx21t24lGQUAh8wOQr0C fdQVGWWoE7YTz0XuWfuMSwPLBN2Cz9WVSnA00vmBcr/Xgut5M96T0N9CI9wA35k/3H gSXK95rH4+MDaERzjDzaM9lkA3mvYTx/t6wAYkxg= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 8QuluY1CkUYA; Mon, 20 Dec 2021 09:25:49 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 384ADA1A3D408; Mon, 20 Dec 2021 09:25:46 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988746; bh=FIayO3i7Y2XcnMYAxSNxAfnmPI914JgdzjyviD40hgA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jPsGc8I+MZw7dtpXGhIH0rJ6GU3ifUqZlW+NyGPDX7RoSc/i/mKIO/HBzAGFbCwEJ MVss0NjIH/r/+YzLqR7W0PSAXvdmja0qV/GhK/u8oz1SiAtDsHc60FIZva3XlBl1A3 8JnQaBmnlfavWpYjvOwp5e2+t11nvYFPV6gLjEsA= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 08/11] usb: gadget: u_audio: Adding suspend call Date: Mon, 20 Dec 2021 09:25:39 +0100 Message-Id: <20211220082542.13750-9-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Adding exported method u_audio_suspend which stops capture and playback PCM streams and notifies about zero sample rates. The method does not free any resources. Signed-off-by: Pavel Hofman --- drivers/usb/gadget/function/u_audio.c | 11 +++++++++++ drivers/usb/gadget/function/u_audio.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 9dbce51c2eb7..58e18952953b 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -734,6 +734,17 @@ void u_audio_stop_playback(struct g_audio *audio_dev) } EXPORT_SYMBOL_GPL(u_audio_stop_playback); +void u_audio_suspend(struct g_audio *audio_dev) +{ + struct snd_uac_chip *uac = audio_dev->uac; + + stop_substream(&uac->p_prm); + stop_substream(&uac->c_prm); + set_reported_srate(&uac->p_prm, 0); + set_reported_srate(&uac->c_prm, 0); +} +EXPORT_SYMBOL_GPL(u_audio_suspend); + int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val) { struct snd_uac_chip *uac = audio_dev->uac; diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h index 76b5b8169444..74e7519d9497 100644 --- a/drivers/usb/gadget/function/u_audio.h +++ b/drivers/usb/gadget/function/u_audio.h @@ -128,4 +128,6 @@ int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val); int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val); int u_audio_set_mute(struct g_audio *g_audio, int playback, int val); +void u_audio_suspend(struct g_audio *g_audio); + #endif /* __U_AUDIO_H */ From patchwork Mon Dec 20 08:25:40 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687333 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 14FB7C433FE for ; Mon, 20 Dec 2021 08:26:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232470AbhLTI0D (ORCPT ); Mon, 20 Dec 2021 03:26:03 -0500 Received: from cable.insite.cz ([84.242.75.189]:46549 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234342AbhLTI0A (ORCPT ); Mon, 20 Dec 2021 03:26:00 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id A3BC7A1A3D403; Mon, 20 Dec 2021 09:25:55 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988755; bh=Zb6YZxBSNRSm20rGOOx/GOnHDVc+cQZuyDkHmzq8FeY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qgJkyn9wsU/scZR67ztUuyBbqBdTOB4RTGiAG85mMswPFmagcFCJ+dK20lx0R7oTY F2aAEFkdZozn/as+zgLwNVGw/0LOOdIw6f5zLB/aLOf2gL8GjEZn2hPUHny/SrpEkR +xyqQZpm6/cBEq8NzUqN3Of9RAe41a2A7zJdkykc= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id HD1sluq2Bp-3; Mon, 20 Dec 2021 09:25:50 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 8AEFAA1A3D409; Mon, 20 Dec 2021 09:25:46 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988746; bh=Zb6YZxBSNRSm20rGOOx/GOnHDVc+cQZuyDkHmzq8FeY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZsFgYwZssNwqOWQxxz4+FLEgdyBklfbv5pcf+4DLZr9jcUbslFpdbHZ7VVTvs5dLh exIZS23vbxI7D5hIe+3dbaUE+IHJveEpKxvpIfKD9An5+EMCvtpFN4H5aVZgn8aCRG 80xKMfx0ihbNb3Wbz13VcXh5BtSa8zygISUzOt/k= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 09/11] usb: gadget: f_uac2: Adding suspend callback Date: Mon, 20 Dec 2021 09:25:40 +0100 Message-Id: <20211220082542.13750-10-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org When USB cable gets disconnected, the undergoing playback/capture stalls, without any notification to u_audio about the change. Experiments with a dwc2 gadget revealed that Suspend interrupt is thrown at cable disconnection, which the gadget framework translates to calling suspend callback of a function, if it is defined. This patch adds the suspend callback to f_uac2 function, calling corresponding method of u_audio in order to stop the respective PCM streams and to notify subscribed clients at cable disconnection. Signed-off-by: Pavel Hofman --- drivers/usb/gadget/function/f_uac2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index ef8e39e80523..984f757de5a4 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1430,6 +1430,14 @@ afunc_disable(struct usb_function *fn) usb_ep_disable(uac2->int_ep); } +static void +afunc_suspend(struct usb_function *fn) +{ + struct f_uac2 *uac2 = func_to_uac2(fn); + + u_audio_suspend(&uac2->g_audio); +} + static int in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) { @@ -2047,6 +2055,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi) uac2->g_audio.func.set_alt = afunc_set_alt; uac2->g_audio.func.get_alt = afunc_get_alt; uac2->g_audio.func.disable = afunc_disable; + uac2->g_audio.func.suspend = afunc_suspend; uac2->g_audio.func.setup = afunc_setup; uac2->g_audio.func.free_func = afunc_free; From patchwork Mon Dec 20 08:25:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687335 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 86B27C4332F for ; Mon, 20 Dec 2021 08:26:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232760AbhLTI0E (ORCPT ); Mon, 20 Dec 2021 03:26:04 -0500 Received: from cable.insite.cz ([84.242.75.189]:47625 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234640AbhLTI0B (ORCPT ); Mon, 20 Dec 2021 03:26:01 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id C1A80A1A3D405; Mon, 20 Dec 2021 09:25:55 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988755; bh=foDRz/w+gGEj4ZWnc3EnCugl8u/3ze6aN+VG/9SZ+HQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=et5xwcvhmKNaw1Rw+gTuWOw2rGy9AeQhkpE4Sf95qNRw2CXyOCOyPDb7/cvfbppq+ +MkBky5GJWFODR/JQvO5eAZyO3q+TSLpxbmzPnElr4jEvBQD0/umlAiREtIs4cNcKy 2c95pT3dlczjjzPM9tKHkbURky4lq+KRZE8uayDg= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 7BNIyXqMVn-m; Mon, 20 Dec 2021 09:25:50 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id DA27EA1A3D40A; Mon, 20 Dec 2021 09:25:46 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988747; bh=foDRz/w+gGEj4ZWnc3EnCugl8u/3ze6aN+VG/9SZ+HQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jiKg8gHQ1g6ivxIk2NEzPDSC3RzgwfyfNhXcq9ffH5VIXEwRAvRktqffW+o/quVzi pwfmA1/0GQZp+5ojRFLHmCwCB14dfO59taEmLT65FwtFVlOhA8oJOLbrC3oEBIVTGl Bne6wMPErVlcRxQHf0fadVVDegSMQMg6pjZhjyDw= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 10/11] usb: gadget: f_uac1: Adding suspend callback Date: Mon, 20 Dec 2021 09:25:41 +0100 Message-Id: <20211220082542.13750-11-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Adding suspend callback to f_uac1 function, calling corresponding method of u_audio in order to stop the respective PCM streams and to notify subscribed clients about the stop. Signed-off-by: Pavel Hofman --- drivers/usb/gadget/function/f_uac1.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 7fd2b5580374..3eb16f1baf90 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -961,6 +961,14 @@ static void f_audio_disable(struct usb_function *f) usb_ep_disable(uac1->int_ep); } +static void +f_audio_suspend(struct usb_function *f) +{ + struct f_uac1 *uac1 = func_to_uac1(f); + + u_audio_suspend(&uac1->g_audio); +} + /*-------------------------------------------------------------------------*/ static struct uac_feature_unit_descriptor *build_fu_desc(int chmask) { @@ -1634,6 +1642,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) uac1->g_audio.func.get_alt = f_audio_get_alt; uac1->g_audio.func.setup = f_audio_setup; uac1->g_audio.func.disable = f_audio_disable; + uac1->g_audio.func.suspend = f_audio_suspend; uac1->g_audio.func.free_func = f_audio_free; return &uac1->g_audio.func; From patchwork Mon Dec 20 08:25:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 12687337 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 049C4C43217 for ; Mon, 20 Dec 2021 08:26:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234304AbhLTI0E (ORCPT ); Mon, 20 Dec 2021 03:26:04 -0500 Received: from cable.insite.cz ([84.242.75.189]:43695 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234689AbhLTI0B (ORCPT ); Mon, 20 Dec 2021 03:26:01 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id E1132A1A3D409; Mon, 20 Dec 2021 09:25:56 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988756; bh=XQRAlk2+V8wj72YNBcUQRGUUNWyTfRhGHm58nuGGSeE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Hf1/WLGxAA/WxjpgiwerK81Sf4Ff/wehuNRgXW9aasEJVJ4EUxeHAfOYlovWGBa/E vuki55ZGHaF+gTnGH7rtqJDlYd1pKt9LLmuFCeLxe0snEoyRbN09qUQATM4JKTzsNY u9jRvElB422B5a5ch4o8x9DJ/cTJU4zkJ18bE2iY= Received: from cable.insite.cz ([84.242.75.189]) by localhost (server.insite.cz [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id bzeWrNhfyeWc; Mon, 20 Dec 2021 09:25:50 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 34B8DA1A3D40B; Mon, 20 Dec 2021 09:25:47 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1639988747; bh=XQRAlk2+V8wj72YNBcUQRGUUNWyTfRhGHm58nuGGSeE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mX0iXQOql1N5euum0cFXvI5+xD6FCHHTcf7Qzo7V9aFPYsYL8qYBgKvuHkCgos/fr +isiDnW/uu7iUE+wODP9Z9K9gV4vIvxz7eo4jjYC/wm73AC2VVGSw/gtyysNXJw7kv u5uRvsU8iVe5zbWgOzt2wnrmiubuAemSwqW5jOC0= From: Pavel Hofman To: linux-usb@vger.kernel.org Cc: Pavel Hofman , Ruslan Bilovol , Felipe Balbi , Jerome Brunet , Julian Scheel , Greg Kroah-Hartman Subject: [PATCH 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS Date: Mon, 20 Dec 2021 09:25:42 +0100 Message-Id: <20211220082542.13750-12-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220082542.13750-1-pavel.hofman@ivitera.com> References: <20211220082542.13750-1-pavel.hofman@ivitera.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org So far bInterval for HS and SS was fixed at 4, disallowing faster samplerates. The patch determines the largest bInterval (4 to 1) for which the required bandwidth of the max samplerate fits the max allowed packet size. If the required bandwidth exceeds max bandwidth for single-packet mode (ep->mc=1), bInterval is left at 1. The FS mode is left at fixed bInterval=1. Signed-off-by: Pavel Hofman --- drivers/usb/gadget/function/f_uac2.c | 90 +++++++++++++++++----------- 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 984f757de5a4..e72f6f42e1b7 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -333,7 +333,7 @@ static struct usb_endpoint_descriptor fs_epout_desc = { .bEndpointAddress = USB_DIR_OUT, /* .bmAttributes = DYNAMIC */ /* .wMaxPacketSize = DYNAMIC */ - .bInterval = 1, + /* .bInterval = DYNAMIC */ }; static struct usb_endpoint_descriptor hs_epout_desc = { @@ -342,7 +342,7 @@ static struct usb_endpoint_descriptor hs_epout_desc = { /* .bmAttributes = DYNAMIC */ /* .wMaxPacketSize = DYNAMIC */ - .bInterval = 4, + /* .bInterval = DYNAMIC */ }; static struct usb_endpoint_descriptor ss_epout_desc = { @@ -352,7 +352,7 @@ static struct usb_endpoint_descriptor ss_epout_desc = { .bEndpointAddress = USB_DIR_OUT, /* .bmAttributes = DYNAMIC */ /* .wMaxPacketSize = DYNAMIC */ - .bInterval = 4, + /* .bInterval = DYNAMIC */ }; static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = { @@ -467,7 +467,7 @@ static struct usb_endpoint_descriptor fs_epin_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, /* .wMaxPacketSize = DYNAMIC */ - .bInterval = 1, + /* .bInterval = DYNAMIC */ }; static struct usb_endpoint_descriptor hs_epin_desc = { @@ -476,7 +476,7 @@ static struct usb_endpoint_descriptor hs_epin_desc = { .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, /* .wMaxPacketSize = DYNAMIC */ - .bInterval = 4, + /* .bInterval = DYNAMIC */ }; static struct usb_endpoint_descriptor ss_epin_desc = { @@ -486,7 +486,7 @@ static struct usb_endpoint_descriptor ss_epin_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, /* .wMaxPacketSize = DYNAMIC */ - .bInterval = 4, + /* .bInterval = DYNAMIC */ }; static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = { @@ -659,29 +659,11 @@ static int get_max_srate(const int *srates) return max_srate; } -static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, - struct usb_endpoint_descriptor *ep_desc, - enum usb_device_speed speed, bool is_playback) +static int get_max_bw_for_binterval(const struct f_uac2_opts *uac2_opts, + u8 binterval, unsigned int factor, bool is_playback) { int chmask, srate, ssize; - u16 max_size_bw, max_size_ep; - unsigned int factor; - - switch (speed) { - case USB_SPEED_FULL: - max_size_ep = 1023; - factor = 1000; - break; - - case USB_SPEED_HIGH: - case USB_SPEED_SUPER: - max_size_ep = 1024; - factor = 8000; - break; - - default: - return -EINVAL; - } + u16 max_size_bw; if (is_playback) { chmask = uac2_opts->p_chmask; @@ -699,14 +681,52 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, 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 << (ep_desc->bInterval - 1)))); + (DIV_ROUND_UP(srate, factor / (1 << (binterval - 1)))); } else { // adding 1 frame provision for Win10 max_size_bw = num_channels(chmask) * ssize * - (DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))) + 1); + (DIV_ROUND_UP(srate, factor / (1 << (binterval - 1))) + 1); } + return max_size_bw; +} + +static int set_ep_max_packet_size_bint(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 binterval; + + switch (speed) { + case USB_SPEED_FULL: + max_size_ep = 1023; + // fixed + binterval = 1; + max_size_bw = get_max_bw_for_binterval(uac2_opts, binterval, 1000, + is_playback); + break; + + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + max_size_ep = 1024; + // checking bInterval from 4 (= 1ms) to 1 if the required bandwidth fits + for (binterval = 4; binterval > 0; --binterval) { + max_size_bw = get_max_bw_for_binterval(uac2_opts, binterval, 8000, + is_playback); + if (max_size_bw <= max_size_ep) { + // found largest bInterval/max_size_bw fitting max_size_ep + break; + } + } + break; + + default: + return -EINVAL; + } + ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw, max_size_ep)); + ep_desc->bInterval = binterval; return 0; } @@ -1119,42 +1139,42 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) } /* Calculate wMaxPacketSize according to audio bandwidth */ - ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL, + ret = set_ep_max_packet_size_bint(uac2_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(uac2_opts, &fs_epout_desc, USB_SPEED_FULL, + ret = set_ep_max_packet_size_bint(uac2_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(uac2_opts, &hs_epin_desc, USB_SPEED_HIGH, + ret = set_ep_max_packet_size_bint(uac2_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(uac2_opts, &hs_epout_desc, USB_SPEED_HIGH, + ret = set_ep_max_packet_size_bint(uac2_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(uac2_opts, &ss_epin_desc, USB_SPEED_SUPER, + ret = set_ep_max_packet_size_bint(uac2_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(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER, + ret = set_ep_max_packet_size_bint(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER, false); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);