Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6da0c634 authored by Hridya Valsaraju's avatar Hridya Valsaraju
Browse files

ANDROID: GKI: sound: usb: Add snd_usb_enable_audio_stream/find_snd_usb_substream



Add functions snd_usb_enable_audio_stream and find_snd_usb_substream
to resolve ABI diff.

Test: build
Bug: 151372815
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
Change-Id: I658d525233be3eb657a3b36c81a3920e65e96a40
(cherry picked from commit f055b384)
[hridya: only include ABI diff, add EXPORT_SYMBOL_GPL,
add some pointer checks]
Signed-off-by: default avatarHridya Valsaraju <hridya@google.com>
parent 1c20c327
Loading
Loading
Loading
Loading
+76 −0
Original line number Diff line number Diff line
@@ -119,6 +119,82 @@ static DEFINE_MUTEX(register_mutex);
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
static struct usb_driver usb_audio_driver;

/**
 * find_snd_usb_substream - helper API to find usb substream context
 * information using card number, pcm device number and direction.
 * @card_num: card number
 * @pcm_idx: pcm device number
 * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
 * @uchip: substream context.
 * disconnect_cb: callback to use for cleanup on disconnect.
 */
struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num,
	unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio
	**uchip, void (*disconnect_cb)(struct snd_usb_audio *chip))
{
	int idx;
	struct snd_usb_stream *as;
	struct snd_usb_substream *subs = NULL;
	struct snd_usb_audio *chip = NULL;

	mutex_lock(&register_mutex);
	/*
	 * legacy audio snd card number assignment is dynamic. Hence
	 * search using chip->card->number
	 */
	for (idx = 0; idx < SNDRV_CARDS; idx++) {
		if (!usb_chip[idx] || !usb_chip[idx]->card)
			continue;
		if (usb_chip[idx]->card->number == card_num) {
			chip = usb_chip[idx];
			break;
		}
	}

	if (!chip || atomic_read(&chip->shutdown)) {
		pr_debug("%s: instance of usb card # %d does not exist\n",
			__func__, card_num);
		goto err;
	}

	if (pcm_idx >= chip->pcm_devs) {
		usb_audio_err(chip, "%s: invalid pcm dev number %u > %d\n",
			      __func__, pcm_idx, chip->pcm_devs);
		goto err;
	}

	if (direction > SNDRV_PCM_STREAM_CAPTURE) {
		usb_audio_err(chip, "%s: invalid direction %u\n", __func__,
			      direction);
		goto err;
	}

	list_for_each_entry(as, &chip->pcm_list, list) {
		if (as->pcm_index == pcm_idx) {
			subs = &as->substream[direction];
			if (subs->interface < 0 && !subs->data_endpoint &&
			    !subs->sync_endpoint) {
				usb_audio_err(chip, "%s: stream disconnected, bail out\n",
					      __func__);
				subs = NULL;
				goto err;
			}
			goto done;
		}
	}

done:
	chip->card_num = card_num;
	chip->disconnect_cb = disconnect_cb;
err:
	*uchip = chip;
	if (!subs)
		pr_err("%s: substream instance not found\n", __func__);
	mutex_unlock(&register_mutex);
	return subs;
}
EXPORT_SYMBOL_GPL(find_snd_usb_substream);

/*
 * disconnect streams
 * called from usb_audio_disconnect()
+4 −0
Original line number Diff line number Diff line
@@ -172,4 +172,8 @@ struct snd_usb_stream {
	struct list_head list;
};

struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num,
	unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio
	**uchip, void (*disconnect_cb)(struct snd_usb_audio *chip));

#endif /* __USBAUDIO_CARD_H */
+147 −0
Original line number Diff line number Diff line
@@ -149,6 +149,69 @@ static struct audioformat *find_format(struct snd_usb_substream *subs)
	return found;
}

/*
 * find a matching audio format as well as non-zero service interval
 */
