From patchwork Mon Dec 20 21:11:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526795 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 8DFF3C433FE for ; Mon, 20 Dec 2021 21:11:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230324AbhLTVLk (ORCPT ); Mon, 20 Dec 2021 16:11:40 -0500 Received: from cable.insite.cz ([84.242.75.189]:54411 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230330AbhLTVLj (ORCPT ); Mon, 20 Dec 2021 16:11:39 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 1FDD3A1A3D40C; Mon, 20 Dec 2021 22:11:38 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034698; bh=4OReWYNWtGWkkIdlPU08sx5H+R+qLS85r9ZPvLOz62Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nfnEwSO7yHZSxgXpQenET45t9tTFGJM5jfY/75nVRZ3UVcH6E7bbbn07Hq+XKdLH6 kCxMjKy2dKol2x0GPFS0m7O7ZutABmmpCQtY6dLmUpDprP0RT3I/qwlsjvVVIHkznL ZQ7Ms2hDl8UYdwN/GIJ5TsFTRO+d0yyS2+MIyla0= 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 WKDC1fHDHq0a; Mon, 20 Dec 2021 22:11:32 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id E0AA1A1A3D401; Mon, 20 Dec 2021 22:11:31 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034692; bh=4OReWYNWtGWkkIdlPU08sx5H+R+qLS85r9ZPvLOz62Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=V/aK+/ZbdVL7UBln1jBi4OiMjqif/QNtW0yQsO/U9qKE/t8Lz6EvJSsIAXvKQ5pWe xOuR17NFM3DRqD+5MdWmFaHkZQG9ge2kgZCqWx41Az9wx8JfaHl4K+xjfA7Fey1Usa HvMnFtkkjFrWqnVaSXByk2mQNgWzzJfNoNt1ODpY= 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 v2 01/11] usb: gadget: u_audio: Subdevice 0 for capture ctls Date: Mon, 20 Dec 2021 22:11:20 +0100 Message-Id: <20211220211130.88590-2-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 21:11:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526274 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 91C4BC433EF for ; Mon, 20 Dec 2021 21:11:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230355AbhLTVLm (ORCPT ); Mon, 20 Dec 2021 16:11:42 -0500 Received: from cable.insite.cz ([84.242.75.189]:49174 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230331AbhLTVLk (ORCPT ); Mon, 20 Dec 2021 16:11:40 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 28B46A1A3D40D; Mon, 20 Dec 2021 22:11:38 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034698; bh=ahBtijJ5iunVsC4K1uKqZgjRN0ieDvzhhXBg11RqPzo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=V1iaEJmtHZpPw6yPB08wBu2inf9WQP29k5v6jeERnrEGq1zm6z7gqn1EKNI8wX0/B jp/UkWh58GQkhx8+H3SnFDFG3dnXQNyC6sbs/8CzOagP9uU52sQOWRGRGkHkT693Uc 7d18q/T2DEfXLJB12IBcK+Cy53cLQQNMkdr7AtoA= 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 RMBUC8SM9_98; Mon, 20 Dec 2021 22:11:32 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 46D91A1A3D402; Mon, 20 Dec 2021 22:11:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034692; bh=ahBtijJ5iunVsC4K1uKqZgjRN0ieDvzhhXBg11RqPzo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EUpk3lbyfqQberL6kYCB+TvvJnXys7kgwZTbw585IRVcSpP4idCa3kvbPcavuQpA1 iLVPf7uYIbSPFvMuhz1kyNwhFm9WQQp+IP0HvzNhq0znUNcK23Lpz36UVndvLEL5ug 5Z986codJwGCeXYQPwaqnzdAX5bO9hhzuiZQ+UcE= 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 v2 02/11] usb: gadget: u_audio: Support multiple sampling rates Date: Mon, 20 Dec 2021 22:11:21 +0100 Message-Id: <20211220211130.88590-3-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 21:11:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526273 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 2D652C4332F for ; Mon, 20 Dec 2021 21:11:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230361AbhLTVLn (ORCPT ); Mon, 20 Dec 2021 16:11:43 -0500 Received: from cable.insite.cz ([84.242.75.189]:54560 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230330AbhLTVLm (ORCPT ); Mon, 20 Dec 2021 16:11:42 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 5D1F2A1A3D400; Mon, 20 Dec 2021 22:11:39 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034699; bh=Xne0b8n0z/LWfnow/J1Xm6QDToOHuC/GquzUfE+nK6Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SIfjsJbd6DTIg89/1ZAjccHrNTsRqsQ7iS5L5Alm0LpwkLNuwT3gSgLJ0Ulr99AbR TuTb89QYiyLP0fI3n2YG62KiNz/1kllXIWfzVP6n6IdMZf2dTwgCjm19AGQezuur7Q tYCsK6MFld9lvGPzc77hlAXKaRY5yT+CUqKhbUbY= 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 oDLp4Sa6YUE4; Mon, 20 Dec 2021 22:11:33 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 98DF9A1A3D403; Mon, 20 Dec 2021 22:11:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034692; bh=Xne0b8n0z/LWfnow/J1Xm6QDToOHuC/GquzUfE+nK6Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iJpCjhOTX1fsNob1Om+CPWBGVuTQDD/BsRzg61F8iDQqzCq0JaBZoFCuKnMOO6fxK XS+fyp4BgvPcN2zyqq0TZcmp9m76C3MAkEH2E30dXMWXlFJTTZIx54CmuxeMGqyOOx YB1yT3PtRPCOIy1C5clISnsf3DTMS35oFKiko/nk= 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 v2 03/11] usb: gadget: f_uac2: Support multiple sampling rates Date: Mon, 20 Dec 2021 22:11:22 +0100 Message-Id: <20211220211130.88590-4-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 21:11:23 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526793 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 1B8E4C433EF for ; Mon, 20 Dec 2021 21:11:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230341AbhLTVLo (ORCPT ); Mon, 20 Dec 2021 16:11:44 -0500 Received: from cable.insite.cz ([84.242.75.189]:36665 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230345AbhLTVLm (ORCPT ); Mon, 20 Dec 2021 16:11:42 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id C7DBAA1A3D402; Mon, 20 Dec 2021 22:11:39 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034699; bh=vx4SmD1oiBOte6EW4yc3j/BqCuKPaYSVyu2JwOsbzRU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LmoQYLFh6m3/RKZk65ZIUcOGjt9JqNGuYwDXXv1KP61jRqhWhIkNDJQFWhXbWzB8X 6S89GfwltAVq3SnMHG3RBu57IqGwo2RAAL1///2aGiC5ihiE73t8nazmPB4v5yXTR6 LPvd2412pC/r933OR35CBabt2qViqMyRXP9rRnjk= 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 AGig1YAUaFom; Mon, 20 Dec 2021 22:11:33 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 0A82FA1A3D404; Mon, 20 Dec 2021 22:11:33 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034693; bh=vx4SmD1oiBOte6EW4yc3j/BqCuKPaYSVyu2JwOsbzRU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WeLKY6ckLqCWKLeDG/cQl6OqM8lJxXw8oJg9GWe192Tc55MEJTgpo6ZzuiZCC92zY XxkMXB96DNxeYrIOT7m3mT8aTjBFdveLoOuStlRuAhMKi3OOChwaKLwORpWN6wRrDK 9acXS5aD/XywDk6UToL0BptSyttu+F/5HHpnRTno= 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 v2 04/11] usb: gadget: f_uac1: Support multiple sampling rates Date: Mon, 20 Dec 2021 22:11:23 +0100 Message-Id: <20211220211130.88590-5-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 --- .../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 | 26 ++-- 5 files changed, 176 insertions(+), 35 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..9a0071cdecb6 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -61,9 +61,10 @@ module_param(p_chmask, uint, S_IRUGO); MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); /* Playback Default 48 KHz */ -static int p_srate = UAC1_DEF_PSRATE; -module_param(p_srate, uint, S_IRUGO); -MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); +static int p_srates[UAC_MAX_RATES] = {UAC1_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 = UAC1_DEF_PSSIZE; @@ -76,9 +77,10 @@ module_param(c_chmask, uint, S_IRUGO); MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); /* Capture Default 48 KHz */ -static int c_srate = UAC1_DEF_CSRATE; -module_param(c_srate, uint, S_IRUGO); -MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); +static int c_srates[UAC_MAX_RATES] = {UAC1_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 = UAC1_DEF_CSSIZE; @@ -283,10 +285,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 21:11:24 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526272 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 D29A2C433F5 for ; Mon, 20 Dec 2021 21:11:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230394AbhLTVLp (ORCPT ); Mon, 20 Dec 2021 16:11:45 -0500 Received: from cable.insite.cz ([84.242.75.189]:58896 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230348AbhLTVLp (ORCPT ); Mon, 20 Dec 2021 16:11:45 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 7CDBBA1A3D40E; Mon, 20 Dec 2021 22:11:43 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034703; bh=5WV1/LEF/ad9TH3t2JcjjaLGwjbTNb/Fnr9SQbHJyLo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gbas27KOflMqf4ouGXMEGIwiP4hZ0N6V50X3DMf3G0bj2d/BCLREvWr9dJMfJFzLs gzx0Fq5eWXzdYwyodzUvqLFekF3mG5xxojssFJQV7BaPGFAI4BLLZrVkD1CKZL/Dqm fTXH/1bkW0vHAYfi0ejOzDQr2YcdAs6GChb8Va7w= 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 F5N4q-MiiQTM; Mon, 20 Dec 2021 22:11:38 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 78A50A1A3D405; Mon, 20 Dec 2021 22:11:33 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034693; bh=5WV1/LEF/ad9TH3t2JcjjaLGwjbTNb/Fnr9SQbHJyLo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BAo9MII0g31/Wamy9wt2jBAvPtHPIqSQdNT36IRZYefr9DDSuXAbE4PO8i6rj0RYu rDrk78yKOZE+uMOokJeXJHKUkLSxsryexhoAOhNJn5UOq0bpQOWIcCqbjA4+4S+4Uy koW/+RYtNKL2/og+93lG7jLBkiRo5Tfd9sUyXa4k= 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 v2 05/11] usb: gadget: f_uac2: Renaming Clock Sources to fixed names Date: Mon, 20 Dec 2021 22:11:24 +0100 Message-Id: <20211220211130.88590-6-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 21:11:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526790 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 75686C433EF for ; Mon, 20 Dec 2021 21:11:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230348AbhLTVLu (ORCPT ); Mon, 20 Dec 2021 16:11:50 -0500 Received: from cable.insite.cz ([84.242.75.189]:56757 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230393AbhLTVLq (ORCPT ); Mon, 20 Dec 2021 16:11:46 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id BE9F3A1A3D401; Mon, 20 Dec 2021 22:11:44 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034704; bh=Miww34CI1HM+vFrrFdUYBLjj1bjD5ruYmQEzP4ZkXpU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qhylWF/GB1YuvigMX6cERujvt8OlvKl/2hDat+V+/rBQQx2f5+tIy8gnB6vOFiExe MY5EohgGNstvYznw3mj92BdnaG0BSgEuf+5zGGAi1PJaYv45RBu361/pZiyLi1bWl6 Rx0sXouQUq0aWRfJn5jVP63tnN7yqPu8cjAY3Ti4= 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 zx_79Dp-dtlK; Mon, 20 Dec 2021 22:11:38 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id C478BA1A3D406; Mon, 20 Dec 2021 22:11:33 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034694; bh=Miww34CI1HM+vFrrFdUYBLjj1bjD5ruYmQEzP4ZkXpU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mumJvQlrAVR8qbsYCY55zwIB44MwJA44s+lzFZ2YysmTdPNhyjU91G5DV1+fez9DT fL+bjTsorr59R9JCt3WUML4IkBsfSQ9txLblZ6jU5aWhSUc8DSz2ThdgGJdXuxB3d1 IAWBbejMjIig/B3DJCNUWMWk4exne0Hb5X0QFJ8c= 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 v2 06/11] usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped) Date: Mon, 20 Dec 2021 22:11:25 +0100 Message-Id: <20211220211130.88590-7-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 21:11:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526271 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 7A804C4332F for ; Mon, 20 Dec 2021 21:11:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230371AbhLTVLr (ORCPT ); Mon, 20 Dec 2021 16:11:47 -0500 Received: from cable.insite.cz ([84.242.75.189]:47382 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230356AbhLTVLp (ORCPT ); Mon, 20 Dec 2021 16:11:45 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 7B689A1A3D404; Mon, 20 Dec 2021 22:11:43 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034703; bh=4cg2A1gETsfLPgX1bNxerCaugEwwxGd1dUXYkPhu0uI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IpHkUEWxM0+nYTzvxHhJOQ6XoP122KnAbLGwf9XFrY+AvVRpLys+zI8iYCfUUoLbb E7DQD7wD+mxa7mwzFDxKWBAQ87/9VQ26HmSBPNU2WZpXb6IA2u3440sUTULrdM6mQn /7ecYKYDDvKqbqrUdZgce2/2PhdgxkmxjcAn7J9I= 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 YrtnCXODN8cw; Mon, 20 Dec 2021 22:11:38 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 22A92A1A3D407; Mon, 20 Dec 2021 22:11:34 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034694; bh=4cg2A1gETsfLPgX1bNxerCaugEwwxGd1dUXYkPhu0uI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MIImZAB1MKlqFKQHGcXarZCl0qY7ob9+4wQLy0lW8e2ACjXx0IxH5ZC4w2bR5ZwAb B5Ig7nBwZAhYgN/dBiD1Z9L7BQccRZt8Oqmo7n/ETAkyFas20c7ml7dFsxQFQl9DiO bHnxZ1dJ5WSVPT27vR4mbaO7TC/xa3V+bwMrDh7M= 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 v2 07/11] usb: gadget: u_audio: Stopping PCM substream at capture/playback stop Date: Mon, 20 Dec 2021 22:11:26 +0100 Message-Id: <20211220211130.88590-8-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 21:11:27 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526792 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 A0790C433FE for ; Mon, 20 Dec 2021 21:11:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230375AbhLTVLq (ORCPT ); Mon, 20 Dec 2021 16:11:46 -0500 Received: from cable.insite.cz ([84.242.75.189]:45513 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230368AbhLTVLp (ORCPT ); Mon, 20 Dec 2021 16:11:45 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id 865FEA1A3D40F; Mon, 20 Dec 2021 22:11:43 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034703; bh=FIayO3i7Y2XcnMYAxSNxAfnmPI914JgdzjyviD40hgA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=h0ynDDHJxBcI0BlvhgF+oUQaOh/PVA1ezviB2KyuQGmOBZQZ+qsOeWpkL34JGAd05 m/Wx9WTI2n/T2XRW9RFJMLHRDw35NzNmX3pr5mN4FiKIaI5z1vSps9L0/YsNK2sIyu GgfP8DPLGTg5g/MYJmubYD78EJGtElZUIJ6YXP6M= 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 BCeKtJ5HIusT; Mon, 20 Dec 2021 22:11:38 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 7E000A1A3D408; Mon, 20 Dec 2021 22:11:34 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034694; bh=FIayO3i7Y2XcnMYAxSNxAfnmPI914JgdzjyviD40hgA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZYQFv2Tn7Q2rBN6DLsOozRGu4mkO7DVW7zwtC90cSP15lvGMCHlPycrSonQdHz29M XvcoD9tD59cDmbA4UgG/lE/FBz3ChfsmE4CCd4EHPdtb/hg0JVXI9wkS7aRGs/aMFz 9KYBE53N0rKNy2CF0bqv4JBwSSocbfNcTEDRHR3w= 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 v2 08/11] usb: gadget: u_audio: Adding suspend call Date: Mon, 20 Dec 2021 22:11:27 +0100 Message-Id: <20211220211130.88590-9-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 21:11:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526270 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 33FA5C433FE for ; Mon, 20 Dec 2021 21:11:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230382AbhLTVLt (ORCPT ); Mon, 20 Dec 2021 16:11:49 -0500 Received: from cable.insite.cz ([84.242.75.189]:33987 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230370AbhLTVLq (ORCPT ); Mon, 20 Dec 2021 16:11:46 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id C35D1A1A3D407; Mon, 20 Dec 2021 22:11:43 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034703; bh=Zb6YZxBSNRSm20rGOOx/GOnHDVc+cQZuyDkHmzq8FeY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DzjOWCO52wg8xV73N2nmkdIGPi6ijRUcyTdQ002nuOOmd1XRlFwoxIjhDYKRgjaFJ /e50fL7YDXFaWBROi1A3A8LXC9WhsKrwpR4zEs+1UHGpSwy6ugyJoixYusZXsVhWEj ymXJKHUd0lC0jZ/RwbxzqVI27NDp+PNz4JxPro/A= 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 l612ALjYZXSs; Mon, 20 Dec 2021 22:11:38 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id D0679A1A3D409; Mon, 20 Dec 2021 22:11:34 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034695; bh=Zb6YZxBSNRSm20rGOOx/GOnHDVc+cQZuyDkHmzq8FeY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QbGn3Busa/Zug6RpRz7Zq4xKiMPLcCR3QuwJVFdEv+ukL2cPaXhjeqit0xTq0i9Ca jO5QFI5IRoMapi02ubomuENkjlr8eldjnq7b3E0XwYbxdCV2wJ5ACCHyyJ1NohN7Zt P3PSISv5kL0sYaEROcMhIERblVXRrKtYo8YtslbA= 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 v2 09/11] usb: gadget: f_uac2: Adding suspend callback Date: Mon, 20 Dec 2021 22:11:28 +0100 Message-Id: <20211220211130.88590-10-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 21:11:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526791 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 BAEFBC433F5 for ; Mon, 20 Dec 2021 21:11:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230395AbhLTVLs (ORCPT ); Mon, 20 Dec 2021 16:11:48 -0500 Received: from cable.insite.cz ([84.242.75.189]:41722 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230357AbhLTVLq (ORCPT ); Mon, 20 Dec 2021 16:11:46 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id C2A3FA1A3D405; Mon, 20 Dec 2021 22:11:43 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034703; bh=foDRz/w+gGEj4ZWnc3EnCugl8u/3ze6aN+VG/9SZ+HQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=c+lQRzgSuHDEJHYSc74a9L1EvP1CyC84562djYpZ4J0IExQ1MsZMmn8fVD/+UwMcf pLAhXwCjd6f6/BI35bcxrKCTz0mQEaulXSMAxQmGVsowck/3+8cAn6ttixWdXtrPLN rRf9C9sJELrhTeEKzV7xqwTMfxKSuhDYzNApBCjQ= 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 gEt_g_mUPN28; Mon, 20 Dec 2021 22:11:38 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 2C34FA1A3D40A; Mon, 20 Dec 2021 22:11:35 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034695; bh=foDRz/w+gGEj4ZWnc3EnCugl8u/3ze6aN+VG/9SZ+HQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JLUFqZ1B+rNUtD9XnYKVzTOnFJRlu6p9/QWSo/vEwTcIuIxdMI58NHC3g0Gagvgas nwr/tXQIUi4SdKazE1/YtfzkrO3aXB7Mi6kFOYjjnzqbNZI2NiTIJFkin/SAcCCLRx BB10HHz1jGu8qLwtnH068pTKp+7ZwYlblIOx/uZc= 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 v2 10/11] usb: gadget: f_uac1: Adding suspend callback Date: Mon, 20 Dec 2021 22:11:29 +0100 Message-Id: <20211220211130.88590-11-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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 21:11:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Hofman X-Patchwork-Id: 526269 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 13C9FC4332F for ; Mon, 20 Dec 2021 21:11:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230393AbhLTVLv (ORCPT ); Mon, 20 Dec 2021 16:11:51 -0500 Received: from cable.insite.cz ([84.242.75.189]:52817 "EHLO cable.insite.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230345AbhLTVLr (ORCPT ); Mon, 20 Dec 2021 16:11:47 -0500 Received: from localhost (localhost [127.0.0.1]) by cable.insite.cz (Postfix) with ESMTP id A0C5EA1A3D400; Mon, 20 Dec 2021 22:11:45 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034705; bh=XQRAlk2+V8wj72YNBcUQRGUUNWyTfRhGHm58nuGGSeE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=i/KvCnFbTZBcE7qSXE0MaZ3cZj6pCEQewo1dke/78tJubUT0TmLyGA9d480qyw4T5 kdYSK4UQ3a4ydYsHuhZt37ixI7H8rquCSQCfKamzbgdjUajop7BWTaisT3e7/sMwO6 XZn+SFLPefBctRJcbbjulndhU+c/+4oA/k55V0u0= 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 KGNvGeyjAWG0; Mon, 20 Dec 2021 22:11:40 +0100 (CET) Received: from precision.doma (dustin.pilsfree.net [81.201.58.138]) (Authenticated sender: pavel) by cable.insite.cz (Postfix) with ESMTPSA id 8D255A1A3D40B; Mon, 20 Dec 2021 22:11:35 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ivitera.com; s=mail; t=1640034695; bh=XQRAlk2+V8wj72YNBcUQRGUUNWyTfRhGHm58nuGGSeE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Aiu9c5yq4lspGqdeKK4ayWaRy5T5YUWJA0A5pou04v5lJigfNof5DddgakLhgYCMs 9Rv+w3Md/RsfpxY4RDznmQd25Ie6ajgb0g/vrt9LJbdIFZ2y8LVttP9vRXnXM24XjU czK1iNwynFeM99xlZHbhubO4bPwEo5lNNNKPlrZg= 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 v2 11/11] usb: gadget: f_uac2: Determining bInterval for HS and SS Date: Mon, 20 Dec 2021 22:11:30 +0100 Message-Id: <20211220211130.88590-12-pavel.hofman@ivitera.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211220211130.88590-1-pavel.hofman@ivitera.com> References: <20211220211130.88590-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__);