diff --git a/sound/usb/card.c b/sound/usb/card.c index 4e535eeddd43..96d8e1742af0 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -119,6 +119,56 @@ MODULE_PARM_DESC(skip_validation, "Skip unit descriptor validation (default: no) static DEFINE_MUTEX(register_mutex); static struct snd_usb_audio *usb_chip[SNDRV_CARDS]; static struct usb_driver usb_audio_driver; +static struct snd_usb_audio_vendor_ops *usb_vendor_ops; + +int snd_vendor_set_ops(struct snd_usb_audio_vendor_ops *ops) +{ + if ((!ops->set_interface) || + (!ops->set_pcm_intf) || + (!ops->set_pcm_connection)) + return -EINVAL; + + usb_vendor_ops = ops; + return 0; +} +EXPORT_SYMBOL_GPL(snd_vendor_set_ops); + +struct snd_usb_audio_vendor_ops *snd_vendor_get_ops(void) +{ + return usb_vendor_ops; +} + +int snd_vendor_set_interface(struct usb_device *udev, + struct usb_host_interface *intf, + int iface, int alt) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + return ops->set_interface(udev, intf, iface, alt); + return 0; +} + +int snd_vendor_set_pcm_intf(struct usb_interface *intf, int iface, int alt, + int direction, struct snd_usb_substream *subs) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + return ops->set_pcm_intf(intf, iface, alt, direction, subs); + return 0; +} + +int snd_vendor_set_pcm_connection(struct usb_device *udev, + enum snd_vendor_pcm_open_close onoff, + int direction) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + return ops->set_pcm_connection(udev, onoff, direction); + return 0; +} /* * disconnect streams diff --git a/sound/usb/card.h b/sound/usb/card.h index e9b6afd2275a..5421f9e53c54 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -216,4 +216,19 @@ struct snd_usb_stream { struct list_head list; }; +int snd_vendor_set_ops(struct snd_usb_audio_vendor_ops *vendor_ops); +struct snd_usb_audio_vendor_ops *snd_vendor_get_ops(void); +int snd_vendor_set_interface(struct usb_device *udev, + struct usb_host_interface *alts, + int iface, int alt); +int snd_vendor_set_rate(int iface, int rate, int alt); +int snd_vendor_set_pcm_intf(struct usb_interface *intf, int iface, int alt, + int direction, struct snd_usb_substream *subs); +int snd_vendor_set_pcm_connection(struct usb_device *udev, + enum snd_vendor_pcm_open_close onoff, + int direction); +int snd_vendor_set_pcm_binterval(const struct audioformat *fp, + const struct audioformat *found, + int *cur_attr, int *attr); + #endif /* __USBAUDIO_CARD_H */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 1e1d7458bce1..cc0903af6a7f 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -641,6 +641,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) struct snd_usb_audio *chip = subs->stream->chip; int retry = 0; int ret; + struct usb_interface *iface; ret = snd_usb_lock_shutdown(chip); if (ret < 0) @@ -653,6 +654,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) again: if (subs->sync_endpoint) { ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint); + if (ret < 0) goto unlock; } @@ -664,6 +666,14 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) snd_usb_set_format_quirk(subs, subs->cur_audiofmt); ret = 0; + iface = usb_ifnum_to_if(chip->dev, subs->data_endpoint->iface); + + ret = snd_vendor_set_pcm_intf(iface, subs->data_endpoint->iface, + subs->data_endpoint->altsetting, + subs->direction, subs); + if (!ret) + goto unlock; + /* reset the pointer */ subs->buffer_bytes = frames_to_bytes(runtime, runtime->buffer_size); subs->inflight_bytes = 0; @@ -1162,6 +1172,11 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream) struct snd_usb_substream *subs = &as->substream[direction]; int ret; + ret = snd_vendor_set_pcm_connection(subs->dev, SOUND_PCM_OPEN, + direction); + if (ret) + return ret; + runtime->hw = snd_usb_hardware; /* need an explicit sync to catch applptr update in low-latency mode */ if (direction == SNDRV_PCM_STREAM_PLAYBACK && @@ -1195,6 +1210,11 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream) struct snd_usb_substream *subs = &as->substream[direction]; int ret; + ret = snd_vendor_set_pcm_connection(subs->dev, SOUND_PCM_CLOSE, + direction); + if (ret) + return ret; + snd_media_stop_pipeline(subs); if (!snd_usb_lock_shutdown(subs->stream->chip)) { diff --git a/sound/usb/stream.c b/sound/usb/stream.c index f10f4e6d3fb8..dbe86811346f 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -1228,6 +1228,8 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, snd_usb_init_pitch(chip, fp); snd_usb_init_sample_rate(chip, fp, fp->rate_max); usb_set_interface(chip->dev, iface_no, altno); + if (protocol > UAC_VERSION_1) + snd_vendor_set_interface(chip->dev, alts, iface_no, 0); } return 0; } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 1b463ce52324..b2cafc0b5eef 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -20,6 +20,7 @@ struct media_device; struct media_intf_devnode; +struct snd_usb_substream; #define MAX_CARD_INTERFACES 16 @@ -210,4 +211,37 @@ extern bool snd_usb_skip_validation; #define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20) #define QUIRK_FLAG_FIXED_RATE (1U << 21) +struct audioformat; + +enum snd_vendor_pcm_open_close { + SOUND_PCM_CLOSE = 0, + SOUND_PCM_OPEN, +}; + +/** + * struct snd_usb_audio_vendor_ops - function callbacks for USB audio accelerators + * @set_interface: called when an interface is initialized + * @set_pcm_intf: called when the pcm interface is set + * @set_pcm_connection: called when pcm is opened/closed + * + * Set of callbacks for some accelerated USB audio streaming hardware. + * + * TODO: make this USB host-controller specific, right now this only works for + * one USB controller in the system at a time, which is only realistic for + * self-contained systems like phones. + */ +struct snd_usb_audio_vendor_ops { + int (*set_interface)(struct usb_device *udev, + struct usb_host_interface *alts, + int iface, int alt); + int (*set_pcm_intf)(struct usb_interface *intf, int iface, int alt, + int direction, struct snd_usb_substream *subs); + int (*set_pcm_connection)(struct usb_device *udev, + enum snd_vendor_pcm_open_close onoff, + int direction); + ANDROID_KABI_RESERVE(1); + ANDROID_KABI_RESERVE(2); + ANDROID_KABI_RESERVE(3); + ANDROID_KABI_RESERVE(4); +}; #endif /* __USBAUDIO_H */