diff --git a/sound/usb/card.c b/sound/usb/card.c index 1d9d0baf82cb..c4bcb3434b12 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -112,6 +112,119 @@ 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->connect) || + (!ops->disconnect) || + (!ops->set_interface) || + (!ops->set_rate) || + (!ops->set_pcm_buf) || + (!ops->set_pcm_intf) || + (!ops->set_pcm_connection) || + (!ops->set_pcm_binterval) || + (!ops->usb_add_ctls)) + 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; +} + +static int snd_vendor_connect(struct usb_interface *intf) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + return ops->connect(intf); + return 0; +} + +static void snd_vendor_disconnect(struct usb_interface *intf) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + ops->disconnect(intf); +} + +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_rate(struct usb_interface *intf, int iface, int rate, + int alt) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + return ops->set_rate(intf, iface, rate, alt); + return 0; +} + +int snd_vendor_set_pcm_buf(struct usb_device *udev, int iface) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + ops->set_pcm_buf(udev, iface); + return 0; +} + +int snd_vendor_set_pcm_intf(struct usb_interface *intf, int iface, int alt, + int direction) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + return ops->set_pcm_intf(intf, iface, alt, direction); + 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; +} + +int snd_vendor_set_pcm_binterval(struct audioformat *fp, + struct audioformat *found, + int *cur_attr, int *attr) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + return ops->set_pcm_binterval(fp, found, cur_attr, attr); + return 0; +} + +static int snd_vendor_usb_add_ctls(struct snd_usb_audio *chip) +{ + struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops(); + + if (ops) + return ops->usb_add_ctls(chip); + return 0; +} + 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)) @@ -666,6 +779,10 @@ static int usb_audio_probe(struct usb_interface *intf, if (err < 0) return err; + err = snd_vendor_connect(intf); + if (err) + return err; + /* * found a config. now register to ALSA */ @@ -727,6 +844,8 @@ static int usb_audio_probe(struct usb_interface *intf, dev_set_drvdata(&dev->dev, chip); + snd_vendor_usb_add_ctls(chip); + /* * For devices with more than one control interface, we assume the * first contains the audio controls. We might need a more specific @@ -815,6 +934,8 @@ static void usb_audio_disconnect(struct usb_interface *intf) if (chip->disconnect_cb) chip->disconnect_cb(chip); + snd_vendor_disconnect(intf); + mutex_lock(®ister_mutex); if (atomic_inc_return(&chip->shutdown) == 1) { struct snd_usb_stream *as; diff --git a/sound/usb/card.h b/sound/usb/card.h index 4b1728983efc..385eb0adf600 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -185,4 +185,21 @@ 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 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(struct usb_interface *intf, int iface, int rate, + int alt); +int snd_vendor_set_pcm_buf(struct usb_device *udev, int iface); +int snd_vendor_set_pcm_intf(struct usb_interface *intf, int iface, int alt, + int direction); +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(struct audioformat *fp, + struct audioformat *found, + int *cur_attr, int *attr); + #endif /* __USBAUDIO_CARD_H */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index b118cf97607f..9a97d7876f39 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -642,8 +642,13 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, * interface is active. */ if (rate != prev_rate) { usb_set_interface(dev, iface, 0); + + snd_vendor_set_interface(dev, alts, iface, 0); + snd_usb_set_interface_quirk(dev); usb_set_interface(dev, iface, fmt->altsetting); + + snd_vendor_set_interface(dev, alts, iface, fmt->altsetting); snd_usb_set_interface_quirk(dev); } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 077c8d121bf8..1947ea919036 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -134,6 +134,8 @@ static struct audioformat *find_format(struct snd_usb_substream *subs) found = fp; cur_attr = attr; } + + snd_vendor_set_pcm_binterval(fp, found, &cur_attr, &attr); } return found; } @@ -642,6 +644,10 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) } dev_dbg(&dev->dev, "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting); + err = snd_vendor_set_pcm_intf(iface, fmt->iface, + fmt->altsetting, subs->direction); + if (err) + return err; snd_usb_set_interface_quirk(dev); } @@ -1040,6 +1046,10 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) struct usb_interface *iface; int ret; + ret = snd_vendor_set_pcm_buf(subs->dev, subs->cur_audiofmt->iface); + if (ret) + return ret; + if (! subs->cur_audiofmt) { dev_err(&subs->dev->dev, "no format is specified!\n"); return -ENXIO; @@ -1073,6 +1083,17 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) if (ret < 0) goto unlock; + if (snd_vendor_get_ops()) { + ret = snd_vendor_set_rate(iface, + subs->cur_audiofmt->iface, + subs->cur_rate, + subs->cur_audiofmt->altsetting); + if (!ret) { + subs->need_setup_ep = false; + goto unlock; + } + } + ret = configure_endpoint(subs); if (ret < 0) goto unlock; @@ -1482,6 +1503,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; + subs->interface = -1; subs->altset_idx = 0; runtime->hw = snd_usb_hardware; @@ -1510,12 +1536,23 @@ 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 (!as->chip->keep_iface && subs->interface >= 0 && !snd_usb_lock_shutdown(subs->stream->chip)) { usb_set_interface(subs->dev, subs->interface, 0); + ret = snd_vendor_set_pcm_intf(usb_ifnum_to_if(subs->dev, + subs->interface), + subs->interface, 0, + direction); + if (ret) + return ret; subs->interface = -1; ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D1); snd_usb_unlock_shutdown(subs->stream->chip); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 04ca1ef77a87..e7e35a326ce2 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -129,4 +129,50 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip); extern bool snd_usb_use_vmalloc; extern bool snd_usb_skip_validation; +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 + * @connect: called when a new interface is found + * @disconnect: called when an interface is removed + * @set_interface: called when an interface is initialized + * @set_rate: called when the rate is set + * @set_pcm_buf: called when the pcm buffer is set + * @set_pcm_intf: called when the pcm interface is set + * @set_pcm_connection: called when pcm is opened/closed + * @set_pcm_binterval: called when the pcm binterval is set + * @usb_add_ctls: called when USB controls are added + * + * 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 (*connect)(struct usb_interface *intf); + void (*disconnect)(struct usb_interface *intf); + + int (*set_interface)(struct usb_device *udev, + struct usb_host_interface *alts, + int iface, int alt); + int (*set_rate)(struct usb_interface *intf, int iface, int rate, + int alt); + int (*set_pcm_buf)(struct usb_device *udev, int iface); + int (*set_pcm_intf)(struct usb_interface *intf, int iface, int alt, + int direction); + int (*set_pcm_connection)(struct usb_device *udev, + enum snd_vendor_pcm_open_close onoff, + int direction); + int (*set_pcm_binterval)(struct audioformat *fp, + struct audioformat *found, + int *cur_attr, int *attr); + int (*usb_add_ctls)(struct snd_usb_audio *chip); +}; + #endif /* __USBAUDIO_H */