Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: gadget: u_audio: Rate ctl notifies about current srate (0=stopped)

The Playback/Capture ctl currently reports rate value set by USB
control selector UAC2_CS_CONTROL_SAM_FREQ (fixed for UAC1). When the
stops playback/capture, the reported value does not change. The gadget
side has no information whether the host has started/stopped
capture/playback.

This patch sets the value reported by the respective rate ctl to zero
when the host side has stopped playback/capture. Also, it calls
snd_ctl_notify when start/stop occurs, so that a subscribed client can
act appropriately.

Tests have confirmed that USB hosts change UAC2_CS_CONTROL_SAM_FREQ
before switching altsetting to activate playback/capture, resulting in
correct order (params->c/p_srate is set to requested rate before
u_audio_start_capture/playback is called).

The gadget rate notifications are used by user-space audio gadget
controller gaudio_ctl https://github.com/pavhofman/gaudio_ctl.

Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com>
Link: https://lore.kernel.org/r/20220121155308.48794-8-pavel.hofman@ivitera.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Pavel Hofman and committed by
Greg Kroah-Hartman
8fe9a03f 695d39ff

+27 -1
+27 -1
drivers/usb/gadget/function/u_audio.c
··· 65 65 66 66 struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */ 67 67 int srate; /* selected samplerate */ 68 + int active; /* playback/capture running */ 68 69 69 70 spinlock_t lock; /* lock for control transfers */ 70 71 ··· 491 490 dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); 492 491 } 493 492 493 + static void set_active(struct uac_rtd_params *prm, bool active) 494 + { 495 + // notifying through the Rate ctrl 496 + struct snd_kcontrol *kctl = prm->snd_kctl_rate; 497 + unsigned long flags; 498 + 499 + spin_lock_irqsave(&prm->lock, flags); 500 + if (prm->active != active) { 501 + prm->active = active; 502 + snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE, 503 + &kctl->id); 504 + } 505 + spin_unlock_irqrestore(&prm->lock, flags); 506 + } 507 + 494 508 int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate) 495 509 { 496 510 struct uac_params *params = &audio_dev->params; ··· 623 607 dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 624 608 } 625 609 610 + set_active(&uac->c_prm, true); 611 + 626 612 ep_fback = audio_dev->in_ep_fback; 627 613 if (!ep_fback) 628 614 return 0; ··· 670 652 { 671 653 struct snd_uac_chip *uac = audio_dev->uac; 672 654 655 + set_active(&uac->c_prm, false); 673 656 if (audio_dev->in_ep_fback) 674 657 free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); 675 658 free_ep(&uac->c_prm, audio_dev->out_ep); ··· 742 723 dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 743 724 } 744 725 726 + set_active(&uac->p_prm, true); 727 + 745 728 return 0; 746 729 } 747 730 EXPORT_SYMBOL_GPL(u_audio_start_playback); ··· 752 731 { 753 732 struct snd_uac_chip *uac = audio_dev->uac; 754 733 734 + set_active(&uac->p_prm, false); 755 735 free_ep(&uac->p_prm, audio_dev->in_ep); 756 736 } 757 737 EXPORT_SYMBOL_GPL(u_audio_stop_playback); ··· 1096 1074 unsigned long flags; 1097 1075 1098 1076 spin_lock_irqsave(&prm->lock, flags); 1099 - ucontrol->value.integer.value[0] = prm->srate; 1077 + if (prm->active) 1078 + ucontrol->value.integer.value[0] = prm->srate; 1079 + else 1080 + /* not active: reporting zero rate */ 1081 + ucontrol->value.integer.value[0] = 0; 1100 1082 spin_unlock_irqrestore(&prm->lock, flags); 1101 1083 return 0; 1102 1084 }