162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *   US-X2Y AUDIO
462306a36Sopenharmony_ci *   Copyright (c) 2002-2004 by Karsten Wiese
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *   based on
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *   (Tentative) USB Audio Driver for ALSA
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *   Main and PCM part
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *   Many codes borrowed from audio.c by
1562306a36Sopenharmony_ci *	    Alan Cox (alan@lxorguk.ukuu.org.uk)
1662306a36Sopenharmony_ci *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/usb.h>
2362306a36Sopenharmony_ci#include <linux/moduleparam.h>
2462306a36Sopenharmony_ci#include <sound/core.h>
2562306a36Sopenharmony_ci#include <sound/info.h>
2662306a36Sopenharmony_ci#include <sound/pcm.h>
2762306a36Sopenharmony_ci#include <sound/pcm_params.h>
2862306a36Sopenharmony_ci#include "usx2y.h"
2962306a36Sopenharmony_ci#include "usbusx2y.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Default value used for nr of packs per urb.
3262306a36Sopenharmony_ci * 1 to 4 have been tested ok on uhci.
3362306a36Sopenharmony_ci * To use 3 on ohci, you'd need a patch:
3462306a36Sopenharmony_ci * look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on
3562306a36Sopenharmony_ci * "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425"
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * 1, 2 and 4 work out of the box on ohci, if I recall correctly.
3862306a36Sopenharmony_ci * Bigger is safer operation, smaller gives lower latencies.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci#define USX2Y_NRPACKS 4
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* If your system works ok with this module's parameter
4362306a36Sopenharmony_ci * nrpacks set to 1, you might as well comment
4462306a36Sopenharmony_ci * this define out, and thereby produce smaller, faster code.
4562306a36Sopenharmony_ci * You'd also set USX2Y_NRPACKS to 1 then.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci#define USX2Y_NRPACKS_VARIABLE 1
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#ifdef USX2Y_NRPACKS_VARIABLE
5062306a36Sopenharmony_cistatic int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */
5162306a36Sopenharmony_ci#define  nr_of_packs() nrpacks
5262306a36Sopenharmony_cimodule_param(nrpacks, int, 0444);
5362306a36Sopenharmony_ciMODULE_PARM_DESC(nrpacks, "Number of packets per URB.");
5462306a36Sopenharmony_ci#else
5562306a36Sopenharmony_ci#define nr_of_packs() USX2Y_NRPACKS
5662306a36Sopenharmony_ci#endif
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int usx2y_urb_capt_retire(struct snd_usx2y_substream *subs)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct urb	*urb = subs->completed_urb;
6162306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
6262306a36Sopenharmony_ci	unsigned char	*cp;
6362306a36Sopenharmony_ci	int		i, len, lens = 0, hwptr_done = subs->hwptr_done;
6462306a36Sopenharmony_ci	int		cnt, blen;
6562306a36Sopenharmony_ci	struct usx2ydev	*usx2y = subs->usx2y;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	for (i = 0; i < nr_of_packs(); i++) {
6862306a36Sopenharmony_ci		cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
6962306a36Sopenharmony_ci		if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
7062306a36Sopenharmony_ci			snd_printk(KERN_ERR
7162306a36Sopenharmony_ci				   "active frame status %i. Most probably some hardware problem.\n",
7262306a36Sopenharmony_ci				   urb->iso_frame_desc[i].status);
7362306a36Sopenharmony_ci			return urb->iso_frame_desc[i].status;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci		len = urb->iso_frame_desc[i].actual_length / usx2y->stride;
7662306a36Sopenharmony_ci		if (!len) {
7762306a36Sopenharmony_ci			snd_printd("0 == len ERROR!\n");
7862306a36Sopenharmony_ci			continue;
7962306a36Sopenharmony_ci		}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		/* copy a data chunk */
8262306a36Sopenharmony_ci		if ((hwptr_done + len) > runtime->buffer_size) {
8362306a36Sopenharmony_ci			cnt = runtime->buffer_size - hwptr_done;
8462306a36Sopenharmony_ci			blen = cnt * usx2y->stride;
8562306a36Sopenharmony_ci			memcpy(runtime->dma_area + hwptr_done * usx2y->stride, cp, blen);
8662306a36Sopenharmony_ci			memcpy(runtime->dma_area, cp + blen, len * usx2y->stride - blen);
8762306a36Sopenharmony_ci		} else {
8862306a36Sopenharmony_ci			memcpy(runtime->dma_area + hwptr_done * usx2y->stride, cp,
8962306a36Sopenharmony_ci			       len * usx2y->stride);
9062306a36Sopenharmony_ci		}
9162306a36Sopenharmony_ci		lens += len;
9262306a36Sopenharmony_ci		hwptr_done += len;
9362306a36Sopenharmony_ci		if (hwptr_done >= runtime->buffer_size)
9462306a36Sopenharmony_ci			hwptr_done -= runtime->buffer_size;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	subs->hwptr_done = hwptr_done;
9862306a36Sopenharmony_ci	subs->transfer_done += lens;
9962306a36Sopenharmony_ci	/* update the pointer, call callback if necessary */
10062306a36Sopenharmony_ci	if (subs->transfer_done >= runtime->period_size) {
10162306a36Sopenharmony_ci		subs->transfer_done -= runtime->period_size;
10262306a36Sopenharmony_ci		snd_pcm_period_elapsed(subs->pcm_substream);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	return 0;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * prepare urb for playback data pipe
10962306a36Sopenharmony_ci *
11062306a36Sopenharmony_ci * we copy the data directly from the pcm buffer.
11162306a36Sopenharmony_ci * the current position to be copied is held in hwptr field.
11262306a36Sopenharmony_ci * since a urb can handle only a single linear buffer, if the total
11362306a36Sopenharmony_ci * transferred area overflows the buffer boundary, we cannot send
11462306a36Sopenharmony_ci * it directly from the buffer.  thus the data is once copied to
11562306a36Sopenharmony_ci * a temporary buffer and urb points to that.
11662306a36Sopenharmony_ci */
11762306a36Sopenharmony_cistatic int usx2y_urb_play_prepare(struct snd_usx2y_substream *subs,
11862306a36Sopenharmony_ci				  struct urb *cap_urb,
11962306a36Sopenharmony_ci				  struct urb *urb)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct usx2ydev *usx2y = subs->usx2y;
12262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
12362306a36Sopenharmony_ci	int count, counts, pack, len;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	count = 0;
12662306a36Sopenharmony_ci	for (pack = 0; pack <  nr_of_packs(); pack++) {
12762306a36Sopenharmony_ci		/* calculate the size of a packet */
12862306a36Sopenharmony_ci		counts = cap_urb->iso_frame_desc[pack].actual_length / usx2y->stride;
12962306a36Sopenharmony_ci		count += counts;
13062306a36Sopenharmony_ci		if (counts < 43 || counts > 50) {
13162306a36Sopenharmony_ci			snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
13262306a36Sopenharmony_ci			return -EPIPE;
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci		/* set up descriptor */
13562306a36Sopenharmony_ci		urb->iso_frame_desc[pack].offset = pack ?
13662306a36Sopenharmony_ci			urb->iso_frame_desc[pack - 1].offset +
13762306a36Sopenharmony_ci			urb->iso_frame_desc[pack - 1].length :
13862306a36Sopenharmony_ci			0;
13962306a36Sopenharmony_ci		urb->iso_frame_desc[pack].length = cap_urb->iso_frame_desc[pack].actual_length;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	if (atomic_read(&subs->state) >= STATE_PRERUNNING) {
14262306a36Sopenharmony_ci		if (subs->hwptr + count > runtime->buffer_size) {
14362306a36Sopenharmony_ci			/* err, the transferred area goes over buffer boundary.
14462306a36Sopenharmony_ci			 * copy the data to the temp buffer.
14562306a36Sopenharmony_ci			 */
14662306a36Sopenharmony_ci			len = runtime->buffer_size - subs->hwptr;
14762306a36Sopenharmony_ci			urb->transfer_buffer = subs->tmpbuf;
14862306a36Sopenharmony_ci			memcpy(subs->tmpbuf, runtime->dma_area +
14962306a36Sopenharmony_ci			       subs->hwptr * usx2y->stride, len * usx2y->stride);
15062306a36Sopenharmony_ci			memcpy(subs->tmpbuf + len * usx2y->stride,
15162306a36Sopenharmony_ci			       runtime->dma_area, (count - len) * usx2y->stride);
15262306a36Sopenharmony_ci			subs->hwptr += count;
15362306a36Sopenharmony_ci			subs->hwptr -= runtime->buffer_size;
15462306a36Sopenharmony_ci		} else {
15562306a36Sopenharmony_ci			/* set the buffer pointer */
15662306a36Sopenharmony_ci			urb->transfer_buffer = runtime->dma_area + subs->hwptr * usx2y->stride;
15762306a36Sopenharmony_ci			subs->hwptr += count;
15862306a36Sopenharmony_ci			if (subs->hwptr >= runtime->buffer_size)
15962306a36Sopenharmony_ci				subs->hwptr -= runtime->buffer_size;
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci	} else {
16262306a36Sopenharmony_ci		urb->transfer_buffer = subs->tmpbuf;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci	urb->transfer_buffer_length = count * usx2y->stride;
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/*
16962306a36Sopenharmony_ci * process after playback data complete
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci * update the current position and call callback if a period is processed.
17262306a36Sopenharmony_ci */
17362306a36Sopenharmony_cistatic void usx2y_urb_play_retire(struct snd_usx2y_substream *subs, struct urb *urb)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
17662306a36Sopenharmony_ci	int		len = urb->actual_length / subs->usx2y->stride;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	subs->transfer_done += len;
17962306a36Sopenharmony_ci	subs->hwptr_done +=  len;
18062306a36Sopenharmony_ci	if (subs->hwptr_done >= runtime->buffer_size)
18162306a36Sopenharmony_ci		subs->hwptr_done -= runtime->buffer_size;
18262306a36Sopenharmony_ci	if (subs->transfer_done >= runtime->period_size) {
18362306a36Sopenharmony_ci		subs->transfer_done -= runtime->period_size;
18462306a36Sopenharmony_ci		snd_pcm_period_elapsed(subs->pcm_substream);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int usx2y_urb_submit(struct snd_usx2y_substream *subs, struct urb *urb, int frame)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int err;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (!urb)
19362306a36Sopenharmony_ci		return -ENODEV;
19462306a36Sopenharmony_ci	urb->start_frame = frame + NRURBS * nr_of_packs();  // let hcd do rollover sanity checks
19562306a36Sopenharmony_ci	urb->hcpriv = NULL;
19662306a36Sopenharmony_ci	urb->dev = subs->usx2y->dev; /* we need to set this at each time */
19762306a36Sopenharmony_ci	err = usb_submit_urb(urb, GFP_ATOMIC);
19862306a36Sopenharmony_ci	if (err < 0) {
19962306a36Sopenharmony_ci		snd_printk(KERN_ERR "usb_submit_urb() returned %i\n", err);
20062306a36Sopenharmony_ci		return err;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	return 0;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int usx2y_usbframe_complete(struct snd_usx2y_substream *capsubs,
20662306a36Sopenharmony_ci				   struct snd_usx2y_substream *playbacksubs,
20762306a36Sopenharmony_ci				   int frame)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	int err, state;
21062306a36Sopenharmony_ci	struct urb *urb = playbacksubs->completed_urb;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	state = atomic_read(&playbacksubs->state);
21362306a36Sopenharmony_ci	if (urb) {
21462306a36Sopenharmony_ci		if (state == STATE_RUNNING)
21562306a36Sopenharmony_ci			usx2y_urb_play_retire(playbacksubs, urb);
21662306a36Sopenharmony_ci		else if (state >= STATE_PRERUNNING)
21762306a36Sopenharmony_ci			atomic_inc(&playbacksubs->state);
21862306a36Sopenharmony_ci	} else {
21962306a36Sopenharmony_ci		switch (state) {
22062306a36Sopenharmony_ci		case STATE_STARTING1:
22162306a36Sopenharmony_ci			urb = playbacksubs->urb[0];
22262306a36Sopenharmony_ci			atomic_inc(&playbacksubs->state);
22362306a36Sopenharmony_ci			break;
22462306a36Sopenharmony_ci		case STATE_STARTING2:
22562306a36Sopenharmony_ci			urb = playbacksubs->urb[1];
22662306a36Sopenharmony_ci			atomic_inc(&playbacksubs->state);
22762306a36Sopenharmony_ci			break;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	if (urb) {
23162306a36Sopenharmony_ci		err = usx2y_urb_play_prepare(playbacksubs, capsubs->completed_urb, urb);
23262306a36Sopenharmony_ci		if (err)
23362306a36Sopenharmony_ci			return err;
23462306a36Sopenharmony_ci		err = usx2y_urb_submit(playbacksubs, urb, frame);
23562306a36Sopenharmony_ci		if (err)
23662306a36Sopenharmony_ci			return err;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	playbacksubs->completed_urb = NULL;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	state = atomic_read(&capsubs->state);
24262306a36Sopenharmony_ci	if (state >= STATE_PREPARED) {
24362306a36Sopenharmony_ci		if (state == STATE_RUNNING) {
24462306a36Sopenharmony_ci			err = usx2y_urb_capt_retire(capsubs);
24562306a36Sopenharmony_ci			if (err)
24662306a36Sopenharmony_ci				return err;
24762306a36Sopenharmony_ci		} else if (state >= STATE_PRERUNNING) {
24862306a36Sopenharmony_ci			atomic_inc(&capsubs->state);
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci		err = usx2y_urb_submit(capsubs, capsubs->completed_urb, frame);
25162306a36Sopenharmony_ci		if (err)
25262306a36Sopenharmony_ci			return err;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci	capsubs->completed_urb = NULL;
25562306a36Sopenharmony_ci	return 0;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic void usx2y_clients_stop(struct usx2ydev *usx2y)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct snd_usx2y_substream *subs;
26162306a36Sopenharmony_ci	struct urb *urb;
26262306a36Sopenharmony_ci	int s, u;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	for (s = 0; s < 4; s++) {
26562306a36Sopenharmony_ci		subs = usx2y->subs[s];
26662306a36Sopenharmony_ci		if (subs) {
26762306a36Sopenharmony_ci			snd_printdd("%i %p state=%i\n", s, subs, atomic_read(&subs->state));
26862306a36Sopenharmony_ci			atomic_set(&subs->state, STATE_STOPPED);
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci	for (s = 0; s < 4; s++) {
27262306a36Sopenharmony_ci		subs = usx2y->subs[s];
27362306a36Sopenharmony_ci		if (subs) {
27462306a36Sopenharmony_ci			if (atomic_read(&subs->state) >= STATE_PRERUNNING)
27562306a36Sopenharmony_ci				snd_pcm_stop_xrun(subs->pcm_substream);
27662306a36Sopenharmony_ci			for (u = 0; u < NRURBS; u++) {
27762306a36Sopenharmony_ci				urb = subs->urb[u];
27862306a36Sopenharmony_ci				if (urb)
27962306a36Sopenharmony_ci					snd_printdd("%i status=%i start_frame=%i\n",
28062306a36Sopenharmony_ci						    u, urb->status, urb->start_frame);
28162306a36Sopenharmony_ci			}
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci	usx2y->prepare_subs = NULL;
28562306a36Sopenharmony_ci	wake_up(&usx2y->prepare_wait_queue);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void usx2y_error_urb_status(struct usx2ydev *usx2y,
28962306a36Sopenharmony_ci				   struct snd_usx2y_substream *subs, struct urb *urb)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	snd_printk(KERN_ERR "ep=%i stalled with status=%i\n", subs->endpoint, urb->status);
29262306a36Sopenharmony_ci	urb->status = 0;
29362306a36Sopenharmony_ci	usx2y_clients_stop(usx2y);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void i_usx2y_urb_complete(struct urb *urb)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct snd_usx2y_substream *subs = urb->context;
29962306a36Sopenharmony_ci	struct usx2ydev *usx2y = subs->usx2y;
30062306a36Sopenharmony_ci	struct snd_usx2y_substream *capsubs, *playbacksubs;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (unlikely(atomic_read(&subs->state) < STATE_PREPARED)) {
30362306a36Sopenharmony_ci		snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
30462306a36Sopenharmony_ci			    usb_get_current_frame_number(usx2y->dev),
30562306a36Sopenharmony_ci			    subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
30662306a36Sopenharmony_ci			    urb->status, urb->start_frame);
30762306a36Sopenharmony_ci		return;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci	if (unlikely(urb->status)) {
31062306a36Sopenharmony_ci		usx2y_error_urb_status(usx2y, subs, urb);
31162306a36Sopenharmony_ci		return;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	subs->completed_urb = urb;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	capsubs = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
31762306a36Sopenharmony_ci	playbacksubs = usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (capsubs->completed_urb &&
32062306a36Sopenharmony_ci	    atomic_read(&capsubs->state) >= STATE_PREPARED &&
32162306a36Sopenharmony_ci	    (playbacksubs->completed_urb ||
32262306a36Sopenharmony_ci	     atomic_read(&playbacksubs->state) < STATE_PREPARED)) {
32362306a36Sopenharmony_ci		if (!usx2y_usbframe_complete(capsubs, playbacksubs, urb->start_frame)) {
32462306a36Sopenharmony_ci			usx2y->wait_iso_frame += nr_of_packs();
32562306a36Sopenharmony_ci		} else {
32662306a36Sopenharmony_ci			snd_printdd("\n");
32762306a36Sopenharmony_ci			usx2y_clients_stop(usx2y);
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void usx2y_urbs_set_complete(struct usx2ydev *usx2y,
33362306a36Sopenharmony_ci				    void (*complete)(struct urb *))
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct snd_usx2y_substream *subs;
33662306a36Sopenharmony_ci	struct urb *urb;
33762306a36Sopenharmony_ci	int s, u;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	for (s = 0; s < 4; s++) {
34062306a36Sopenharmony_ci		subs = usx2y->subs[s];
34162306a36Sopenharmony_ci		if (subs) {
34262306a36Sopenharmony_ci			for (u = 0; u < NRURBS; u++) {
34362306a36Sopenharmony_ci				urb = subs->urb[u];
34462306a36Sopenharmony_ci				if (urb)
34562306a36Sopenharmony_ci					urb->complete = complete;
34662306a36Sopenharmony_ci			}
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void usx2y_subs_startup_finish(struct usx2ydev *usx2y)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	usx2y_urbs_set_complete(usx2y, i_usx2y_urb_complete);
35462306a36Sopenharmony_ci	usx2y->prepare_subs = NULL;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void i_usx2y_subs_startup(struct urb *urb)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct snd_usx2y_substream *subs = urb->context;
36062306a36Sopenharmony_ci	struct usx2ydev *usx2y = subs->usx2y;
36162306a36Sopenharmony_ci	struct snd_usx2y_substream *prepare_subs = usx2y->prepare_subs;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (prepare_subs) {
36462306a36Sopenharmony_ci		if (urb->start_frame == prepare_subs->urb[0]->start_frame) {
36562306a36Sopenharmony_ci			usx2y_subs_startup_finish(usx2y);
36662306a36Sopenharmony_ci			atomic_inc(&prepare_subs->state);
36762306a36Sopenharmony_ci			wake_up(&usx2y->prepare_wait_queue);
36862306a36Sopenharmony_ci		}
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	i_usx2y_urb_complete(urb);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic void usx2y_subs_prepare(struct snd_usx2y_substream *subs)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	snd_printdd("usx2y_substream_prepare(%p) ep=%i urb0=%p urb1=%p\n",
37762306a36Sopenharmony_ci		    subs, subs->endpoint, subs->urb[0], subs->urb[1]);
37862306a36Sopenharmony_ci	/* reset the pointer */
37962306a36Sopenharmony_ci	subs->hwptr = 0;
38062306a36Sopenharmony_ci	subs->hwptr_done = 0;
38162306a36Sopenharmony_ci	subs->transfer_done = 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic void usx2y_urb_release(struct urb **urb, int free_tb)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	if (*urb) {
38762306a36Sopenharmony_ci		usb_kill_urb(*urb);
38862306a36Sopenharmony_ci		if (free_tb)
38962306a36Sopenharmony_ci			kfree((*urb)->transfer_buffer);
39062306a36Sopenharmony_ci		usb_free_urb(*urb);
39162306a36Sopenharmony_ci		*urb = NULL;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/*
39662306a36Sopenharmony_ci * release a substreams urbs
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_cistatic void usx2y_urbs_release(struct snd_usx2y_substream *subs)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	int i;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	snd_printdd("%s %i\n", __func__, subs->endpoint);
40362306a36Sopenharmony_ci	for (i = 0; i < NRURBS; i++)
40462306a36Sopenharmony_ci		usx2y_urb_release(subs->urb + i,
40562306a36Sopenharmony_ci				  subs != subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	kfree(subs->tmpbuf);
40862306a36Sopenharmony_ci	subs->tmpbuf = NULL;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/*
41262306a36Sopenharmony_ci * initialize a substream's urbs
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_cistatic int usx2y_urbs_allocate(struct snd_usx2y_substream *subs)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	int i;
41762306a36Sopenharmony_ci	unsigned int pipe;
41862306a36Sopenharmony_ci	int is_playback = subs == subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
41962306a36Sopenharmony_ci	struct usb_device *dev = subs->usx2y->dev;
42062306a36Sopenharmony_ci	struct urb **purb;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
42362306a36Sopenharmony_ci			usb_rcvisocpipe(dev, subs->endpoint);
42462306a36Sopenharmony_ci	subs->maxpacksize = usb_maxpacket(dev, pipe);
42562306a36Sopenharmony_ci	if (!subs->maxpacksize)
42662306a36Sopenharmony_ci		return -EINVAL;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (is_playback && !subs->tmpbuf) {	/* allocate a temporary buffer for playback */
42962306a36Sopenharmony_ci		subs->tmpbuf = kcalloc(nr_of_packs(), subs->maxpacksize, GFP_KERNEL);
43062306a36Sopenharmony_ci		if (!subs->tmpbuf)
43162306a36Sopenharmony_ci			return -ENOMEM;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci	/* allocate and initialize data urbs */
43462306a36Sopenharmony_ci	for (i = 0; i < NRURBS; i++) {
43562306a36Sopenharmony_ci		purb = subs->urb + i;
43662306a36Sopenharmony_ci		if (*purb) {
43762306a36Sopenharmony_ci			usb_kill_urb(*purb);
43862306a36Sopenharmony_ci			continue;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci		*purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
44162306a36Sopenharmony_ci		if (!*purb) {
44262306a36Sopenharmony_ci			usx2y_urbs_release(subs);
44362306a36Sopenharmony_ci			return -ENOMEM;
44462306a36Sopenharmony_ci		}
44562306a36Sopenharmony_ci		if (!is_playback && !(*purb)->transfer_buffer) {
44662306a36Sopenharmony_ci			/* allocate a capture buffer per urb */
44762306a36Sopenharmony_ci			(*purb)->transfer_buffer =
44862306a36Sopenharmony_ci				kmalloc_array(subs->maxpacksize,
44962306a36Sopenharmony_ci					      nr_of_packs(), GFP_KERNEL);
45062306a36Sopenharmony_ci			if (!(*purb)->transfer_buffer) {
45162306a36Sopenharmony_ci				usx2y_urbs_release(subs);
45262306a36Sopenharmony_ci				return -ENOMEM;
45362306a36Sopenharmony_ci			}
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci		(*purb)->dev = dev;
45662306a36Sopenharmony_ci		(*purb)->pipe = pipe;
45762306a36Sopenharmony_ci		(*purb)->number_of_packets = nr_of_packs();
45862306a36Sopenharmony_ci		(*purb)->context = subs;
45962306a36Sopenharmony_ci		(*purb)->interval = 1;
46062306a36Sopenharmony_ci		(*purb)->complete = i_usx2y_subs_startup;
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci	return 0;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic void usx2y_subs_startup(struct snd_usx2y_substream *subs)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct usx2ydev *usx2y = subs->usx2y;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	usx2y->prepare_subs = subs;
47062306a36Sopenharmony_ci	subs->urb[0]->start_frame = -1;
47162306a36Sopenharmony_ci	wmb();
47262306a36Sopenharmony_ci	usx2y_urbs_set_complete(usx2y, i_usx2y_subs_startup);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic int usx2y_urbs_start(struct snd_usx2y_substream *subs)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	int i, err;
47862306a36Sopenharmony_ci	struct usx2ydev *usx2y = subs->usx2y;
47962306a36Sopenharmony_ci	struct urb *urb;
48062306a36Sopenharmony_ci	unsigned long pack;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	err = usx2y_urbs_allocate(subs);
48362306a36Sopenharmony_ci	if (err < 0)
48462306a36Sopenharmony_ci		return err;
48562306a36Sopenharmony_ci	subs->completed_urb = NULL;
48662306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
48762306a36Sopenharmony_ci		struct snd_usx2y_substream *subs = usx2y->subs[i];
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		if (subs && atomic_read(&subs->state) >= STATE_PREPARED)
49062306a36Sopenharmony_ci			goto start;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci start:
49462306a36Sopenharmony_ci	usx2y_subs_startup(subs);
49562306a36Sopenharmony_ci	for (i = 0; i < NRURBS; i++) {
49662306a36Sopenharmony_ci		urb = subs->urb[i];
49762306a36Sopenharmony_ci		if (usb_pipein(urb->pipe)) {
49862306a36Sopenharmony_ci			if (!i)
49962306a36Sopenharmony_ci				atomic_set(&subs->state, STATE_STARTING3);
50062306a36Sopenharmony_ci			urb->dev = usx2y->dev;
50162306a36Sopenharmony_ci			for (pack = 0; pack < nr_of_packs(); pack++) {
50262306a36Sopenharmony_ci				urb->iso_frame_desc[pack].offset = subs->maxpacksize * pack;
50362306a36Sopenharmony_ci				urb->iso_frame_desc[pack].length = subs->maxpacksize;
50462306a36Sopenharmony_ci			}
50562306a36Sopenharmony_ci			urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
50662306a36Sopenharmony_ci			err = usb_submit_urb(urb, GFP_ATOMIC);
50762306a36Sopenharmony_ci			if (err < 0) {
50862306a36Sopenharmony_ci				snd_printk(KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err);
50962306a36Sopenharmony_ci				err = -EPIPE;
51062306a36Sopenharmony_ci				goto cleanup;
51162306a36Sopenharmony_ci			} else {
51262306a36Sopenharmony_ci				if (!i)
51362306a36Sopenharmony_ci					usx2y->wait_iso_frame = urb->start_frame;
51462306a36Sopenharmony_ci			}
51562306a36Sopenharmony_ci			urb->transfer_flags = 0;
51662306a36Sopenharmony_ci		} else {
51762306a36Sopenharmony_ci			atomic_set(&subs->state, STATE_STARTING1);
51862306a36Sopenharmony_ci			break;
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci	err = 0;
52262306a36Sopenharmony_ci	wait_event(usx2y->prepare_wait_queue, !usx2y->prepare_subs);
52362306a36Sopenharmony_ci	if (atomic_read(&subs->state) != STATE_PREPARED)
52462306a36Sopenharmony_ci		err = -EPIPE;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci cleanup:
52762306a36Sopenharmony_ci	if (err) {
52862306a36Sopenharmony_ci		usx2y_subs_startup_finish(usx2y);
52962306a36Sopenharmony_ci		usx2y_clients_stop(usx2y);	// something is completely wrong > stop everything
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci	return err;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/*
53562306a36Sopenharmony_ci * return the current pcm pointer.  just return the hwptr_done value.
53662306a36Sopenharmony_ci */
53762306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_usx2y_pcm_pointer(struct snd_pcm_substream *substream)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct snd_usx2y_substream *subs = substream->runtime->private_data;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	return subs->hwptr_done;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci/*
54562306a36Sopenharmony_ci * start/stop substream
54662306a36Sopenharmony_ci */
54762306a36Sopenharmony_cistatic int snd_usx2y_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct snd_usx2y_substream *subs = substream->runtime->private_data;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	switch (cmd) {
55262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
55362306a36Sopenharmony_ci		snd_printdd("%s(START)\n", __func__);
55462306a36Sopenharmony_ci		if (atomic_read(&subs->state) == STATE_PREPARED &&
55562306a36Sopenharmony_ci		    atomic_read(&subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]->state) >= STATE_PREPARED) {
55662306a36Sopenharmony_ci			atomic_set(&subs->state, STATE_PRERUNNING);
55762306a36Sopenharmony_ci		} else {
55862306a36Sopenharmony_ci			snd_printdd("\n");
55962306a36Sopenharmony_ci			return -EPIPE;
56062306a36Sopenharmony_ci		}
56162306a36Sopenharmony_ci		break;
56262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
56362306a36Sopenharmony_ci		snd_printdd("%s(STOP)\n", __func__);
56462306a36Sopenharmony_ci		if (atomic_read(&subs->state) >= STATE_PRERUNNING)
56562306a36Sopenharmony_ci			atomic_set(&subs->state, STATE_PREPARED);
56662306a36Sopenharmony_ci		break;
56762306a36Sopenharmony_ci	default:
56862306a36Sopenharmony_ci		return -EINVAL;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci	return 0;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/*
57462306a36Sopenharmony_ci * allocate a buffer, setup samplerate
57562306a36Sopenharmony_ci *
57662306a36Sopenharmony_ci * so far we use a physically linear buffer although packetize transfer
57762306a36Sopenharmony_ci * doesn't need a continuous area.
57862306a36Sopenharmony_ci * if sg buffer is supported on the later version of alsa, we'll follow
57962306a36Sopenharmony_ci * that.
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_cistruct s_c2 {
58262306a36Sopenharmony_ci	char c1, c2;
58362306a36Sopenharmony_ci};
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic const struct s_c2 setrate_44100[] = {
58662306a36Sopenharmony_ci	{ 0x14, 0x08},	// this line sets 44100, well actually a little less
58762306a36Sopenharmony_ci	{ 0x18, 0x40},	// only tascam / frontier design knows the further lines .......
58862306a36Sopenharmony_ci	{ 0x18, 0x42},
58962306a36Sopenharmony_ci	{ 0x18, 0x45},
59062306a36Sopenharmony_ci	{ 0x18, 0x46},
59162306a36Sopenharmony_ci	{ 0x18, 0x48},
59262306a36Sopenharmony_ci	{ 0x18, 0x4A},
59362306a36Sopenharmony_ci	{ 0x18, 0x4C},
59462306a36Sopenharmony_ci	{ 0x18, 0x4E},
59562306a36Sopenharmony_ci	{ 0x18, 0x50},
59662306a36Sopenharmony_ci	{ 0x18, 0x52},
59762306a36Sopenharmony_ci	{ 0x18, 0x54},
59862306a36Sopenharmony_ci	{ 0x18, 0x56},
59962306a36Sopenharmony_ci	{ 0x18, 0x58},
60062306a36Sopenharmony_ci	{ 0x18, 0x5A},
60162306a36Sopenharmony_ci	{ 0x18, 0x5C},
60262306a36Sopenharmony_ci	{ 0x18, 0x5E},
60362306a36Sopenharmony_ci	{ 0x18, 0x60},
60462306a36Sopenharmony_ci	{ 0x18, 0x62},
60562306a36Sopenharmony_ci	{ 0x18, 0x64},
60662306a36Sopenharmony_ci	{ 0x18, 0x66},
60762306a36Sopenharmony_ci	{ 0x18, 0x68},
60862306a36Sopenharmony_ci	{ 0x18, 0x6A},
60962306a36Sopenharmony_ci	{ 0x18, 0x6C},
61062306a36Sopenharmony_ci	{ 0x18, 0x6E},
61162306a36Sopenharmony_ci	{ 0x18, 0x70},
61262306a36Sopenharmony_ci	{ 0x18, 0x72},
61362306a36Sopenharmony_ci	{ 0x18, 0x74},
61462306a36Sopenharmony_ci	{ 0x18, 0x76},
61562306a36Sopenharmony_ci	{ 0x18, 0x78},
61662306a36Sopenharmony_ci	{ 0x18, 0x7A},
61762306a36Sopenharmony_ci	{ 0x18, 0x7C},
61862306a36Sopenharmony_ci	{ 0x18, 0x7E}
61962306a36Sopenharmony_ci};
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic const struct s_c2 setrate_48000[] = {
62262306a36Sopenharmony_ci	{ 0x14, 0x09},	// this line sets 48000, well actually a little less
62362306a36Sopenharmony_ci	{ 0x18, 0x40},	// only tascam / frontier design knows the further lines .......
62462306a36Sopenharmony_ci	{ 0x18, 0x42},
62562306a36Sopenharmony_ci	{ 0x18, 0x45},
62662306a36Sopenharmony_ci	{ 0x18, 0x46},
62762306a36Sopenharmony_ci	{ 0x18, 0x48},
62862306a36Sopenharmony_ci	{ 0x18, 0x4A},
62962306a36Sopenharmony_ci	{ 0x18, 0x4C},
63062306a36Sopenharmony_ci	{ 0x18, 0x4E},
63162306a36Sopenharmony_ci	{ 0x18, 0x50},
63262306a36Sopenharmony_ci	{ 0x18, 0x52},
63362306a36Sopenharmony_ci	{ 0x18, 0x54},
63462306a36Sopenharmony_ci	{ 0x18, 0x56},
63562306a36Sopenharmony_ci	{ 0x18, 0x58},
63662306a36Sopenharmony_ci	{ 0x18, 0x5A},
63762306a36Sopenharmony_ci	{ 0x18, 0x5C},
63862306a36Sopenharmony_ci	{ 0x18, 0x5E},
63962306a36Sopenharmony_ci	{ 0x18, 0x60},
64062306a36Sopenharmony_ci	{ 0x18, 0x62},
64162306a36Sopenharmony_ci	{ 0x18, 0x64},
64262306a36Sopenharmony_ci	{ 0x18, 0x66},
64362306a36Sopenharmony_ci	{ 0x18, 0x68},
64462306a36Sopenharmony_ci	{ 0x18, 0x6A},
64562306a36Sopenharmony_ci	{ 0x18, 0x6C},
64662306a36Sopenharmony_ci	{ 0x18, 0x6E},
64762306a36Sopenharmony_ci	{ 0x18, 0x70},
64862306a36Sopenharmony_ci	{ 0x18, 0x73},
64962306a36Sopenharmony_ci	{ 0x18, 0x74},
65062306a36Sopenharmony_ci	{ 0x18, 0x76},
65162306a36Sopenharmony_ci	{ 0x18, 0x78},
65262306a36Sopenharmony_ci	{ 0x18, 0x7A},
65362306a36Sopenharmony_ci	{ 0x18, 0x7C},
65462306a36Sopenharmony_ci	{ 0x18, 0x7E}
65562306a36Sopenharmony_ci};
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci#define NOOF_SETRATE_URBS ARRAY_SIZE(setrate_48000)
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic void i_usx2y_04int(struct urb *urb)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	struct usx2ydev *usx2y = urb->context;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (urb->status)
66462306a36Sopenharmony_ci		snd_printk(KERN_ERR "snd_usx2y_04int() urb->status=%i\n", urb->status);
66562306a36Sopenharmony_ci	if (!--usx2y->us04->len)
66662306a36Sopenharmony_ci		wake_up(&usx2y->in04_wait_queue);
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int usx2y_rate_set(struct usx2ydev *usx2y, int rate)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	int err = 0, i;
67262306a36Sopenharmony_ci	struct snd_usx2y_urb_seq *us = NULL;
67362306a36Sopenharmony_ci	int *usbdata = NULL;
67462306a36Sopenharmony_ci	const struct s_c2 *ra = rate == 48000 ? setrate_48000 : setrate_44100;
67562306a36Sopenharmony_ci	struct urb *urb;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (usx2y->rate != rate) {
67862306a36Sopenharmony_ci		us = kzalloc(struct_size(us, urb, NOOF_SETRATE_URBS),
67962306a36Sopenharmony_ci			     GFP_KERNEL);
68062306a36Sopenharmony_ci		if (!us) {
68162306a36Sopenharmony_ci			err = -ENOMEM;
68262306a36Sopenharmony_ci			goto cleanup;
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci		usbdata = kmalloc_array(NOOF_SETRATE_URBS, sizeof(int),
68562306a36Sopenharmony_ci					GFP_KERNEL);
68662306a36Sopenharmony_ci		if (!usbdata) {
68762306a36Sopenharmony_ci			err = -ENOMEM;
68862306a36Sopenharmony_ci			goto cleanup;
68962306a36Sopenharmony_ci		}
69062306a36Sopenharmony_ci		for (i = 0; i < NOOF_SETRATE_URBS; ++i) {
69162306a36Sopenharmony_ci			us->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
69262306a36Sopenharmony_ci			if (!us->urb[i]) {
69362306a36Sopenharmony_ci				err = -ENOMEM;
69462306a36Sopenharmony_ci				goto cleanup;
69562306a36Sopenharmony_ci			}
69662306a36Sopenharmony_ci			((char *)(usbdata + i))[0] = ra[i].c1;
69762306a36Sopenharmony_ci			((char *)(usbdata + i))[1] = ra[i].c2;
69862306a36Sopenharmony_ci			usb_fill_bulk_urb(us->urb[i], usx2y->dev, usb_sndbulkpipe(usx2y->dev, 4),
69962306a36Sopenharmony_ci					  usbdata + i, 2, i_usx2y_04int, usx2y);
70062306a36Sopenharmony_ci		}
70162306a36Sopenharmony_ci		err = usb_urb_ep_type_check(us->urb[0]);
70262306a36Sopenharmony_ci		if (err < 0)
70362306a36Sopenharmony_ci			goto cleanup;
70462306a36Sopenharmony_ci		us->submitted =	0;
70562306a36Sopenharmony_ci		us->len =	NOOF_SETRATE_URBS;
70662306a36Sopenharmony_ci		usx2y->us04 =	us;
70762306a36Sopenharmony_ci		wait_event_timeout(usx2y->in04_wait_queue, !us->len, HZ);
70862306a36Sopenharmony_ci		usx2y->us04 =	NULL;
70962306a36Sopenharmony_ci		if (us->len)
71062306a36Sopenharmony_ci			err = -ENODEV;
71162306a36Sopenharmony_ci	cleanup:
71262306a36Sopenharmony_ci		if (us) {
71362306a36Sopenharmony_ci			us->submitted =	2*NOOF_SETRATE_URBS;
71462306a36Sopenharmony_ci			for (i = 0; i < NOOF_SETRATE_URBS; ++i) {
71562306a36Sopenharmony_ci				urb = us->urb[i];
71662306a36Sopenharmony_ci				if (!urb)
71762306a36Sopenharmony_ci					continue;
71862306a36Sopenharmony_ci				if (urb->status) {
71962306a36Sopenharmony_ci					if (!err)
72062306a36Sopenharmony_ci						err = -ENODEV;
72162306a36Sopenharmony_ci					usb_kill_urb(urb);
72262306a36Sopenharmony_ci				}
72362306a36Sopenharmony_ci				usb_free_urb(urb);
72462306a36Sopenharmony_ci			}
72562306a36Sopenharmony_ci			usx2y->us04 = NULL;
72662306a36Sopenharmony_ci			kfree(usbdata);
72762306a36Sopenharmony_ci			kfree(us);
72862306a36Sopenharmony_ci			if (!err)
72962306a36Sopenharmony_ci				usx2y->rate = rate;
73062306a36Sopenharmony_ci		}
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return err;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic int usx2y_format_set(struct usx2ydev *usx2y, snd_pcm_format_t format)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	int alternate, err;
73962306a36Sopenharmony_ci	struct list_head *p;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (format == SNDRV_PCM_FORMAT_S24_3LE) {
74262306a36Sopenharmony_ci		alternate = 2;
74362306a36Sopenharmony_ci		usx2y->stride = 6;
74462306a36Sopenharmony_ci	} else {
74562306a36Sopenharmony_ci		alternate = 1;
74662306a36Sopenharmony_ci		usx2y->stride = 4;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci	list_for_each(p, &usx2y->midi_list) {
74962306a36Sopenharmony_ci		snd_usbmidi_input_stop(p);
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci	usb_kill_urb(usx2y->in04_urb);
75262306a36Sopenharmony_ci	err = usb_set_interface(usx2y->dev, 0, alternate);
75362306a36Sopenharmony_ci	if (err) {
75462306a36Sopenharmony_ci		snd_printk(KERN_ERR "usb_set_interface error\n");
75562306a36Sopenharmony_ci		return err;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci	usx2y->in04_urb->dev = usx2y->dev;
75862306a36Sopenharmony_ci	err = usb_submit_urb(usx2y->in04_urb, GFP_KERNEL);
75962306a36Sopenharmony_ci	list_for_each(p, &usx2y->midi_list) {
76062306a36Sopenharmony_ci		snd_usbmidi_input_start(p);
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci	usx2y->format = format;
76362306a36Sopenharmony_ci	usx2y->rate = 0;
76462306a36Sopenharmony_ci	return err;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic int snd_usx2y_pcm_hw_params(struct snd_pcm_substream *substream,
76962306a36Sopenharmony_ci				   struct snd_pcm_hw_params *hw_params)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	int			err = 0;
77262306a36Sopenharmony_ci	unsigned int		rate = params_rate(hw_params);
77362306a36Sopenharmony_ci	snd_pcm_format_t	format = params_format(hw_params);
77462306a36Sopenharmony_ci	struct snd_card *card = substream->pstr->pcm->card;
77562306a36Sopenharmony_ci	struct usx2ydev	*dev = usx2y(card);
77662306a36Sopenharmony_ci	struct snd_usx2y_substream *subs;
77762306a36Sopenharmony_ci	struct snd_pcm_substream *test_substream;
77862306a36Sopenharmony_ci	int i;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	mutex_lock(&usx2y(card)->pcm_mutex);
78162306a36Sopenharmony_ci	snd_printdd("snd_usx2y_hw_params(%p, %p)\n", substream, hw_params);
78262306a36Sopenharmony_ci	/* all pcm substreams off one usx2y have to operate at the same
78362306a36Sopenharmony_ci	 * rate & format
78462306a36Sopenharmony_ci	 */
78562306a36Sopenharmony_ci	for (i = 0; i < dev->pcm_devs * 2; i++) {
78662306a36Sopenharmony_ci		subs = dev->subs[i];
78762306a36Sopenharmony_ci		if (!subs)
78862306a36Sopenharmony_ci			continue;
78962306a36Sopenharmony_ci		test_substream = subs->pcm_substream;
79062306a36Sopenharmony_ci		if (!test_substream || test_substream == substream ||
79162306a36Sopenharmony_ci		    !test_substream->runtime)
79262306a36Sopenharmony_ci			continue;
79362306a36Sopenharmony_ci		if ((test_substream->runtime->format &&
79462306a36Sopenharmony_ci		     test_substream->runtime->format != format) ||
79562306a36Sopenharmony_ci		    (test_substream->runtime->rate &&
79662306a36Sopenharmony_ci		     test_substream->runtime->rate != rate)) {
79762306a36Sopenharmony_ci			err = -EINVAL;
79862306a36Sopenharmony_ci			goto error;
79962306a36Sopenharmony_ci		}
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci error:
80362306a36Sopenharmony_ci	mutex_unlock(&usx2y(card)->pcm_mutex);
80462306a36Sopenharmony_ci	return err;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci/*
80862306a36Sopenharmony_ci * free the buffer
80962306a36Sopenharmony_ci */
81062306a36Sopenharmony_cistatic int snd_usx2y_pcm_hw_free(struct snd_pcm_substream *substream)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
81362306a36Sopenharmony_ci	struct snd_usx2y_substream *subs = runtime->private_data;
81462306a36Sopenharmony_ci	struct snd_usx2y_substream *cap_subs, *playback_subs;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	mutex_lock(&subs->usx2y->pcm_mutex);
81762306a36Sopenharmony_ci	snd_printdd("snd_usx2y_hw_free(%p)\n", substream);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
82062306a36Sopenharmony_ci		cap_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
82162306a36Sopenharmony_ci		atomic_set(&subs->state, STATE_STOPPED);
82262306a36Sopenharmony_ci		usx2y_urbs_release(subs);
82362306a36Sopenharmony_ci		if (!cap_subs->pcm_substream ||
82462306a36Sopenharmony_ci		    !cap_subs->pcm_substream->runtime ||
82562306a36Sopenharmony_ci		    cap_subs->pcm_substream->runtime->state < SNDRV_PCM_STATE_PREPARED) {
82662306a36Sopenharmony_ci			atomic_set(&cap_subs->state, STATE_STOPPED);
82762306a36Sopenharmony_ci			usx2y_urbs_release(cap_subs);
82862306a36Sopenharmony_ci		}
82962306a36Sopenharmony_ci	} else {
83062306a36Sopenharmony_ci		playback_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
83162306a36Sopenharmony_ci		if (atomic_read(&playback_subs->state) < STATE_PREPARED) {
83262306a36Sopenharmony_ci			atomic_set(&subs->state, STATE_STOPPED);
83362306a36Sopenharmony_ci			usx2y_urbs_release(subs);
83462306a36Sopenharmony_ci		}
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci	mutex_unlock(&subs->usx2y->pcm_mutex);
83762306a36Sopenharmony_ci	return 0;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci/*
84162306a36Sopenharmony_ci * prepare callback
84262306a36Sopenharmony_ci *
84362306a36Sopenharmony_ci * set format and initialize urbs
84462306a36Sopenharmony_ci */
84562306a36Sopenharmony_cistatic int snd_usx2y_pcm_prepare(struct snd_pcm_substream *substream)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
84862306a36Sopenharmony_ci	struct snd_usx2y_substream *subs = runtime->private_data;
84962306a36Sopenharmony_ci	struct usx2ydev *usx2y = subs->usx2y;
85062306a36Sopenharmony_ci	struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
85162306a36Sopenharmony_ci	int err = 0;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	snd_printdd("%s(%p)\n", __func__, substream);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	mutex_lock(&usx2y->pcm_mutex);
85662306a36Sopenharmony_ci	usx2y_subs_prepare(subs);
85762306a36Sopenharmony_ci	// Start hardware streams
85862306a36Sopenharmony_ci	// SyncStream first....
85962306a36Sopenharmony_ci	if (atomic_read(&capsubs->state) < STATE_PREPARED) {
86062306a36Sopenharmony_ci		if (usx2y->format != runtime->format) {
86162306a36Sopenharmony_ci			err = usx2y_format_set(usx2y, runtime->format);
86262306a36Sopenharmony_ci			if (err < 0)
86362306a36Sopenharmony_ci				goto up_prepare_mutex;
86462306a36Sopenharmony_ci		}
86562306a36Sopenharmony_ci		if (usx2y->rate != runtime->rate) {
86662306a36Sopenharmony_ci			err = usx2y_rate_set(usx2y, runtime->rate);
86762306a36Sopenharmony_ci			if (err < 0)
86862306a36Sopenharmony_ci				goto up_prepare_mutex;
86962306a36Sopenharmony_ci		}
87062306a36Sopenharmony_ci		snd_printdd("starting capture pipe for %s\n", subs == capsubs ? "self" : "playpipe");
87162306a36Sopenharmony_ci		err = usx2y_urbs_start(capsubs);
87262306a36Sopenharmony_ci		if (err < 0)
87362306a36Sopenharmony_ci			goto up_prepare_mutex;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	if (subs != capsubs && atomic_read(&subs->state) < STATE_PREPARED)
87762306a36Sopenharmony_ci		err = usx2y_urbs_start(subs);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci up_prepare_mutex:
88062306a36Sopenharmony_ci	mutex_unlock(&usx2y->pcm_mutex);
88162306a36Sopenharmony_ci	return err;
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_usx2y_2c = {
88562306a36Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
88662306a36Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
88762306a36Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID |
88862306a36Sopenharmony_ci				 SNDRV_PCM_INFO_BATCH),
88962306a36Sopenharmony_ci	.formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
89062306a36Sopenharmony_ci	.rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
89162306a36Sopenharmony_ci	.rate_min =                44100,
89262306a36Sopenharmony_ci	.rate_max =                48000,
89362306a36Sopenharmony_ci	.channels_min =            2,
89462306a36Sopenharmony_ci	.channels_max =            2,
89562306a36Sopenharmony_ci	.buffer_bytes_max =	(2*128*1024),
89662306a36Sopenharmony_ci	.period_bytes_min =	64,
89762306a36Sopenharmony_ci	.period_bytes_max =	(128*1024),
89862306a36Sopenharmony_ci	.periods_min =		2,
89962306a36Sopenharmony_ci	.periods_max =		1024,
90062306a36Sopenharmony_ci	.fifo_size =              0
90162306a36Sopenharmony_ci};
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic int snd_usx2y_pcm_open(struct snd_pcm_substream *substream)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct snd_usx2y_substream	*subs =
90662306a36Sopenharmony_ci		((struct snd_usx2y_substream **)
90762306a36Sopenharmony_ci		 snd_pcm_substream_chip(substream))[substream->stream];
90862306a36Sopenharmony_ci	struct snd_pcm_runtime	*runtime = substream->runtime;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (subs->usx2y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS)
91162306a36Sopenharmony_ci		return -EBUSY;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	runtime->hw = snd_usx2y_2c;
91462306a36Sopenharmony_ci	runtime->private_data = subs;
91562306a36Sopenharmony_ci	subs->pcm_substream = substream;
91662306a36Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
91762306a36Sopenharmony_ci	return 0;
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic int snd_usx2y_pcm_close(struct snd_pcm_substream *substream)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
92362306a36Sopenharmony_ci	struct snd_usx2y_substream *subs = runtime->private_data;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	subs->pcm_substream = NULL;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	return 0;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_usx2y_pcm_ops = {
93162306a36Sopenharmony_ci	.open =		snd_usx2y_pcm_open,
93262306a36Sopenharmony_ci	.close =	snd_usx2y_pcm_close,
93362306a36Sopenharmony_ci	.hw_params =	snd_usx2y_pcm_hw_params,
93462306a36Sopenharmony_ci	.hw_free =	snd_usx2y_pcm_hw_free,
93562306a36Sopenharmony_ci	.prepare =	snd_usx2y_pcm_prepare,
93662306a36Sopenharmony_ci	.trigger =	snd_usx2y_pcm_trigger,
93762306a36Sopenharmony_ci	.pointer =	snd_usx2y_pcm_pointer,
93862306a36Sopenharmony_ci};
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci/*
94162306a36Sopenharmony_ci * free a usb stream instance
94262306a36Sopenharmony_ci */
94362306a36Sopenharmony_cistatic void usx2y_audio_stream_free(struct snd_usx2y_substream **usx2y_substream)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	int stream;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	for_each_pcm_streams(stream) {
94862306a36Sopenharmony_ci		kfree(usx2y_substream[stream]);
94962306a36Sopenharmony_ci		usx2y_substream[stream] = NULL;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_cistatic void snd_usx2y_pcm_private_free(struct snd_pcm *pcm)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	struct snd_usx2y_substream **usx2y_stream = pcm->private_data;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	if (usx2y_stream)
95862306a36Sopenharmony_ci		usx2y_audio_stream_free(usx2y_stream);
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic int usx2y_audio_stream_new(struct snd_card *card, int playback_endpoint, int capture_endpoint)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	struct snd_pcm *pcm;
96462306a36Sopenharmony_ci	int err, i;
96562306a36Sopenharmony_ci	struct snd_usx2y_substream **usx2y_substream =
96662306a36Sopenharmony_ci		usx2y(card)->subs + 2 * usx2y(card)->pcm_devs;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	for (i = playback_endpoint ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
96962306a36Sopenharmony_ci	     i <= SNDRV_PCM_STREAM_CAPTURE; ++i) {
97062306a36Sopenharmony_ci		usx2y_substream[i] = kzalloc(sizeof(struct snd_usx2y_substream), GFP_KERNEL);
97162306a36Sopenharmony_ci		if (!usx2y_substream[i])
97262306a36Sopenharmony_ci			return -ENOMEM;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		usx2y_substream[i]->usx2y = usx2y(card);
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (playback_endpoint)
97862306a36Sopenharmony_ci		usx2y_substream[SNDRV_PCM_STREAM_PLAYBACK]->endpoint = playback_endpoint;
97962306a36Sopenharmony_ci	usx2y_substream[SNDRV_PCM_STREAM_CAPTURE]->endpoint = capture_endpoint;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	err = snd_pcm_new(card, NAME_ALLCAPS" Audio", usx2y(card)->pcm_devs,
98262306a36Sopenharmony_ci			  playback_endpoint ? 1 : 0, 1,
98362306a36Sopenharmony_ci			  &pcm);
98462306a36Sopenharmony_ci	if (err < 0) {
98562306a36Sopenharmony_ci		usx2y_audio_stream_free(usx2y_substream);
98662306a36Sopenharmony_ci		return err;
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	if (playback_endpoint)
99062306a36Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usx2y_pcm_ops);
99162306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usx2y_pcm_ops);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	pcm->private_data = usx2y_substream;
99462306a36Sopenharmony_ci	pcm->private_free = snd_usx2y_pcm_private_free;
99562306a36Sopenharmony_ci	pcm->info_flags = 0;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	sprintf(pcm->name, NAME_ALLCAPS" Audio #%d", usx2y(card)->pcm_devs);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (playback_endpoint) {
100062306a36Sopenharmony_ci		snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
100162306a36Sopenharmony_ci					   SNDRV_DMA_TYPE_CONTINUOUS,
100262306a36Sopenharmony_ci					   NULL,
100362306a36Sopenharmony_ci					   64*1024, 128*1024);
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
100762306a36Sopenharmony_ci				   SNDRV_DMA_TYPE_CONTINUOUS,
100862306a36Sopenharmony_ci				   NULL,
100962306a36Sopenharmony_ci				   64*1024, 128*1024);
101062306a36Sopenharmony_ci	usx2y(card)->pcm_devs++;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	return 0;
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci/*
101662306a36Sopenharmony_ci * create a chip instance and set its names.
101762306a36Sopenharmony_ci */
101862306a36Sopenharmony_ciint usx2y_audio_create(struct snd_card *card)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	int err;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	err = usx2y_audio_stream_new(card, 0xA, 0x8);
102362306a36Sopenharmony_ci	if (err < 0)
102462306a36Sopenharmony_ci		return err;
102562306a36Sopenharmony_ci	if (le16_to_cpu(usx2y(card)->dev->descriptor.idProduct) == USB_ID_US428) {
102662306a36Sopenharmony_ci		err = usx2y_audio_stream_new(card, 0, 0xA);
102762306a36Sopenharmony_ci		if (err < 0)
102862306a36Sopenharmony_ci			return err;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci	if (le16_to_cpu(usx2y(card)->dev->descriptor.idProduct) != USB_ID_US122)
103162306a36Sopenharmony_ci		err = usx2y_rate_set(usx2y(card), 44100);	// Lets us428 recognize output-volume settings, disturbs us122.
103262306a36Sopenharmony_ci	return err;
103362306a36Sopenharmony_ci}
1034