Loading sound/usb/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -162,5 +162,14 @@ config SND_BCD2000 source "sound/usb/line6/Kconfig" config SND_USB_AUDIO_QMI tristate "USB Audio QMI Service driver" depends on QCOM_QMI_HELPERS help Starts USB Audio QMI server to communicate with remote entity to perform operations like enable or disable particular audio stream on a connected USB device, subsystem restart and device disconnect. endif # SND_USB sound/usb/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -29,3 +29,4 @@ obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/ obj-$(CONFIG_SND_USB_LINE6) += line6/ obj-$(CONFIG_SND_USB_AUDIO_QMI) += usb_audio_qmi_v01.o usb_audio_qmi_svc.o sound/usb/card.c +71 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,71 @@ static DEFINE_MUTEX(register_mutex); static struct snd_usb_audio *usb_chip[SNDRV_CARDS]; static struct usb_driver usb_audio_driver; 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]) 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 crad # %d does not exist\n", __func__, card_num); goto err; } if (pcm_idx >= chip->pcm_devs) { pr_err("%s: invalid pcm dev number %u > %d\n", __func__, pcm_idx, chip->pcm_devs); goto err; } if (direction > SNDRV_PCM_STREAM_CAPTURE) { pr_err("%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) { pr_debug("%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_debug("%s: substream instance not found\n", __func__); mutex_unlock(®ister_mutex); return subs; } /* * disconnect streams * called from usb_audio_disconnect() Loading Loading @@ -366,6 +431,7 @@ static void snd_usb_audio_free(struct snd_card *card) list_for_each_entry_safe(ep, n, &chip->ep_list, list) snd_usb_endpoint_free(ep); mutex_destroy(&chip->dev_lock); mutex_destroy(&chip->mutex); if (!atomic_read(&chip->shutdown)) dev_set_drvdata(&chip->dev->dev, NULL); Loading Loading @@ -493,6 +559,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, chip = card->private_data; mutex_init(&chip->mutex); mutex_init(&chip->dev_lock); init_waitqueue_head(&chip->shutdown_wait); chip->index = idx; chip->dev = dev; Loading Loading @@ -688,6 +755,8 @@ static int usb_audio_probe(struct usb_interface *intf, usb_chip[chip->index] = chip; chip->num_interfaces++; usb_set_intfdata(intf, chip); intf->needs_remote_wakeup = 1; usb_enable_autosuspend(chip->dev); atomic_dec(&chip->active); mutex_unlock(®ister_mutex); return 0; Loading Loading @@ -717,6 +786,8 @@ static void usb_audio_disconnect(struct usb_interface *intf) return; card = chip->card; if (chip->disconnect_cb) chip->disconnect_cb(chip); mutex_lock(®ister_mutex); if (atomic_inc_return(&chip->shutdown) == 1) { Loading sound/usb/card.h +4 −0 Original line number Diff line number Diff line Loading @@ -171,4 +171,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 */ sound/usb/pcm.c +130 −0 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -565,6 +628,73 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) return 0; } 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 (!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_err(&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 (atomic_read(&subs->stream->chip->shutdown)) { ret = -ENODEV; } else { ret = set_format(subs, fmt); if (ret < 0) return ret; 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; } /* * Return the score of matching two audioformats. * Veto the audioformat if: Loading Loading
sound/usb/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -162,5 +162,14 @@ config SND_BCD2000 source "sound/usb/line6/Kconfig" config SND_USB_AUDIO_QMI tristate "USB Audio QMI Service driver" depends on QCOM_QMI_HELPERS help Starts USB Audio QMI server to communicate with remote entity to perform operations like enable or disable particular audio stream on a connected USB device, subsystem restart and device disconnect. endif # SND_USB
sound/usb/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -29,3 +29,4 @@ obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/ obj-$(CONFIG_SND_USB_LINE6) += line6/ obj-$(CONFIG_SND_USB_AUDIO_QMI) += usb_audio_qmi_v01.o usb_audio_qmi_svc.o
sound/usb/card.c +71 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,71 @@ static DEFINE_MUTEX(register_mutex); static struct snd_usb_audio *usb_chip[SNDRV_CARDS]; static struct usb_driver usb_audio_driver; 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]) 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 crad # %d does not exist\n", __func__, card_num); goto err; } if (pcm_idx >= chip->pcm_devs) { pr_err("%s: invalid pcm dev number %u > %d\n", __func__, pcm_idx, chip->pcm_devs); goto err; } if (direction > SNDRV_PCM_STREAM_CAPTURE) { pr_err("%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) { pr_debug("%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_debug("%s: substream instance not found\n", __func__); mutex_unlock(®ister_mutex); return subs; } /* * disconnect streams * called from usb_audio_disconnect() Loading Loading @@ -366,6 +431,7 @@ static void snd_usb_audio_free(struct snd_card *card) list_for_each_entry_safe(ep, n, &chip->ep_list, list) snd_usb_endpoint_free(ep); mutex_destroy(&chip->dev_lock); mutex_destroy(&chip->mutex); if (!atomic_read(&chip->shutdown)) dev_set_drvdata(&chip->dev->dev, NULL); Loading Loading @@ -493,6 +559,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, chip = card->private_data; mutex_init(&chip->mutex); mutex_init(&chip->dev_lock); init_waitqueue_head(&chip->shutdown_wait); chip->index = idx; chip->dev = dev; Loading Loading @@ -688,6 +755,8 @@ static int usb_audio_probe(struct usb_interface *intf, usb_chip[chip->index] = chip; chip->num_interfaces++; usb_set_intfdata(intf, chip); intf->needs_remote_wakeup = 1; usb_enable_autosuspend(chip->dev); atomic_dec(&chip->active); mutex_unlock(®ister_mutex); return 0; Loading Loading @@ -717,6 +786,8 @@ static void usb_audio_disconnect(struct usb_interface *intf) return; card = chip->card; if (chip->disconnect_cb) chip->disconnect_cb(chip); mutex_lock(®ister_mutex); if (atomic_inc_return(&chip->shutdown) == 1) { Loading
sound/usb/card.h +4 −0 Original line number Diff line number Diff line Loading @@ -171,4 +171,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 */
sound/usb/pcm.c +130 −0 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -565,6 +628,73 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) return 0; } 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 (!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_err(&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 (atomic_read(&subs->stream->chip->shutdown)) { ret = -ENODEV; } else { ret = set_format(subs, fmt); if (ret < 0) return ret; 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; } /* * Return the score of matching two audioformats. * Veto the audioformat if: Loading