linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/5] USB Audio Gadget refactoring
@ 2016-05-23 23:50 Ruslan Bilovol
  2016-05-23 23:50 ` [RFC PATCH 1/5] usb: gadget: f_uac2: remove platform driver/device creation Ruslan Bilovol
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-05-23 23:50 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Daniel Mack, Jassi Brar, linux-usb, linux-kernel

I came to this patch series when wanted to do two things:
 - use UAC1 as virtual ALSA sound card on gadget side,
   just like UAC2 is used so it's possible to do rate
   resampling
 - have both playback/capture support in UAC1

Since I wanted to have same behavior for both UAC1/UAC2,
obviously I've got an utility part (u_audio.c) for
virtual ALSA sound card handling like we have
for ethernet(u_ether) or serial(u_serial) functions.
Function-specific parts (f_uac1/f_uac2) became almost 
as storage for class-specfic USB descriptors, some
boilerplate for configfs, binding and few USB
config request handling.

Major change to f_uac1 it that it can't do
direct play to existing ALSA sound card anymore,
representing audio on gadget side as virtual
ALSA sound card where audio streams are simply
sinked to and sourced from it, so it may break
current usecase for some people (and that's why
it's RFC).

Luckily, it's possible to use existing user-space
applications for audio routing between Audio Gadget
and real sound card. I personally use alsaloop tool
from alsautils and have ability to create PCM
loopback between two different ALSA cards using
rate resampling, which is not possible with previous
"direct play to ALSA card" approach in f_uac1. 

While here, also dropped redundant platform
driver/device creation in f_uac2 driver as well as
"never implemented" volume/mute functionality in f_uac1
that made this work even easier to do.

This series is tested with both legacy g_audio.ko and
modern configfs approaches under Ubuntu 14.04 (UAC1 and
UAC2) and under Windows7 x64 (UAC1 only) having
perfect results in all cases.

Some changes may have lack of good description that may
be obvious for me but not so clear for others, but I
hope to fix it in next versions.

Comments, testing are welcome.

Ruslan Bilovol (5):
  usb: gadget: f_uac2: remove platform driver/device creation
  usb: gadget: f_uac2: split out audio core
  usb: gadget: f_uac1: drop volume/mute functionality
  usb: gadget: f_uac1: switch to u_audio core utilities
  usb: gadget: f_uac1: add capture support

 drivers/usb/gadget/Kconfig            |  13 +-
 drivers/usb/gadget/function/Makefile  |   3 +-
 drivers/usb/gadget/function/f_uac1.c  | 842 +++++++++++++---------------------
 drivers/usb/gadget/function/f_uac2.c  | 778 ++++---------------------------
 drivers/usb/gadget/function/u_audio.c | 632 +++++++++++++++++++++++++
 drivers/usb/gadget/function/u_audio.h |  93 ++++
 drivers/usb/gadget/function/u_uac1.c  | 314 -------------
 drivers/usb/gadget/function/u_uac1.h  |  71 +--
 drivers/usb/gadget/legacy/Kconfig     |   1 +
 drivers/usb/gadget/legacy/audio.c     |  54 ++-
 10 files changed, 1208 insertions(+), 1593 deletions(-)
 create mode 100644 drivers/usb/gadget/function/u_audio.c
 create mode 100644 drivers/usb/gadget/function/u_audio.h
 delete mode 100644 drivers/usb/gadget/function/u_uac1.c

-- 
1.9.1

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 1/5] usb: gadget: f_uac2: remove platform driver/device creation
  2016-05-23 23:50 [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
@ 2016-05-23 23:50 ` Ruslan Bilovol
  2016-05-23 23:50 ` [RFC PATCH 2/5] usb: gadget: f_uac2: split out audio core Ruslan Bilovol
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-05-23 23:50 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Daniel Mack, Jassi Brar, linux-usb, linux-kernel

Simplify f_uac2 by removing platform driver/device
creation; use composite's usb_gadget device as
parent for sound card and for debug prints.
This removes extra layer of code without any functional
change.

Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
---
 drivers/usb/gadget/function/f_uac2.c | 107 +++++++++--------------------------
 1 file changed, 28 insertions(+), 79 deletions(-)

diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 186d4b1..8b46f05 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -13,7 +13,6 @@
 
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
-#include <linux/platform_device.h>
 #include <linux/module.h>
 
 #include <sound/core.h>
@@ -54,8 +53,6 @@
 #define UNFLW_CTRL	8
 #define OVFLW_CTRL	10
 
-static const char *uac2_name = "snd_uac2";
-
 struct uac2_req {
 	struct uac2_rtd_params *pp; /* parent param */
 	struct usb_request *req;
@@ -84,9 +81,6 @@ struct uac2_rtd_params {
 };
 
 struct snd_uac2_chip {
-	struct platform_device pdev;
-	struct platform_driver pdrv;
-
 	struct uac2_rtd_params p_prm;
 	struct uac2_rtd_params c_prm;
 
@@ -125,6 +119,7 @@ struct audio_dev {
 
 	struct usb_ep *in_ep, *out_ep;
 	struct usb_function func;
+	struct usb_gadget *gadget;
 
 	/* The ALSA Sound Card it represents on the USB-Client side */
 	struct snd_uac2_chip uac2;
@@ -143,12 +138,6 @@ struct audio_dev *uac2_to_agdev(struct snd_uac2_chip *u)
 }
 
 static inline
-struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p)
-{
-	return container_of(p, struct snd_uac2_chip, pdev);
-}
-
-static inline
 struct f_uac2_opts *agdev_to_uac2_opts(struct audio_dev *agdev)
 {
 	return container_of(agdev->func.fi, struct f_uac2_opts, func_inst);
@@ -257,7 +246,7 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
 
 exit:
 	if (usb_ep_queue(ep, req, GFP_ATOMIC))
-		dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
+		dev_err(uac2->card->dev, "%d Error!\n", __LINE__);
 
 	if (update_alsa)
 		snd_pcm_period_elapsed(substream);
@@ -441,23 +430,22 @@ static struct snd_pcm_ops uac2_pcm_ops = {
 	.prepare = uac2_pcm_null,
 };
 
-static int snd_uac2_probe(struct platform_device *pdev)
+static int snd_uac2_probe(struct audio_dev *audio_dev)
 {
-	struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev);
+	struct snd_uac2_chip *uac2 = &audio_dev->uac2;
 	struct snd_card *card;
 	struct snd_pcm *pcm;
-	struct audio_dev *audio_dev;
 	struct f_uac2_opts *opts;
 	int err;
 	int p_chmask, c_chmask;
 
-	audio_dev = uac2_to_agdev(uac2);
 	opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst);
 	p_chmask = opts->p_chmask;
 	c_chmask = opts->c_chmask;
 
 	/* Choose any slot, with no id */
-	err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card);
+	err = snd_card_new(&audio_dev->gadget->dev,
+			-1, NULL, THIS_MODULE, 0, &card);
 	if (err < 0)
 		return err;
 
@@ -482,16 +470,15 @@ static int snd_uac2_probe(struct platform_device *pdev)
 
 	strcpy(card->driver, "UAC2_Gadget");
 	strcpy(card->shortname, "UAC2_Gadget");
-	sprintf(card->longname, "UAC2_Gadget %i", pdev->id);
+	sprintf(card->longname, "UAC2_Gadget %i", card->dev->id);
 
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
 		snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
 
 	err = snd_card_register(card);
-	if (!err) {
-		platform_set_drvdata(pdev, card);
+
+	if (!err)
 		return 0;
-	}
 
 snd_fail:
 	snd_card_free(card);
@@ -502,9 +489,9 @@ snd_fail:
 	return err;
 }
 
-static int snd_uac2_remove(struct platform_device *pdev)
+static int snd_uac2_remove(struct audio_dev *audio_dev)
 {
-	struct snd_card *card = platform_get_drvdata(pdev);
+	struct snd_card *card = audio_dev->uac2.card;
 
 	if (card)
 		return snd_card_free(card);
@@ -512,45 +499,6 @@ static int snd_uac2_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static void snd_uac2_release(struct device *dev)
-{
-	dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
-}
-
-static int alsa_uac2_init(struct audio_dev *agdev)
-{
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
-	int err;
-
-	uac2->pdrv.probe = snd_uac2_probe;
-	uac2->pdrv.remove = snd_uac2_remove;
-	uac2->pdrv.driver.name = uac2_name;
-
-	uac2->pdev.id = 0;
-	uac2->pdev.name = uac2_name;
-	uac2->pdev.dev.release = snd_uac2_release;
-
-	/* Register snd_uac2 driver */
-	err = platform_driver_register(&uac2->pdrv);
-	if (err)
-		return err;
-
-	/* Register snd_uac2 device */
-	err = platform_device_register(&uac2->pdev);
-	if (err)
-		platform_driver_unregister(&uac2->pdrv);
-
-	return err;
-}
-
-static void alsa_uac2_exit(struct audio_dev *agdev)
-{
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
-
-	platform_driver_unregister(&uac2->pdrv);
-	platform_device_unregister(&uac2->pdev);
-}
-
 
 /* --------- USB Function Interface ------------- */
 
@@ -971,7 +919,7 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
 	}
 
 	if (usb_ep_disable(ep))
-		dev_err(&uac2->pdev.dev,
+		dev_err(uac2->card->dev,
 			"%s:%d Error!\n", __func__, __LINE__);
 }
 
@@ -1005,7 +953,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 	struct snd_uac2_chip *uac2 = &agdev->uac2;
 	struct usb_composite_dev *cdev = cfg->cdev;
 	struct usb_gadget *gadget = cdev->gadget;
-	struct device *dev = &uac2->pdev.dev;
+	struct device *dev = &gadget->dev;
 	struct uac2_rtd_params *prm;
 	struct f_uac2_opts *uac2_opts;
 	struct usb_string *us;
@@ -1076,6 +1024,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 	agdev->as_in_intf = ret;
 	agdev->as_in_alt = 0;
 
+	agdev->gadget = gadget;
+
 	agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
 	if (!agdev->out_ep) {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
@@ -1121,7 +1071,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 		goto err_free_descs;
 	}
 
-	ret = alsa_uac2_init(agdev);
+	ret = snd_uac2_probe(agdev);
 	if (ret)
 		goto err_free_descs;
 	return 0;
@@ -1131,6 +1081,7 @@ err_free_descs:
 err:
 	kfree(agdev->uac2.p_prm.rbuf);
 	kfree(agdev->uac2.c_prm.rbuf);
+	agdev->gadget = NULL;
 	return -EINVAL;
 }
 
@@ -1141,7 +1092,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
 	struct audio_dev *agdev = func_to_agdev(fn);
 	struct snd_uac2_chip *uac2 = &agdev->uac2;
 	struct usb_gadget *gadget = cdev->gadget;
-	struct device *dev = &uac2->pdev.dev;
+	struct device *dev = &gadget->dev;
 	struct usb_request *req;
 	struct usb_ep *ep;
 	struct uac2_rtd_params *prm;
@@ -1242,7 +1193,6 @@ static int
 afunc_get_alt(struct usb_function *fn, unsigned intf)
 {
 	struct audio_dev *agdev = func_to_agdev(fn);
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
 
 	if (intf == agdev->ac_intf)
 		return agdev->ac_alt;
@@ -1251,7 +1201,7 @@ afunc_get_alt(struct usb_function *fn, unsigned intf)
 	else if (intf == agdev->as_in_intf)
 		return agdev->as_in_alt;
 	else
-		dev_err(&uac2->pdev.dev,
+		dev_err(&agdev->gadget->dev,
 			"%s:%d Invalid Interface %d!\n",
 			__func__, __LINE__, intf);
 
@@ -1276,7 +1226,6 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 {
 	struct usb_request *req = fn->config->cdev->req;
 	struct audio_dev *agdev = func_to_agdev(fn);
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
 	struct f_uac2_opts *opts;
 	u16 w_length = le16_to_cpu(cr->wLength);
 	u16 w_index = le16_to_cpu(cr->wIndex);
@@ -1304,7 +1253,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 		*(u8 *)req->buf = 1;
 		value = min_t(unsigned, w_length, 1);
 	} else {
-		dev_err(&uac2->pdev.dev,
+		dev_err(&agdev->gadget->dev,
 			"%s:%d control_selector=%d TODO!\n",
 			__func__, __LINE__, control_selector);
 	}
@@ -1317,7 +1266,6 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 {
 	struct usb_request *req = fn->config->cdev->req;
 	struct audio_dev *agdev = func_to_agdev(fn);
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
 	struct f_uac2_opts *opts;
 	u16 w_length = le16_to_cpu(cr->wLength);
 	u16 w_index = le16_to_cpu(cr->wIndex);
@@ -1347,7 +1295,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 		value = min_t(unsigned, w_length, sizeof r);
 		memcpy(req->buf, &r, value);
 	} else {
-		dev_err(&uac2->pdev.dev,
+		dev_err(&agdev->gadget->dev,
 			"%s:%d control_selector=%d TODO!\n",
 			__func__, __LINE__, control_selector);
 	}
@@ -1383,12 +1331,11 @@ static int
 setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 {
 	struct audio_dev *agdev = func_to_agdev(fn);
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
 	u16 w_index = le16_to_cpu(cr->wIndex);
 	u8 intf = w_index & 0xff;
 
 	if (intf != agdev->ac_intf) {
-		dev_err(&uac2->pdev.dev,
+		dev_err(&agdev->gadget->dev,
 			"%s:%d Error!\n", __func__, __LINE__);
 		return -EOPNOTSUPP;
 	}
@@ -1406,7 +1353,6 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 {
 	struct usb_composite_dev *cdev = fn->config->cdev;
 	struct audio_dev *agdev = func_to_agdev(fn);
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
 	struct usb_request *req = cdev->req;
 	u16 w_length = le16_to_cpu(cr->wLength);
 	int value = -EOPNOTSUPP;
@@ -1418,14 +1364,15 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 	if ((cr->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE)
 		value = setup_rq_inf(fn, cr);
 	else
-		dev_err(&uac2->pdev.dev, "%s:%d Error!\n", __func__, __LINE__);
+		dev_err(&agdev->gadget->dev, "%s:%d Error!\n",
+				__func__, __LINE__);
 
 	if (value >= 0) {
 		req->length = value;
 		req->zero = value < w_length;
 		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
 		if (value < 0) {
-			dev_err(&uac2->pdev.dev,
+			dev_err(&agdev->gadget->dev,
 				"%s:%d Error!\n", __func__, __LINE__);
 			req->status = 0;
 		}
@@ -1564,7 +1511,7 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
 	struct audio_dev *agdev = func_to_agdev(f);
 	struct uac2_rtd_params *prm;
 
-	alsa_uac2_exit(agdev);
+	snd_uac2_remove(agdev);
 
 	prm = &agdev->uac2.p_prm;
 	kfree(prm->rbuf);
@@ -1572,6 +1519,8 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
 	prm = &agdev->uac2.c_prm;
 	kfree(prm->rbuf);
 	usb_free_all_descriptors(f);
+
+	agdev->gadget = NULL;
 }
 
 static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 2/5] usb: gadget: f_uac2: split out audio core
  2016-05-23 23:50 [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
  2016-05-23 23:50 ` [RFC PATCH 1/5] usb: gadget: f_uac2: remove platform driver/device creation Ruslan Bilovol
@ 2016-05-23 23:50 ` Ruslan Bilovol
  2016-05-23 23:50 ` [RFC PATCH 3/5] usb: gadget: f_uac1: drop volume/mute functionality Ruslan Bilovol
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-05-23 23:50 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Daniel Mack, Jassi Brar, linux-usb, linux-kernel

Abstract the peripheral side ALSA sound card code from
the f_uac2 function into a component that can be called
by various functions, so the various flavors can be split
apart and selectively reused.

Visible changes:
 - add uac_params structure to pass audio paramteres for
   gaudio_setup
 - make ALSA sound card's name configurable
 - add [in/out]_ep_maxpsize
 - allocate snd_uac_chip structure during gaudio_setup
 - add gaudio_[start/stop]_[capture/playback] functions

Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
---
 drivers/usb/gadget/Kconfig            |   4 +
 drivers/usb/gadget/function/Makefile  |   1 +
 drivers/usb/gadget/function/f_uac2.c  | 699 ++++------------------------------
 drivers/usb/gadget/function/u_audio.c | 632 ++++++++++++++++++++++++++++++
 drivers/usb/gadget/function/u_audio.h |  93 +++++
 drivers/usb/gadget/legacy/Kconfig     |   1 +
 6 files changed, 813 insertions(+), 617 deletions(-)
 create mode 100644 drivers/usb/gadget/function/u_audio.c
 create mode 100644 drivers/usb/gadget/function/u_audio.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index af5d922..42d8508 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -157,6 +157,9 @@ config USB_U_SERIAL
 config USB_U_ETHER
 	tristate
 
+config USB_U_AUDIO
+	tristate
+
 config USB_F_SERIAL
 	tristate
 
@@ -399,6 +402,7 @@ config USB_CONFIGFS_F_UAC2
 	depends on SND
 	select USB_LIBCOMPOSITE
 	select SND_PCM
+	select USB_U_AUDIO
 	select USB_F_UAC2
 	help
 	  This Audio function is compatible with USB Audio Class
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index cb8c225..b29f2ae 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -32,6 +32,7 @@ usb_f_mass_storage-y		:= f_mass_storage.o storage_common.o
 obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
 usb_f_fs-y			:= f_fs.o
 obj-$(CONFIG_USB_F_FS)		+= usb_f_fs.o
+obj-$(CONFIG_USB_U_AUDIO)	+= u_audio.o
 usb_f_uac1-y			:= f_uac1.o u_uac1.o
 obj-$(CONFIG_USB_F_UAC1)	+= usb_f_uac1.o
 usb_f_uac2-y			:= f_uac2.o
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 8b46f05..a6c91a6 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -15,15 +15,9 @@
 #include <linux/usb/audio-v2.h>
 #include <linux/module.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
+#include "u_audio.h"
 #include "u_uac2.h"
 
