diff mbox

[v3,1/2] ALSA: jack: create jack kcontrols for every jack input device

Message ID 1426865953-1606-2-git-send-email-yang.jie@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jie, Yang March 20, 2015, 3:39 p.m. UTC
Currently the ALSA jack core registers only input devices for each jack
registered. These jack input devices are not readable by userspace devices
that run as non root.

This patch adds support for additionally registering jack kcontrol devices
for every input jack registered. This allows non root userspace to read
jack status.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Modified-by: Jie Yang <yang.jie@intel.com>
Signed-off-by: Jie Yang <yang.jie@intel.com>
Reveiwed-by: Mark Brown <broonie@kernel.org>
---
 include/sound/jack.h |   8 ++++
 sound/core/jack.c    | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 124 insertions(+), 2 deletions(-)

Comments

Takashi Iwai March 20, 2015, 4:17 p.m. UTC | #1
At Fri, 20 Mar 2015 23:39:12 +0800,
Jie Yang wrote:
> 
> Currently the ALSA jack core registers only input devices for each jack
> registered. These jack input devices are not readable by userspace devices
> that run as non root.
> 
> This patch adds support for additionally registering jack kcontrol devices
> for every input jack registered. This allows non root userspace to read
> jack status.
> 
> Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
> Modified-by: Jie Yang <yang.jie@intel.com>
> Signed-off-by: Jie Yang <yang.jie@intel.com>
> Reveiwed-by: Mark Brown <broonie@kernel.org>

Thanks for a quick update.  It's getting better, but still some points
to be fixed.  Let's see...


