From 6da0c634f8bda1a50698a245374b8f86fdda6640 Mon Sep 17 00:00:00 2001 From: Hridya Valsaraju Date: Thu, 12 Mar 2020 09:57:06 -0700 Subject: [PATCH] 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 Change-Id: I658d525233be3eb657a3b36c81a3920e65e96a40 (cherry picked from commit f055b3843f78a67497bbab7da5ea6405823bb861) [hridya: only include ABI diff, add EXPORT_SYMBOL_GPL, add some pointer checks] Signed-off-by: Hridya Valsaraju --- sound/usb/card.c | 76 ++++++++++++++++++++++ sound/usb/card.h | 4 ++ sound/usb/pcm.c | 147 +++++++++++++++++++++++++++++++++++++++++++ sound/usb/pcm.h | 4 +- sound/usb/usbaudio.h | 2 + 5 files changed, 231 insertions(+), 2 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 746a72e23cf9..e0ef5c29bd69 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -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() diff --git a/sound/usb/card.h b/sound/usb/card.h index 7f11655bde50..1282799a8b46 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -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 */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 8b91be394407..3ba1f9c5d87a 100644 --- a/sound/usb/pcm.c +++ b/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: diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index 9833627c1eca..37fbcb3c314a 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -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 */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 4a0a5a071d51..d41098e84880 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.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...) \