15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2eb9fecb9SRuslan Bilovol /*
3eb9fecb9SRuslan Bilovol  * u_audio.c -- interface to USB gadget "ALSA sound card" utilities
4eb9fecb9SRuslan Bilovol  *
5eb9fecb9SRuslan Bilovol  * Copyright (C) 2016
6eb9fecb9SRuslan Bilovol  * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com>
7eb9fecb9SRuslan Bilovol  *
8eb9fecb9SRuslan Bilovol  * Sound card implementation was cut-and-pasted with changes
9eb9fecb9SRuslan Bilovol  * from f_uac2.c and has:
10eb9fecb9SRuslan Bilovol  *    Copyright (C) 2011
11eb9fecb9SRuslan Bilovol  *    Yadwinder Singh (yadi.brar01@gmail.com)
12eb9fecb9SRuslan Bilovol  *    Jaswinder Singh (jaswinder.singh@linaro.org)
13eb9fecb9SRuslan Bilovol  */
14eb9fecb9SRuslan Bilovol 
15eb9fecb9SRuslan Bilovol #include <linux/module.h>
16eb9fecb9SRuslan Bilovol #include <sound/core.h>
17eb9fecb9SRuslan Bilovol #include <sound/pcm.h>
18eb9fecb9SRuslan Bilovol #include <sound/pcm_params.h>
19eb9fecb9SRuslan Bilovol 
20eb9fecb9SRuslan Bilovol #include "u_audio.h"
21eb9fecb9SRuslan Bilovol 
22eb9fecb9SRuslan Bilovol #define BUFF_SIZE_MAX	(PAGE_SIZE * 16)
23eb9fecb9SRuslan Bilovol #define PRD_SIZE_MAX	PAGE_SIZE
24eb9fecb9SRuslan Bilovol #define MIN_PERIODS	4
25eb9fecb9SRuslan Bilovol 
26eb9fecb9SRuslan Bilovol /* Runtime data params for one stream */
27eb9fecb9SRuslan Bilovol struct uac_rtd_params {
28eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac; /* parent chip */
29eb9fecb9SRuslan Bilovol 	bool ep_enabled; /* if the ep is enabled */
30eb9fecb9SRuslan Bilovol 
31eb9fecb9SRuslan Bilovol 	struct snd_pcm_substream *ss;
32eb9fecb9SRuslan Bilovol 
33eb9fecb9SRuslan Bilovol 	/* Ring buffer */
34eb9fecb9SRuslan Bilovol 	ssize_t hw_ptr;
35eb9fecb9SRuslan Bilovol 
36eb9fecb9SRuslan Bilovol 	void *rbuf;
37eb9fecb9SRuslan Bilovol 
38f4408a98SJonas Stenvall 	unsigned int max_psize;	/* MaxPacketSize of endpoint */
39*29865117SJerome Brunet 	struct usb_request **reqs;
40eb9fecb9SRuslan Bilovol 
41eb9fecb9SRuslan Bilovol 	spinlock_t lock;
42eb9fecb9SRuslan Bilovol };
43eb9fecb9SRuslan Bilovol 
44eb9fecb9SRuslan Bilovol struct snd_uac_chip {
45eb9fecb9SRuslan Bilovol 	struct g_audio *audio_dev;
46eb9fecb9SRuslan Bilovol 
47eb9fecb9SRuslan Bilovol 	struct uac_rtd_params p_prm;
48eb9fecb9SRuslan Bilovol 	struct uac_rtd_params c_prm;
49eb9fecb9SRuslan Bilovol 
50eb9fecb9SRuslan Bilovol 	struct snd_card *card;
51eb9fecb9SRuslan Bilovol 	struct snd_pcm *pcm;
52eb9fecb9SRuslan Bilovol 
53eb9fecb9SRuslan Bilovol 	/* timekeeping for the playback endpoint */
54eb9fecb9SRuslan Bilovol 	unsigned int p_interval;
55eb9fecb9SRuslan Bilovol 	unsigned int p_residue;
56eb9fecb9SRuslan Bilovol 
57eb9fecb9SRuslan Bilovol 	/* pre-calculated values for playback iso completion */
58eb9fecb9SRuslan Bilovol 	unsigned int p_pktsize;
59eb9fecb9SRuslan Bilovol 	unsigned int p_pktsize_residue;
60eb9fecb9SRuslan Bilovol 	unsigned int p_framesize;
61eb9fecb9SRuslan Bilovol };
62eb9fecb9SRuslan Bilovol 
632ab3c34cSBhumika Goyal static const struct snd_pcm_hardware uac_pcm_hardware = {
64eb9fecb9SRuslan Bilovol 	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
65eb9fecb9SRuslan Bilovol 		 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
66eb9fecb9SRuslan Bilovol 		 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
67eb9fecb9SRuslan Bilovol 	.rates = SNDRV_PCM_RATE_CONTINUOUS,
68eb9fecb9SRuslan Bilovol 	.periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX,
69eb9fecb9SRuslan Bilovol 	.buffer_bytes_max = BUFF_SIZE_MAX,
70eb9fecb9SRuslan Bilovol 	.period_bytes_max = PRD_SIZE_MAX,
71eb9fecb9SRuslan Bilovol 	.periods_min = MIN_PERIODS,
72eb9fecb9SRuslan Bilovol };
73eb9fecb9SRuslan Bilovol 
74eb9fecb9SRuslan Bilovol static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
75eb9fecb9SRuslan Bilovol {
76f4408a98SJonas Stenvall 	unsigned int pending;
7756bc6158SVladimir Zapolskiy 	unsigned long flags, flags2;
78eb9fecb9SRuslan Bilovol 	unsigned int hw_ptr;
79eb9fecb9SRuslan Bilovol 	int status = req->status;
80eb9fecb9SRuslan Bilovol 	struct snd_pcm_substream *substream;
8196afb54eSVladimir Zapolskiy 	struct snd_pcm_runtime *runtime;
82*29865117SJerome Brunet 	struct uac_rtd_params *prm = req->context;
83eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = prm->uac;
84eb9fecb9SRuslan Bilovol 
85eb9fecb9SRuslan Bilovol 	/* i/f shutting down */
867de8681bSJack Pham 	if (!prm->ep_enabled) {
877de8681bSJack Pham 		usb_ep_free_request(ep, req);
887de8681bSJack Pham 		return;
897de8681bSJack Pham 	}
907de8681bSJack Pham 
917de8681bSJack Pham 	if (req->status == -ESHUTDOWN)
92eb9fecb9SRuslan Bilovol 		return;
93eb9fecb9SRuslan Bilovol 
94eb9fecb9SRuslan Bilovol 	/*
95eb9fecb9SRuslan Bilovol 	 * We can't really do much about bad xfers.
96eb9fecb9SRuslan Bilovol 	 * Afterall, the ISOCH xfers could fail legitimately.
97eb9fecb9SRuslan Bilovol 	 */
98eb9fecb9SRuslan Bilovol 	if (status)
99eb9fecb9SRuslan Bilovol 		pr_debug("%s: iso_complete status(%d) %d/%d\n",
100eb9fecb9SRuslan Bilovol 			__func__, status, req->actual, req->length);
101eb9fecb9SRuslan Bilovol 
102eb9fecb9SRuslan Bilovol 	substream = prm->ss;
103eb9fecb9SRuslan Bilovol 
104eb9fecb9SRuslan Bilovol 	/* Do nothing if ALSA isn't active */
105eb9fecb9SRuslan Bilovol 	if (!substream)
106eb9fecb9SRuslan Bilovol 		goto exit;
107eb9fecb9SRuslan Bilovol 
10856bc6158SVladimir Zapolskiy 	snd_pcm_stream_lock_irqsave(substream, flags2);
10956bc6158SVladimir Zapolskiy 
11096afb54eSVladimir Zapolskiy 	runtime = substream->runtime;
11156bc6158SVladimir Zapolskiy 	if (!runtime || !snd_pcm_running(substream)) {
11256bc6158SVladimir Zapolskiy 		snd_pcm_stream_unlock_irqrestore(substream, flags2);
11356bc6158SVladimir Zapolskiy 		goto exit;
11456bc6158SVladimir Zapolskiy 	}
11556bc6158SVladimir Zapolskiy 
116eb9fecb9SRuslan Bilovol 	spin_lock_irqsave(&prm->lock, flags);
117eb9fecb9SRuslan Bilovol 
118eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
119eb9fecb9SRuslan Bilovol 		/*
120eb9fecb9SRuslan Bilovol 		 * For each IN packet, take the quotient of the current data
121eb9fecb9SRuslan Bilovol 		 * rate and the endpoint's interval as the base packet size.
122eb9fecb9SRuslan Bilovol 		 * If there is a residue from this division, add it to the
123eb9fecb9SRuslan Bilovol 		 * residue accumulator.
124eb9fecb9SRuslan Bilovol 		 */
125eb9fecb9SRuslan Bilovol 		req->length = uac->p_pktsize;
126eb9fecb9SRuslan Bilovol 		uac->p_residue += uac->p_pktsize_residue;
127eb9fecb9SRuslan Bilovol 
128eb9fecb9SRuslan Bilovol 		/*
129eb9fecb9SRuslan Bilovol 		 * Whenever there are more bytes in the accumulator than we
130eb9fecb9SRuslan Bilovol 		 * need to add one more sample frame, increase this packet's
131eb9fecb9SRuslan Bilovol 		 * size and decrease the accumulator.
132eb9fecb9SRuslan Bilovol 		 */
133eb9fecb9SRuslan Bilovol 		if (uac->p_residue / uac->p_interval >= uac->p_framesize) {
134eb9fecb9SRuslan Bilovol 			req->length += uac->p_framesize;
135eb9fecb9SRuslan Bilovol 			uac->p_residue -= uac->p_framesize *
136eb9fecb9SRuslan Bilovol 					   uac->p_interval;
137eb9fecb9SRuslan Bilovol 		}
138eb9fecb9SRuslan Bilovol 
139eb9fecb9SRuslan Bilovol 		req->actual = req->length;
140eb9fecb9SRuslan Bilovol 	}
141eb9fecb9SRuslan Bilovol 
142eb9fecb9SRuslan Bilovol 	hw_ptr = prm->hw_ptr;
143eb9fecb9SRuslan Bilovol 
144eb9fecb9SRuslan Bilovol 	spin_unlock_irqrestore(&prm->lock, flags);
145eb9fecb9SRuslan Bilovol 
146eb9fecb9SRuslan Bilovol 	/* Pack USB load in ALSA ring buffer */
14796afb54eSVladimir Zapolskiy 	pending = runtime->dma_bytes - hw_ptr;
148eb9fecb9SRuslan Bilovol 
149eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
150eb9fecb9SRuslan Bilovol 		if (unlikely(pending < req->actual)) {
15196afb54eSVladimir Zapolskiy 			memcpy(req->buf, runtime->dma_area + hw_ptr, pending);
15296afb54eSVladimir Zapolskiy 			memcpy(req->buf + pending, runtime->dma_area,
153eb9fecb9SRuslan Bilovol 			       req->actual - pending);
154eb9fecb9SRuslan Bilovol 		} else {
15596afb54eSVladimir Zapolskiy 			memcpy(req->buf, runtime->dma_area + hw_ptr,
15696afb54eSVladimir Zapolskiy 			       req->actual);
157eb9fecb9SRuslan Bilovol 		}
158eb9fecb9SRuslan Bilovol 	} else {
159eb9fecb9SRuslan Bilovol 		if (unlikely(pending < req->actual)) {
16096afb54eSVladimir Zapolskiy 			memcpy(runtime->dma_area + hw_ptr, req->buf, pending);
16196afb54eSVladimir Zapolskiy 			memcpy(runtime->dma_area, req->buf + pending,
162eb9fecb9SRuslan Bilovol 			       req->actual - pending);
163eb9fecb9SRuslan Bilovol 		} else {
16496afb54eSVladimir Zapolskiy 			memcpy(runtime->dma_area + hw_ptr, req->buf,
16596afb54eSVladimir Zapolskiy 			       req->actual);
166eb9fecb9SRuslan Bilovol 		}
167eb9fecb9SRuslan Bilovol 	}
168eb9fecb9SRuslan Bilovol 
1696b37bd78SJoshua Frkuska 	spin_lock_irqsave(&prm->lock, flags);
1706b37bd78SJoshua Frkuska 	/* update hw_ptr after data is copied to memory */
17196afb54eSVladimir Zapolskiy 	prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes;
172773e53d5SVladimir Zapolskiy 	hw_ptr = prm->hw_ptr;
1736b37bd78SJoshua Frkuska 	spin_unlock_irqrestore(&prm->lock, flags);
17456bc6158SVladimir Zapolskiy 	snd_pcm_stream_unlock_irqrestore(substream, flags2);
1756b37bd78SJoshua Frkuska 
176773e53d5SVladimir Zapolskiy 	if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
177773e53d5SVladimir Zapolskiy 		snd_pcm_period_elapsed(substream);
178773e53d5SVladimir Zapolskiy 
179eb9fecb9SRuslan Bilovol exit:
180eb9fecb9SRuslan Bilovol 	if (usb_ep_queue(ep, req, GFP_ATOMIC))
181eb9fecb9SRuslan Bilovol 		dev_err(uac->card->dev, "%d Error!\n", __LINE__);
182eb9fecb9SRuslan Bilovol }
183eb9fecb9SRuslan Bilovol 
184eb9fecb9SRuslan Bilovol static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
185eb9fecb9SRuslan Bilovol {
186eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
187eb9fecb9SRuslan Bilovol 	struct uac_rtd_params *prm;
188eb9fecb9SRuslan Bilovol 	struct g_audio *audio_dev;
189eb9fecb9SRuslan Bilovol 	struct uac_params *params;
190eb9fecb9SRuslan Bilovol 	unsigned long flags;
191eb9fecb9SRuslan Bilovol 	int err = 0;
192eb9fecb9SRuslan Bilovol 
193eb9fecb9SRuslan Bilovol 	audio_dev = uac->audio_dev;
194eb9fecb9SRuslan Bilovol 	params = &audio_dev->params;
195eb9fecb9SRuslan Bilovol 
196eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
197eb9fecb9SRuslan Bilovol 		prm = &uac->p_prm;
198eb9fecb9SRuslan Bilovol 	else
199eb9fecb9SRuslan Bilovol 		prm = &uac->c_prm;
200eb9fecb9SRuslan Bilovol 
201eb9fecb9SRuslan Bilovol 	spin_lock_irqsave(&prm->lock, flags);
202eb9fecb9SRuslan Bilovol 
203eb9fecb9SRuslan Bilovol 	/* Reset */
204eb9fecb9SRuslan Bilovol 	prm->hw_ptr = 0;
205eb9fecb9SRuslan Bilovol 
206eb9fecb9SRuslan Bilovol 	switch (cmd) {
207eb9fecb9SRuslan Bilovol 	case SNDRV_PCM_TRIGGER_START:
208eb9fecb9SRuslan Bilovol 	case SNDRV_PCM_TRIGGER_RESUME:
209eb9fecb9SRuslan Bilovol 		prm->ss = substream;
210eb9fecb9SRuslan Bilovol 		break;
211eb9fecb9SRuslan Bilovol 	case SNDRV_PCM_TRIGGER_STOP:
212eb9fecb9SRuslan Bilovol 	case SNDRV_PCM_TRIGGER_SUSPEND:
213eb9fecb9SRuslan Bilovol 		prm->ss = NULL;
214eb9fecb9SRuslan Bilovol 		break;
215eb9fecb9SRuslan Bilovol 	default:
216eb9fecb9SRuslan Bilovol 		err = -EINVAL;
217eb9fecb9SRuslan Bilovol 	}
218eb9fecb9SRuslan Bilovol 
219eb9fecb9SRuslan Bilovol 	spin_unlock_irqrestore(&prm->lock, flags);
220eb9fecb9SRuslan Bilovol 
221eb9fecb9SRuslan Bilovol 	/* Clear buffer after Play stops */
222eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss)
223eb9fecb9SRuslan Bilovol 		memset(prm->rbuf, 0, prm->max_psize * params->req_number);
224eb9fecb9SRuslan Bilovol 
225eb9fecb9SRuslan Bilovol 	return err;
226eb9fecb9SRuslan Bilovol }
227eb9fecb9SRuslan Bilovol 
228eb9fecb9SRuslan Bilovol static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream)
229eb9fecb9SRuslan Bilovol {
230eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
231eb9fecb9SRuslan Bilovol 	struct uac_rtd_params *prm;
232eb9fecb9SRuslan Bilovol 
233eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
234eb9fecb9SRuslan Bilovol 		prm = &uac->p_prm;
235eb9fecb9SRuslan Bilovol 	else
236eb9fecb9SRuslan Bilovol 		prm = &uac->c_prm;
237eb9fecb9SRuslan Bilovol 
238eb9fecb9SRuslan Bilovol 	return bytes_to_frames(substream->runtime, prm->hw_ptr);
239eb9fecb9SRuslan Bilovol }
240eb9fecb9SRuslan Bilovol 
24125dbd75dSJerome Brunet static u64 uac_ssize_to_fmt(int ssize)
24225dbd75dSJerome Brunet {
24325dbd75dSJerome Brunet 	u64 ret;
24425dbd75dSJerome Brunet 
24525dbd75dSJerome Brunet 	switch (ssize) {
24625dbd75dSJerome Brunet 	case 3:
24725dbd75dSJerome Brunet 		ret = SNDRV_PCM_FMTBIT_S24_3LE;
24825dbd75dSJerome Brunet 		break;
24925dbd75dSJerome Brunet 	case 4:
25025dbd75dSJerome Brunet 		ret = SNDRV_PCM_FMTBIT_S32_LE;
25125dbd75dSJerome Brunet 		break;
25225dbd75dSJerome Brunet 	default:
25325dbd75dSJerome Brunet 		ret = SNDRV_PCM_FMTBIT_S16_LE;
25425dbd75dSJerome Brunet 		break;
25525dbd75dSJerome Brunet 	}
25625dbd75dSJerome Brunet 
25725dbd75dSJerome Brunet 	return ret;
25825dbd75dSJerome Brunet }
25925dbd75dSJerome Brunet 
260eb9fecb9SRuslan Bilovol static int uac_pcm_open(struct snd_pcm_substream *substream)
261eb9fecb9SRuslan Bilovol {
262eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
263eb9fecb9SRuslan Bilovol 	struct snd_pcm_runtime *runtime = substream->runtime;
264eb9fecb9SRuslan Bilovol 	struct g_audio *audio_dev;
265eb9fecb9SRuslan Bilovol 	struct uac_params *params;
266eb9fecb9SRuslan Bilovol 	int p_ssize, c_ssize;
267eb9fecb9SRuslan Bilovol 	int p_srate, c_srate;
268eb9fecb9SRuslan Bilovol 	int p_chmask, c_chmask;
269eb9fecb9SRuslan Bilovol 
270eb9fecb9SRuslan Bilovol 	audio_dev = uac->audio_dev;
271eb9fecb9SRuslan Bilovol 	params = &audio_dev->params;
272eb9fecb9SRuslan Bilovol 	p_ssize = params->p_ssize;
273eb9fecb9SRuslan Bilovol 	c_ssize = params->c_ssize;
274eb9fecb9SRuslan Bilovol 	p_srate = params->p_srate;
275eb9fecb9SRuslan Bilovol 	c_srate = params->c_srate;
276eb9fecb9SRuslan Bilovol 	p_chmask = params->p_chmask;
277eb9fecb9SRuslan Bilovol 	c_chmask = params->c_chmask;
278eb9fecb9SRuslan Bilovol 	uac->p_residue = 0;
279eb9fecb9SRuslan Bilovol 
280eb9fecb9SRuslan Bilovol 	runtime->hw = uac_pcm_hardware;
281eb9fecb9SRuslan Bilovol 
282eb9fecb9SRuslan Bilovol 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
283eb9fecb9SRuslan Bilovol 		spin_lock_init(&uac->p_prm.lock);
284eb9fecb9SRuslan Bilovol 		runtime->hw.rate_min = p_srate;
28525dbd75dSJerome Brunet 		runtime->hw.formats = uac_ssize_to_fmt(p_ssize);
286eb9fecb9SRuslan Bilovol 		runtime->hw.channels_min = num_channels(p_chmask);
287eb9fecb9SRuslan Bilovol 		runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize
288eb9fecb9SRuslan Bilovol 						/ runtime->hw.periods_min;
289eb9fecb9SRuslan Bilovol 	} else {
290eb9fecb9SRuslan Bilovol 		spin_lock_init(&uac->c_prm.lock);
291eb9fecb9SRuslan Bilovol 		runtime->hw.rate_min = c_srate;
29225dbd75dSJerome Brunet 		runtime->hw.formats = uac_ssize_to_fmt(c_ssize);
293eb9fecb9SRuslan Bilovol 		runtime->hw.channels_min = num_channels(c_chmask);
294eb9fecb9SRuslan Bilovol 		runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize
295eb9fecb9SRuslan Bilovol 						/ runtime->hw.periods_min;
296eb9fecb9SRuslan Bilovol 	}
297eb9fecb9SRuslan Bilovol 
298eb9fecb9SRuslan Bilovol 	runtime->hw.rate_max = runtime->hw.rate_min;
299eb9fecb9SRuslan Bilovol 	runtime->hw.channels_max = runtime->hw.channels_min;
300eb9fecb9SRuslan Bilovol 
301eb9fecb9SRuslan Bilovol 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
302eb9fecb9SRuslan Bilovol 
303eb9fecb9SRuslan Bilovol 	return 0;
304eb9fecb9SRuslan Bilovol }
305eb9fecb9SRuslan Bilovol 
306eb9fecb9SRuslan Bilovol /* ALSA cries without these function pointers */
307eb9fecb9SRuslan Bilovol static int uac_pcm_null(struct snd_pcm_substream *substream)
308eb9fecb9SRuslan Bilovol {
309eb9fecb9SRuslan Bilovol 	return 0;
310eb9fecb9SRuslan Bilovol }
311eb9fecb9SRuslan Bilovol 
312640c0be8SArvind Yadav static const struct snd_pcm_ops uac_pcm_ops = {
313eb9fecb9SRuslan Bilovol 	.open = uac_pcm_open,
314eb9fecb9SRuslan Bilovol 	.close = uac_pcm_null,
315eb9fecb9SRuslan Bilovol 	.trigger = uac_pcm_trigger,
316eb9fecb9SRuslan Bilovol 	.pointer = uac_pcm_pointer,
317eb9fecb9SRuslan Bilovol 	.prepare = uac_pcm_null,
318eb9fecb9SRuslan Bilovol };
319eb9fecb9SRuslan Bilovol 
320eb9fecb9SRuslan Bilovol static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep)
321eb9fecb9SRuslan Bilovol {
322eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = prm->uac;
323eb9fecb9SRuslan Bilovol 	struct g_audio *audio_dev;
324eb9fecb9SRuslan Bilovol 	struct uac_params *params;
325eb9fecb9SRuslan Bilovol 	int i;
326eb9fecb9SRuslan Bilovol 
327eb9fecb9SRuslan Bilovol 	if (!prm->ep_enabled)
328eb9fecb9SRuslan Bilovol 		return;
329eb9fecb9SRuslan Bilovol 
330eb9fecb9SRuslan Bilovol 	prm->ep_enabled = false;
331eb9fecb9SRuslan Bilovol 
332eb9fecb9SRuslan Bilovol 	audio_dev = uac->audio_dev;
333eb9fecb9SRuslan Bilovol 	params = &audio_dev->params;
334eb9fecb9SRuslan Bilovol 
335eb9fecb9SRuslan Bilovol 	for (i = 0; i < params->req_number; i++) {
336*29865117SJerome Brunet 		if (prm->reqs[i]) {
337*29865117SJerome Brunet 			if (usb_ep_dequeue(ep, prm->reqs[i]))
338*29865117SJerome Brunet 				usb_ep_free_request(ep, prm->reqs[i]);
3397de8681bSJack Pham 			/*
3407de8681bSJack Pham 			 * If usb_ep_dequeue() cannot successfully dequeue the
3417de8681bSJack Pham 			 * request, the request will be freed by the completion
3427de8681bSJack Pham 			 * callback.
3437de8681bSJack Pham 			 */
3447de8681bSJack Pham 
345*29865117SJerome Brunet 			prm->reqs[i] = NULL;
346eb9fecb9SRuslan Bilovol 		}
347eb9fecb9SRuslan Bilovol 	}
348eb9fecb9SRuslan Bilovol 
349eb9fecb9SRuslan Bilovol 	if (usb_ep_disable(ep))
350eb9fecb9SRuslan Bilovol 		dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
351eb9fecb9SRuslan Bilovol }
352eb9fecb9SRuslan Bilovol 
353eb9fecb9SRuslan Bilovol 
354eb9fecb9SRuslan Bilovol int u_audio_start_capture(struct g_audio *audio_dev)
355eb9fecb9SRuslan Bilovol {
356eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = audio_dev->uac;
357eb9fecb9SRuslan Bilovol 	struct usb_gadget *gadget = audio_dev->gadget;
358eb9fecb9SRuslan Bilovol 	struct device *dev = &gadget->dev;
359eb9fecb9SRuslan Bilovol 	struct usb_request *req;
360eb9fecb9SRuslan Bilovol 	struct usb_ep *ep;
361eb9fecb9SRuslan Bilovol 	struct uac_rtd_params *prm;
362eb9fecb9SRuslan Bilovol 	struct uac_params *params = &audio_dev->params;
363eb9fecb9SRuslan Bilovol 	int req_len, i;
364eb9fecb9SRuslan Bilovol 
365eb9fecb9SRuslan Bilovol 	ep = audio_dev->out_ep;
366eb9fecb9SRuslan Bilovol 	prm = &uac->c_prm;
367eb9fecb9SRuslan Bilovol 	config_ep_by_speed(gadget, &audio_dev->func, ep);
368904967c6SJohn Keeping 	req_len = ep->maxpacket;
369eb9fecb9SRuslan Bilovol 
370eb9fecb9SRuslan Bilovol 	prm->ep_enabled = true;
371eb9fecb9SRuslan Bilovol 	usb_ep_enable(ep);
372eb9fecb9SRuslan Bilovol 
373eb9fecb9SRuslan Bilovol 	for (i = 0; i < params->req_number; i++) {
374*29865117SJerome Brunet 		if (!prm->reqs[i]) {
375eb9fecb9SRuslan Bilovol 			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
376eb9fecb9SRuslan Bilovol 			if (req == NULL)
377eb9fecb9SRuslan Bilovol 				return -ENOMEM;
378eb9fecb9SRuslan Bilovol 
379*29865117SJerome Brunet 			prm->reqs[i] = req;
380eb9fecb9SRuslan Bilovol 
381eb9fecb9SRuslan Bilovol 			req->zero = 0;
382*29865117SJerome Brunet 			req->context = prm;
383eb9fecb9SRuslan Bilovol 			req->length = req_len;
384eb9fecb9SRuslan Bilovol 			req->complete = u_audio_iso_complete;
385904967c6SJohn Keeping 			req->buf = prm->rbuf + i * ep->maxpacket;
386eb9fecb9SRuslan Bilovol 		}
387eb9fecb9SRuslan Bilovol 
388*29865117SJerome Brunet 		if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC))
389eb9fecb9SRuslan Bilovol 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
390eb9fecb9SRuslan Bilovol 	}
391eb9fecb9SRuslan Bilovol 
392eb9fecb9SRuslan Bilovol 	return 0;
393eb9fecb9SRuslan Bilovol }
394eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_capture);
395eb9fecb9SRuslan Bilovol 
396eb9fecb9SRuslan Bilovol void u_audio_stop_capture(struct g_audio *audio_dev)
397eb9fecb9SRuslan Bilovol {
398eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = audio_dev->uac;
399eb9fecb9SRuslan Bilovol 
400eb9fecb9SRuslan Bilovol 	free_ep(&uac->c_prm, audio_dev->out_ep);
401eb9fecb9SRuslan Bilovol }
402eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_capture);
403eb9fecb9SRuslan Bilovol 
404eb9fecb9SRuslan Bilovol int u_audio_start_playback(struct g_audio *audio_dev)
405eb9fecb9SRuslan Bilovol {
406eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = audio_dev->uac;
407eb9fecb9SRuslan Bilovol 	struct usb_gadget *gadget = audio_dev->gadget;
408eb9fecb9SRuslan Bilovol 	struct device *dev = &gadget->dev;
409eb9fecb9SRuslan Bilovol 	struct usb_request *req;
410eb9fecb9SRuslan Bilovol 	struct usb_ep *ep;
411eb9fecb9SRuslan Bilovol 	struct uac_rtd_params *prm;
412eb9fecb9SRuslan Bilovol 	struct uac_params *params = &audio_dev->params;
4136b02af34SJohn Keeping 	unsigned int factor;
414eb9fecb9SRuslan Bilovol 	const struct usb_endpoint_descriptor *ep_desc;
415eb9fecb9SRuslan Bilovol 	int req_len, i;
416eb9fecb9SRuslan Bilovol 
417eb9fecb9SRuslan Bilovol 	ep = audio_dev->in_ep;
418eb9fecb9SRuslan Bilovol 	prm = &uac->p_prm;
419eb9fecb9SRuslan Bilovol 	config_ep_by_speed(gadget, &audio_dev->func, ep);
420eb9fecb9SRuslan Bilovol 
421eb9fecb9SRuslan Bilovol 	ep_desc = ep->desc;
422eb9fecb9SRuslan Bilovol 
423eb9fecb9SRuslan Bilovol 	/* pre-calculate the playback endpoint's interval */
424eb9fecb9SRuslan Bilovol 	if (gadget->speed == USB_SPEED_FULL)
425eb9fecb9SRuslan Bilovol 		factor = 1000;
426eb9fecb9SRuslan Bilovol 	else
427eb9fecb9SRuslan Bilovol 		factor = 8000;
428eb9fecb9SRuslan Bilovol 
429eb9fecb9SRuslan Bilovol 	/* pre-compute some values for iso_complete() */
430eb9fecb9SRuslan Bilovol 	uac->p_framesize = params->p_ssize *
431eb9fecb9SRuslan Bilovol 			    num_channels(params->p_chmask);
432eb9fecb9SRuslan Bilovol 	uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
4336b02af34SJohn Keeping 	uac->p_pktsize = min_t(unsigned int,
4346b02af34SJohn Keeping 				uac->p_framesize *
4356b02af34SJohn Keeping 					(params->p_srate / uac->p_interval),
436904967c6SJohn Keeping 				ep->maxpacket);
437eb9fecb9SRuslan Bilovol 
438904967c6SJohn Keeping 	if (uac->p_pktsize < ep->maxpacket)
4396b02af34SJohn Keeping 		uac->p_pktsize_residue = uac->p_framesize *
4406b02af34SJohn Keeping 			(params->p_srate % uac->p_interval);
441eb9fecb9SRuslan Bilovol 	else
442eb9fecb9SRuslan Bilovol 		uac->p_pktsize_residue = 0;
443eb9fecb9SRuslan Bilovol 
444eb9fecb9SRuslan Bilovol 	req_len = uac->p_pktsize;
445eb9fecb9SRuslan Bilovol 	uac->p_residue = 0;
446eb9fecb9SRuslan Bilovol 
447eb9fecb9SRuslan Bilovol 	prm->ep_enabled = true;
448eb9fecb9SRuslan Bilovol 	usb_ep_enable(ep);
449eb9fecb9SRuslan Bilovol 
450eb9fecb9SRuslan Bilovol 	for (i = 0; i < params->req_number; i++) {
451*29865117SJerome Brunet 		if (!prm->reqs[i]) {
452eb9fecb9SRuslan Bilovol 			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
453eb9fecb9SRuslan Bilovol 			if (req == NULL)
454eb9fecb9SRuslan Bilovol 				return -ENOMEM;
455eb9fecb9SRuslan Bilovol 
456*29865117SJerome Brunet 			prm->reqs[i] = req;
457eb9fecb9SRuslan Bilovol 
458eb9fecb9SRuslan Bilovol 			req->zero = 0;
459*29865117SJerome Brunet 			req->context = prm;
460eb9fecb9SRuslan Bilovol 			req->length = req_len;
461eb9fecb9SRuslan Bilovol 			req->complete = u_audio_iso_complete;
462904967c6SJohn Keeping 			req->buf = prm->rbuf + i * ep->maxpacket;
463eb9fecb9SRuslan Bilovol 		}
464eb9fecb9SRuslan Bilovol 
465*29865117SJerome Brunet 		if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC))
466eb9fecb9SRuslan Bilovol 			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
467eb9fecb9SRuslan Bilovol 	}
468eb9fecb9SRuslan Bilovol 
469eb9fecb9SRuslan Bilovol 	return 0;
470eb9fecb9SRuslan Bilovol }
471eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_start_playback);
472eb9fecb9SRuslan Bilovol 
473eb9fecb9SRuslan Bilovol void u_audio_stop_playback(struct g_audio *audio_dev)
474eb9fecb9SRuslan Bilovol {
475eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac = audio_dev->uac;
476eb9fecb9SRuslan Bilovol 
477eb9fecb9SRuslan Bilovol 	free_ep(&uac->p_prm, audio_dev->in_ep);
478eb9fecb9SRuslan Bilovol }
479eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(u_audio_stop_playback);
480eb9fecb9SRuslan Bilovol 
481eb9fecb9SRuslan Bilovol int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
482eb9fecb9SRuslan Bilovol 					const char *card_name)
483eb9fecb9SRuslan Bilovol {
484eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac;
485eb9fecb9SRuslan Bilovol 	struct snd_card *card;
486eb9fecb9SRuslan Bilovol 	struct snd_pcm *pcm;
487eb9fecb9SRuslan Bilovol 	struct uac_params *params;
488eb9fecb9SRuslan Bilovol 	int p_chmask, c_chmask;
489eb9fecb9SRuslan Bilovol 	int err;
490eb9fecb9SRuslan Bilovol 
491eb9fecb9SRuslan Bilovol 	if (!g_audio)
492eb9fecb9SRuslan Bilovol 		return -EINVAL;
493eb9fecb9SRuslan Bilovol 
494eb9fecb9SRuslan Bilovol 	uac = kzalloc(sizeof(*uac), GFP_KERNEL);
495eb9fecb9SRuslan Bilovol 	if (!uac)
496eb9fecb9SRuslan Bilovol 		return -ENOMEM;
497eb9fecb9SRuslan Bilovol 	g_audio->uac = uac;
498eb9fecb9SRuslan Bilovol 	uac->audio_dev = g_audio;
499eb9fecb9SRuslan Bilovol 
500eb9fecb9SRuslan Bilovol 	params = &g_audio->params;
501eb9fecb9SRuslan Bilovol 	p_chmask = params->p_chmask;
502eb9fecb9SRuslan Bilovol 	c_chmask = params->c_chmask;
503eb9fecb9SRuslan Bilovol 
504eb9fecb9SRuslan Bilovol 	if (c_chmask) {
505eb9fecb9SRuslan Bilovol 		struct uac_rtd_params *prm = &uac->c_prm;
506eb9fecb9SRuslan Bilovol 
507eb9fecb9SRuslan Bilovol 		uac->c_prm.uac = uac;
508eb9fecb9SRuslan Bilovol 		prm->max_psize = g_audio->out_ep_maxpsize;
509eb9fecb9SRuslan Bilovol 
510*29865117SJerome Brunet 		prm->reqs = kcalloc(params->req_number,
511*29865117SJerome Brunet 				    sizeof(struct usb_request *),
512eb9fecb9SRuslan Bilovol 				    GFP_KERNEL);
513*29865117SJerome Brunet 		if (!prm->reqs) {
514eb9fecb9SRuslan Bilovol 			err = -ENOMEM;
515eb9fecb9SRuslan Bilovol 			goto fail;
516eb9fecb9SRuslan Bilovol 		}
517eb9fecb9SRuslan Bilovol 
518eb9fecb9SRuslan Bilovol 		prm->rbuf = kcalloc(params->req_number, prm->max_psize,
519eb9fecb9SRuslan Bilovol 				GFP_KERNEL);
520eb9fecb9SRuslan Bilovol 		if (!prm->rbuf) {
521eb9fecb9SRuslan Bilovol 			prm->max_psize = 0;
522eb9fecb9SRuslan Bilovol 			err = -ENOMEM;
523eb9fecb9SRuslan Bilovol 			goto fail;
524eb9fecb9SRuslan Bilovol 		}
525eb9fecb9SRuslan Bilovol 	}
526eb9fecb9SRuslan Bilovol 
527eb9fecb9SRuslan Bilovol 	if (p_chmask) {
528eb9fecb9SRuslan Bilovol 		struct uac_rtd_params *prm = &uac->p_prm;
529eb9fecb9SRuslan Bilovol 
530eb9fecb9SRuslan Bilovol 		uac->p_prm.uac = uac;
531eb9fecb9SRuslan Bilovol 		prm->max_psize = g_audio->in_ep_maxpsize;
532eb9fecb9SRuslan Bilovol 
533*29865117SJerome Brunet 		prm->reqs = kcalloc(params->req_number,
534*29865117SJerome Brunet 				    sizeof(struct usb_request *),
535eb9fecb9SRuslan Bilovol 				    GFP_KERNEL);
536*29865117SJerome Brunet 		if (!prm->reqs) {
537eb9fecb9SRuslan Bilovol 			err = -ENOMEM;
538eb9fecb9SRuslan Bilovol 			goto fail;
539eb9fecb9SRuslan Bilovol 		}
540eb9fecb9SRuslan Bilovol 
541eb9fecb9SRuslan Bilovol 		prm->rbuf = kcalloc(params->req_number, prm->max_psize,
542eb9fecb9SRuslan Bilovol 				GFP_KERNEL);
543eb9fecb9SRuslan Bilovol 		if (!prm->rbuf) {
544eb9fecb9SRuslan Bilovol 			prm->max_psize = 0;
545eb9fecb9SRuslan Bilovol 			err = -ENOMEM;
546eb9fecb9SRuslan Bilovol 			goto fail;
547eb9fecb9SRuslan Bilovol 		}
548eb9fecb9SRuslan Bilovol 	}
549eb9fecb9SRuslan Bilovol 
550eb9fecb9SRuslan Bilovol 	/* Choose any slot, with no id */
551eb9fecb9SRuslan Bilovol 	err = snd_card_new(&g_audio->gadget->dev,
552eb9fecb9SRuslan Bilovol 			-1, NULL, THIS_MODULE, 0, &card);
553eb9fecb9SRuslan Bilovol 	if (err < 0)
554eb9fecb9SRuslan Bilovol 		goto fail;
555eb9fecb9SRuslan Bilovol 
556eb9fecb9SRuslan Bilovol 	uac->card = card;
557eb9fecb9SRuslan Bilovol 
558eb9fecb9SRuslan Bilovol 	/*
559eb9fecb9SRuslan Bilovol 	 * Create first PCM device
560eb9fecb9SRuslan Bilovol 	 * Create a substream only for non-zero channel streams
561eb9fecb9SRuslan Bilovol 	 */
562eb9fecb9SRuslan Bilovol 	err = snd_pcm_new(uac->card, pcm_name, 0,
563eb9fecb9SRuslan Bilovol 			       p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
564eb9fecb9SRuslan Bilovol 	if (err < 0)
565eb9fecb9SRuslan Bilovol 		goto snd_fail;
566eb9fecb9SRuslan Bilovol 
567dfa042faSEugeniu Rosca 	strlcpy(pcm->name, pcm_name, sizeof(pcm->name));
568eb9fecb9SRuslan Bilovol 	pcm->private_data = uac;
569eb9fecb9SRuslan Bilovol 	uac->pcm = pcm;
570eb9fecb9SRuslan Bilovol 
571eb9fecb9SRuslan Bilovol 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
572eb9fecb9SRuslan Bilovol 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
573eb9fecb9SRuslan Bilovol 
574dfa042faSEugeniu Rosca 	strlcpy(card->driver, card_name, sizeof(card->driver));
575dfa042faSEugeniu Rosca 	strlcpy(card->shortname, card_name, sizeof(card->shortname));
576eb9fecb9SRuslan Bilovol 	sprintf(card->longname, "%s %i", card_name, card->dev->id);
577eb9fecb9SRuslan Bilovol 
578d27ab1e6STakashi Iwai 	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
57967b2945dSTakashi Iwai 				       NULL, 0, BUFF_SIZE_MAX);
580eb9fecb9SRuslan Bilovol 
581eb9fecb9SRuslan Bilovol 	err = snd_card_register(card);
582eb9fecb9SRuslan Bilovol 
583eb9fecb9SRuslan Bilovol 	if (!err)
584eb9fecb9SRuslan Bilovol 		return 0;
585eb9fecb9SRuslan Bilovol 
586eb9fecb9SRuslan Bilovol snd_fail:
587eb9fecb9SRuslan Bilovol 	snd_card_free(card);
588eb9fecb9SRuslan Bilovol fail:
589*29865117SJerome Brunet 	kfree(uac->p_prm.reqs);
590*29865117SJerome Brunet 	kfree(uac->c_prm.reqs);
591eb9fecb9SRuslan Bilovol 	kfree(uac->p_prm.rbuf);
592eb9fecb9SRuslan Bilovol 	kfree(uac->c_prm.rbuf);
593eb9fecb9SRuslan Bilovol 	kfree(uac);
594eb9fecb9SRuslan Bilovol 
595eb9fecb9SRuslan Bilovol 	return err;
596eb9fecb9SRuslan Bilovol }
597eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_setup);
598eb9fecb9SRuslan Bilovol 
599eb9fecb9SRuslan Bilovol void g_audio_cleanup(struct g_audio *g_audio)
600eb9fecb9SRuslan Bilovol {
601eb9fecb9SRuslan Bilovol 	struct snd_uac_chip *uac;
602eb9fecb9SRuslan Bilovol 	struct snd_card *card;
603eb9fecb9SRuslan Bilovol 
604eb9fecb9SRuslan Bilovol 	if (!g_audio || !g_audio->uac)
605eb9fecb9SRuslan Bilovol 		return;
606eb9fecb9SRuslan Bilovol 
607eb9fecb9SRuslan Bilovol 	uac = g_audio->uac;
608eb9fecb9SRuslan Bilovol 	card = uac->card;
609eb9fecb9SRuslan Bilovol 	if (card)
610eb9fecb9SRuslan Bilovol 		snd_card_free(card);
611eb9fecb9SRuslan Bilovol 
612*29865117SJerome Brunet 	kfree(uac->p_prm.reqs);
613*29865117SJerome Brunet 	kfree(uac->c_prm.reqs);
614eb9fecb9SRuslan Bilovol 	kfree(uac->p_prm.rbuf);
615eb9fecb9SRuslan Bilovol 	kfree(uac->c_prm.rbuf);
616eb9fecb9SRuslan Bilovol 	kfree(uac);
617eb9fecb9SRuslan Bilovol }
618eb9fecb9SRuslan Bilovol EXPORT_SYMBOL_GPL(g_audio_cleanup);
619eb9fecb9SRuslan Bilovol 
620eb9fecb9SRuslan Bilovol MODULE_LICENSE("GPL");
621eb9fecb9SRuslan Bilovol MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities");
622eb9fecb9SRuslan Bilovol MODULE_AUTHOR("Ruslan Bilovol");
623