-/* Keep everyone on toes */
-#define USB_XFERS	2
-
 /*
  * The driver implements a simple UAC_2 topology.
  * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture
@@ -53,453 +47,23 @@
 #define UNFLW_CTRL	8
 #define OVFLW_CTRL	10
 
-struct uac2_req {
-	struct uac2_rtd_params *pp; /* parent param */
-	struct usb_request *req;
-};
-
-struct uac2_rtd_params {
-	struct snd_uac2_chip *uac2; /* parent chip */
-	bool ep_enabled; /* if the ep is enabled */
-	/* Size of the ring buffer */
-	size_t dma_bytes;
-	unsigned char *dma_area;
-
-	struct snd_pcm_substream *ss;
-
-	/* Ring buffer */
-	ssize_t hw_ptr;
-
-	void *rbuf;
-
-	size_t period_size;
-
-	unsigned max_psize;
-	struct uac2_req ureq[USB_XFERS];
-
-	spinlock_t lock;
-};
-
-struct snd_uac2_chip {
-	struct uac2_rtd_params p_prm;
-	struct uac2_rtd_params c_prm;
-
-	struct snd_card *card;
-	struct snd_pcm *pcm;
-
-	/* timekeeping for the playback endpoint */
-	unsigned int p_interval;
-	unsigned int p_residue;
-
-	/* pre-calculated values for playback iso completion */
-	unsigned int p_pktsize;
-	unsigned int p_pktsize_residue;
-	unsigned int p_framesize;
-};
-
-#define BUFF_SIZE_MAX	(PAGE_SIZE * 16)
-#define PRD_SIZE_MAX	PAGE_SIZE
-#define MIN_PERIODS	4
-
-static struct snd_pcm_hardware uac2_pcm_hardware = {
-	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
-		 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
-		 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
-	.rates = SNDRV_PCM_RATE_CONTINUOUS,
-	.periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX,
-	.buffer_bytes_max = BUFF_SIZE_MAX,
-	.period_bytes_max = PRD_SIZE_MAX,
-	.periods_min = MIN_PERIODS,
-};
-
-struct audio_dev {
-	u8 ac_intf, ac_alt;
-	u8 as_out_intf, as_out_alt;
-	u8 as_in_intf, as_in_alt;
-
-	struct usb_ep *in_ep, *out_ep;
-	struct usb_function func;
-	struct usb_gadget *gadget;
-
-	/* The ALSA Sound Card it represents on the USB-Client side */
-	struct snd_uac2_chip uac2;
+struct f_uac2 {
+	struct gaudio gaudio;
+	u8 ac_intf, as_in_intf, as_out_intf;
+	u8 ac_alt, as_in_alt, as_out_alt;	/* needed for get_alt() */
 };
 
-static inline
-struct audio_dev *func_to_agdev(struct usb_function *f)
-{
-	return container_of(f, struct audio_dev, func);
-}
-
-static inline
-struct audio_dev *uac2_to_agdev(struct snd_uac2_chip *u)
+static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
 {
-	return container_of(u, struct audio_dev, uac2);
+	return container_of(f, struct f_uac2, gaudio.func);
 }
 
 static inline
-struct f_uac2_opts *agdev_to_uac2_opts(struct audio_dev *agdev)
+struct f_uac2_opts *gaudio_to_uac2_opts(struct gaudio *agdev)
 {
 	return container_of(agdev->func.fi, struct f_uac2_opts, func_inst);
 }
 
-static inline
-uint num_channels(uint chanmask)
-{
-	uint num = 0;
-
-	while (chanmask) {
-		num += (chanmask & 1);
-		chanmask >>= 1;
-	}
-
-	return num;
-}
-
-static void
-agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
-{
-	unsigned pending;
-	unsigned long flags;
-	unsigned int hw_ptr;
-	bool update_alsa = false;
-	int status = req->status;
-	struct uac2_req *ur = req->context;
-	struct snd_pcm_substream *substream;
-	struct uac2_rtd_params *prm = ur->pp;
-	struct snd_uac2_chip *uac2 = prm->uac2;
-
-	/* i/f shutting down */
-	if (!prm->ep_enabled || req->status == -ESHUTDOWN)
-		return;
-
-	/*
-	 * We can't really do much about bad xfers.
-	 * Afterall, the ISOCH xfers could fail legitimately.
-	 */
-	if (status)
-		pr_debug("%s: iso_complete status(%d) %d/%d\n",
-			__func__, status, req->actual, req->length);
-
-	substream = prm->ss;
-
-	/* Do nothing if ALSA isn't active */
-	if (!substream)
-		goto exit;
-
-	spin_lock_irqsave(&prm->lock, flags);
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		/*
-		 * For each IN packet, take the quotient of the current data
-		 * rate and the endpoint's interval as the base packet size.
-		 * If there is a residue from this division, add it to the
-		 * residue accumulator.
-		 */
-		req->length = uac2->p_pktsize;
-		uac2->p_residue += uac2->p_pktsize_residue;
-
-		/*
-		 * Whenever there are more bytes in the accumulator than we
-		 * need to add one more sample frame, increase this packet's
-		 * size and decrease the accumulator.
-		 */
-		if (uac2->p_residue / uac2->p_interval >= uac2->p_framesize) {
-			req->length += uac2->p_framesize;
-			uac2->p_residue -= uac2->p_framesize *
-					   uac2->p_interval;
-		}
-
-		req->actual = req->length;
-	}
-
-	pending = prm->hw_ptr % prm->period_size;
-	pending += req->actual;
-	if (pending >= prm->period_size)
-		update_alsa = true;
-
-	hw_ptr = prm->hw_ptr;
-	prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
-
-	spin_unlock_irqrestore(&prm->lock, flags);
-
-	/* Pack USB load in ALSA ring buffer */
-	pending = prm->dma_bytes - hw_ptr;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		if (unlikely(pending < req->actual)) {
-			memcpy(req->buf, prm->dma_area + hw_ptr, pending);
-			memcpy(req->buf + pending, prm->dma_area,
-			       req->actual - pending);
-		} else {
-			memcpy(req->buf, prm->dma_area + hw_ptr, req->actual);
-		}
-	} else {
-		if (unlikely(pending < req->actual)) {
-			memcpy(prm->dma_area + hw_ptr, req->buf, pending);
-			memcpy(prm->dma_area, req->buf + pending,
-			       req->actual - pending);
-		} else {
-			memcpy(prm->dma_area + hw_ptr, req->buf, req->actual);
-		}
-	}
-
-exit:
-	if (usb_ep_queue(ep, req, GFP_ATOMIC))
-		dev_err(uac2->card->dev, "%d Error!\n", __LINE__);
-
-	if (update_alsa)
-		snd_pcm_period_elapsed(substream);
-
-	return;
-}
-
-static int
-uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-	struct uac2_rtd_params *prm;
-	unsigned long flags;
-	int err = 0;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		prm = &uac2->p_prm;
-	else
-		prm = &uac2->c_prm;
-
-	spin_lock_irqsave(&prm->lock, flags);
-
-	/* Reset */
-	prm->hw_ptr = 0;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		prm->ss = substream;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		prm->ss = NULL;
-		break;
-	default:
-		err = -EINVAL;
-	}
-
-	spin_unlock_irqrestore(&prm->lock, flags);
-
-	/* Clear buffer after Play stops */
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss)
-		memset(prm->rbuf, 0, prm->max_psize * USB_XFERS);
-
-	return err;
-}
-
-static snd_pcm_uframes_t uac2_pcm_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-	struct uac2_rtd_params *prm;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		prm = &uac2->p_prm;
-	else
-		prm = &uac2->c_prm;
-
-	return bytes_to_frames(substream->runtime, prm->hw_ptr);
-}
-
-static int uac2_pcm_hw_params(struct snd_pcm_substream *substream,
-			       struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-	struct uac2_rtd_params *prm;
-	int err;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		prm = &uac2->p_prm;
-	else
-		prm = &uac2->c_prm;
-
-	err = snd_pcm_lib_malloc_pages(substream,
-					params_buffer_bytes(hw_params));
-	if (err >= 0) {
-		prm->dma_bytes = substream->runtime->dma_bytes;
-		prm->dma_area = substream->runtime->dma_area;
-		prm->period_size = params_period_bytes(hw_params);
-	}
-
-	return err;
-}
-
-static int uac2_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-	struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-	struct uac2_rtd_params *prm;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		prm = &uac2->p_prm;
-	else
-		prm = &uac2->c_prm;
-
-	prm->dma_area = NULL;
-	prm->dma_bytes = 0;
-	prm->period_size = 0;
-
-	return snd_pcm_lib_free_pages(substream);
-}
-
-static int uac2_pcm_open(struct snd_pcm_substream *substream)
-{
-	struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct audio_dev *audio_dev;
-	struct f_uac2_opts *opts;
-	int p_ssize, c_ssize;
-	int p_srate, c_srate;
-	int p_chmask, c_chmask;
-
-	audio_dev = uac2_to_agdev(uac2);
-	opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst);
-	p_ssize = opts->p_ssize;
-	c_ssize = opts->c_ssize;
-	p_srate = opts->p_srate;
-	c_srate = opts->c_srate;
-	p_chmask = opts->p_chmask;
-	c_chmask = opts->c_chmask;
-	uac2->p_residue = 0;
-
-	runtime->hw = uac2_pcm_hardware;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		spin_lock_init(&uac2->p_prm.lock);
-		runtime->hw.rate_min = p_srate;
-		switch (p_ssize) {
-		case 3:
-			runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
-			break;
-		case 4:
-			runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
-			break;
-		default:
-			runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
-			break;
-		}
-		runtime->hw.channels_min = num_channels(p_chmask);
-		runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize
-						/ runtime->hw.periods_min;
-	} else {
-		spin_lock_init(&uac2->c_prm.lock);
-		runtime->hw.rate_min = c_srate;
-		switch (c_ssize) {
-		case 3:
-			runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
-			break;
-		case 4:
-			runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
-			break;
-		default:
-			runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
-			break;
-		}
-		runtime->hw.channels_min = num_channels(c_chmask);
-		runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize
-						/ runtime->hw.periods_min;
-	}
-
-	runtime->hw.rate_max = runtime->hw.rate_min;
-	runtime->hw.channels_max = runtime->hw.channels_min;
-
-	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-
-	return 0;
-}
-
-/* ALSA cries without these function pointers */
-static int uac2_pcm_null(struct snd_pcm_substream *substream)
-{
-	return 0;
-}
-
-static struct snd_pcm_ops uac2_pcm_ops = {
-	.open = uac2_pcm_open,
-	.close = uac2_pcm_null,
-	.ioctl = snd_pcm_lib_ioctl,
-	.hw_params = uac2_pcm_hw_params,
-	.hw_free = uac2_pcm_hw_free,
-	.trigger = uac2_pcm_trigger,
-	.pointer = uac2_pcm_pointer,
-	.prepare = uac2_pcm_null,
-};
-
-static int snd_uac2_probe(struct audio_dev *audio_dev)
-{
-	struct snd_uac2_chip *uac2 = &audio_dev->uac2;
-	struct snd_card *card;
-	struct snd_pcm *pcm;
-	struct f_uac2_opts *opts;
-	int err;
-	int p_chmask, c_chmask;
-
-	opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst);
-	p_chmask = opts->p_chmask;
-	c_chmask = opts->c_chmask;
-
-	/* Choose any slot, with no id */
-	err = snd_card_new(&audio_dev->gadget->dev,
-			-1, NULL, THIS_MODULE, 0, &card);
-	if (err < 0)
-		return err;
-
-	uac2->card = card;
-
-	/*
-	 * Create first PCM device
-	 * Create a substream only for non-zero channel streams
-	 */
-	err = snd_pcm_new(uac2->card, "UAC2 PCM", 0,
-			       p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
-	if (err < 0)
-		goto snd_fail;
-
-	strcpy(pcm->name, "UAC2 PCM");
-	pcm->private_data = uac2;
-
-	uac2->pcm = pcm;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac2_pcm_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac2_pcm_ops);
-
-	strcpy(card->driver, "UAC2_Gadget");
-	strcpy(card->shortname, "UAC2_Gadget");
-	sprintf(card->longname, "UAC2_Gadget %i", card->dev->id);
-
-	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
-		snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
-
-	err = snd_card_register(card);
-
-	if (!err)
-		return 0;
-
-snd_fail:
-	snd_card_free(card);
-
-	uac2->pcm = NULL;
-	uac2->card = NULL;
-
-	return err;
-}
-
-static int snd_uac2_remove(struct audio_dev *audio_dev)
-{
-	struct snd_card *card = audio_dev->uac2.card;
-
-	if (card)
-		return snd_card_free(card);
-
-	return 0;
-}
-
-
 /* --------- USB Function Interface ------------- */
 
 enum {
@@ -899,30 +463,6 @@ struct cntrl_range_lay3 {
 	__u32	dRES;
 } __packed;
 
-static inline void
-free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
-{
-	struct snd_uac2_chip *uac2 = prm->uac2;
-	int i;
-
-	if (!prm->ep_enabled)
-		return;
-
-	prm->ep_enabled = false;
-
-	for (i = 0; i < USB_XFERS; i++) {
-		if (prm->ureq[i].req) {
-			usb_ep_dequeue(ep, prm->ureq[i].req);
-			usb_ep_free_request(ep, prm->ureq[i].req);
-			prm->ureq[i].req = NULL;
-		}
-	}
-
-	if (usb_ep_disable(ep))
-		dev_err(uac2->card->dev,
-			"%s:%d Error!\n", __func__, __LINE__);
-}
-
 static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
 	struct usb_endpoint_descriptor *ep_desc,
 	unsigned int factor, bool is_playback)
@@ -949,12 +489,11 @@ static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
 static int
 afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 {
-	struct audio_dev *agdev = func_to_agdev(fn);
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
+	struct f_uac2 *uac2 = func_to_uac2(fn);
+	struct gaudio *agdev = func_to_gaudio(fn);
 	struct usb_composite_dev *cdev = cfg->cdev;
 	struct usb_gadget *gadget = cdev->gadget;
 	struct device *dev = &gadget->dev;
-	struct uac2_rtd_params *prm;
 	struct f_uac2_opts *uac2_opts;
 	struct usb_string *us;
 	int ret;
@@ -1001,8 +540,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 		return ret;
 	}
 	std_ac_if_desc.bInterfaceNumber = ret;
-	agdev->ac_intf = ret;
-	agdev->ac_alt = 0;
+	uac2->ac_intf = ret;
+	uac2->ac_alt = 0;
 
 	ret = usb_interface_id(cfg, fn);
 	if (ret < 0) {
@@ -1011,8 +550,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 	}
 	std_as_out_if0_desc.bInterfaceNumber = ret;
 	std_as_out_if1_desc.bInterfaceNumber = ret;
-	agdev->as_out_intf = ret;
-	agdev->as_out_alt = 0;
+	uac2->as_out_intf = ret;
+	uac2->as_out_alt = 0;
 
 	ret = usb_interface_id(cfg, fn);
 	if (ret < 0) {
@@ -1021,8 +560,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 	}
 	std_as_in_if0_desc.bInterfaceNumber = ret;
 	std_as_in_if1_desc.bInterfaceNumber = ret;
-	agdev->as_in_intf = ret;
-	agdev->as_in_alt = 0;
+	uac2->as_in_intf = ret;
+	uac2->as_in_alt = 0;
 
 	agdev->gadget = gadget;
 
@@ -1038,15 +577,17 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 		goto err;
 	}
 
-	uac2->p_prm.uac2 = uac2;
-	uac2->c_prm.uac2 = uac2;
-
 	/* Calculate wMaxPacketSize according to audio bandwidth */
 	set_ep_max_packet_size(uac2_opts, &fs_epin_desc, 1000, true);
 	set_ep_max_packet_size(uac2_opts, &fs_epout_desc, 1000, false);
 	set_ep_max_packet_size(uac2_opts, &hs_epin_desc, 8000, true);
 	set_ep_max_packet_size(uac2_opts, &hs_epout_desc, 8000, false);
 
+	agdev->in_ep_maxpsize = max(fs_epin_desc.wMaxPacketSize,
+					hs_epin_desc.wMaxPacketSize);
+	agdev->out_ep_maxpsize = max(fs_epout_desc.wMaxPacketSize,
+					hs_epout_desc.wMaxPacketSize);
+
 	hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
 	hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
 
@@ -1055,23 +596,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 	if (ret)
 		goto err;
 
-	prm = &agdev->uac2.c_prm;
-	prm->max_psize = hs_epout_desc.wMaxPacketSize;
-	prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
-	if (!prm->rbuf) {
-		prm->max_psize = 0;
-		goto err_free_descs;
-	}
-
-	prm = &agdev->uac2.p_prm;
-	prm->max_psize = hs_epin_desc.wMaxPacketSize;
-	prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
-	if (!prm->rbuf) {
-		prm->max_psize = 0;
-		goto err_free_descs;
-	}
-
-	ret = snd_uac2_probe(agdev);
+	agdev->params.p_chmask = uac2_opts->p_chmask;
+	agdev->params.p_srate = uac2_opts->p_srate;
+	agdev->params.p_ssize = uac2_opts->p_ssize;
+	agdev->params.c_chmask = uac2_opts->c_chmask;
+	agdev->params.c_srate = uac2_opts->c_srate;
+	agdev->params.c_ssize = uac2_opts->c_ssize;
+	ret = gaudio_setup(agdev, "UAC2 PCM", "UAC2_Gadget");
 	if (ret)
 		goto err_free_descs;
 	return 0;
@@ -1079,8 +610,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 err_free_descs:
 	usb_free_all_descriptors(fn);
 err:
-	kfree(agdev->uac2.p_prm.rbuf);
-	kfree(agdev->uac2.c_prm.rbuf);
 	agdev->gadget = NULL;
 	return -EINVAL;
 }
