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: Hemant Kumar <hemantk@codeaurora.org>
Change-Id: I658d525233be3eb657a3b36c81a3920e65e96a40
(cherry picked from commit f055b3843f)
[hridya: only include ABI diff, add EXPORT_SYMBOL_GPL,
add some pointer checks]
Signed-off-by: Hridya Valsaraju <hridya@google.com>
This commit is contained in:
Hridya Valsaraju 2020-03-12 09:57:06 -07:00
parent 1c20c327b6
commit 6da0c634f8
5 changed files with 231 additions and 2 deletions

View File

@ -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()

View File

@ -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 */

View File

@ -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:

View File

@ -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 */

View File

@ -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...) \