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:
parent
1c20c327b6
commit
6da0c634f8
@ -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(®ister_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(®ister_mutex);
|
||||
return subs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(find_snd_usb_substream);
|
||||
|
||||
/*
|
||||
* disconnect streams
|
||||
* called from usb_audio_disconnect()
|
||||
|
@ -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
sound/usb/pcm.c
147
sound/usb/pcm.c
@ -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:
|
||||
|
@ -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 */
|
||||
|
@ -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...) \
|
||||
|
Loading…
Reference in New Issue
Block a user