static struct audioformat *find_format_and_si(struct snd_usb_substream *subs,
	unsigned int datainterval)
{
	unsigned int i;
	struct audioformat *fp;
	struct audioformat *found = NULL;
	int cur_attr = 0, attr;

	list_for_each_entry(fp, &subs->fmt_list, list) {
		if (datainterval != fp->datainterval)
			continue;
		if (!(fp->formats & pcm_format_to_bits(subs->pcm_format)))
			continue;
		if (fp->channels != subs->channels)
			continue;
		if (subs->cur_rate < fp->rate_min ||
		    subs->cur_rate > fp->rate_max)
			continue;
		if (!(fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
			for (i = 0; i < fp->nr_rates; i++)
				if (fp->rate_table[i] == subs->cur_rate)
					break;
			if (i >= fp->nr_rates)
				continue;
		}
		attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE;
		if (!found) {
			found = fp;
			cur_attr = attr;
			continue;
		}
		/* avoid async out and adaptive in if the other method
		 * supports the same format.
		 * this is a workaround for the case like
		 * M-audio audiophile USB.
		 */
		if (attr != cur_attr) {
			if ((attr == USB_ENDPOINT_SYNC_ASYNC &&
			     subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
			    (attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
			     subs->direction == SNDRV_PCM_STREAM_CAPTURE))
				continue;
			if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC &&
			     subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
			    (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
			     subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {
				found = fp;
				cur_attr = attr;
				continue;
			}
		}
		/* find the format with the largest max. packet size */
		if (fp->maxpacksize > found->maxpacksize) {
			found = fp;
			cur_attr = attr;
		}
	}
	return found;
}

static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
			 struct usb_host_interface *alts,
			 struct audioformat *fmt)
@@ -580,6 +643,90 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
	return 0;
}

/**
 * snd_usb_enable_audio_stream - Enable/disable the specified usb substream.
 * @subs: pointer to the usb substream.
 * @datainterval: data packet interval.
 * @enable: if true, enable the usb substream. Else disable.
 */
int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
	int datainterval, bool enable)
{
	struct audioformat *fmt;
	struct usb_host_interface *alts;
	struct usb_interface *iface;
	int ret;

	if (!subs || !subs->stream)
		return -EINVAL;

	if (!enable) {
		if (subs->interface >= 0) {
			usb_set_interface(subs->dev, subs->interface, 0);
			subs->altset_idx = 0;
			subs->interface = -1;
			subs->cur_audiofmt = NULL;
		}

		snd_usb_autosuspend(subs->stream->chip);
		return 0;
	}

	snd_usb_autoresume(subs->stream->chip);
	if (datainterval != -EINVAL)
		fmt = find_format_and_si(subs, datainterval);
	else
		fmt = find_format(subs);
	if (!fmt) {
		dev_dbg(&subs->dev->dev,
		"cannot set format: format = %#x, rate = %d, channels = %d\n",
			   subs->pcm_format, subs->cur_rate, subs->channels);
		return -EINVAL;
	}

	subs->altset_idx = 0;
	subs->interface = -1;

	if (!subs->stream->chip)
		return -EINVAL;

	if (atomic_read(&subs->stream->chip->shutdown)) {
		ret = -ENODEV;
	} else {
		ret = set_format(subs, fmt);
		if (ret < 0)
			return ret;

		if (!subs->cur_audiofmt)
			return -EINVAL;

		iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
		if (!iface) {
			dev_err(&subs->dev->dev, "Could not get iface %d\n",
				subs->cur_audiofmt->iface);
			return -ENODEV;
		}

		alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
		ret = snd_usb_init_sample_rate(subs->stream->chip,
					       subs->cur_audiofmt->iface,
					       alts,
					       subs->cur_audiofmt,
					       subs->cur_rate);
		if (ret < 0) {
			dev_err(&subs->dev->dev, "failed to set rate %d\n",
				subs->cur_rate);
			return ret;
		}
	}

	subs->interface = fmt->iface;
	subs->altset_idx = fmt->altset_idx;

	return 0;
}
EXPORT_SYMBOL_GPL(snd_usb_enable_audio_stream);

/*
 * Return the score of matching two audioformats.
 * Veto the audioformat if:
+2 −2
Original line number Diff line number Diff line
@@ -13,6 +13,6 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
		       struct usb_host_interface *alts,
		       struct audioformat *fmt);
void snd_usb_preallocate_buffer(struct snd_usb_substream *subs);


int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
	int datainterval, bool enable);
#endif /* __USBAUDIO_PCM_H */
+2 −0
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ struct snd_usb_audio {
					 */

	struct usb_host_interface *ctrl_intf;	/* the audio control interface */
	int card_num;	/* cache pcm card number to use upon disconnect */
	void (*disconnect_cb)(struct snd_usb_audio *chip); /* callback to cleanup on disconnect */
};

#define usb_audio_err(chip, fmt, args...) \