@@ -1089,14 +618,10 @@ static int
 afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
 {
 	struct usb_composite_dev *cdev = fn->config->cdev;
-	struct audio_dev *agdev = func_to_agdev(fn);
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
+	struct f_uac2 *uac2 = func_to_uac2(fn);
 	struct usb_gadget *gadget = cdev->gadget;
 	struct device *dev = &gadget->dev;
-	struct usb_request *req;
-	struct usb_ep *ep;
-	struct uac2_rtd_params *prm;
-	int req_len, i;
+	int ret = 0;
 
 	/* No i/f has more than 2 alt settings */
 	if (alt > 1) {
@@ -1104,7 +629,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
 		return -EINVAL;
 	}
 
-	if (intf == agdev->ac_intf) {
+	if (intf == uac2->ac_intf) {
 		/* Control I/f has only 1 AltSetting - 0 */
 		if (alt) {
 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
@@ -1113,93 +638,40 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
 		return 0;
 	}
 
-	if (intf == agdev->as_out_intf) {
-		ep = agdev->out_ep;
-		prm = &uac2->c_prm;
-		config_ep_by_speed(gadget, fn, ep);
-		agdev->as_out_alt = alt;
-		req_len = prm->max_psize;
-	} else if (intf == agdev->as_in_intf) {
-		struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev);
-		unsigned int factor, rate;
-		struct usb_endpoint_descriptor *ep_desc;
-
-		ep = agdev->in_ep;
-		prm = &uac2->p_prm;
-		config_ep_by_speed(gadget, fn, ep);
-		agdev->as_in_alt = alt;
-
-		/* pre-calculate the playback endpoint's interval */
-		if (gadget->speed == USB_SPEED_FULL) {
-			ep_desc = &fs_epin_desc;
-			factor = 1000;
-		} else {
-			ep_desc = &hs_epin_desc;
-			factor = 8000;
-		}
-
-		/* pre-compute some values for iso_complete() */
-		uac2->p_framesize = opts->p_ssize *
-				    num_channels(opts->p_chmask);
-		rate = opts->p_srate * uac2->p_framesize;
-		uac2->p_interval = factor / (1 << (ep_desc->bInterval - 1));
-		uac2->p_pktsize = min_t(unsigned int, rate / uac2->p_interval,
-					prm->max_psize);
+	if (intf == uac2->as_out_intf) {
+		uac2->as_out_alt = alt;
 
-		if (uac2->p_pktsize < prm->max_psize)
-			uac2->p_pktsize_residue = rate % uac2->p_interval;
+		if (alt)
+			ret = gaudio_start_capture(&uac2->gaudio);
 		else
-			uac2->p_pktsize_residue = 0;
+			gaudio_stop_capture(&uac2->gaudio);
+	} else if (intf == uac2->as_in_intf) {
+		uac2->as_in_alt = alt;
 
-		req_len = uac2->p_pktsize;
-		uac2->p_residue = 0;
+		if (alt)
+			ret = gaudio_start_playback(&uac2->gaudio);
+		else
+			gaudio_stop_playback(&uac2->gaudio);
 	} else {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 		return -EINVAL;
 	}
 
-	if (alt == 0) {
-		free_ep(prm, ep);
-		return 0;
-	}
-
-	prm->ep_enabled = true;
-	usb_ep_enable(ep);
-
-	for (i = 0; i < USB_XFERS; i++) {
-		if (!prm->ureq[i].req) {
-			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
-			if (req == NULL)
-				return -ENOMEM;
-
-			prm->ureq[i].req = req;
-			prm->ureq[i].pp = prm;
-
-			req->zero = 0;
-			req->context = &prm->ureq[i];
-			req->length = req_len;
-			req->complete = agdev_iso_complete;
-			req->buf = prm->rbuf + i * prm->max_psize;
-		}
-
-		if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
-			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-	}
-
-	return 0;
+	return ret;
 }
 
 static int
 afunc_get_alt(struct usb_function *fn, unsigned intf)
 {
-	struct audio_dev *agdev = func_to_agdev(fn);
-
-	if (intf == agdev->ac_intf)
-		return agdev->ac_alt;
-	else if (intf == agdev->as_out_intf)
-		return agdev->as_out_alt;
-	else if (intf == agdev->as_in_intf)
-		return agdev->as_in_alt;
+	struct f_uac2 *uac2 = func_to_uac2(fn);
+	struct gaudio *agdev = func_to_gaudio(fn);
+
+	if (intf == uac2->ac_intf)
+		return uac2->ac_alt;
+	else if (intf == uac2->as_out_intf)
+		return uac2->as_out_alt;
+	else if (intf == uac2->as_in_intf)
+		return uac2->as_in_alt;
 	else
 		dev_err(&agdev->gadget->dev,
 			"%s:%d Invalid Interface %d!\n",
@@ -1211,21 +683,19 @@ afunc_get_alt(struct usb_function *fn, unsigned intf)
 static void
 afunc_disable(struct usb_function *fn)
 {
-	struct audio_dev *agdev = func_to_agdev(fn);
-	struct snd_uac2_chip *uac2 = &agdev->uac2;
-
-	free_ep(&uac2->p_prm, agdev->in_ep);
-	agdev->as_in_alt = 0;
+	struct f_uac2 *uac2 = func_to_uac2(fn);
 
-	free_ep(&uac2->c_prm, agdev->out_ep);
-	agdev->as_out_alt = 0;
+	uac2->as_in_alt = 0;
+	uac2->as_out_alt = 0;
+	gaudio_stop_capture(&uac2->gaudio);
+	gaudio_stop_playback(&uac2->gaudio);
 }
 
 static int
 in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 {
 	struct usb_request *req = fn->config->cdev->req;
-	struct audio_dev *agdev = func_to_agdev(fn);
+	struct gaudio *agdev = func_to_gaudio(fn);
 	struct f_uac2_opts *opts;
 	u16 w_length = le16_to_cpu(cr->wLength);
 	u16 w_index = le16_to_cpu(cr->wIndex);
@@ -1235,7 +705,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 	int value = -EOPNOTSUPP;
 	int p_srate, c_srate;
 
-	opts = agdev_to_uac2_opts(agdev);
+	opts = gaudio_to_uac2_opts(agdev);
 	p_srate = opts->p_srate;
 	c_srate = opts->c_srate;
 
@@ -1265,7 +735,7 @@ static int
 in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 {
 	struct usb_request *req = fn->config->cdev->req;
-	struct audio_dev *agdev = func_to_agdev(fn);
+	struct gaudio *agdev = func_to_gaudio(fn);
 	struct f_uac2_opts *opts;
 	u16 w_length = le16_to_cpu(cr->wLength);
 	u16 w_index = le16_to_cpu(cr->wIndex);
@@ -1276,7 +746,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 	int value = -EOPNOTSUPP;
 	int p_srate, c_srate;
 
-	opts = agdev_to_uac2_opts(agdev);
+	opts = gaudio_to_uac2_opts(agdev);
 	p_srate = opts->p_srate;
 	c_srate = opts->c_srate;
 
@@ -1330,11 +800,12 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 static int
 setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 {
-	struct audio_dev *agdev = func_to_agdev(fn);
+	struct f_uac2 *uac2 = func_to_uac2(fn);
+	struct gaudio *agdev = func_to_gaudio(fn);
 	u16 w_index = le16_to_cpu(cr->wIndex);
 	u8 intf = w_index & 0xff;
 
-	if (intf != agdev->ac_intf) {
+	if (intf != uac2->ac_intf) {
 		dev_err(&agdev->gadget->dev,
 			"%s:%d Error!\n", __func__, __LINE__);
 		return -EOPNOTSUPP;
@@ -1352,7 +823,7 @@ static int
 afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
 {
 	struct usb_composite_dev *cdev = fn->config->cdev;
-	struct audio_dev *agdev = func_to_agdev(fn);
+	struct gaudio *agdev = func_to_gaudio(fn);
 	struct usb_request *req = cdev->req;
 	u16 w_length = le16_to_cpu(cr->wLength);
 	int value = -EOPNOTSUPP;
@@ -1495,10 +966,10 @@ static struct usb_function_instance *afunc_alloc_inst(void)
 
 static void afunc_free(struct usb_function *f)
 {
-	struct audio_dev *agdev;
+	struct gaudio *agdev;
 	struct f_uac2_opts *opts;
 
-	agdev = func_to_agdev(f);
+	agdev = func_to_gaudio(f);
 	opts = container_of(f->fi, struct f_uac2_opts, func_inst);
 	kfree(agdev);
 	mutex_lock(&opts->lock);
@@ -1508,16 +979,9 @@ static void afunc_free(struct usb_function *f)
 
 static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
 {
-	struct audio_dev *agdev = func_to_agdev(f);
-	struct uac2_rtd_params *prm;
-
-	snd_uac2_remove(agdev);
-
-	prm = &agdev->uac2.p_prm;
-	kfree(prm->rbuf);
+	struct gaudio *agdev = func_to_gaudio(f);
 
-	prm = &agdev->uac2.c_prm;
-	kfree(prm->rbuf);
+	gaudio_cleanup(agdev);
 	usb_free_all_descriptors(f);
 
 	agdev->gadget = NULL;
@@ -1525,11 +989,11 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
 
 static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
 {
-	struct audio_dev *agdev;
+	struct f_uac2	*uac2;
 	struct f_uac2_opts *opts;
 
-	agdev = kzalloc(sizeof(*agdev), GFP_KERNEL);
-	if (agdev == NULL)
+	uac2 = kzalloc(sizeof(*uac2), GFP_KERNEL);
+	if (uac2 == NULL)
 		return ERR_PTR(-ENOMEM);
 
 	opts = container_of(fi, struct f_uac2_opts, func_inst);
@@ -1537,19 +1001,20 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
 	++opts->refcnt;
 	mutex_unlock(&opts->lock);
 
-	agdev->func.name = "uac2_func";
-	agdev->func.bind = afunc_bind;
-	agdev->func.unbind = afunc_unbind;
-	agdev->func.set_alt = afunc_set_alt;
-	agdev->func.get_alt = afunc_get_alt;
-	agdev->func.disable = afunc_disable;
-	agdev->func.setup = afunc_setup;
-	agdev->func.free_func = afunc_free;
+	uac2->gaudio.func.name = "uac2_func";
+	uac2->gaudio.func.bind = afunc_bind;
+	uac2->gaudio.func.unbind = afunc_unbind;
+	uac2->gaudio.func.set_alt = afunc_set_alt;
+	uac2->gaudio.func.get_alt = afunc_get_alt;
+	uac2->gaudio.func.disable = afunc_disable;
+	uac2->gaudio.func.setup = afunc_setup;
+	uac2->gaudio.func.free_func = afunc_free;
 
-	return &agdev->func;
+	return &uac2->gaudio.func;
 }
 
 DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Yadwinder Singh");
 MODULE_AUTHOR("Jaswinder Singh");
+MODULE_AUTHOR("Ruslan Bilovol");
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
new file mode 100644
index 0000000..1e9dd95
--- /dev/null
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -0,0 +1,632 @@
+/*
+ * u_audio.c -- interface to USB gadget "ALSA sound card" utilities
+ *
+ * Copyright (C) 2016
+ * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ *
+ * Based on f_uac2.c which is:
+ *    Copyright (C) 2011
+ *    Yadwinder Singh (yadi.brar01@gmail.com)
+ *    Jaswinder Singh (jaswinder.singh@linaro.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "u_audio.h"
+
+#define BUFF_SIZE_MAX	(PAGE_SIZE * 16)
+#define PRD_SIZE_MAX	PAGE_SIZE
+#define MIN_PERIODS	4
+/* Keep everyone on toes */
+#define USB_XFERS	2
+
+struct uac_req {
+	struct uac_rtd_params *pp; /* parent param */
+	struct usb_request *req;
+};
+
+/* Runtime data params for one stream */
+struct uac_rtd_params {
+	struct snd_uac_chip *uac; /* parent chip */
+	bool ep_enabled; /* if the ep is enabled */
+	/* Size of the ring buffer */
+	size_t dma_bytes;
+	unsigned char *dma_area;
+
+	struct snd_pcm_substream *ss;
+
+	/* Ring buffer */
+	ssize_t hw_ptr;
+
+	void *rbuf;
+
+	size_t period_size;
+
+	unsigned max_psize;	/* MaxPacketSize of endpoint */
+	struct uac_req ureq[USB_XFERS];
+
+	spinlock_t lock;
+};
+
+struct snd_uac_chip {
+	struct gaudio *audio_dev;
+
+	struct uac_rtd_params p_prm;
+	struct uac_rtd_params c_prm;
+
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+
+	/* timekeeping for the playback endpoint */
+	unsigned int p_interval;
+	unsigned int p_residue;
+
+	/* pre-calculated values for playback iso completion */
+	unsigned int p_pktsize;
+	unsigned int p_pktsize_residue;
+	unsigned int p_framesize;
+};
+
+static struct snd_pcm_hardware uac_pcm_hardware = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
+		 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
+		 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS,
+	.periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX,
+	.buffer_bytes_max = BUFF_SIZE_MAX,
+	.period_bytes_max = PRD_SIZE_MAX,
+	.periods_min = MIN_PERIODS,
+};
+
+static void gaudio_iso_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	unsigned pending;
+	unsigned long flags;
+	unsigned int hw_ptr;
+	bool update_alsa = false;
+	int status = req->status;
+	struct uac_req *ur = req->context;
+	struct snd_pcm_substream *substream;
+	struct uac_rtd_params *prm = ur->pp;
+	struct snd_uac_chip *uac = prm->uac;
+
+	/* i/f shutting down */
+	if (!prm->ep_enabled || req->status == -ESHUTDOWN)
+		return;
+
+	/*
+	 * We can't really do much about bad xfers.
+	 * Afterall, the ISOCH xfers could fail legitimately.
+	 */
+	if (status)
+		pr_debug("%s: iso_complete status(%d) %d/%d\n",
+			__func__, status, req->actual, req->length);
+
+	substream = prm->ss;
+
+	/* Do nothing if ALSA isn't active */
+	if (!substream)
+		goto exit;
+
+	spin_lock_irqsave(&prm->lock, flags);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/*
+		 * For each IN packet, take the quotient of the current data
+		 * rate and the endpoint's interval as the base packet size.
+		 * If there is a residue from this division, add it to the
+		 * residue accumulator.
+		 */
+		req->length = uac->p_pktsize;
+		uac->p_residue += uac->p_pktsize_residue;
+
+		/*
+		 * Whenever there are more bytes in the accumulator than we
+		 * need to add one more sample frame, increase this packet's
+		 * size and decrease the accumulator.
+		 */
+		if (uac->p_residue / uac->p_interval >= uac->p_framesize) {
+			req->length += uac->p_framesize;
+			uac->p_residue -= uac->p_framesize *
+					   uac->p_interval;
+		}
+
+		req->actual = req->length;
+	}
+
+	pending = prm->hw_ptr % prm->period_size;
+	pending += req->actual;
+	if (pending >= prm->period_size)
+		update_alsa = true;
+
+	hw_ptr = prm->hw_ptr;
+	prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
+
+	spin_unlock_irqrestore(&prm->lock, flags);
+
+	/* Pack USB load in ALSA ring buffer */
+	pending = prm->dma_bytes - hw_ptr;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (unlikely(pending < req->actual)) {
+			memcpy(req->buf, prm->dma_area + hw_ptr, pending);
+			memcpy(req->buf + pending, prm->dma_area,
+			       req->actual - pending);
+		} else {
+			memcpy(req->buf, prm->dma_area + hw_ptr, req->actual);
+		}
+	} else {
+		if (unlikely(pending < req->actual)) {
+			memcpy(prm->dma_area + hw_ptr, req->buf, pending);
+			memcpy(prm->dma_area, req->buf + pending,
+			       req->actual - pending);
+		} else {
+			memcpy(prm->dma_area + hw_ptr, req->buf, req->actual);
+		}
+	}
+
+exit:
+	if (usb_ep_queue(ep, req, GFP_ATOMIC))
+		dev_err(uac->card->dev, "%d Error!\n", __LINE__);
+
+	if (update_alsa)
+		snd_pcm_period_elapsed(substream);
+}
+
+static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
+	struct uac_rtd_params *prm;
+	unsigned long flags;
+	int err = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prm = &uac->p_prm;
+	else
+		prm = &uac->c_prm;
+
+	spin_lock_irqsave(&prm->lock, flags);
+
+	/* Reset */
+	prm->hw_ptr = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		prm->ss = substream;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		prm->ss = NULL;
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	spin_unlock_irqrestore(&prm->lock, flags);
+
+	/* Clear buffer after Play stops */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss)
+		memset(prm->rbuf, 0, prm->max_psize * USB_XFERS);
+
+	return err;
+}
+
+static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
+	struct uac_rtd_params *prm;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prm = &uac->p_prm;
+	else
+		prm = &uac->c_prm;
+
+	return bytes_to_frames(substream->runtime, prm->hw_ptr);
+}
+
+static int uac_pcm_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
+	struct uac_rtd_params *prm;
+	int err;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prm = &uac->p_prm;
+	else
+		prm = &uac->c_prm;
+
+	err = snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+	if (err >= 0) {
+		prm->dma_bytes = substream->runtime->dma_bytes;
+		prm->dma_area = substream->runtime->dma_area;
+		prm->period_size = params_period_bytes(hw_params);
+	}
+
+	return err;
+}
+
+static int uac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
+	struct uac_rtd_params *prm;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prm = &uac->p_prm;
+	else
+		prm = &uac->c_prm;
+
+	prm->dma_area = NULL;
+	prm->dma_bytes = 0;
+	prm->period_size = 0;
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int uac_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct gaudio *audio_dev;
+	struct uac_params *params;
+	int p_ssize, c_ssize;
+	int p_srate, c_srate;
+	int p_chmask, c_chmask;
+
+	audio_dev = uac->audio_dev;
+	params = &audio_dev->params;
+	p_ssize = params->p_ssize;
+	c_ssize = params->c_ssize;
+	p_srate = params->p_srate;
+	c_srate = params->c_srate;
+	p_chmask = params->p_chmask;
+	c_chmask = params->c_chmask;
+	uac->p_residue = 0;
+
+	runtime->hw = uac_pcm_hardware;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		spin_lock_init(&uac->p_prm.lock);
+		runtime->hw.rate_min = p_srate;
+		switch (p_ssize) {
+		case 3:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
+			break;
+		case 4:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+			break;
+		default:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+			break;
+		}
+		runtime->hw.channels_min = num_channels(p_chmask);
+		runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize
+						/ runtime->hw.periods_min;
+	} else {
+		spin_lock_init(&uac->c_prm.lock);
+		runtime->hw.rate_min = c_srate;
+		switch (c_ssize) {
+		case 3:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
+			break;
+		case 4:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+			break;
+		default:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+			break;
+		}
+		runtime->hw.channels_min = num_channels(c_chmask);
+		runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize
+						/ runtime->hw.periods_min;
+	}
+
+	runtime->hw.rate_max = runtime->hw.rate_min;
+	runtime->hw.channels_max = runtime->hw.channels_min;
+
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+	return 0;
+}
+
+/* ALSA cries without these function pointers */
+static int uac_pcm_null(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static struct snd_pcm_ops uac_pcm_ops = {
+	.open = uac_pcm_open,
+	.close = uac_pcm_null,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = uac_pcm_hw_params,
+	.hw_free = uac_pcm_hw_free,
+	.trigger = uac_pcm_trigger,
+	.pointer = uac_pcm_pointer,
+	.prepare = uac_pcm_null,
+};
+
+static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep)
+{
+	struct snd_uac_chip *uac = prm->uac;
+	int i;
+
+	if (!prm->ep_enabled)
+		return;
+
+	prm->ep_enabled = false;
+
+	for (i = 0; i < USB_XFERS; i++) {
+		if (prm->ureq[i].req) {
+			usb_ep_dequeue(ep, prm->ureq[i].req);
+			usb_ep_free_request(ep, prm->ureq[i].req);
+			prm->ureq[i].req = NULL;
+		}
+	}
+
+	if (usb_ep_disable(ep))
+		dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
+}
+
+
+int gaudio_start_capture(struct gaudio *audio_dev)
+{
+	struct gaudio *agdev = audio_dev;
+	struct snd_uac_chip *uac = agdev->uac;
+	struct usb_gadget *gadget = agdev->gadget;
+	struct device *dev = &gadget->dev;
+	struct usb_request *req;
+	struct usb_ep *ep;
+	struct uac_rtd_params *prm;
+	int req_len, i;
+
+	ep = agdev->out_ep;
+	prm = &uac->c_prm;
+	config_ep_by_speed(gadget, &agdev->func, ep);
+	req_len = prm->max_psize;
+
+	prm->ep_enabled = true;
+	usb_ep_enable(ep);
+
+	for (i = 0; i < USB_XFERS; i++) {
+		if (!prm->ureq[i].req) {
+			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+			if (req == NULL)
+				return -ENOMEM;
+
+			prm->ureq[i].req = req;
+			prm->ureq[i].pp = prm;
+
+			req->zero = 0;
+			req->context = &prm->ureq[i];
+			req->length = req_len;
+			req->complete = gaudio_iso_complete;
+			req->buf = prm->rbuf + i * prm->max_psize;
+		}
+
+		if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
+			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gaudio_start_capture);
+
+void gaudio_stop_capture(struct gaudio *audio_dev)
+{
+	struct snd_uac_chip *uac = audio_dev->uac;
+
+	free_ep(&uac->c_prm, audio_dev->out_ep);
+}
+EXPORT_SYMBOL_GPL(gaudio_stop_capture);
+
+int gaudio_start_playback(struct gaudio *audio_dev)
+{
+	struct gaudio *agdev = audio_dev;
+	struct snd_uac_chip *uac = agdev->uac;
+	struct usb_gadget *gadget = agdev->gadget;
+	struct device *dev = &gadget->dev;
+	struct usb_request *req;
+	struct usb_ep *ep;
+	struct uac_rtd_params *prm;
+	struct uac_params *params = &agdev->params;
+	unsigned int factor, rate;
+	const struct usb_endpoint_descriptor *ep_desc;
+	int req_len, i;
+
+	ep = agdev->in_ep;
+	prm = &uac->p_prm;
+	config_ep_by_speed(gadget, &agdev->func, ep);
+
+	ep_desc = ep->desc;
+
+	/* pre-calculate the playback endpoint's interval */
+	if (gadget->speed == USB_SPEED_FULL)
+		factor = 1000;
+	else
+		factor = 8000;
+
+	/* pre-compute some values for iso_complete() */
+	uac->p_framesize = params->p_ssize *
+			    num_channels(params->p_chmask);
+	rate = params->p_srate * uac->p_framesize;
+	uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
+	uac->p_pktsize = min_t(unsigned int, rate / uac->p_interval,
+				prm->max_psize);
+
+	if (uac->p_pktsize < prm->max_psize)
+		uac->p_pktsize_residue = rate % uac->p_interval;
+	else
+		uac->p_pktsize_residue = 0;
+
+	req_len = uac->p_pktsize;
+	uac->p_residue = 0;
+
+	prm->ep_enabled = true;
+	usb_ep_enable(ep);
+
+	for (i = 0; i < USB_XFERS; i++) {
+		if (!prm->ureq[i].req) {
+			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+			if (req == NULL)
+				return -ENOMEM;
+
+			prm->ureq[i].req = req;
+			prm->ureq[i].pp = prm;
+
+			req->zero = 0;
+			req->context = &prm->ureq[i];
+			req->length = req_len;
+			req->complete = gaudio_iso_complete;
+			req->buf = prm->rbuf + i * prm->max_psize;
+		}
+
+		if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
+			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gaudio_start_playback);
+
+void gaudio_stop_playback(struct gaudio *audio_dev)
+{
+	struct snd_uac_chip *uac = audio_dev->uac;
+
+	free_ep(&uac->p_prm, audio_dev->in_ep);
+}
+EXPORT_SYMBOL_GPL(gaudio_stop_playback);
+
+int gaudio_setup(struct gaudio *gaudio, const char *pcm_name,
+					const char *card_name)
+{
+	struct snd_uac_chip *uac;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct uac_params *params;
+	int p_chmask, c_chmask;
+	int err;
+
+	if (!gaudio)
+		return -EINVAL;
+
+	uac = kzalloc(sizeof(*uac), GFP_KERNEL);
+	if (!uac)
+		return -ENOMEM;
+	gaudio->uac = uac;
+	uac->audio_dev = gaudio;
+
+	params = &gaudio->params;
+	p_chmask = params->p_chmask;
+	c_chmask = params->c_chmask;
+
+	if (c_chmask) {
+		struct uac_rtd_params *prm = &uac->c_prm;
+
+		uac->c_prm.uac = uac;
+		prm->max_psize = gaudio->out_ep_maxpsize;
+		prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
+		if (!prm->rbuf) {
+			prm->max_psize = 0;
+			err = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	if (p_chmask) {
+		struct uac_rtd_params *prm = &uac->p_prm;
+
+		uac->p_prm.uac = uac;
+		prm->max_psize = gaudio->in_ep_maxpsize;
+		prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
+		if (!prm->rbuf) {
+			prm->max_psize = 0;
+			err = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	/* Choose any slot, with no id */
+	err = snd_card_new(&gaudio->gadget->dev,
+			-1, NULL, THIS_MODULE, 0, &card);
+	if (err < 0)
+		goto fail;
+
+	uac->card = card;
+
+	/*
+	 * Create first PCM device
+	 * Create a substream only for non-zero channel streams
+	 */
+	err = snd_pcm_new(uac->card, pcm_name, 0,
+			       p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
+	if (err < 0)
+		goto snd_fail;
+
+	strcpy(pcm->name, pcm_name);
+	pcm->private_data = uac;
+	uac->pcm = pcm;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
+
+	strcpy(card->driver, card_name);
+	strcpy(card->shortname, card_name);
+	sprintf(card->longname, "%s %i", card_name, card->dev->id);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
+
+	err = snd_card_register(card);
+
+	if (!err)
+		return 0;
+
+snd_fail:
+	snd_card_free(card);
+fail:
+	kfree(uac->p_prm.rbuf);
+	kfree(uac->c_prm.rbuf);
+	kfree(uac);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(gaudio_setup);
+
+void gaudio_cleanup(struct gaudio *gaudio)
+{
+	struct snd_uac_chip *uac;
+	struct snd_card *card;
+
+	if (!gaudio || !gaudio->uac)
+		return;
+
+	uac = gaudio->uac;
+	card = uac->card;
+	if (card)
+		snd_card_free(card);
+
+	kfree(uac->p_prm.rbuf);
+	kfree(uac->c_prm.rbuf);
+	kfree(uac);
+}
+EXPORT_SYMBOL_GPL(gaudio_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities");
+MODULE_AUTHOR("Ruslan Bilovol");
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
new file mode 100644
index 0000000..d6f9a2d
--- /dev/null
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -0,0 +1,93 @@
+/*
+ * u_audio.h -- interface to USB gadget "ALSA sound card" utilities
+ *
+ * Copyright (C) 2016
+ * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __U_AUDIO_H
+#define __U_AUDIO_H
+
+#include <linux/usb/composite.h>
+
+struct uac_params {
+	/* playback */
+	int p_chmask;	/* channel mask */
+	int p_srate;	/* rate in Hz */
+	int p_ssize;	/* sample size */
+
+	/* capture */
+	int c_chmask;	/* channel mask */
+	int c_srate;	/* rate in Hz */
+	int c_ssize;	/* sample size */
+};
+
+struct gaudio {
+	struct usb_function func;
+	struct usb_gadget *gadget;
+
+	struct usb_ep *in_ep;
+	struct usb_ep *out_ep;
+
+	/* Max packet size for all in_ep possible speeds */
+	unsigned int in_ep_maxpsize;
+	/* Max packet size for all out_ep possible speeds */
+	unsigned int out_ep_maxpsize;
+
+	/* The ALSA Sound Card it represents on the USB-Client side */
+	struct snd_uac_chip *uac;
+
+	struct uac_params params;
+};
+
+static inline struct gaudio *func_to_gaudio(struct usb_function *f)
+{
+	return container_of(f, struct gaudio, func);
+}
+
+static inline uint num_channels(uint chanmask)
+{
+	uint num = 0;
+
+	while (chanmask) {
+		num += (chanmask & 1);
+		chanmask >>= 1;
+	}
+
+	return num;
+}
+
+/*
+ * gaudio_setup - initialize one virtual ALSA sound card
+ * @gaudio: struct with filled params, in_ep_maxpsize, out_ep_maxpsize
+ * @pcm_name: the id string for a PCM instance of this sound card
+ * @card_name: name of this soundcard
+ *
+ * This sets up the single virtual ALSA sound card that may be exported by a
+ * gadget driver using this framework.
+ *
+ * Context: may sleep
+ *
+ * Returns zero on success, or a negative error on failure.
+ */
+int gaudio_setup(struct gaudio *gaudio, const char *pcm_name,
+					const char *card_name);
+void gaudio_cleanup(struct gaudio *gaudio);
+
+int gaudio_start_capture(struct gaudio *gaudio);
+void gaudio_stop_capture(struct gaudio *gaudio);
+int gaudio_start_playback(struct gaudio *gaudio);
+void gaudio_stop_playback(struct gaudio *gaudio);
+
+#endif /* __U_AUDIO_H */
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index 0b36878..5344064 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -56,6 +56,7 @@ config USB_AUDIO
 	select SND_PCM
 	select USB_F_UAC1 if GADGET_UAC1
 	select USB_F_UAC2 if !GADGET_UAC1
+	select USB_U_AUDIO if USB_F_UAC2
 	help
 	  This Gadget Audio driver is compatible with USB Audio Class
 	  specification 2.0. It implements 1 AudioControl interface,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 3/5] usb: gadget: f_uac1: drop volume/mute functionality
  2016-05-23 23:50 [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
  2016-05-23 23:50 ` [RFC PATCH 1/5] usb: gadget: f_uac2: remove platform driver/device creation Ruslan Bilovol
  2016-05-23 23:50 ` [RFC PATCH 2/5] usb: gadget: f_uac2: split out audio core Ruslan Bilovol
@ 2016-05-23 23:50 ` Ruslan Bilovol
  2016-05-23 23:50 ` [RFC PATCH 4/5] usb: gadget: f_uac1: switch to u_audio core utilities Ruslan Bilovol
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-05-23 23:50 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Daniel Mack, Jassi Brar, linux-usb, linux-kernel

The volume/mute feature unit was dummy implementation
since this driver creation (2009) and never had
real volume control or mute functionality.
Since it was never implemented, drop it and
increase maintainability of the driver.
Those who want real volume/mute support may
revert this patch and add needed handlers

Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
---
 drivers/usb/gadget/function/f_uac1.c | 182 +----------------------------------
 1 file changed, 5 insertions(+), 177 deletions(-)

diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index f2ac0cb..ba498af 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -17,9 +17,6 @@
 
 #include "u_uac1.h"
 
-static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
-static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
-
 /*
  * DESCRIPTORS ... most are static, but strings and full
  * configuration descriptors are built on demand.
@@ -49,9 +46,9 @@ static struct usb_interface_descriptor ac_interface_desc = {
 DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
 
 #define UAC_DT_AC_HEADER_LENGTH	UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
-/* 1 input terminal, 1 output terminal and 1 feature unit */
+/* 1 input terminal and 1 output terminal */
 #define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
-	+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
+	+ UAC_DT_OUTPUT_TERMINAL_SIZE)
 /* B.3.2  Class-Specific AC Interface Descriptor */
 static struct uac1_ac_header_descriptor_1 ac_header_desc = {
 	.bLength =		UAC_DT_AC_HEADER_LENGTH,
@@ -77,54 +74,15 @@ static struct uac_input_terminal_descriptor input_terminal_desc = {
 	.wChannelConfig =	0x3,
 };
 
-DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
-
-#define FEATURE_UNIT_ID		2
-static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
-	.bLength		= UAC_DT_FEATURE_UNIT_SIZE(0),
-	.bDescriptorType	= USB_DT_CS_INTERFACE,
-	.bDescriptorSubtype	= UAC_FEATURE_UNIT,
-	.bUnitID		= FEATURE_UNIT_ID,
-	.bSourceID		= INPUT_TERMINAL_ID,
-	.bControlSize		= 2,
-	.bmaControls[0]		= (UAC_FU_MUTE | UAC_FU_VOLUME),
-};
-
-static struct usb_audio_control mute_control = {
-	.list = LIST_HEAD_INIT(mute_control.list),
-	.name = "Mute Control",
-	.type = UAC_FU_MUTE,
-	/* Todo: add real Mute control code */
-	.set = generic_set_cmd,
-	.get = generic_get_cmd,
-};
-
-static struct usb_audio_control volume_control = {
-	.list = LIST_HEAD_INIT(volume_control.list),
-	.name = "Volume Control",
-	.type = UAC_FU_VOLUME,
-	/* Todo: add real Volume control code */
-	.set = generic_set_cmd,
-	.get = generic_get_cmd,
-};
-
-static struct usb_audio_control_selector feature_unit = {
-	.list = LIST_HEAD_INIT(feature_unit.list),
-	.id = FEATURE_UNIT_ID,
-	.name = "Mute & Volume Control",
-	.type = UAC_FEATURE_UNIT,
-	.desc = (struct usb_descriptor_header *)&feature_unit_desc,
-};
-
-#define OUTPUT_TERMINAL_ID	3
+#define OUTPUT_TERMINAL_ID	2
 static struct uac1_output_terminal_descriptor output_terminal_desc = {
 	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE,
 	.bDescriptorType	= USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL,
 	.bTerminalID		= OUTPUT_TERMINAL_ID,
 	.wTerminalType		= UAC_OUTPUT_TERMINAL_SPEAKER,
-	.bAssocTerminal		= FEATURE_UNIT_ID,
-	.bSourceID		= FEATURE_UNIT_ID,
+	.bAssocTerminal		= 0,
+	.bSourceID		= INPUT_TERMINAL_ID,
 };
 
 /* B.4.1  Standard AS Interface Descriptor */
@@ -195,7 +153,6 @@ static struct usb_descriptor_header *f_audio_desc[] = {
 
 	(struct usb_descriptor_header *)&input_terminal_desc,
 	(struct usb_descriptor_header *)&output_terminal_desc,
-	(struct usb_descriptor_header *)&feature_unit_desc,
 
 	(struct usb_descriptor_header *)&as_interface_alt_0_desc,
 	(struct usb_descriptor_header *)&as_interface_alt_1_desc,
@@ -212,7 +169,6 @@ enum {
 	STR_AC_IF,
 	STR_INPUT_TERMINAL,
 	STR_INPUT_TERMINAL_CH_NAMES,
-	STR_FEAT_DESC_0,
 	STR_OUTPUT_TERMINAL,
 	STR_AS_IF_ALT0,
 	STR_AS_IF_ALT1,
@@ -222,7 +178,6 @@ static struct usb_string strings_uac1[] = {
 	[STR_AC_IF].s = "AC Interface",
 	[STR_INPUT_TERMINAL].s = "Input terminal",
 	[STR_INPUT_TERMINAL_CH_NAMES].s = "Channels",
-	[STR_FEAT_DESC_0].s = "Volume control & mute",
 	[STR_OUTPUT_TERMINAL].s = "Output terminal",
 	[STR_AS_IF_ALT0].s = "AS Interface",
 	[STR_AS_IF_ALT1].s = "AS Interface",
@@ -284,11 +239,6 @@ struct f_audio {
 	struct f_audio_buf *copy_buf;
 	struct work_struct playback_work;
 	struct list_head play_queue;
-
-	/* Control Set command */
-	struct list_head cs;
-	u8 set_cmd;
-	struct usb_audio_control *set_con;
 };
 
 static inline struct f_audio *func_to_audio(struct usb_function *f)
@@ -359,7 +309,6 @@ static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
 {
 	struct f_audio *audio = req->context;
 	int status = req->status;
-	u32 data = 0;
 	struct usb_ep *out_ep = audio->out_ep;
 
 	switch (status) {
@@ -367,92 +316,12 @@ static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
 	case 0:				/* normal completion? */
 		if (ep == out_ep)
 			f_audio_out_ep_complete(ep, req);
-		else if (audio->set_con) {
-			memcpy(&data, req->buf, req->length);
-			audio->set_con->set(audio->set_con, audio->set_cmd,
-					le16_to_cpu(data));
-			audio->set_con = NULL;
-		}
 		break;
 	default:
 		break;
 	}
 }
 
-static int audio_set_intf_req(struct usb_function *f,
-		const struct usb_ctrlrequest *ctrl)
-{
-	struct f_audio		*audio = func_to_audio(f);
-	struct usb_composite_dev *cdev = f->config->cdev;
-	struct usb_request	*req = cdev->req;
-	u8			id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
-	u16			len = le16_to_cpu(ctrl->wLength);
-	u16			w_value = le16_to_cpu(ctrl->wValue);
-	u8			con_sel = (w_value >> 8) & 0xFF;
-	u8			cmd = (ctrl->bRequest & 0x0F);
-	struct usb_audio_control_selector *cs;
-	struct usb_audio_control *con;
-
-	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
-			ctrl->bRequest, w_value, len, id);
-
-	list_for_each_entry(cs, &audio->cs, list) {
-		if (cs->id == id) {
-			list_for_each_entry(con, &cs->control, list) {
-				if (con->type == con_sel) {
-					audio->set_con = con;
-					break;
-				}
-			}
-			break;
-		}
-	}
-
-	audio->set_cmd = cmd;
-	req->context = audio;
-	req->complete = f_audio_complete;
-
-	return len;
-}
-
-static int audio_get_intf_req(struct usb_function *f,
-		const struct usb_ctrlrequest *ctrl)
-{
-	struct f_audio		*audio = func_to_audio(f);
-	struct usb_composite_dev *cdev = f->config->cdev;
-	struct usb_request	*req = cdev->req;
-	int			value = -EOPNOTSUPP;
-	u8			id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
-	u16			len = le16_to_cpu(ctrl->wLength);
-	u16			w_value = le16_to_cpu(ctrl->wValue);
-	u8			con_sel = (w_value >> 8) & 0xFF;
-	u8			cmd = (ctrl->bRequest & 0x0F);
-	struct usb_audio_control_selector *cs;
-	struct usb_audio_control *con;
-
-	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
-			ctrl->bRequest, w_value, len, id);
-
-	list_for_each_entry(cs, &audio->cs, list) {
-		if (cs->id == id) {
-			list_for_each_entry(con, &cs->control, list) {
-				if (con->type == con_sel && con->get) {
-					value = con->get(con, cmd);
-					break;
-				}
-			}
-			break;
-		}
-	}
-
-	req->context = audio;
-	req->complete = f_audio_complete;
-	len = min_t(size_t, sizeof(value), len);
-	memcpy(req->buf, &value, len);
-
-	return len;
-}
-
 static int audio_set_endpoint_req(struct usb_function *f,
 		const struct usb_ctrlrequest *ctrl)
 {
@@ -531,14 +400,6 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
 	 * activation uses set_alt().
 	 */
 	switch (ctrl->bRequestType) {
-	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
-		value = audio_set_intf_req(f, ctrl);
-		break;
-
-	case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
-		value = audio_get_intf_req(f, ctrl);
-		break;
-
 	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
 		value = audio_set_endpoint_req(f, ctrl);
 		break;
@@ -689,7 +550,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 	ac_interface_desc.iInterface = us[STR_AC_IF].id;
 	input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id;
 	input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id;
-	feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id;
 	output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id;
 	as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id;
 	as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id;
@@ -734,36 +594,6 @@ fail:
 
 /*-------------------------------------------------------------------------*/
 
-static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
-{
-	con->data[cmd] = value;
-
-	return 0;
-}
-
-static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
-{
-	return con->data[cmd];
-}
-
-/* Todo: add more control selecotor dynamically */
-static int control_selector_init(struct f_audio *audio)
-{
-	INIT_LIST_HEAD(&audio->cs);
-	list_add(&feature_unit.list, &audio->cs);
-
-	INIT_LIST_HEAD(&feature_unit.control);
-	list_add(&mute_control.list, &feature_unit.control);
-	list_add(&volume_control.list, &feature_unit.control);
-
-	volume_control.data[UAC__CUR] = 0xffc0;
-	volume_control.data[UAC__MIN] = 0xe3a0;
-	volume_control.data[UAC__MAX] = 0xfff0;
-	volume_control.data[UAC__RES] = 0x0030;
-
-	return 0;
-}
-
 static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item)
 {
 	return container_of(to_config_group(item), struct f_uac1_opts,
@@ -970,8 +800,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
 	audio->card.func.disable = f_audio_disable;
 	audio->card.func.free_func = f_audio_free;
 
-	control_selector_init(audio);
-
 	INIT_WORK(&audio->playback_work, f_audio_playback_work);
 
 	return &audio->card.func;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 4/5] usb: gadget: f_uac1: switch to u_audio core utilities
  2016-05-23 23:50 [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
                   ` (2 preceding siblings ...)
  2016-05-23 23:50 ` [RFC PATCH 3/5] usb: gadget: f_uac1: drop volume/mute functionality Ruslan Bilovol
@ 2016-05-23 23:50 ` Ruslan Bilovol
  2016-05-23 23:50 ` [RFC PATCH 5/5] usb: gadget: f_uac1: add capture support Ruslan Bilovol
  2016-06-08  8:03 ` [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
  5 siblings, 0 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-05-23 23:50 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Daniel Mack, Jassi Brar, linux-usb, linux-kernel

Reuse existing u_audio core utilities making f_uac1
much simpler.

This also drops previous f_uac1 approach (write audio
samples directly to existing ALSA sound card) and moves
to more generic/flexible one - create an f_uac1 ALSA
sound card that represents USB Audio function and
allows to be used by userspace tools.

As a side effect, using u_audio it will be much
easier to create gadget -> PC Host audio stream
in the future

Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
---
 drivers/usb/gadget/Kconfig           |   8 +-
 drivers/usb/gadget/function/Makefile |   2 +-
 drivers/usb/gadget/function/f_uac1.c | 431 +++++++++++------------------------
 drivers/usb/gadget/function/u_uac1.c | 314 -------------------------
 drivers/usb/gadget/function/u_uac1.h |  65 +-----
 drivers/usb/gadget/legacy/Kconfig    |   2 +-
 drivers/usb/gadget/legacy/audio.c    |  42 ++--
 7 files changed, 162 insertions(+), 702 deletions(-)
 delete mode 100644 drivers/usb/gadget/function/u_uac1.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 42d8508..fd6ee1d 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -389,12 +389,16 @@ config USB_CONFIGFS_F_UAC1
 	depends on SND
 	select USB_LIBCOMPOSITE
 	select SND_PCM
+	select USB_U_AUDIO
 	select USB_F_UAC1
 	help
 	  This Audio function implements 1 AudioControl interface,
 	  1 AudioStreaming Interface each for USB-OUT and USB-IN.
-	  This driver requires a real Audio codec to be present
-	  on the device.
+	  This driver doesn't expect any real Audio codec to be present
+	  on the device - the audio streams are simply sinked to
+	  a virtual ALSA sound card created. The user-space
+	  application may choose to do whatever it wants with the data
+	  received from the USB Host.
 
 config USB_CONFIGFS_F_UAC2
 	bool "Audio Class 2.0"
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index b29f2ae..bd9d7d5 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -33,7 +33,7 @@ obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
 usb_f_fs-y			:= f_fs.o
 obj-$(CONFIG_USB_F_FS)		+= usb_f_fs.o
 obj-$(CONFIG_USB_U_AUDIO)	+= u_audio.o
-usb_f_uac1-y			:= f_uac1.o u_uac1.o
+usb_f_uac1-y			:= f_uac1.o
 obj-$(CONFIG_USB_F_UAC1)	+= usb_f_uac1.o
 usb_f_uac2-y			:= f_uac2.o
 obj-$(CONFIG_USB_F_UAC2)	+= usb_f_uac2.o
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index ba498af..120bba9 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -4,6 +4,8 @@
  * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
  * Copyright (C) 2008 Analog Devices, Inc
  *
+ * Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ *
  * Enter bugs at http://blackfin.uclinux.org/
  *
  * Licensed under the GPL-2 or later.
@@ -15,8 +17,20 @@
 #include <linux/device.h>
 #include <linux/atomic.h>
 
+#include "u_audio.h"
 #include "u_uac1.h"
 
+struct f_uac1 {
+	struct gaudio gaudio;
+	u8 ac_intf, as_out_intf;
+	u8 ac_alt, as_out_alt;	/* needed for get_alt() */
+};
+
+static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
+{
+	return container_of(f, struct f_uac1, gaudio.func);
+}
+
 /*
  * DESCRIPTORS ... most are static, but strings and full
  * configuration descriptors are built on demand.
@@ -198,130 +212,6 @@ static struct usb_gadget_strings *uac1_strings[] = {
  * This function is an ALSA sound card following USB Audio Class Spec 1.0.
  */
 
-/*-------------------------------------------------------------------------*/
-struct f_audio_buf {
-	u8 *buf;
-	int actual;
-	struct list_head list;
-};
-
-static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
-{
-	struct f_audio_buf *copy_buf;
-
-	copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
-	if (!copy_buf)
-		return ERR_PTR(-ENOMEM);
-
-	copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC);
-	if (!copy_buf->buf) {
-		kfree(copy_buf);
-		return ERR_PTR(-ENOMEM);
-	}
-
-	return copy_buf;
-}
-
-static void f_audio_buffer_free(struct f_audio_buf *audio_buf)
-{
-	kfree(audio_buf->buf);
-	kfree(audio_buf);
-}
-/*-------------------------------------------------------------------------*/
-
-struct f_audio {
-	struct gaudio			card;
-
-	/* endpoints handle full and/or high speeds */
-	struct usb_ep			*out_ep;
-
-	spinlock_t			lock;
-	struct f_audio_buf *copy_buf;
-	struct work_struct playback_work;
-	struct list_head play_queue;
-};
-
-static inline struct f_audio *func_to_audio(struct usb_function *f)
-{
-	return container_of(f, struct f_audio, card.func);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void f_audio_playback_work(struct work_struct *data)
-{
-	struct f_audio *audio = container_of(data, struct f_audio,
-					playback_work);
-	struct f_audio_buf *play_buf;
-
-	spin_lock_irq(&audio->lock);
-	if (list_empty(&audio->play_queue)) {
-		spin_unlock_irq(&audio->lock);
-		return;
-	}
-	play_buf = list_first_entry(&audio->play_queue,
-			struct f_audio_buf, list);
-	list_del(&play_buf->list);
-	spin_unlock_irq(&audio->lock);
-
-	u_audio_playback(&audio->card, play_buf->buf, play_buf->actual);
-	f_audio_buffer_free(play_buf);
-}
-
-static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
-{
-	struct f_audio *audio = req->context;
-	struct usb_composite_dev *cdev = audio->card.func.config->cdev;
-	struct f_audio_buf *copy_buf = audio->copy_buf;
-	struct f_uac1_opts *opts;
-	int audio_buf_size;
-	int err;
-
-	opts = container_of(audio->card.func.fi, struct f_uac1_opts,
-			    func_inst);
-	audio_buf_size = opts->audio_buf_size;
-
-	if (!copy_buf)
-		return -EINVAL;
-
-	/* Copy buffer is full, add it to the play_queue */
-	if (audio_buf_size - copy_buf->actual < req->actual) {
-		list_add_tail(&copy_buf->list, &audio->play_queue);
-		schedule_work(&audio->playback_work);
-		copy_buf = f_audio_buffer_alloc(audio_buf_size);
-		if (IS_ERR(copy_buf))
-			return -ENOMEM;
-	}
-
-	memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual);
-	copy_buf->actual += req->actual;
-	audio->copy_buf = copy_buf;
-
-	err = usb_ep_queue(ep, req, GFP_ATOMIC);
-	if (err)
-		ERROR(cdev, "%s queue req: %d\n", ep->name, err);
-
-	return 0;
-
-}
-
-static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
-{
-	struct f_audio *audio = req->context;
-	int status = req->status;
-	struct usb_ep *out_ep = audio->out_ep;
-
-	switch (status) {
-
-	case 0:				/* normal completion? */
-		if (ep == out_ep)
-			f_audio_out_ep_complete(ep, req);
-		break;
-	default:
-		break;
-	}
-}
-
 static int audio_set_endpoint_req(struct usb_function *f,
 		const struct usb_ctrlrequest *ctrl)
 {
@@ -432,118 +322,88 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
 
 static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
-	struct f_audio		*audio = func_to_audio(f);
 	struct usb_composite_dev *cdev = f->config->cdev;
-	struct usb_ep *out_ep = audio->out_ep;
-	struct usb_request *req;
-	struct f_uac1_opts *opts;
-	int req_buf_size, req_count, audio_buf_size;
-	int i = 0, err = 0;
-
-	DBG(cdev, "intf %d, alt %d\n", intf, alt);
+	struct usb_gadget *gadget = cdev->gadget;
+	struct device *dev = &gadget->dev;
+	struct f_uac1 *uac1 = func_to_uac1(f);
+	int ret = 0;
+
+	/* No i/f has more than 2 alt settings */
+	if (alt > 1) {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return -EINVAL;
+	}
 
-	opts = container_of(f->fi, struct f_uac1_opts, func_inst);
-	req_buf_size = opts->req_buf_size;
-	req_count = opts->req_count;
-	audio_buf_size = opts->audio_buf_size;
-
-	if (intf == 1) {
-		if (alt == 1) {
-			err = config_ep_by_speed(cdev->gadget, f, out_ep);
-			if (err)
-				return err;
-
-			usb_ep_enable(out_ep);
-			audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
-			if (IS_ERR(audio->copy_buf))
-				return -ENOMEM;
-
-			/*
-			 * allocate a bunch of read buffers
-			 * and queue them all at once.
-			 */
-			for (i = 0; i < req_count && err == 0; i++) {
-				req = usb_ep_alloc_request(out_ep, GFP_ATOMIC);
-				if (req) {
-					req->buf = kzalloc(req_buf_size,
-							GFP_ATOMIC);
-					if (req->buf) {
-						req->length = req_buf_size;
-						req->context = audio;
-						req->complete =
-							f_audio_complete;
-						err = usb_ep_queue(out_ep,
-							req, GFP_ATOMIC);
-						if (err)
-							ERROR(cdev,
-							"%s queue req: %d\n",
-							out_ep->name, err);
-					} else
-						err = -ENOMEM;
-				} else
-					err = -ENOMEM;
-			}
-
-		} else {
-			struct f_audio_buf *copy_buf = audio->copy_buf;
-			if (copy_buf) {
-				list_add_tail(&copy_buf->list,
-						&audio->play_queue);
-				schedule_work(&audio->playback_work);
-			}
+	if (intf == uac1->ac_intf) {
+		/* Control I/f has only 1 AltSetting - 0 */
+		if (alt) {
+			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+			return -EINVAL;
 		}
+		return 0;
 	}
 
-	return err;
+	if (intf == uac1->as_out_intf) {
+		uac1->as_out_alt = alt;
+
+		if (alt)
+			ret = gaudio_start_capture(&uac1->gaudio);
+		else
+			gaudio_stop_capture(&uac1->gaudio);
+	} else {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	return ret;
 }
 
-static void f_audio_disable(struct usb_function *f)
+static int f_audio_get_alt(struct usb_function *f, unsigned intf)
 {
-	return;
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_gadget *gadget = cdev->gadget;
+	struct device *dev = &gadget->dev;
+	struct f_uac1 *uac1 = func_to_uac1(f);
+
+	if (intf == uac1->ac_intf)
+		return uac1->ac_alt;
+	else if (intf == uac1->as_out_intf)
+		return uac1->as_out_alt;
+	else
+		dev_err(dev, "%s:%d Invalid Interface %d!\n",
+			__func__, __LINE__, intf);
+
+	return -EINVAL;
 }
 
-/*-------------------------------------------------------------------------*/
 
-static void f_audio_build_desc(struct f_audio *audio)
+static void f_audio_disable(struct usb_function *f)
 {
-	struct gaudio *card = &audio->card;
-	u8 *sam_freq;
-	int rate;
+	struct f_uac1 *uac1 = func_to_uac1(f);
 
-	/* Set channel numbers */
-	input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card);
-	as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card);
+	uac1->as_out_alt = 0;
 
-	/* Set sample rates */
-	rate = u_audio_get_playback_rate(card);
-	sam_freq = as_type_i_desc.tSamFreq[0];
-	memcpy(sam_freq, &rate, 3);
-
-	/* Todo: Set Sample bits and other parameters */
-
-	return;
+	gaudio_stop_capture(&uac1->gaudio);
 }
 
+/*-------------------------------------------------------------------------*/
+
 /* audio function driver setup/binding */
-static int
-f_audio_bind(struct usb_configuration *c, struct usb_function *f)
+static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
-	struct f_audio		*audio = func_to_audio(f);
+	struct usb_gadget	*gadget = cdev->gadget;
+	struct f_uac1		*uac1 = func_to_uac1(f);
+	struct gaudio		*audio = func_to_gaudio(f);
 	struct usb_string	*us;
 	int			status;
 	struct usb_ep		*ep = NULL;
 	struct f_uac1_opts	*audio_opts;
+	u8 *sam_freq;
+	int rate;
 
 	audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
-	audio->card.gadget = c->cdev->gadget;
-	/* set up ASLA audio devices */
-	if (!audio_opts->bound) {
-		status = gaudio_setup(&audio->card);
-		if (status < 0)
-			return status;
-		audio_opts->bound = true;
-	}
+
 	us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
 	if (IS_ERR(us))
 		return PTR_ERR(us);
@@ -554,20 +414,35 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 	as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id;
 	as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id;
 
+	/* Set channel numbers */
+	input_terminal_desc.bNrChannels = num_channels(audio_opts->c_chmask);
+	input_terminal_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask);
+	as_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask);
+	as_type_i_desc.bSubframeSize = audio_opts->c_ssize;
+	as_type_i_desc.bBitResolution = audio_opts->c_ssize * 8;
 
-	f_audio_build_desc(audio);
+	/* Set sample rates */
+	rate = audio_opts->c_srate;
+	sam_freq = as_type_i_desc.tSamFreq[0];
+	memcpy(sam_freq, &rate, 3);
 
 	/* allocate instance-specific interface IDs, and patch descriptors */
 	status = usb_interface_id(c, f);
 	if (status < 0)
 		goto fail;
 	ac_interface_desc.bInterfaceNumber = status;
+	uac1->ac_intf = status;
+	uac1->ac_alt = 0;
 
 	status = usb_interface_id(c, f);
 	if (status < 0)
 		goto fail;
 	as_interface_alt_0_desc.bInterfaceNumber = status;
 	as_interface_alt_1_desc.bInterfaceNumber = status;
+	uac1->as_out_intf = status;
+	uac1->as_out_alt = 0;
+
+	audio->gadget = gadget;
 
 	status = -ENODEV;
 
@@ -578,17 +453,26 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 	audio->out_ep = ep;
 	audio->out_ep->desc = &as_out_ep_desc;
 
-	status = -ENOMEM;
-
 	/* copy descriptors, and track endpoint copies */
 	status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
 					NULL);
 	if (status)
 		goto fail;
+
+	audio->out_ep_maxpsize = as_out_ep_desc.wMaxPacketSize;
+	audio->params.c_chmask = audio_opts->c_chmask;
+	audio->params.c_srate = audio_opts->c_srate;
+	audio->params.c_ssize = audio_opts->c_ssize;
+
+	status = gaudio_setup(audio, "UAC1_PCM", "UAC1_Gadget");
+	if (status)
+		goto err_card_register;
+
 	return 0;
 
+err_card_register:
+	usb_free_all_descriptors(f);
 fail:
-	gaudio_cleanup(&audio->card);
 	return status;
 }
 
@@ -611,7 +495,7 @@ static struct configfs_item_operations f_uac1_item_ops = {
 	.release	= f_uac1_attr_release,
 };
 
-#define UAC1_INT_ATTRIBUTE(name)					\
+#define UAC1_ATTRIBUTE(name)					\
 static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\
 					 char *page)			\
 {									\
@@ -652,64 +536,14 @@ end:									\
 									\
 CONFIGFS_ATTR(f_uac1_opts_, name)
 
-UAC1_INT_ATTRIBUTE(req_buf_size);
-UAC1_INT_ATTRIBUTE(req_count);
-UAC1_INT_ATTRIBUTE(audio_buf_size);
-
-#define UAC1_STR_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;							\
-									\
-	mutex_lock(&opts->lock);					\
-	result = sprintf(page, "%s\n", opts->name);			\
-	mutex_unlock(&opts->lock);					\
-									\
-	return result;							\
-}									\
-									\
-static ssize_t f_uac1_opts_##name##_store(struct config_item *item,	\
-					  const char *page, size_t len)	\
-{									\
-	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
-	int ret = -EBUSY;						\
-	char *tmp;							\
-									\
-	mutex_lock(&opts->lock);					\
-	if (opts->refcnt)						\
-		goto end;						\
-									\
-	tmp = kstrndup(page, len, GFP_KERNEL);				\
-	if (tmp) {							\
-		ret = -ENOMEM;						\
-		goto end;						\
-	}								\
-	if (opts->name##_alloc)						\
-		kfree(opts->name);					\
-	opts->name##_alloc = true;					\
-	opts->name = tmp;						\
-	ret = len;							\
-									\
-end:									\
-	mutex_unlock(&opts->lock);					\
-	return ret;							\
-}									\
-									\
-CONFIGFS_ATTR(f_uac1_opts_, name)
-
-UAC1_STR_ATTRIBUTE(fn_play);
-UAC1_STR_ATTRIBUTE(fn_cap);
-UAC1_STR_ATTRIBUTE(fn_cntl);
+UAC1_ATTRIBUTE(c_chmask);
+UAC1_ATTRIBUTE(c_srate);
+UAC1_ATTRIBUTE(c_ssize);
 
 static struct configfs_attribute *f_uac1_attrs[] = {
-	&f_uac1_opts_attr_req_buf_size,
-	&f_uac1_opts_attr_req_count,
-	&f_uac1_opts_attr_audio_buf_size,
-	&f_uac1_opts_attr_fn_play,
-	&f_uac1_opts_attr_fn_cap,
-	&f_uac1_opts_attr_fn_cntl,
+	&f_uac1_opts_attr_c_chmask,
+	&f_uac1_opts_attr_c_srate,
+	&f_uac1_opts_attr_c_ssize,
 	NULL,
 };
 
@@ -724,12 +558,6 @@ static void f_audio_free_inst(struct usb_function_instance *f)
 	struct f_uac1_opts *opts;
 
 	opts = container_of(f, struct f_uac1_opts, func_inst);
-	if (opts->fn_play_alloc)
-		kfree(opts->fn_play);
-	if (opts->fn_cap_alloc)
-		kfree(opts->fn_cap);
-	if (opts->fn_cntl_alloc)
-		kfree(opts->fn_cntl);
 	kfree(opts);
 }
 
@@ -747,21 +575,18 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
 	config_group_init_type_name(&opts->func_inst.group, "",
 				    &f_uac1_func_type);
 
-	opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
-	opts->req_count = UAC1_REQ_COUNT;
-	opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE;
-	opts->fn_play = FILE_PCM_PLAYBACK;
-	opts->fn_cap = FILE_PCM_CAPTURE;
-	opts->fn_cntl = FILE_CONTROL;
+	opts->c_chmask = UAC1_DEF_CCHMASK;
+	opts->c_srate = UAC1_DEF_CSRATE;
+	opts->c_ssize = UAC1_DEF_CSSIZE;
 	return &opts->func_inst;
 }
 
 static void f_audio_free(struct usb_function *f)
 {
-	struct f_audio *audio = func_to_audio(f);
+	struct gaudio *audio;
 	struct f_uac1_opts *opts;
 
-	gaudio_cleanup(&audio->card);
+	audio = func_to_gaudio(f);
 	opts = container_of(f->fi, struct f_uac1_opts, func_inst);
 	kfree(audio);
 	mutex_lock(&opts->lock);
@@ -771,40 +596,42 @@ static void f_audio_free(struct usb_function *f)
 
 static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
 {
+	struct gaudio *audio = func_to_gaudio(f);
+
+	gaudio_cleanup(audio);
 	usb_free_all_descriptors(f);
+
+	audio->gadget = NULL;
 }
 
 static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
 {
-	struct f_audio *audio;
+	struct f_uac1 *uac1;
 	struct f_uac1_opts *opts;
 
 	/* allocate and initialize one new instance */
-	audio = kzalloc(sizeof(*audio), GFP_KERNEL);
-	if (!audio)
+	uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL);
+	if (!uac1)
 		return ERR_PTR(-ENOMEM);
 
-	audio->card.func.name = "g_audio";
-
 	opts = container_of(fi, struct f_uac1_opts, func_inst);
 	mutex_lock(&opts->lock);
 	++opts->refcnt;
 	mutex_unlock(&opts->lock);
-	INIT_LIST_HEAD(&audio->play_queue);
-	spin_lock_init(&audio->lock);
-
-	audio->card.func.bind = f_audio_bind;
-	audio->card.func.unbind = f_audio_unbind;
-	audio->card.func.set_alt = f_audio_set_alt;
-	audio->card.func.setup = f_audio_setup;
-	audio->card.func.disable = f_audio_disable;
-	audio->card.func.free_func = f_audio_free;
 
-	INIT_WORK(&audio->playback_work, f_audio_playback_work);
+	uac1->gaudio.func.name = "g_audio";
+	uac1->gaudio.func.bind = f_audio_bind;
+	uac1->gaudio.func.unbind = f_audio_unbind;
+	uac1->gaudio.func.set_alt = f_audio_set_alt;
+	uac1->gaudio.func.get_alt = f_audio_get_alt;
+	uac1->gaudio.func.setup = f_audio_setup;
+	uac1->gaudio.func.disable = f_audio_disable;
+	uac1->gaudio.func.free_func = f_audio_free;
 
-	return &audio->card.func;
+	return &uac1->gaudio.func;
 }
 
 DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Bryan Wu");
+MODULE_AUTHOR("Ruslan Bilovol");
diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c
deleted file mode 100644
index c78c841..0000000
--- a/drivers/usb/gadget/function/u_uac1.c
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * u_uac1.c -- ALSA audio utilities for Gadget stack
- *
- * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
- * Copyright (C) 2008 Analog Devices, Inc
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/ctype.h>
-#include <linux/random.h>
-#include <linux/syscalls.h>
-
-#include "u_uac1.h"
-
-/*
- * This component encapsulates the ALSA devices for USB audio gadget
- */
-
-/*-------------------------------------------------------------------------*/
-
-/**
- * Some ALSA internal helper functions
- */
-static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
-{
-	struct snd_interval t;
-	t.empty = 0;
-	t.min = t.max = val;
-	t.openmin = t.openmax = 0;
-	t.integer = 1;
-	return snd_interval_refine(i, &t);
-}
-
-static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
-				 snd_pcm_hw_param_t var, unsigned int val,
-				 int dir)
-{
-	int changed;
-	if (hw_is_mask(var)) {
-		struct snd_mask *m = hw_param_mask(params, var);
-		if (val == 0 && dir < 0) {
-			changed = -EINVAL;
-			snd_mask_none(m);
-		} else {
-			if (dir > 0)
-				val++;
-			else if (dir < 0)
-				val--;
-			changed = snd_mask_refine_set(
-					hw_param_mask(params, var), val);
-		}
-	} else if (hw_is_interval(var)) {
-		struct snd_interval *i = hw_param_interval(params, var);
-		if (val == 0 && dir < 0) {
-			changed = -EINVAL;
-			snd_interval_none(i);
-		} else if (dir == 0)
-			changed = snd_interval_refine_set(i, val);
-		else {
-			struct snd_interval t;
-			t.openmin = 1;
-			t.openmax = 1;
-			t.empty = 0;
-			t.integer = 0;
-			if (dir < 0) {
-				t.min = val - 1;
-				t.max = val;
-			} else {
-				t.min = val;
-				t.max = val+1;
-			}
-			changed = snd_interval_refine(i, &t);
-		}
-	} else
-		return -EINVAL;
-	if (changed) {
-		params->cmask |= 1 << var;
-		params->rmask |= 1 << var;
-	}
-	return changed;
-}
-/*-------------------------------------------------------------------------*/
-
-/**
- * Set default hardware params
- */
-static int playback_default_hw_params(struct gaudio_snd_dev *snd)
-{
-	struct snd_pcm_substream *substream = snd->substream;
-	struct snd_pcm_hw_params *params;
-	snd_pcm_sframes_t result;
-
-       /*
-	* SNDRV_PCM_ACCESS_RW_INTERLEAVED,
-	* SNDRV_PCM_FORMAT_S16_LE
-	* CHANNELS: 2
-	* RATE: 48000
-	*/
-	snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
-	snd->format = SNDRV_PCM_FORMAT_S16_LE;
-	snd->channels = 2;
-	snd->rate = 48000;
-
-	params = kzalloc(sizeof(*params), GFP_KERNEL);
-	if (!params)
-		return -ENOMEM;
-
-	_snd_pcm_hw_params_any(params);
-	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
-			snd->access, 0);
-	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
-			snd->format, 0);
-	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
-			snd->channels, 0);
-	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
-			snd->rate, 0);
-
-	snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
-	snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
-
-	result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
-	if (result < 0) {
-		ERROR(snd->card,
-			"Preparing sound card failed: %d\n", (int)result);
-		kfree(params);
-		return result;
-	}
-
-	/* Store the hardware parameters */
-	snd->access = params_access(params);
-	snd->format = params_format(params);
-	snd->channels = params_channels(params);
-	snd->rate = params_rate(params);
-
-	kfree(params);
-
-	INFO(snd->card,
-		"Hardware params: access %x, format %x, channels %d, rate %d\n",
-		snd->access, snd->format, snd->channels, snd->rate);
-
-	return 0;
-}
-
-/**
- * Playback audio buffer data by ALSA PCM device
- */
-size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
-{
-	struct gaudio_snd_dev	*snd = &card->playback;
-	struct snd_pcm_substream *substream = snd->substream;
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	mm_segment_t old_fs;
-	ssize_t result;
-	snd_pcm_sframes_t frames;
-
-try_again:
-	if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
-		runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
-		result = snd_pcm_kernel_ioctl(substream,
-				SNDRV_PCM_IOCTL_PREPARE, NULL);
-		if (result < 0) {
-			ERROR(card, "Preparing sound card failed: %d\n",
-					(int)result);
-			return result;
-		}
-	}
-
-	frames = bytes_to_frames(runtime, count);
-	old_fs = get_fs();
-	set_fs(KERNEL_DS);
-	result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames);
-	if (result != frames) {
-		ERROR(card, "Playback error: %d\n", (int)result);
-		set_fs(old_fs);
-		goto try_again;
-	}
-	set_fs(old_fs);
-
-	return 0;
-}
-
-int u_audio_get_playback_channels(struct gaudio *card)
-{
-	return card->playback.channels;
-}
-
-int u_audio_get_playback_rate(struct gaudio *card)
-{
-	return card->playback.rate;
-}
-
-/**
- * Open ALSA PCM and control device files
- * Initial the PCM or control device
- */
-static int gaudio_open_snd_dev(struct gaudio *card)
-{
-	struct snd_pcm_file *pcm_file;
-	struct gaudio_snd_dev *snd;
-	struct f_uac1_opts *opts;
-	char *fn_play, *fn_cap, *fn_cntl;
-
-	opts = container_of(card->func.fi, struct f_uac1_opts, func_inst);
-	fn_play = opts->fn_play;
-	fn_cap = opts->fn_cap;
-	fn_cntl = opts->fn_cntl;
-
-	/* Open control device */
-	snd = &card->control;
-	snd->filp = filp_open(fn_cntl, O_RDWR, 0);
-	if (IS_ERR(snd->filp)) {
-		int ret = PTR_ERR(snd->filp);
-		ERROR(card, "unable to open sound control device file: %s\n",
-				fn_cntl);
-		snd->filp = NULL;
-		return ret;
-	}
-	snd->card = card;
-
-	/* Open PCM playback device and setup substream */
-	snd = &card->playback;
-	snd->filp = filp_open(fn_play, O_WRONLY, 0);
-	if (IS_ERR(snd->filp)) {
-		int ret = PTR_ERR(snd->filp);
-
-		ERROR(card, "No such PCM playback device: %s\n", fn_play);
-		snd->filp = NULL;
-		return ret;
-	}
-	pcm_file = snd->filp->private_data;
-	snd->substream = pcm_file->substream;
-	snd->card = card;
-	playback_default_hw_params(snd);
-
-	/* Open PCM capture device and setup substream */
-	snd = &card->capture;
-	snd->filp = filp_open(fn_cap, O_RDONLY, 0);
-	if (IS_ERR(snd->filp)) {
-		ERROR(card, "No such PCM capture device: %s\n", fn_cap);
-		snd->substream = NULL;
-		snd->card = NULL;
-		snd->filp = NULL;
-	} else {
-		pcm_file = snd->filp->private_data;
-		snd->substream = pcm_file->substream;
-		snd->card = card;
-	}
-
-	return 0;
-}
-
-/**
- * Close ALSA PCM and control device files
- */
-static int gaudio_close_snd_dev(struct gaudio *gau)
-{
-	struct gaudio_snd_dev	*snd;
-
-	/* Close control device */
-	snd = &gau->control;
-	if (snd->filp)
-		filp_close(snd->filp, NULL);
-
-	/* Close PCM playback device and setup substream */
-	snd = &gau->playback;
-	if (snd->filp)
-		filp_close(snd->filp, NULL);
-
-	/* Close PCM capture device and setup substream */
-	snd = &gau->capture;
-	if (snd->filp)
-		filp_close(snd->filp, NULL);
-
-	return 0;
-}
-
-/**
- * gaudio_setup - setup ALSA interface and preparing for USB transfer
- *
- * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
- *
- * Returns negative errno, or zero on success
- */
-int gaudio_setup(struct gaudio *card)
-{
-	int	ret;
-
-	ret = gaudio_open_snd_dev(card);
-	if (ret)
-		ERROR(card, "we need at least one control device\n");
-
-	return ret;
-
-}
-
-/**
- * gaudio_cleanup - remove ALSA device interface
- *
- * This is called to free all resources allocated by @gaudio_setup().
- */
-void gaudio_cleanup(struct gaudio *the_card)
-{
-	if (the_card)
-		gaudio_close_snd_dev(the_card);
-}
-
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 5c2ac8e..fb871d6 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -9,74 +9,29 @@
  * Licensed under the GPL-2 or later.
  */
 
-#ifndef __U_AUDIO_H
-#define __U_AUDIO_H
+#ifndef __U_UAC1_H
+#define __U_UAC1_H
 
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/composite.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
-#define FILE_PCM_PLAYBACK	"/dev/snd/pcmC0D0p"
-#define FILE_PCM_CAPTURE	"/dev/snd/pcmC0D0c"
-#define FILE_CONTROL		"/dev/snd/controlC0"
-
 #define UAC1_OUT_EP_MAX_PACKET_SIZE	200
-#define UAC1_REQ_COUNT			256
-#define UAC1_AUDIO_BUF_SIZE		48000
-
-/*
- * This represents the USB side of an audio card device, managed by a USB
- * function which provides control and stream interfaces.
- */
-
-struct gaudio_snd_dev {
-	struct gaudio			*card;
-	struct file			*filp;
-	struct snd_pcm_substream	*substream;
-	int				access;
-	int				format;
-	int				channels;
-	int				rate;
-};
+#define UAC1_DEF_CCHMASK	0x3
+#define UAC1_DEF_CSRATE		48000
+#define UAC1_DEF_CSSIZE		2
 
-struct gaudio {
-	struct usb_function		func;
-	struct usb_gadget		*gadget;
-
-	/* ALSA sound device interfaces */
-	struct gaudio_snd_dev		control;
-	struct gaudio_snd_dev		playback;
-	struct gaudio_snd_dev		capture;
-
-	/* TODO */
-};
 
 struct f_uac1_opts {
 	struct usb_function_instance	func_inst;
-	int				req_buf_size;
-	int				req_count;
-	int				audio_buf_size;
-	char				*fn_play;
-	char				*fn_cap;
-	char				*fn_cntl;
+	int				c_chmask;
+	int				c_srate;
+	int				c_ssize;
 	unsigned			bound:1;
-	unsigned			fn_play_alloc:1;
-	unsigned			fn_cap_alloc:1;
-	unsigned			fn_cntl_alloc:1;
+
 	struct mutex			lock;
 	int				refcnt;
 };
 
-int gaudio_setup(struct gaudio *card);
-void gaudio_cleanup(struct gaudio *the_card);
-
-size_t u_audio_playback(struct gaudio *card, void *buf, size_t count);
-int u_audio_get_playback_channels(struct gaudio *card);
-int u_audio_get_playback_rate(struct gaudio *card);
-
-#endif /* __U_AUDIO_H */
+#endif /* __U_UAC1_H */
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index 5344064..5781383 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -56,7 +56,7 @@ config USB_AUDIO
 	select SND_PCM
 	select USB_F_UAC1 if GADGET_UAC1
 	select USB_F_UAC2 if !GADGET_UAC1
-	select USB_U_AUDIO if USB_F_UAC2
+	select USB_U_AUDIO
 	help
 	  This Gadget Audio driver is compatible with USB Audio Class
 	  specification 2.0. It implements 1 AudioControl interface,
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 5d7b3c6..84cce80 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -55,29 +55,20 @@ MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
 #else
 #include "u_uac1.h"
 
-static char *fn_play = FILE_PCM_PLAYBACK;
-module_param(fn_play, charp, S_IRUGO);
-MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
-
-static char *fn_cap = FILE_PCM_CAPTURE;
-module_param(fn_cap, charp, S_IRUGO);
-MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
-
-static char *fn_cntl = FILE_CONTROL;
-module_param(fn_cntl, charp, S_IRUGO);
-MODULE_PARM_DESC(fn_cntl, "Control device file name");
-
-static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
-module_param(req_buf_size, int, S_IRUGO);
-MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
+/* Capture(USB-OUT) Default Stereo - Fl/Fr */
+static int c_chmask = UAC1_DEF_CCHMASK;
+module_param(c_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
 
-static int req_count = UAC1_REQ_COUNT;
-module_param(req_count, int, S_IRUGO);
-MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
+/* 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 audio_buf_size = UAC1_AUDIO_BUF_SIZE;
-module_param(audio_buf_size, int, S_IRUGO);
-MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
+/* Capture Default 16bits/sample */
+static int c_ssize = UAC1_DEF_CSSIZE;
+module_param(c_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
 #endif
 
 /* string IDs are assigned dynamically */
@@ -231,12 +222,9 @@ static int audio_bind(struct usb_composite_dev *cdev)
 	uac2_opts->c_ssize = c_ssize;
 #else
 	uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
-	uac1_opts->fn_play = fn_play;
-	uac1_opts->fn_cap = fn_cap;
-	uac1_opts->fn_cntl = fn_cntl;
-	uac1_opts->req_buf_size = req_buf_size;
-	uac1_opts->req_count = req_count;
-	uac1_opts->audio_buf_size = audio_buf_size;
+	uac1_opts->c_chmask = c_chmask;
+	uac1_opts->c_srate = c_srate;
+	uac1_opts->c_ssize = c_ssize;
 #endif
 
 	status = usb_string_ids_tab(cdev, strings_dev);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/5] usb: gadget: f_uac1: add capture support
  2016-05-23 23:50 [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
                   ` (3 preceding siblings ...)
  2016-05-23 23:50 ` [RFC PATCH 4/5] usb: gadget: f_uac1: switch to u_audio core utilities Ruslan Bilovol
@ 2016-05-23 23:50 ` Ruslan Bilovol
  2016-06-08  8:03 ` [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
  5 siblings, 0 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-05-23 23:50 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Daniel Mack, Jassi Brar, linux-usb, linux-kernel

Add capture support (gadget->host) to the f_uac1
driver. This requires renaming of some descriptors,
enums etc that were used exclusively for playback
path. To make it meaningful, f_uac2 driver naming
convention has been used.

By default, capture interface has 48000kHz/2ch
configuration, same as playback channel has.

Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
---
 drivers/usb/gadget/Kconfig           |   7 +-
 drivers/usb/gadget/function/f_uac1.c | 265 ++++++++++++++++++++++++++++-------
 drivers/usb/gadget/function/u_uac1.h |   6 +
 drivers/usb/gadget/legacy/audio.c    |  18 +++
 4 files changed, 239 insertions(+), 57 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index fd6ee1d..ffe611a 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -395,10 +395,11 @@ config USB_CONFIGFS_F_UAC1
 	  This Audio function implements 1 AudioControl interface,
 	  1 AudioStreaming Interface each for USB-OUT and USB-IN.
 	  This driver doesn't expect any real Audio codec to be present
-	  on the device - the audio streams are simply sinked to
-	  a virtual ALSA sound card created. The user-space
+	  on the device - the audio streams are simply sinked to and
+	  sourced from a virtual ALSA sound card created. The user-space
 	  application may choose to do whatever it wants with the data
-	  received from the USB Host.
+	  received from the USB Host and choose to provide whatever it
+	  wants as audio data to the USB Host.
 
 config USB_CONFIGFS_F_UAC2
 	bool "Audio Class 2.0"
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 120bba9..fcbf204 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -22,8 +22,8 @@
 
 struct f_uac1 {
 	struct gaudio gaudio;
-	u8 ac_intf, as_out_intf;
-	u8 ac_alt, as_out_alt;	/* needed for get_alt() */
+	u8 ac_intf, as_in_intf, as_out_intf;
+	u8 ac_alt, as_in_alt, as_out_alt;	/* needed for get_alt() */
 };
 
 static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
@@ -37,12 +37,17 @@ static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
  */
 
 /*
- * We have two interfaces- AudioControl and AudioStreaming
- * TODO: only supcard playback currently
+ * We have three interfaces- AudioControl and 2 AudioStreaming
+ *
+ * The driver implements a simple UAC_1 topology.
+ * USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture
+ * ALSA_Playback -> IT_3 -> OT_4 -> USB-IN
  */
-#define F_AUDIO_AC_INTERFACE	0
-#define F_AUDIO_AS_INTERFACE	1
-#define F_AUDIO_NUM_INTERFACES	1
+#define F_AUDIO_AC_INTERFACE		0
+#define F_AUDIO_AS_OUT_INTERFACE	1
+#define F_AUDIO_AS_IN_INTERFACE		2
+/* Number of streaming interfaces */
+#define F_AUDIO_NUM_INTERFACES		2
 
 /* B.3.1  Standard AC Interface Descriptor */
 static struct usb_interface_descriptor ac_interface_desc = {
@@ -57,14 +62,14 @@ static struct usb_interface_descriptor ac_interface_desc = {
  * The number of AudioStreaming and MIDIStreaming interfaces
  * in the Audio Interface Collection
  */
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
 
 #define UAC_DT_AC_HEADER_LENGTH	UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
-/* 1 input terminal and 1 output terminal */
-#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
-	+ UAC_DT_OUTPUT_TERMINAL_SIZE)
+/* 2 input terminals and 2 output terminals */
+#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \
+	+ 2*UAC_DT_INPUT_TERMINAL_SIZE + 2*UAC_DT_OUTPUT_TERMINAL_SIZE)
 /* B.3.2  Class-Specific AC Interface Descriptor */
-static struct uac1_ac_header_descriptor_1 ac_header_desc = {
+static struct uac1_ac_header_descriptor_2 ac_header_desc = {
 	.bLength =		UAC_DT_AC_HEADER_LENGTH,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype =	UAC_HEADER,
@@ -72,35 +77,76 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc = {
 	.wTotalLength =		__constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
 	.bInCollection =	F_AUDIO_NUM_INTERFACES,
 	.baInterfaceNr = {
-	/* Interface number of the first AudioStream interface */
+	/* Interface number of the AudioStream interfaces */
 		[0] =		1,
+		[1] =		2,
 	}
 };
 
-#define INPUT_TERMINAL_ID	1
-static struct uac_input_terminal_descriptor input_terminal_desc = {
+#define USB_OUT_IT_ID	1
+static struct uac_input_terminal_descriptor usb_out_it_desc = {
 	.bLength =		UAC_DT_INPUT_TERMINAL_SIZE,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype =	UAC_INPUT_TERMINAL,
-	.bTerminalID =		INPUT_TERMINAL_ID,
+	.bTerminalID =		USB_OUT_IT_ID,
 	.wTerminalType =	UAC_TERMINAL_STREAMING,
 	.bAssocTerminal =	0,
 	.wChannelConfig =	0x3,
 };
 
-#define OUTPUT_TERMINAL_ID	2
-static struct uac1_output_terminal_descriptor output_terminal_desc = {
+#define IO_OUT_OT_ID	2
+static struct uac1_output_terminal_descriptor io_out_ot_desc = {
 	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE,
 	.bDescriptorType	= USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL,
-	.bTerminalID		= OUTPUT_TERMINAL_ID,
+	.bTerminalID		= IO_OUT_OT_ID,
 	.wTerminalType		= UAC_OUTPUT_TERMINAL_SPEAKER,
 	.bAssocTerminal		= 0,
-	.bSourceID		= INPUT_TERMINAL_ID,
+	.bSourceID		= USB_OUT_IT_ID,
+};
+
+#define IO_IN_IT_ID	3
+static struct uac_input_terminal_descriptor io_in_it_desc = {
+	.bLength		= UAC_DT_INPUT_TERMINAL_SIZE,
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype	= UAC_INPUT_TERMINAL,
+	.bTerminalID		= IO_IN_IT_ID,
+	.wTerminalType		= UAC_INPUT_TERMINAL_MICROPHONE,
+	.bAssocTerminal		= 0,
+	.wChannelConfig		= 0x3,
+};
+
+#define USB_IN_OT_ID	4
+static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
+	.bLength =		UAC_DT_OUTPUT_TERMINAL_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_OUTPUT_TERMINAL,
+	.bTerminalID =		USB_IN_OT_ID,
+	.wTerminalType =	UAC_TERMINAL_STREAMING,
+	.bAssocTerminal =	0,
+	.bSourceID =		IO_IN_IT_ID,
 };
 
 /* B.4.1  Standard AS Interface Descriptor */
-static struct usb_interface_descriptor as_interface_alt_0_desc = {
+static struct usb_interface_descriptor as_out_interface_alt_0_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_out_interface_alt_1_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_in_interface_alt_0_desc = {
 	.bLength =		USB_DT_INTERFACE_SIZE,
 	.bDescriptorType =	USB_DT_INTERFACE,
 	.bAlternateSetting =	0,
@@ -109,7 +155,7 @@ static struct usb_interface_descriptor as_interface_alt_0_desc = {
 	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
 };
 
-static struct usb_interface_descriptor as_interface_alt_1_desc = {
+static struct usb_interface_descriptor as_in_interface_alt_1_desc = {
 	.bLength =		USB_DT_INTERFACE_SIZE,
 	.bDescriptorType =	USB_DT_INTERFACE,
 	.bAlternateSetting =	1,
@@ -119,18 +165,27 @@ static struct usb_interface_descriptor as_interface_alt_1_desc = {
 };
 
 /* B.4.2  Class-Specific AS Interface Descriptor */
-static struct uac1_as_header_descriptor as_header_desc = {
+static struct uac1_as_header_descriptor as_out_header_desc = {
 	.bLength =		UAC_DT_AS_HEADER_SIZE,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype =	UAC_AS_GENERAL,
-	.bTerminalLink =	INPUT_TERMINAL_ID,
+	.bTerminalLink =	USB_OUT_IT_ID,
+	.bDelay =		1,
+	.wFormatTag =		UAC_FORMAT_TYPE_I_PCM,
+};
+
+static struct uac1_as_header_descriptor as_in_header_desc = {
+	.bLength =		UAC_DT_AS_HEADER_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_AS_GENERAL,
+	.bTerminalLink =	USB_IN_OT_ID,
 	.bDelay =		1,
 	.wFormatTag =		UAC_FORMAT_TYPE_I_PCM,
 };
 
 DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
 
-static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
+static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {
 	.bLength =		UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
@@ -161,40 +216,92 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
 	.wLockDelay =		__constant_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),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
+	.bFormatType =		UAC_FORMAT_TYPE_I,
+	.bSubframeSize =	2,
+	.bBitResolution =	16,
+	.bSamFreqType =		1,
+};
+
+/* Standard ISO OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor as_in_ep_desc  = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_SYNC_ASYNC
+				| USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
+	.bInterval =		4,
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
+	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,
+	.bDescriptorType =	USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype =	UAC_EP_GENERAL,
+	.bmAttributes =		1,
+	.bLockDelayUnits =	0,
+	.wLockDelay =		0,
+};
+
 static struct usb_descriptor_header *f_audio_desc[] = {
 	(struct usb_descriptor_header *)&ac_interface_desc,
 	(struct usb_descriptor_header *)&ac_header_desc,
 
-	(struct usb_descriptor_header *)&input_terminal_desc,
-	(struct usb_descriptor_header *)&output_terminal_desc,
+	(struct usb_descriptor_header *)&usb_out_it_desc,
+	(struct usb_descriptor_header *)&io_out_ot_desc,
+	(struct usb_descriptor_header *)&io_in_it_desc,
+	(struct usb_descriptor_header *)&usb_in_ot_desc,
 
-	(struct usb_descriptor_header *)&as_interface_alt_0_desc,
-	(struct usb_descriptor_header *)&as_interface_alt_1_desc,
-	(struct usb_descriptor_header *)&as_header_desc,
+	(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
+	(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
+	(struct usb_descriptor_header *)&as_out_header_desc,
 
-	(struct usb_descriptor_header *)&as_type_i_desc,
+	(struct usb_descriptor_header *)&as_out_type_i_desc,
 
 	(struct usb_descriptor_header *)&as_out_ep_desc,
 	(struct usb_descriptor_header *)&as_iso_out_desc,
+
+	(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
+	(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
+	(struct usb_descriptor_header *)&as_in_header_desc,
+
+	(struct usb_descriptor_header *)&as_in_type_i_desc,
+
+	(struct usb_descriptor_header *)&as_in_ep_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
 	NULL,
 };
 
 enum {
 	STR_AC_IF,
-	STR_INPUT_TERMINAL,
-	STR_INPUT_TERMINAL_CH_NAMES,
-	STR_OUTPUT_TERMINAL,
-	STR_AS_IF_ALT0,
-	STR_AS_IF_ALT1,
+	STR_USB_OUT_IT,
+	STR_USB_OUT_IT_CH_NAMES,
+	STR_IO_OUT_OT,
+	STR_IO_IN_IT,
+	STR_IO_IN_IT_CH_NAMES,
+	STR_USB_IN_OT,
+	STR_AS_OUT_IF_ALT0,
+	STR_AS_OUT_IF_ALT1,
+	STR_AS_IN_IF_ALT0,
+	STR_AS_IN_IF_ALT1,
 };
 
 static struct usb_string strings_uac1[] = {
 	[STR_AC_IF].s = "AC Interface",
-	[STR_INPUT_TERMINAL].s = "Input terminal",
-	[STR_INPUT_TERMINAL_CH_NAMES].s = "Channels",
-	[STR_OUTPUT_TERMINAL].s = "Output terminal",
-	[STR_AS_IF_ALT0].s = "AS Interface",
-	[STR_AS_IF_ALT1].s = "AS Interface",
+	[STR_USB_OUT_IT].s = "Playback Input terminal",
+	[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
+	[STR_IO_OUT_OT].s = "Playback Output terminal",
+	[STR_IO_IN_IT].s = "Capture Input terminal",
+	[STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
+	[STR_USB_IN_OT].s = "Capture Output terminal",
+	[STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
+	[STR_AS_OUT_IF_ALT1].s = "Playback Active",
+	[STR_AS_IN_IF_ALT0].s = "Capture Inactive",
+	[STR_AS_IN_IF_ALT1].s = "Capture Active",
 	{ },
 };
 
@@ -350,6 +457,13 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 			ret = gaudio_start_capture(&uac1->gaudio);
 		else
 			gaudio_stop_capture(&uac1->gaudio);
+	} else if (intf == uac1->as_in_intf) {
+		uac1->as_in_alt = alt;
+
+			if (alt)
+				ret = gaudio_start_playback(&uac1->gaudio);
+			else
+				gaudio_stop_playback(&uac1->gaudio);
 	} else {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 		return -EINVAL;
@@ -369,6 +483,8 @@ static int f_audio_get_alt(struct usb_function *f, unsigned intf)
 		return uac1->ac_alt;
 	else if (intf == uac1->as_out_intf)
 		return uac1->as_out_alt;
+	else if (intf == uac1->as_in_intf)
+		return uac1->as_in_alt;
 	else
 		dev_err(dev, "%s:%d Invalid Interface %d!\n",
 			__func__, __LINE__, intf);
@@ -382,6 +498,7 @@ static void f_audio_disable(struct usb_function *f)
 	struct f_uac1 *uac1 = func_to_uac1(f);
 
 	uac1->as_out_alt = 0;
+	uac1->as_in_alt = 0;
 
 	gaudio_stop_capture(&uac1->gaudio);
 }
@@ -408,22 +525,35 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 	if (IS_ERR(us))
 		return PTR_ERR(us);
 	ac_interface_desc.iInterface = us[STR_AC_IF].id;
-	input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id;
-	input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id;
-	output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id;
-	as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id;
-	as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id;
+	usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id;
+	usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id;
+	io_out_ot_desc.iTerminal = us[STR_IO_OUT_OT].id;
+	as_out_interface_alt_0_desc.iInterface = us[STR_AS_OUT_IF_ALT0].id;
+	as_out_interface_alt_1_desc.iInterface = us[STR_AS_OUT_IF_ALT1].id;
+	io_in_it_desc.iTerminal = us[STR_IO_IN_IT].id;
+	io_in_it_desc.iChannelNames = us[STR_IO_IN_IT_CH_NAMES].id;
+	usb_in_ot_desc.iTerminal = us[STR_USB_IN_OT].id;
+	as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id;
+	as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id;
 
 	/* Set channel numbers */
-	input_terminal_desc.bNrChannels = num_channels(audio_opts->c_chmask);
-	input_terminal_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask);
-	as_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask);
-	as_type_i_desc.bSubframeSize = audio_opts->c_ssize;
-	as_type_i_desc.bBitResolution = audio_opts->c_ssize * 8;
+	usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask);
+	usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask);
+	as_out_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask);
+	as_out_type_i_desc.bSubframeSize = audio_opts->c_ssize;
+	as_out_type_i_desc.bBitResolution = audio_opts->c_ssize * 8;
+	io_in_it_desc.bNrChannels = num_channels(audio_opts->p_chmask);
+	io_in_it_desc.wChannelConfig = cpu_to_le16(audio_opts->p_chmask);
+	as_in_type_i_desc.bNrChannels = num_channels(audio_opts->p_chmask);
+	as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize;
+	as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8;
 
 	/* Set sample rates */
 	rate = audio_opts->c_srate;
-	sam_freq = as_type_i_desc.tSamFreq[0];
+	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);
 
 	/* allocate instance-specific interface IDs, and patch descriptors */
@@ -437,11 +567,19 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 	status = usb_interface_id(c, f);
 	if (status < 0)
 		goto fail;
-	as_interface_alt_0_desc.bInterfaceNumber = status;
-	as_interface_alt_1_desc.bInterfaceNumber = status;
+	as_out_interface_alt_0_desc.bInterfaceNumber = status;
+	as_out_interface_alt_1_desc.bInterfaceNumber = status;
 	uac1->as_out_intf = status;
 	uac1->as_out_alt = 0;
 
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	as_in_interface_alt_0_desc.bInterfaceNumber = status;
+	as_in_interface_alt_1_desc.bInterfaceNumber = status;
+	uac1->as_in_intf = status;
+	uac1->as_in_alt = 0;
+
 	audio->gadget = gadget;
 
 	status = -ENODEV;
@@ -453,6 +591,12 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 	audio->out_ep = ep;
 	audio->out_ep->desc = &as_out_ep_desc;
 
+	ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
+	if (!ep)
+		goto fail;
+	audio->in_ep = ep;
+	audio->in_ep->desc = &as_in_ep_desc;
+
 	/* copy descriptors, and track endpoint copies */
 	status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
 					NULL);
@@ -460,9 +604,13 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 		goto fail;
 
 	audio->out_ep_maxpsize = as_out_ep_desc.wMaxPacketSize;
+	audio->in_ep_maxpsize = as_in_ep_desc.wMaxPacketSize;
 	audio->params.c_chmask = audio_opts->c_chmask;
 	audio->params.c_srate = audio_opts->c_srate;
 	audio->params.c_ssize = audio_opts->c_ssize;
+	audio->params.p_chmask = audio_opts->p_chmask;
+	audio->params.p_srate = audio_opts->p_srate;
+	audio->params.p_ssize = audio_opts->p_ssize;
 
 	status = gaudio_setup(audio, "UAC1_PCM", "UAC1_Gadget");
 	if (status)
@@ -539,11 +687,17 @@ CONFIGFS_ATTR(f_uac1_opts_, name)
 UAC1_ATTRIBUTE(c_chmask);
 UAC1_ATTRIBUTE(c_srate);
 UAC1_ATTRIBUTE(c_ssize);
+UAC1_ATTRIBUTE(p_chmask);
+UAC1_ATTRIBUTE(p_srate);
+UAC1_ATTRIBUTE(p_ssize);
 
 static struct configfs_attribute *f_uac1_attrs[] = {
 	&f_uac1_opts_attr_c_chmask,
 	&f_uac1_opts_attr_c_srate,
 	&f_uac1_opts_attr_c_ssize,
+	&f_uac1_opts_attr_p_chmask,
+	&f_uac1_opts_attr_p_srate,
+	&f_uac1_opts_attr_p_ssize,
 	NULL,
 };
 
@@ -578,6 +732,9 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
 	opts->c_chmask = UAC1_DEF_CCHMASK;
 	opts->c_srate = UAC1_DEF_CSRATE;
 	opts->c_ssize = UAC1_DEF_CSSIZE;
+	opts->p_chmask = UAC1_DEF_PCHMASK;
+	opts->p_srate = UAC1_DEF_PSRATE;
+	opts->p_ssize = UAC1_DEF_PSSIZE;
 	return &opts->func_inst;
 }
 
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index fb871d6..3ce7451 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -21,6 +21,9 @@
 #define UAC1_DEF_CCHMASK	0x3
 #define UAC1_DEF_CSRATE		48000
 #define UAC1_DEF_CSSIZE		2
+#define UAC1_DEF_PCHMASK	0x3
+#define UAC1_DEF_PSRATE		48000
+#define UAC1_DEF_PSSIZE		2
 
 
 struct f_uac1_opts {
@@ -28,6 +31,9 @@ struct f_uac1_opts {
 	int				c_chmask;
 	int				c_srate;
 	int				c_ssize;
+	int				p_chmask;
+	int				p_srate;
+	int				p_ssize;
 	unsigned			bound:1;
 
 	struct mutex			lock;
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 84cce80..0211c07 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -55,6 +55,21 @@ MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
 #else
 #include "u_uac1.h"
 
+/* Playback(USB-IN) Default Stereo - Fl/Fr */
+static int p_chmask = UAC1_DEF_PCHMASK;
+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");
+
+/* Playback Default 16bits/sample */
+static int p_ssize = UAC1_DEF_PSSIZE;
+module_param(p_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
+
 /* Capture(USB-OUT) Default Stereo - Fl/Fr */
 static int c_chmask = UAC1_DEF_CCHMASK;
 module_param(c_chmask, uint, S_IRUGO);
@@ -222,6 +237,9 @@ static int audio_bind(struct usb_composite_dev *cdev)
 	uac2_opts->c_ssize = c_ssize;
 #else
 	uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
+	uac1_opts->p_chmask = p_chmask;
+	uac1_opts->p_srate = p_srate;
+	uac1_opts->p_ssize = p_ssize;
 	uac1_opts->c_chmask = c_chmask;
 	uac1_opts->c_srate = c_srate;
 	uac1_opts->c_ssize = c_ssize;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-05-23 23:50 [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
                   ` (4 preceding siblings ...)
  2016-05-23 23:50 ` [RFC PATCH 5/5] usb: gadget: f_uac1: add capture support Ruslan Bilovol
@ 2016-06-08  8:03 ` Ruslan Bilovol
  2016-07-14 21:38   ` Ruslan Bilovol
  5 siblings, 1 reply; 16+ messages in thread
From: Ruslan Bilovol @ 2016-06-08  8:03 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Daniel Mack, Jassi Brar, linux-usb, linux-kernel

Hi guys,

Any feedback on this patch series? Has anybody had a chance to test it?

Regards,
Ruslan

On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
<ruslan.bilovol@gmail.com> wrote:
> I came to this patch series when wanted to do two things:
>  - use UAC1 as virtual ALSA sound card on gadget side,
>    just like UAC2 is used so it's possible to do rate
>    resampling
>  - have both playback/capture support in UAC1
>
> Since I wanted to have same behavior for both UAC1/UAC2,
> obviously I've got an utility part (u_audio.c) for
> virtual ALSA sound card handling like we have
> for ethernet(u_ether) or serial(u_serial) functions.
> Function-specific parts (f_uac1/f_uac2) became almost
> as storage for class-specfic USB descriptors, some
> boilerplate for configfs, binding and few USB
> config request handling.
>
> Major change to f_uac1 it that it can't do
> direct play to existing ALSA sound card anymore,
> representing audio on gadget side as virtual
> ALSA sound card where audio streams are simply
> sinked to and sourced from it, so it may break
> current usecase for some people (and that's why
> it's RFC).
>
> Luckily, it's possible to use existing user-space
> applications for audio routing between Audio Gadget
> and real sound card. I personally use alsaloop tool
> from alsautils and have ability to create PCM
> loopback between two different ALSA cards using
> rate resampling, which is not possible with previous
> "direct play to ALSA card" approach in f_uac1.
>
> While here, also dropped redundant platform
> driver/device creation in f_uac2 driver as well as
> "never implemented" volume/mute functionality in f_uac1
> that made this work even easier to do.
>
> This series is tested with both legacy g_audio.ko and
> modern configfs approaches under Ubuntu 14.04 (UAC1 and
> UAC2) and under Windows7 x64 (UAC1 only) having
> perfect results in all cases.
>
> Some changes may have lack of good description that may
> be obvious for me but not so clear for others, but I
> hope to fix it in next versions.
>
> Comments, testing are welcome.
>
> Ruslan Bilovol (5):
>   usb: gadget: f_uac2: remove platform driver/device creation
>   usb: gadget: f_uac2: split out audio core
>   usb: gadget: f_uac1: drop volume/mute functionality
>   usb: gadget: f_uac1: switch to u_audio core utilities
>   usb: gadget: f_uac1: add capture support
>
>  drivers/usb/gadget/Kconfig            |  13 +-
>  drivers/usb/gadget/function/Makefile  |   3 +-
>  drivers/usb/gadget/function/f_uac1.c  | 842 +++++++++++++---------------------
>  drivers/usb/gadget/function/f_uac2.c  | 778 ++++---------------------------
>  drivers/usb/gadget/function/u_audio.c | 632 +++++++++++++++++++++++++
>  drivers/usb/gadget/function/u_audio.h |  93 ++++
>  drivers/usb/gadget/function/u_uac1.c  | 314 -------------
>  drivers/usb/gadget/function/u_uac1.h  |  71 +--
>  drivers/usb/gadget/legacy/Kconfig     |   1 +
>  drivers/usb/gadget/legacy/audio.c     |  54 ++-
>  10 files changed, 1208 insertions(+), 1593 deletions(-)
>  create mode 100644 drivers/usb/gadget/function/u_audio.c
>  create mode 100644 drivers/usb/gadget/function/u_audio.h
>  delete mode 100644 drivers/usb/gadget/function/u_uac1.c
>
> --
> 1.9.1
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-06-08  8:03 ` [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
@ 2016-07-14 21:38   ` Ruslan Bilovol
  2016-07-15  7:43     ` Clemens Ladisch
  0 siblings, 1 reply; 16+ messages in thread
From: Ruslan Bilovol @ 2016-07-14 21:38 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Daniel Mack, Jassi Brar, linux-usb, linux-kernel

Ping?

On Wed, Jun 8, 2016 at 11:03 AM, Ruslan Bilovol
<ruslan.bilovol@gmail.com> wrote:
> Hi guys,
>
> Any feedback on this patch series? Has anybody had a chance to test it?
>
> Regards,
> Ruslan
>
> On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
> <ruslan.bilovol@gmail.com> wrote:
>> I came to this patch series when wanted to do two things:
>>  - use UAC1 as virtual ALSA sound card on gadget side,
>>    just like UAC2 is used so it's possible to do rate
>>    resampling
>>  - have both playback/capture support in UAC1
>>
>> Since I wanted to have same behavior for both UAC1/UAC2,
>> obviously I've got an utility part (u_audio.c) for
>> virtual ALSA sound card handling like we have
>> for ethernet(u_ether) or serial(u_serial) functions.
>> Function-specific parts (f_uac1/f_uac2) became almost
>> as storage for class-specfic USB descriptors, some
>> boilerplate for configfs, binding and few USB
>> config request handling.
>>
>> Major change to f_uac1 it that it can't do
>> direct play to existing ALSA sound card anymore,
>> representing audio on gadget side as virtual
>> ALSA sound card where audio streams are simply
>> sinked to and sourced from it, so it may break
>> current usecase for some people (and that's why
>> it's RFC).
>>
>> Luckily, it's possible to use existing user-space
>> applications for audio routing between Audio Gadget
>> and real sound card. I personally use alsaloop tool
>> from alsautils and have ability to create PCM
>> loopback between two different ALSA cards using
>> rate resampling, which is not possible with previous
>> "direct play to ALSA card" approach in f_uac1.
>>
>> While here, also dropped redundant platform
>> driver/device creation in f_uac2 driver as well as
>> "never implemented" volume/mute functionality in f_uac1
>> that made this work even easier to do.
>>
>> This series is tested with both legacy g_audio.ko and
>> modern configfs approaches under Ubuntu 14.04 (UAC1 and
>> UAC2) and under Windows7 x64 (UAC1 only) having
>> perfect results in all cases.
>>
>> Some changes may have lack of good description that may
>> be obvious for me but not so clear for others, but I
>> hope to fix it in next versions.
>>
>> Comments, testing are welcome.
>>
>> Ruslan Bilovol (5):
>>   usb: gadget: f_uac2: remove platform driver/device creation
>>   usb: gadget: f_uac2: split out audio core
>>   usb: gadget: f_uac1: drop volume/mute functionality
>>   usb: gadget: f_uac1: switch to u_audio core utilities
>>   usb: gadget: f_uac1: add capture support
>>
>>  drivers/usb/gadget/Kconfig            |  13 +-
>>  drivers/usb/gadget/function/Makefile  |   3 +-
>>  drivers/usb/gadget/function/f_uac1.c  | 842 +++++++++++++---------------------
>>  drivers/usb/gadget/function/f_uac2.c  | 778 ++++---------------------------
>>  drivers/usb/gadget/function/u_audio.c | 632 +++++++++++++++++++++++++
>>  drivers/usb/gadget/function/u_audio.h |  93 ++++
>>  drivers/usb/gadget/function/u_uac1.c  | 314 -------------
>>  drivers/usb/gadget/function/u_uac1.h  |  71 +--
>>  drivers/usb/gadget/legacy/Kconfig     |   1 +
>>  drivers/usb/gadget/legacy/audio.c     |  54 ++-
>>  10 files changed, 1208 insertions(+), 1593 deletions(-)
>>  create mode 100644 drivers/usb/gadget/function/u_audio.c
>>  create mode 100644 drivers/usb/gadget/function/u_audio.h
>>  delete mode 100644 drivers/usb/gadget/function/u_uac1.c
>>
>> --
>> 1.9.1
>>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-07-14 21:38   ` Ruslan Bilovol
@ 2016-07-15  7:43     ` Clemens Ladisch
  2016-07-26  1:31       ` Ruslan Bilovol
  0 siblings, 1 reply; 16+ messages in thread
From: Clemens Ladisch @ 2016-07-15  7:43 UTC (permalink / raw)
  To: Ruslan Bilovol
  Cc: Felipe Balbi, Daniel Mack, Jassi Brar, linux-usb, linux-kernel

>> On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
>> <ruslan.bilovol@gmail.com> wrote:
>>> it may break current usecase for some people

And what are the benefits that justify breaking the kernel API?


Regards,
Clemens

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-07-15  7:43     ` Clemens Ladisch
@ 2016-07-26  1:31       ` Ruslan Bilovol
  2016-07-26  8:06         ` Clemens Ladisch
  2016-07-26  8:53         ` Jassi Brar
  0 siblings, 2 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-07-26  1:31 UTC (permalink / raw)
  To: Clemens Ladisch
  Cc: Felipe Balbi, Daniel Mack, Jassi Brar, linux-usb, linux-kernel

On Fri, Jul 15, 2016 at 10:43 AM, Clemens Ladisch <clemens@ladisch.de> wrote:
>>> On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
>>> <ruslan.bilovol@gmail.com> wrote:
>>>> it may break current usecase for some people
>
> And what are the benefits that justify breaking the kernel API?


Main limitation with current f_uac1 design is - it can be used only on systems
with real ALSA card present and can have only exact number of
channels / sampling rate as sink card has.
Yet it is not flexible - can't do audio processing between f_uac1 and the card.
Also if someone wants to bind f_uac1 it to another sound card he has to
unload g_audio or reconfigure it through configfs - that means USB
reenumeration on host device.

If you have a "virtual sound card", audio processing is done in userspace
and is more flexible. You even don't need to have a real sound card and
can use some userspace application for playing/capturing audio samples.
Moreover, existing f_uac2 (that is USB Audio Class 2.0 function
implementation) already uses approach of "virtual sound card"

A real cases when it's required to have UAC1 gadget represented as virtual
sound card on gadget side:
 - android accessory f_audio_source.c implementation, android plays audio
   directly to UAC1 "virtual sound card"
 - some 3G/LTE voice USB sticks with Linux as firmware have user-space
   application inside that receives audio from network and need to source/sink
   it to some sound card (UAC1)
 - USB sound card with complex audio processing inside, made on small
   Linux-powered device ("sound-studio")

What's annoying is we have two quite similar USB Audio Classes
implementations(f_uac1 and f_uac2) that have opposite audio
representations: first transfers audio samples directly to real ALSA card,
second exposes virtual ALSA card. That means you have to implement
similar things (capture/playback, etc) in different way. With new design
both implementations provide same "API" (virtual sound card), allowing
us to reuse a lot of code and implement new features much easier (look
at adding of capture support to f_uac1 in PATCH 5/5 - that was very simple
and consists almost from adding new USB descriptors and reusing existing
code from newly created u_audio.c)

Also new USB Audio Gadget design follows existing approach for
another USB Classes:
 - serial gadgets use u_serial and expose virtual TTY port
 - networking gadgets use u_ether and expose virtual network interface
 - uvc gadget exposes virtual v4l device
 - midi gadget exposes virtual sound card
 - etc, etc

Of course disadvantage of new approach for UAC1 gadget is you need to
use some userspace application for routing audio from virtual to real
sound card, like in case of UAC2 gadget. But thanks to existing
applications like alsaloop it's not difficult nowadays.

The answer I want to get in this RFC - can we drop current f_uac1 approach,
simplify USB Audio Gadget by reusing common code.
Or "we do not break userspace" (or API) and have to live with it forever.

Best regards,
Ruslan

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-07-26  1:31       ` Ruslan Bilovol
@ 2016-07-26  8:06         ` Clemens Ladisch
  2016-07-26 21:22           ` Ruslan Bilovol
  2016-07-26  8:53         ` Jassi Brar
  1 sibling, 1 reply; 16+ messages in thread
From: Clemens Ladisch @ 2016-07-26  8:06 UTC (permalink / raw)
  To: Ruslan Bilovol
  Cc: Felipe Balbi, Daniel Mack, Jassi Brar, linux-usb, linux-kernel

Ruslan Bilovol wrote:
> On Fri, Jul 15, 2016 at 10:43 AM, Clemens Ladisch <clemens@ladisch.de> wrote:
>>>> On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
>>>> <ruslan.bilovol@gmail.com> wrote:
>>>>> it may break current usecase for some people
>>
>> And what are the benefits that justify breaking the kernel API?
>
> Main limitation with current f_uac1 design is - it can be used only on systems
> with real ALSA card present and can have only exact number of
> channels / sampling rate as sink card has. [...]
> A real cases when it's required to have UAC1 gadget represented as virtual
> sound card on gadget side: [...]

Thanks.

> Of course disadvantage of new approach for UAC1 gadget is you need to
> use some userspace application for routing audio from virtual to real
> sound card, like in case of UAC2 gadget. But thanks to existing
> applications like alsaloop it's not difficult nowadays.

I don't know what the maintainer will say, but you would increase the
chances of this change being accepted when you show a concrete example
of how to change the userspace configuration from the old to the new
driver.  (And add it to the documentation.)


Regards,
Clemens

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-07-26  1:31       ` Ruslan Bilovol
  2016-07-26  8:06         ` Clemens Ladisch
@ 2016-07-26  8:53         ` Jassi Brar
  2016-07-26 12:02           ` Krzysztof Opasiak
  2016-07-26 21:38           ` Ruslan Bilovol
  1 sibling, 2 replies; 16+ messages in thread
From: Jassi Brar @ 2016-07-26  8:53 UTC (permalink / raw)
  To: Ruslan Bilovol
  Cc: Clemens Ladisch, Felipe Balbi, Daniel Mack, linux-usb, linux-kernel

On Tue, Jul 26, 2016 at 7:01 AM, Ruslan Bilovol
<ruslan.bilovol@gmail.com> wrote:
> On Fri, Jul 15, 2016 at 10:43 AM, Clemens Ladisch <clemens@ladisch.de> wrote:
>>>> On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
>>>> <ruslan.bilovol@gmail.com> wrote:
>>>>> it may break current usecase for some people
>>
>> And what are the benefits that justify breaking the kernel API?
>
>
> Main limitation with current f_uac1 design is - it can be used only on systems
> with real ALSA card present and can have only exact number of
> channels / sampling rate as sink card has.
> Yet it is not flexible - can't do audio processing between f_uac1 and the card.
> Also if someone wants to bind f_uac1 it to another sound card he has to
> unload g_audio or reconfigure it through configfs - that means USB
> reenumeration on host device.
>
> If you have a "virtual sound card", audio processing is done in userspace
> and is more flexible. You even don't need to have a real sound card and
> can use some userspace application for playing/capturing audio samples.
> Moreover, existing f_uac2 (that is USB Audio Class 2.0 function
> implementation) already uses approach of "virtual sound card"
>
While I agree the virtual sound card approach is the right way, I am
not sure if we should break the userspace api that the existing UAC1
driver exposes. Maybe we should add another virtual-sound-card
exposing UAC1 driver ... and hopefully very similar to (or just port
of) the f_audio_source.c from android.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-07-26  8:53         ` Jassi Brar
@ 2016-07-26 12:02           ` Krzysztof Opasiak
  2016-07-26 21:55             ` Ruslan Bilovol
  2016-07-26 21:38           ` Ruslan Bilovol
  1 sibling, 1 reply; 16+ messages in thread
From: Krzysztof Opasiak @ 2016-07-26 12:02 UTC (permalink / raw)
  To: Jassi Brar, Ruslan Bilovol
  Cc: Clemens Ladisch, Felipe Balbi, Daniel Mack, linux-usb, linux-kernel



On 07/26/2016 10:53 AM, Jassi Brar wrote:
> On Tue, Jul 26, 2016 at 7:01 AM, Ruslan Bilovol
> <ruslan.bilovol@gmail.com> wrote:
>> On Fri, Jul 15, 2016 at 10:43 AM, Clemens Ladisch <clemens@ladisch.de> wrote:
>>>>> On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
>>>>> <ruslan.bilovol@gmail.com> wrote:
>>>>>> it may break current usecase for some people
>>>
>>> And what are the benefits that justify breaking the kernel API?
>>
>>
>> Main limitation with current f_uac1 design is - it can be used only on systems
>> with real ALSA card present and can have only exact number of
>> channels / sampling rate as sink card has.
>> Yet it is not flexible - can't do audio processing between f_uac1 and the card.
>> Also if someone wants to bind f_uac1 it to another sound card he has to
>> unload g_audio or reconfigure it through configfs - that means USB
>> reenumeration on host device.
>>
>> If you have a "virtual sound card", audio processing is done in userspace
>> and is more flexible. You even don't need to have a real sound card and
>> can use some userspace application for playing/capturing audio samples.
>> Moreover, existing f_uac2 (that is USB Audio Class 2.0 function
>> implementation) already uses approach of "virtual sound card"
>>
> While I agree the virtual sound card approach is the right way, I am
> not sure if we should break the userspace api that the existing UAC1
> driver exposes. Maybe we should add another virtual-sound-card
> exposing UAC1 driver ... and hopefully very similar to (or just port
> of) the f_audio_source.c from android.

Definitely agree with this opinion. I don't see any benefits of breaking
the API here instead of adding just another USB function. Maybe even
some pieces of code could be shared with f_uac1.c but I think that this
should be a brand new function.

Best regards,
-- 
Krzysztof Opasiak
Samsung R&D Institute Poland
Samsung Electronics

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-07-26  8:06         ` Clemens Ladisch
@ 2016-07-26 21:22           ` Ruslan Bilovol
  0 siblings, 0 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-07-26 21:22 UTC (permalink / raw)
  To: Clemens Ladisch
  Cc: Felipe Balbi, Daniel Mack, Jassi Brar, linux-usb, linux-kernel

On Tue, Jul 26, 2016 at 11:06 AM, Clemens Ladisch <clemens@ladisch.de> wrote:
> Ruslan Bilovol wrote:
>> On Fri, Jul 15, 2016 at 10:43 AM, Clemens Ladisch <clemens@ladisch.de> wrote:
>>>>> On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
>>>>> <ruslan.bilovol@gmail.com> wrote:
>>>>>> it may break current usecase for some people
>>>
>>> And what are the benefits that justify breaking the kernel API?
>>
>> Main limitation with current f_uac1 design is - it can be used only on systems
>> with real ALSA card present and can have only exact number of
>> channels / sampling rate as sink card has. [...]
>> A real cases when it's required to have UAC1 gadget represented as virtual
>> sound card on gadget side: [...]
>
> Thanks.
>
>> Of course disadvantage of new approach for UAC1 gadget is you need to
>> use some userspace application for routing audio from virtual to real
>> sound card, like in case of UAC2 gadget. But thanks to existing
>> applications like alsaloop it's not difficult nowadays.
>
> I don't know what the maintainer will say, but you would increase the
> chances of this change being accepted when you show a concrete example
> of how to change the userspace configuration from the old to the new
> driver.  (And add it to the documentation.)

Good point, need to update documentation in next files (make similar to UAC2):
  Documentation/usb/gadget-testing.txt
  Documentation/ABI/testing/configfs-usb-gadget-uac1

Best regards,
Ruslan

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-07-26  8:53         ` Jassi Brar
  2016-07-26 12:02           ` Krzysztof Opasiak
@ 2016-07-26 21:38           ` Ruslan Bilovol
  1 sibling, 0 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-07-26 21:38 UTC (permalink / raw)
  To: Jassi Brar
  Cc: Clemens Ladisch, Felipe Balbi, Daniel Mack, linux-usb, linux-kernel

On Tue, Jul 26, 2016 at 11:53 AM, Jassi Brar <jassisinghbrar@gmail.com> wrote:
> On Tue, Jul 26, 2016 at 7:01 AM, Ruslan Bilovol
> <ruslan.bilovol@gmail.com> wrote:
>> On Fri, Jul 15, 2016 at 10:43 AM, Clemens Ladisch <clemens@ladisch.de> wrote:
>>>>> On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
>>>>> <ruslan.bilovol@gmail.com> wrote:
>>>>>> it may break current usecase for some people
>>>
>>> And what are the benefits that justify breaking the kernel API?
>>
>>
>> Main limitation with current f_uac1 design is - it can be used only on systems
>> with real ALSA card present and can have only exact number of
>> channels / sampling rate as sink card has.
>> Yet it is not flexible - can't do audio processing between f_uac1 and the card.
>> Also if someone wants to bind f_uac1 it to another sound card he has to
>> unload g_audio or reconfigure it through configfs - that means USB
>> reenumeration on host device.
>>
>> If you have a "virtual sound card", audio processing is done in userspace
>> and is more flexible. You even don't need to have a real sound card and
>> can use some userspace application for playing/capturing audio samples.
>> Moreover, existing f_uac2 (that is USB Audio Class 2.0 function
>> implementation) already uses approach of "virtual sound card"
>>
> While I agree the virtual sound card approach is the right way, I am
> not sure if we should break the userspace api that the existing UAC1
> driver exposes. Maybe we should add another virtual-sound-card
> exposing UAC1 driver

This approach is quite easy to implement and I though about it when
started to work on this patch series, but due to my lazyness I wanted
to get some comments before doing extra work.

> ... and hopefully very similar to (or just port
> of) the f_audio_source.c from android.

Current patch series reuses code from f_uac2 (in u_audio) and
makes it in some sense similar to f_audio_source.c.

The f_audio_source.c implementation has additional functionality
like more accurate data transferring (w.r.t timings), that can be
ported to u_audio separately.

Best regards,
Ruslan

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/5] USB Audio Gadget refactoring
  2016-07-26 12:02           ` Krzysztof Opasiak
@ 2016-07-26 21:55             ` Ruslan Bilovol
  0 siblings, 0 replies; 16+ messages in thread
From: Ruslan Bilovol @ 2016-07-26 21:55 UTC (permalink / raw)
  To: Krzysztof Opasiak
  Cc: Jassi Brar, Clemens Ladisch, Felipe Balbi, Daniel Mack,
	linux-usb, linux-kernel

On Tue, Jul 26, 2016 at 3:02 PM, Krzysztof Opasiak
<k.opasiak@samsung.com> wrote:
>
>
> On 07/26/2016 10:53 AM, Jassi Brar wrote:
>> On Tue, Jul 26, 2016 at 7:01 AM, Ruslan Bilovol
>> <ruslan.bilovol@gmail.com> wrote:
>>> On Fri, Jul 15, 2016 at 10:43 AM, Clemens Ladisch <clemens@ladisch.de> wrote:
>>>>>> On Tue, May 24, 2016 at 2:50 AM, Ruslan Bilovol
>>>>>> <ruslan.bilovol@gmail.com> wrote:
>>>>>>> it may break current usecase for some people
>>>>
>>>> And what are the benefits that justify breaking the kernel API?
>>>
>>>
>>> Main limitation with current f_uac1 design is - it can be used only on systems
>>> with real ALSA card present and can have only exact number of
>>> channels / sampling rate as sink card has.
>>> Yet it is not flexible - can't do audio processing between f_uac1 and the card.
>>> Also if someone wants to bind f_uac1 it to another sound card he has to
>>> unload g_audio or reconfigure it through configfs - that means USB
>>> reenumeration on host device.
>>>
>>> If you have a "virtual sound card", audio processing is done in userspace
>>> and is more flexible. You even don't need to have a real sound card and
>>> can use some userspace application for playing/capturing audio samples.
>>> Moreover, existing f_uac2 (that is USB Audio Class 2.0 function
>>> implementation) already uses approach of "virtual sound card"
>>>
>> While I agree the virtual sound card approach is the right way, I am
>> not sure if we should break the userspace api that the existing UAC1
>> driver exposes. Maybe we should add another virtual-sound-card
>> exposing UAC1 driver ... and hopefully very similar to (or just port
>> of) the f_audio_source.c from android.
>
> Definitely agree with this opinion. I don't see any benefits of breaking
> the API here instead of adding just another USB function. Maybe even
> some pieces of code could be shared with f_uac1.c but I think that this
> should be a brand new function.
>

So if we want to keep old API working, easiest (and cleanest) way is
to create a new f_uac1.c version and kconfig symbol, for example
f_uac1_newapi.c and CONFIG_USB_F_UAC1_NEWAPI
There is no sence to share some pieces of code with f_uac1.c just
because it is changed too drastically.

So I'll implement it in v2 if there is no any objections

Best regards,
Ruslan Bilovol

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2016-07-26 21:55 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-23 23:50 [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
2016-05-23 23:50 ` [RFC PATCH 1/5] usb: gadget: f_uac2: remove platform driver/device creation Ruslan Bilovol
2016-05-23 23:50 ` [RFC PATCH 2/5] usb: gadget: f_uac2: split out audio core Ruslan Bilovol
2016-05-23 23:50 ` [RFC PATCH 3/5] usb: gadget: f_uac1: drop volume/mute functionality Ruslan Bilovol
2016-05-23 23:50 ` [RFC PATCH 4/5] usb: gadget: f_uac1: switch to u_audio core utilities Ruslan Bilovol
2016-05-23 23:50 ` [RFC PATCH 5/5] usb: gadget: f_uac1: add capture support Ruslan Bilovol
2016-06-08  8:03 ` [RFC PATCH 0/5] USB Audio Gadget refactoring Ruslan Bilovol
2016-07-14 21:38   ` Ruslan Bilovol
2016-07-15  7:43     ` Clemens Ladisch
2016-07-26  1:31       ` Ruslan Bilovol
2016-07-26  8:06         ` Clemens Ladisch
2016-07-26 21:22           ` Ruslan Bilovol
2016-07-26  8:53         ` Jassi Brar
2016-07-26 12:02           ` Krzysztof Opasiak
2016-07-26 21:55             ` Ruslan Bilovol
2016-07-26 21:38           ` Ruslan Bilovol

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).