@@ -117,6 +117,118 @@ 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->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(const struct audioformat *fp,
+ const 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;
+}
/*
* disconnect streams
@@ -753,6 +865,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
*/
@@ -820,6 +936,8 @@ static int usb_audio_probe(struct usb_interface *intf,
if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND)
usb_disable_autosuspend(interface_to_usbdev(intf));
+ 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
@@ -907,6 +1025,8 @@ static void usb_audio_disconnect(struct usb_interface *intf)
card = chip->card;
+ snd_vendor_disconnect(intf);
+
mutex_lock(®ister_mutex);
if (atomic_inc_return(&chip->shutdown) == 1) {
struct snd_usb_stream *as;
@@ -204,4 +204,25 @@ 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));
+
+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(const struct audioformat *fp,
+ const struct audioformat *found,
+ int *cur_attr, int *attr);
+
#endif /* __USBAUDIO_CARD_H */
@@ -184,4 +184,50 @@ extern bool snd_usb_skip_validation;
#define QUIRK_FLAG_DSD_RAW (1U << 15)
#define QUIRK_FLAG_SET_IFACE_FIRST (1U << 16)
+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)(const struct audioformat *fp,
+ const struct audioformat *found,
+ int *cur_attr, int *attr);
+ int (*usb_add_ctls)(struct snd_usb_audio *chip);
+};
+
#endif /* __USBAUDIO_H */