> +struct snd_jack_kctl {
> +	struct snd_kcontrol *kctl;
> +	struct list_head jack_list;		/* list of controls belong to the same jack*/

jack_ prefix is superfluous.

> +static int jack_ctl_name_found(struct snd_card *card, const char *name, int index)

Use bool.

> +{
> +	struct snd_kcontrol *kctl;
> +	int len = strlen(name);
> +
> +	down_read(&card->controls_rwsem);
> +
> +	list_for_each_entry(kctl, &card->controls, list) {
> +		if (!strncmp(name, kctl->id.name, len) &&
> +		    !strcmp(" Jack", kctl->id.name + len) &&
> +		    kctl->id.index == index) {

This doesn't work when ctl elements are with multiple counts.  A
single kctl object may contain multiple snd_kcontrol_volatile entries.
That's why snd_ctl_find_id() checks like:

	list_for_each_entry(kctl, &card->controls, list) {
		....
		if (kctl->id.index > id->index)
			continue;
		if (kctl->id.index + kctl->count <= id->index)
			continue;

Also, more importantly, your code checks only the name string.  But
the same name string can be assigned for different iface, device and
subdevice entries.  You need to compare them as well.

That said, it'd be even simpler just to call snd_ctl_find_id() after
creating a ctl id instance instead of writing an own funciton.  It
ends up with a bit more stack usage, but it should be still
acceptable.


> +static int snd_jack_new_kctl(struct snd_card *card, struct snd_jack *jack, int type)
> +{
> +	struct snd_kcontrol *kctl;
> +	struct snd_jack_kctl *jack_kctl;
> +	int i, err, index, state = 0 /* use 0 for default state ?? */;
> +
> +	INIT_LIST_HEAD(&jack->kctl_list);
> +	for (i = 0; i < fls(SND_JACK_BTN_0); i++) {
> +		int testbit = 1 << i;
> +
> +		/* we only new headphone kctl for headset jack */
> +		if ((testbit == SND_JACK_MICROPHONE) &&
> +				((type & SND_JACK_HEADSET) == SND_JACK_HEADSET))
> +			continue;

The parentheses are superfluous.

> +		/* we only new lineout kctl for avout jack */
> +		if ((testbit == SND_JACK_VIDEOOUT) &&
> +				((type & SND_JACK_AVOUT) == SND_JACK_AVOUT))
> +			continue;
> +
> +		if (type & testbit) {

This check can be at the beginning of the loop, i.e.

		if (!(type & testbit))
			continue;

then you'll reduce one indent level.


Now, a bigger question:

> +			index = get_available_index(card,jack->id);
> +			kctl = snd_kctl_jack_new(jack->id, index, card);

So, here you are creating multiple kctl elements with the very same
name string.  Did you take a look at the actual usages of
snd_jack_new(), especially snd_soc_card_jack_new()?

Try like

% git grep -A2 snd_soc_card_jack_new sound/soc/

sound/soc/fsl/imx-es8328.c:             ret = snd_soc_card_jack_new(rtd->card, "Headphone",
sound/soc/fsl/imx-es8328.c-                                         SND_JACK_HEADPHONE | SND_JACK_BTN_0,
sound/soc/fsl/imx-es8328.c-                                         &headset_jack, NULL, 0);
--
sound/soc/fsl/wm1133-ev1.c:     snd_soc_card_jack_new(rtd->card, "Headphone", SND_JACK_HEADPHONE,
sound/soc/fsl/wm1133-ev1.c-                           &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
sound/soc/fsl/wm1133-ev1.c-     wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE);
--
sound/soc/fsl/wm1133-ev1.c:     snd_soc_card_jack_new(rtd->card, "Microphone",
sound/soc/fsl/wm1133-ev1.c-                           SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack,
sound/soc/fsl/wm1133-ev1.c-                           mic_jack_pins, ARRAY_SIZE(mic_jack_pins));
.....

As you'll find through the whole output, many calls are combined with
SND_JACK_BTN_* bits.  And think again how this will result with your
patch.  A call like

	snd_soc_card_jack_new(card, "Headphone",
		SND_JACK_HEADPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1, ...)

would give three kctls, all "Headphone Jack" with indices 0, 1 and 2.
How user-space knows what is for what?

Also, when you look through the grep above, you'll find that many
calls already contain "Jack" name suffix.  So, they will result in
a name like "Headset Jack Jack".

One last bit:
> +			/* set initial jack state */
> +			snd_kctl_jack_report(card, kctl, state & testbit);

This can be dropped.  The value is zero as default, so unless the
actual value is different, you don't need to call it. 

Or, yet another last bit: remove EXPORT_SYMBOL_GPL() from ctljack.c,
as they are merely internal functions.  But this can be done another
patch later.


Takashi
Jie, Yang March 21, 2015, 2:22 a.m. UTC | #2
> -----Original Message-----
> From: Takashi Iwai [mailto:tiwai@suse.de]
> Sent: Saturday, March 21, 2015 12:17 AM
> To: Jie, Yang
> Cc: broonie@kernel.org; alsa-devel@alsa-project.org; Girdwood, Liam R;
> Liam Girdwood
> Subject: Re: [PATCH v3 1/2] ALSA: jack: create jack kcontrols for every jack
> input device
> 
> At Fri, 20 Mar 2015 23:39:12 +0800,
> Jie Yang wrote:
> >
> > Currently the ALSA jack core registers only input devices for each
> > jack registered. These jack input devices are not readable by
> > userspace devices that run as non root.
> >
> > This patch adds support for additionally registering jack kcontrol
> > devices for every input jack registered. This allows non root
> > userspace to read jack status.
> >
> > Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
> > Modified-by: Jie Yang <yang.jie@intel.com>
> > Signed-off-by: Jie Yang <yang.jie@intel.com>
> > Reveiwed-by: Mark Brown <broonie@kernel.org>
> 
> Thanks for a quick update.  It's getting better, but still some points to be fixed.
> Let's see...
> 
> 
> > +struct snd_jack_kctl {
> > +	struct snd_kcontrol *kctl;
> > +	struct list_head jack_list;		/* list of controls belong to
> the same jack*/
> 
> jack_ prefix is superfluous.
[Keyon] OK.
> 
> > +static int jack_ctl_name_found(struct snd_card *card, const char
> > +*name, int index)
> 
> Use bool.
[Keyon] OK, will change it.
> 
> > +{
> > +	struct snd_kcontrol *kctl;
> > +	int len = strlen(name);
> > +
> > +	down_read(&card->controls_rwsem);
> > +
> > +	list_for_each_entry(kctl, &card->controls, list) {
> > +		if (!strncmp(name, kctl->id.name, len) &&
> > +		    !strcmp(" Jack", kctl->id.name + len) &&
> > +		    kctl->id.index == index) {
> 
> This doesn't work when ctl elements are with multiple counts.  A single kctl
> object may contain multiple snd_kcontrol_volatile entries.
> That's why snd_ctl_find_id() checks like:
> 
> 	list_for_each_entry(kctl, &card->controls, list) {
> 		....
> 		if (kctl->id.index > id->index)
> 			continue;
> 		if (kctl->id.index + kctl->count <= id->index)
> 			continue;
> 
> Also, more importantly, your code checks only the name string.  But the
> same name string can be assigned for different iface, device and subdevice
> entries.  You need to compare them as well.
> 
> That said, it'd be even simpler just to call snd_ctl_find_id() after creating a ctl
> id instance instead of writing an own funciton.  It ends up with a bit more
> stack usage, but it should be still acceptable.
[Keyon] Here I did the getting index work by imitating function from
had_jack.c:get_unique_index(), and I also noticed and concerned about
not checking other snd_ctl_elem_id items.

Thank you for explaining this detail, Takashi, will change to use snd_ctl_find_id(). 

> 
> 
> > +static int snd_jack_new_kctl(struct snd_card *card, struct snd_jack
> > +*jack, int type) {
> > +	struct snd_kcontrol *kctl;
> > +	struct snd_jack_kctl *jack_kctl;
> > +	int i, err, index, state = 0 /* use 0 for default state ?? */;
> > +
> > +	INIT_LIST_HEAD(&jack->kctl_list);
> > +	for (i = 0; i < fls(SND_JACK_BTN_0); i++) {
> > +		int testbit = 1 << i;
> > +
> > +		/* we only new headphone kctl for headset jack */
> > +		if ((testbit == SND_JACK_MICROPHONE) &&
> > +				((type & SND_JACK_HEADSET) ==
> SND_JACK_HEADSET))
> > +			continue;
> 
> The parentheses are superfluous.
[Keyon] OK.
> 
> > +		/* we only new lineout kctl for avout jack */
> > +		if ((testbit == SND_JACK_VIDEOOUT) &&
> > +				((type & SND_JACK_AVOUT) ==
> SND_JACK_AVOUT))
> > +			continue;
> > +
> > +		if (type & testbit) {
> 
> This check can be at the beginning of the loop, i.e.
> 
> 		if (!(type & testbit))
> 			continue;
> 
> then you'll reduce one indent level.
[Keyon] good idea. Will change it.
> 
> 
> Now, a bigger question:
> 
> > +			index = get_available_index(card,jack->id);
> > +			kctl = snd_kctl_jack_new(jack->id, index, card);
> 
> So, here you are creating multiple kctl elements with the very same name
> string.  Did you take a look at the actual usages of snd_jack_new(), especially
> snd_soc_card_jack_new()?
[Keyon] yes, I noticed this.  I can also find it at soc/intel/broadwell.c:

        ret = snd_soc_card_jack_new(rtd->card, "Headset",
                SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset,
                broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins));

it will create 2 kctl elements with the same name string "Headset":

numid=12,iface=CARD,name='Headset Jack'
numid=13,iface=CARD,name='Headset Jack',index=1

> 
> Try like
> 
> % git grep -A2 snd_soc_card_jack_new sound/soc/
> 
> sound/soc/fsl/imx-es8328.c:             ret = snd_soc_card_jack_new(rtd->card,
> "Headphone",
> sound/soc/fsl/imx-es8328.c-                                         SND_JACK_HEADPHONE |
> SND_JACK_BTN_0,
> sound/soc/fsl/imx-es8328.c-                                         &headset_jack, NULL, 0);
> --
> sound/soc/fsl/wm1133-ev1.c:     snd_soc_card_jack_new(rtd->card,
> "Headphone", SND_JACK_HEADPHONE,
> sound/soc/fsl/wm1133-ev1.c-                           &hp_jack, hp_jack_pins,
> ARRAY_SIZE(hp_jack_pins));
> sound/soc/fsl/wm1133-ev1.c-     wm8350_hp_jack_detect(codec,
> WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE);
> --
> sound/soc/fsl/wm1133-ev1.c:     snd_soc_card_jack_new(rtd->card,
> "Microphone",
> sound/soc/fsl/wm1133-ev1.c-                           SND_JACK_MICROPHONE |
> SND_JACK_BTN_0, &mic_jack,
> sound/soc/fsl/wm1133-ev1.c-                           mic_jack_pins,
> ARRAY_SIZE(mic_jack_pins));
> .....
> 
> As you'll find through the whole output, many calls are combined with
> SND_JACK_BTN_* bits.  And think again how this will result with your patch.
> A call like
> 
> 	snd_soc_card_jack_new(card, "Headphone",
> 		SND_JACK_HEADPHONE | SND_JACK_BTN_0 |
> SND_JACK_BTN_1, ...)
> 
> would give three kctls, all "Headphone Jack" with indices 0, 1 and 2.
> How user-space knows what is for what?
[Keyon] yes, this is an issue I was thinking about, we need discussing and conclusion
about that, including comments from user-space, here adding Tanu.
Here I am thinking about
1. do we really need kctls for buttons(SND_JACK_BTN_n)? if no, that is simplest, 
we can filter out them at this kctl creating stage.
2. if yes, then we need an name regenerator here, to generate understandable
(for user-space) kctl names for buttons.

> 
> Also, when you look through the grep above, you'll find that many calls
> already contain "Jack" name suffix.  So, they will result in a name like
> "Headset Jack Jack".
[Keyon] do you think we should implement intelligent name regenerator here
to remove this suffix? 

And furthermore, we can check more here, e.g. if the name ("Headset") is 
matched with the type(type should have SND_JACK_HEADSET bits), or 
something like that. 

> 
> One last bit:
> > +			/* set initial jack state */
> > +			snd_kctl_jack_report(card, kctl, state & testbit);
> 
> This can be dropped.  The value is zero as default, so unless the actual value
> is different, you don't need to call it.
[Keyon] OK.
> 
> Or, yet another last bit: remove EXPORT_SYMBOL_GPL() from ctljack.c, as
> they are merely internal functions.  But this can be done another patch later.
[Keyon] OK, I will add this to another patch.
> 
> 
> Takashi
Takashi Iwai March 21, 2015, 7:46 a.m. UTC | #3
At Sat, 21 Mar 2015 02:22:47 +0000,
Jie, Yang wrote:
> 
> > > +{
> > > +	struct snd_kcontrol *kctl;
> > > +	int len = strlen(name);
> > > +
> > > +	down_read(&card->controls_rwsem);
> > > +
> > > +	list_for_each_entry(kctl, &card->controls, list) {
> > > +		if (!strncmp(name, kctl->id.name, len) &&
> > > +		    !strcmp(" Jack", kctl->id.name + len) &&
> > > +		    kctl->id.index == index) {
> > 
> > This doesn't work when ctl elements are with multiple counts.  A single kctl
> > object may contain multiple snd_kcontrol_volatile entries.
> > That's why snd_ctl_find_id() checks like:
> > 
> > 	list_for_each_entry(kctl, &card->controls, list) {
> > 		....
> > 		if (kctl->id.index > id->index)
> > 			continue;
> > 		if (kctl->id.index + kctl->count <= id->index)
> > 			continue;
> > 
> > Also, more importantly, your code checks only the name string.  But the
> > same name string can be assigned for different iface, device and subdevice
> > entries.  You need to compare them as well.
> > 
> > That said, it'd be even simpler just to call snd_ctl_find_id() after creating a ctl
> > id instance instead of writing an own funciton.  It ends up with a bit more
> > stack usage, but it should be still acceptable.
> [Keyon] Here I did the getting index work by imitating function from
> had_jack.c:get_unique_index(), and I also noticed and concerned about
> not checking other snd_ctl_elem_id items.

In the case of hda_jack.c it works like that because it checks the own
list of ctls, not the global list.  The driver knows that there will
be no other "XXX Jack" controls.

> > 
> > Now, a bigger question:
> > 
> > > +			index = get_available_index(card,jack->id);
> > > +			kctl = snd_kctl_jack_new(jack->id, index, card);
> > 
> > So, here you are creating multiple kctl elements with the very same name
> > string.  Did you take a look at the actual usages of snd_jack_new(), especially
> > snd_soc_card_jack_new()?
> [Keyon] yes, I noticed this.  I can also find it at soc/intel/broadwell.c:
> 
>         ret = snd_soc_card_jack_new(rtd->card, "Headset",
>                 SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset,
>                 broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins));
> 
> it will create 2 kctl elements with the same name string "Headset":
> 
> numid=12,iface=CARD,name='Headset Jack'
> numid=13,iface=CARD,name='Headset Jack',index=1
> 
> > 
> > Try like
> > 
> > % git grep -A2 snd_soc_card_jack_new sound/soc/
> > 
> > sound/soc/fsl/imx-es8328.c:             ret = snd_soc_card_jack_new(rtd->card,
> > "Headphone",
> > sound/soc/fsl/imx-es8328.c-                                         SND_JACK_HEADPHONE |
> > SND_JACK_BTN_0,
> > sound/soc/fsl/imx-es8328.c-                                         &headset_jack, NULL, 0);
> > --
> > sound/soc/fsl/wm1133-ev1.c:     snd_soc_card_jack_new(rtd->card,
> > "Headphone", SND_JACK_HEADPHONE,
> > sound/soc/fsl/wm1133-ev1.c-                           &hp_jack, hp_jack_pins,
> > ARRAY_SIZE(hp_jack_pins));
> > sound/soc/fsl/wm1133-ev1.c-     wm8350_hp_jack_detect(codec,
> > WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE);
> > --
> > sound/soc/fsl/wm1133-ev1.c:     snd_soc_card_jack_new(rtd->card,
> > "Microphone",
> > sound/soc/fsl/wm1133-ev1.c-                           SND_JACK_MICROPHONE |
> > SND_JACK_BTN_0, &mic_jack,
> > sound/soc/fsl/wm1133-ev1.c-                           mic_jack_pins,
> > ARRAY_SIZE(mic_jack_pins));
> > .....
> > 
> > As you'll find through the whole output, many calls are combined with
> > SND_JACK_BTN_* bits.  And think again how this will result with your patch.
> > A call like
> > 
> > 	snd_soc_card_jack_new(card, "Headphone",
> > 		SND_JACK_HEADPHONE | SND_JACK_BTN_0 |
> > SND_JACK_BTN_1, ...)
> > 
> > would give three kctls, all "Headphone Jack" with indices 0, 1 and 2.
> > How user-space knows what is for what?
> [Keyon] yes, this is an issue I was thinking about, we need discussing and conclusion
> about that, including comments from user-space, here adding Tanu.
> Here I am thinking about
> 1. do we really need kctls for buttons(SND_JACK_BTN_n)? if no, that is simplest, 
> we can filter out them at this kctl creating stage.
> 2. if yes, then we need an name regenerator here, to generate understandable
> (for user-space) kctl names for buttons.

IMO, we should create kctls with proper unique names.

> > Also, when you look through the grep above, you'll find that many calls
> > already contain "Jack" name suffix.  So, they will result in a name like
> > "Headset Jack Jack".
> [Keyon] do you think we should implement intelligent name regenerator here
> to remove this suffix? 

Yes, "XXX Jack" is easy to check.

> And furthermore, we can check more here, e.g. if the name ("Headset") is 
> matched with the type(type should have SND_JACK_HEADSET bits), or 
> something like that. 

I don't think we need a naming police in the core code.


Takashi
Tanu Kaskinen March 23, 2015, 10:08 a.m. UTC | #4
On Sat, 2015-03-21 at 02:22 +0000, Jie, Yang wrote:
> > As you'll find through the whole output, many calls are combined with
> > SND_JACK_BTN_* bits.  And think again how this will result with your patch.
> > A call like
> > 
> > 	snd_soc_card_jack_new(card, "Headphone",
> > 		SND_JACK_HEADPHONE | SND_JACK_BTN_0 |
> > SND_JACK_BTN_1, ...)
> > 
> > would give three kctls, all "Headphone Jack" with indices 0, 1 and 2.
> > How user-space knows what is for what?
> [Keyon] yes, this is an issue I was thinking about, we need discussing and conclusion
> about that, including comments from user-space, here adding Tanu.
> Here I am thinking about
> 1. do we really need kctls for buttons(SND_JACK_BTN_n)? if no, that is simplest, 
> we can filter out them at this kctl creating stage.
> 2. if yes, then we need an name regenerator here, to generate understandable
> (for user-space) kctl names for buttons.

From PulseAudio point of view, you must make sure that "Headphone Jack"
with index 0 doesn't refer to a button, because the assumption is that
"Headphone Jack" at index 0 refers to a jack and not a button (indexes
other than 0 are currently ignored). Other than that, PulseAudio doesn't
currently care, because there's no support for buttons.

When thinking about how to implement button support in PulseAudio, the
first question is that are kcontrols a realiable interface for getting
button events? What happens when the button is pressed? Does one press
cause only one on->off or off->on state transition, or is the state "on"
only for the duration of the press? If the former, then kcontrols should
be fine, but if the latter, is it guaranteed that the application sees
the event? If the button press is a short one, causing a quick
off->on->off transition, is it possible that the application gets a
notification of a mixer change, but when the application then inspects
the mixer state, the button state is already "off", so the application
doesn't get enough information about what happened?

If buttons are exposed as kcontrols, I suppose "Headphone Button N"
would be a fine name.
Takashi Iwai March 23, 2015, 11:57 a.m. UTC | #5
At Mon, 23 Mar 2015 12:08:17 +0200,
Tanu Kaskinen wrote:
> 
> On Sat, 2015-03-21 at 02:22 +0000, Jie, Yang wrote:
> > > As you'll find through the whole output, many calls are combined with
> > > SND_JACK_BTN_* bits.  And think again how this will result with your patch.
> > > A call like
> > > 
> > > 	snd_soc_card_jack_new(card, "Headphone",
> > > 		SND_JACK_HEADPHONE | SND_JACK_BTN_0 |
> > > SND_JACK_BTN_1, ...)
> > > 
> > > would give three kctls, all "Headphone Jack" with indices 0, 1 and 2.
> > > How user-space knows what is for what?
> > [Keyon] yes, this is an issue I was thinking about, we need discussing and conclusion
> > about that, including comments from user-space, here adding Tanu.
> > Here I am thinking about
> > 1. do we really need kctls for buttons(SND_JACK_BTN_n)? if no, that is simplest, 
> > we can filter out them at this kctl creating stage.
> > 2. if yes, then we need an name regenerator here, to generate understandable
> > (for user-space) kctl names for buttons.
> 
> >From PulseAudio point of view, you must make sure that "Headphone Jack"
> with index 0 doesn't refer to a button, because the assumption is that
> "Headphone Jack" at index 0 refers to a jack and not a button (indexes
> other than 0 are currently ignored). Other than that, PulseAudio doesn't
> currently care, because there's no support for buttons.
> 
> When thinking about how to implement button support in PulseAudio, the
> first question is that are kcontrols a realiable interface for getting
> button events? What happens when the button is pressed? Does one press
> cause only one on->off or off->on state transition, or is the state "on"
> only for the duration of the press? If the former, then kcontrols should
> be fine, but if the latter, is it guaranteed that the application sees
> the event? If the button press is a short one, causing a quick
> off->on->off transition, is it possible that the application gets a
> notification of a mixer change, but when the application then inspects
> the mixer state, the button state is already "off", so the application
> doesn't get enough information about what happened?

It's a good point.  The notification via kctl can't follow the
complete state changes since it's nothing but a notification telling
"something changed".  So, events like buttons that need the complete
state change history, the current implementation isn't appropriate.
For things like jacks, we usually care only about the final state, and
the state change is much less frequent, so kctl notification works
well.


Takashi
Mark Brown March 23, 2015, 2:57 p.m. UTC | #6
On Mon, Mar 23, 2015 at 12:57:02PM +0100, Takashi Iwai wrote:

> It's a good point.  The notification via kctl can't follow the
> complete state changes since it's nothing but a notification telling
> "something changed".  So, events like buttons that need the complete
> state change history, the current implementation isn't appropriate.
> For things like jacks, we usually care only about the final state, and
> the state change is much less frequent, so kctl notification works
> well.

I'd expect the buttons to be going through only as input devices, not as
kcontrols at all.
Jie, Yang March 25, 2015, 4:11 a.m. UTC | #7
> -----Original Message-----
> From: Takashi Iwai [mailto:tiwai@suse.de]
> Sent: Monday, March 23, 2015 7:57 PM
> To: Tanu Kaskinen
> Cc: Jie, Yang; broonie@kernel.org; alsa-devel@alsa-project.org; Girdwood,
> Liam R; Liam Girdwood
> Subject: Re: [PATCH v3 1/2] ALSA: jack: create jack kcontrols for every jack
> input device
> 
> At Mon, 23 Mar 2015 12:08:17 +0200,
> Tanu Kaskinen wrote:
> >
> > On Sat, 2015-03-21 at 02:22 +0000, Jie, Yang wrote:
> > > > As you'll find through the whole output, many calls are combined
> > > > with
> > > > SND_JACK_BTN_* bits.  And think again how this will result with your
> patch.
> > > > A call like
> > > >
> > > > 	snd_soc_card_jack_new(card, "Headphone",
> > > > 		SND_JACK_HEADPHONE | SND_JACK_BTN_0 |
> SND_JACK_BTN_1, ...)
> > > >
> > > > would give three kctls, all "Headphone Jack" with indices 0, 1 and 2.
> > > > How user-space knows what is for what?
> > > [Keyon] yes, this is an issue I was thinking about, we need
> > > discussing and conclusion about that, including comments from user-
> space, here adding Tanu.
> > > Here I am thinking about
> > > 1. do we really need kctls for buttons(SND_JACK_BTN_n)? if no, that
> > > is simplest, we can filter out them at this kctl creating stage.
> > > 2. if yes, then we need an name regenerator here, to generate
> > > understandable (for user-space) kctl names for buttons.
> >
> > >From PulseAudio point of view, you must make sure that "Headphone
> Jack"
> > with index 0 doesn't refer to a button, because the assumption is that
> > "Headphone Jack" at index 0 refers to a jack and not a button (indexes
> > other than 0 are currently ignored). Other than that, PulseAudio
> > doesn't currently care, because there's no support for buttons.
> >
> > When thinking about how to implement button support in PulseAudio, the
> > first question is that are kcontrols a realiable interface for getting
> > button events? What happens when the button is pressed? Does one
> press
> > cause only one on->off or off->on state transition, or is the state "on"
> > only for the duration of the press? If the former, then kcontrols
> > should be fine, but if the latter, is it guaranteed that the
> > application sees the event? If the button press is a short one,
> > causing a quick
> > off->on->off transition, is it possible that the application gets a
> > notification of a mixer change, but when the application then inspects
> > the mixer state, the button state is already "off", so the application
> > doesn't get enough information about what happened?
> 
> It's a good point.  The notification via kctl can't follow the complete state
> changes since it's nothing but a notification telling "something changed".  So,
> events like buttons that need the complete state change history, the current
> implementation isn't appropriate.
> For things like jacks, we usually care only about the final state, and the state
> change is much less frequent, so kctl notification works well.
[Keyon] Agree that we don't need create kctl for button(just leave it to input
event notification/handler), then I can update patch to filter out them at kctl
creating stage, which may make life easier IMO. :)

BTW, Takashi, according to what Tanu mentioned, kctls indexes other than 0 
are currently ignored by PA, so creating and exporting kctls with non-zero 
indices for the same name in HD-Audio Jack before, seems no use for upper
layer?

> 
> 
> Takashi
Raymond Yau March 25, 2015, 5:27 a.m. UTC | #8
> BTW, Takashi, according to what Tanu mentioned, kctls indexes other than 0
> are currently ignored by PA, so creating and exporting kctls with non-zero
> indices for the same name in HD-Audio Jack before, seems no use for upper
> layer?

Does this mean that pulseaudio don't support notebook with dual headphone
jacks since you need to use  jack states of both headphone jacks to
determine the speaker is muted or not even you assign different name to the
headphone jacks

For notebook with dock station ,  headphone jack and dock headphone jack
usually share volume
Takashi Iwai March 25, 2015, 6:13 a.m. UTC | #9
At Wed, 25 Mar 2015 04:11:28 +0000,
Jie, Yang wrote:
> 
> > -----Original Message-----
> > From: Takashi Iwai [mailto:tiwai@suse.de]
> > Sent: Monday, March 23, 2015 7:57 PM
> > To: Tanu Kaskinen
> > Cc: Jie, Yang; broonie@kernel.org; alsa-devel@alsa-project.org; Girdwood,
> > Liam R; Liam Girdwood
> > Subject: Re: [PATCH v3 1/2] ALSA: jack: create jack kcontrols for every jack
> > input device
> > 
> > At Mon, 23 Mar 2015 12:08:17 +0200,
> > Tanu Kaskinen wrote:
> > >
> > > On Sat, 2015-03-21 at 02:22 +0000, Jie, Yang wrote:
> > > > > As you'll find through the whole output, many calls are combined
> > > > > with
> > > > > SND_JACK_BTN_* bits.  And think again how this will result with your
> > patch.
> > > > > A call like
> > > > >
> > > > > 	snd_soc_card_jack_new(card, "Headphone",
> > > > > 		SND_JACK_HEADPHONE | SND_JACK_BTN_0 |
> > SND_JACK_BTN_1, ...)
> > > > >
> > > > > would give three kctls, all "Headphone Jack" with indices 0, 1 and 2.
> > > > > How user-space knows what is for what?
> > > > [Keyon] yes, this is an issue I was thinking about, we need
> > > > discussing and conclusion about that, including comments from user-
> > space, here adding Tanu.
> > > > Here I am thinking about
> > > > 1. do we really need kctls for buttons(SND_JACK_BTN_n)? if no, that
> > > > is simplest, we can filter out them at this kctl creating stage.
> > > > 2. if yes, then we need an name regenerator here, to generate
> > > > understandable (for user-space) kctl names for buttons.
> > >
> > > >From PulseAudio point of view, you must make sure that "Headphone
> > Jack"
> > > with index 0 doesn't refer to a button, because the assumption is that
> > > "Headphone Jack" at index 0 refers to a jack and not a button (indexes
> > > other than 0 are currently ignored). Other than that, PulseAudio
> > > doesn't currently care, because there's no support for buttons.
> > >
> > > When thinking about how to implement button support in PulseAudio, the
> > > first question is that are kcontrols a realiable interface for getting
> > > button events? What happens when the button is pressed? Does one
> > press
> > > cause only one on->off or off->on state transition, or is the state "on"
> > > only for the duration of the press? If the former, then kcontrols
> > > should be fine, but if the latter, is it guaranteed that the
> > > application sees the event? If the button press is a short one,
> > > causing a quick
> > > off->on->off transition, is it possible that the application gets a
> > > notification of a mixer change, but when the application then inspects
> > > the mixer state, the button state is already "off", so the application
> > > doesn't get enough information about what happened?
> > 
> > It's a good point.  The notification via kctl can't follow the complete state
> > changes since it's nothing but a notification telling "something changed".  So,
> > events like buttons that need the complete state change history, the current
> > implementation isn't appropriate.
> > For things like jacks, we usually care only about the final state, and the state
> > change is much less frequent, so kctl notification works well.
> [Keyon] Agree that we don't need create kctl for button(just leave it to input
> event notification/handler), then I can update patch to filter out them at kctl
> creating stage, which may make life easier IMO. :)
> 
> BTW, Takashi, according to what Tanu mentioned, kctls indexes other than 0 
> are currently ignored by PA, so creating and exporting kctls with non-zero 
> indices for the same name in HD-Audio Jack before, seems no use for upper
> layer?

No "big" use.  Who knows.

But I don't mind to convert the usage of index number in hda_jack.c to
a unique string instead so that the name can be used for both input
and kctl jacks.


Takashi
diff mbox

Patch

diff --git a/include/sound/jack.h b/include/sound/jack.h
index 2182350..cd5652d 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -73,6 +73,8 @@  enum snd_jack_types {
 
 struct snd_jack {
 	struct input_dev *input_dev;
+	struct list_head kctl_list;
+	struct snd_card *card;
 	int registered;
 	int type;
 	const char *id;
@@ -82,6 +84,12 @@  struct snd_jack {
 	void (*private_free)(struct snd_jack *);
 };
 
+struct snd_jack_kctl {
+	struct snd_kcontrol *kctl;
+	struct list_head jack_list;		/* list of controls belong to the same jack*/
+	unsigned int mask_bit;	/* the corresponding bit of the jack type */
+};
+
 #ifdef CONFIG_SND_JACK
 
 int snd_jack_new(struct snd_card *card, const char *id, int type,
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 8658578..82c8316 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -24,6 +24,7 @@ 
 #include <linux/module.h>
 #include <sound/jack.h>
 #include <sound/core.h>
+#include <sound/control.h>
 
 static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
 	SW_HEADPHONE_INSERT,
@@ -54,7 +55,13 @@  static int snd_jack_dev_disconnect(struct snd_device *device)
 static int snd_jack_dev_free(struct snd_device *device)
 {
 	struct snd_jack *jack = device->device_data;
+	struct snd_card *card = device->card;
+	struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
 
+	list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, jack_list) {
+		list_del_init(&jack_kctl->jack_list);
+		snd_ctl_remove(card, jack_kctl->kctl);
+	}
 	if (jack->private_free)
 		jack->private_free(jack);
 
@@ -100,6 +107,98 @@  static int snd_jack_dev_register(struct snd_device *device)
 	return err;
 }
 
+static int jack_ctl_name_found(struct snd_card *card, const char *name, int index)
+{
+	struct snd_kcontrol *kctl;
+	int len = strlen(name);
+
+	down_read(&card->controls_rwsem);
+
+	list_for_each_entry(kctl, &card->controls, list) {
+		if (!strncmp(name, kctl->id.name, len) &&
+		    !strcmp(" Jack", kctl->id.name + len) &&
+		    kctl->id.index == index) {
+			up_read(&card->controls_rwsem);
+			return 0;
+		}
+	}
+
+	up_read(&card->controls_rwsem);
+	return 1;
+}
+
+/* get the first unused/available index number for the given kctl name */
+static int get_available_index(struct snd_card *card, const char *name)
+{
+	int idx = 0;
+
+	while (!jack_ctl_name_found(card, name, idx))
+		idx++;
+
+	return idx;
+}
+
+static void kctl_private_free(struct snd_kcontrol *kctl)
+{
+	struct snd_jack_kctl *jack_kctl = kctl->private_data;
+
+	list_del(&jack_kctl->jack_list);
+	kfree(jack_kctl);
+}
+
+static int snd_jack_new_kctl(struct snd_card *card, struct snd_jack *jack, int type)
+{
+	struct snd_kcontrol *kctl;
+	struct snd_jack_kctl *jack_kctl;
+	int i, err, index, state = 0 /* use 0 for default state ?? */;
+
+	INIT_LIST_HEAD(&jack->kctl_list);
+	for (i = 0; i < fls(SND_JACK_BTN_0); i++) {
+		int testbit = 1 << i;
+
+		/* we only new headphone kctl for headset jack */
+		if ((testbit == SND_JACK_MICROPHONE) &&
+				((type & SND_JACK_HEADSET) == SND_JACK_HEADSET))
+			continue;
+
+		/* we only new lineout kctl for avout jack */
+		if ((testbit == SND_JACK_VIDEOOUT) &&
+				((type & SND_JACK_AVOUT) == SND_JACK_AVOUT))
+			continue;
+
+		if (type & testbit) {
+			index = get_available_index(card,jack->id);
+			kctl = snd_kctl_jack_new(jack->id, index, card);
+			if (!kctl)
+				return -ENOMEM;
+
+			err = snd_ctl_add(card, kctl);
+			if (err < 0)
+				return err;
+
+			jack_kctl = kzalloc(sizeof(*jack_kctl), GFP_KERNEL);
+
+			if (!jack_kctl)
+				return -ENOMEM;
+
+			jack_kctl->kctl = kctl;
+
+			kctl->private_data = jack_kctl;
+			kctl->private_free = kctl_private_free;
+
+			/* use jack_bit_idx for the kctl type bit */
+			jack_kctl->mask_bit = testbit;
+
+			list_add_tail(&jack_kctl->jack_list, &jack->kctl_list);
+
+			/* set initial jack state */
+			snd_kctl_jack_report(card, kctl, state & testbit);
+		}
+	}
+
+	return 0;
+}
+
 /**
  * snd_jack_new - Create a new jack
  * @card:  the card instance
@@ -138,7 +237,7 @@  int snd_jack_new(struct snd_card *card, const char *id, int type,
 	}
 
 	jack->input_dev->phys = "ALSA";
-
+	jack->card = card;
 	jack->type = type;
 
 	for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
@@ -150,10 +249,17 @@  int snd_jack_new(struct snd_card *card, const char *id, int type,
 	if (err < 0)
 		goto fail_input;
 
+	/* register jack kcontrols  */
+	err = snd_jack_new_kctl(card, jack, type);
+	if (err < 0)
+		goto fail_kctl;
+
 	*jjack = jack;
 
 	return 0;
 
+fail_kctl:
+	snd_device_free(card, jack);
 fail_input:
 	input_free_device(jack->input_dev);
 	kfree(jack->id);
@@ -230,6 +336,7 @@  EXPORT_SYMBOL(snd_jack_set_key);
  */
 void snd_jack_report(struct snd_jack *jack, int status)
 {
+	struct snd_jack_kctl *jack_kctl;
 	int i;
 
 	if (!jack)
@@ -245,13 +352,20 @@  void snd_jack_report(struct snd_jack *jack, int status)
 
 	for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
 		int testbit = 1 << i;
-		if (jack->type & testbit)
+		if (jack->type & testbit) {
 			input_report_switch(jack->input_dev,
 					    jack_switch_types[i],
 					    status & testbit);
+		}
 	}
 
 	input_sync(jack->input_dev);
+
+	list_for_each_entry(jack_kctl, &jack->kctl_list, jack_list) {
+		snd_kctl_jack_report(jack->card, jack_kctl->kctl,
+					status & jack_kctl->mask_bit);
+	}
+
 }
 EXPORT_SYMBOL(snd_jack_report);