alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [RFC][PATCH v3 0/4] design a way to change audio Jack state by software
@ 2020-12-28  7:59 Hui Wang
  2020-12-28  8:00 ` [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs Hui Wang
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Hui Wang @ 2020-12-28  7:59 UTC (permalink / raw)
  To: alsa-devel, tiwai, perex, kai.vehmanen

the changes in the v3 (for easy to review, divide change into 4 patches):
 - address the comment about the snd_jack_report() and _snd_jack_report(),
   the v2 design is hard to understand and is hard to review, in the v3,
   separate the jack_report to snd_jack_report() and snd_jack_inject_report(),
   hw jack events call snd_jack_report() as before, if a jack contains multi
   jack_kctl and the jack_kctl's sw_inject is enabled, the status and the
   related input-dev's events will not be reproted. The injection events call
   snd_jack_inject_report(). This change is squashed to 0001-xxx.patch,  the
   rest part of 0001-xxx.patch is same as v2.

 - address the comment about folders'name in the 0002-xxx.patch, so far, drop
   the '/', ',', '=' and ' ' from the folders' name.

 - address the comment about adding more debugfs nodes in the 0003-xxx.patch,
   it adds kctl_id, mask_bits, status and type.

 - address the comment about save-n-restore jack's hw status in the
   0004-xxx.patch, adding a hw_status_cache and save the last reported jack
   hw event, once the sw_inject is disabled, will restore all jack_kctl's
   state under the same snd_jack with hw_status_cache.

Now the layout of folders looks like below:
/sys/kernel/debug# tree sound-core/
sound-core/
└── card0
    ├── HDMIDPpcm3Jack
    │   ├── jackin_inject
    │   ├── kctl_id
    │   ├── mask_bits
    │   ├── status
    │   ├── sw_inject_enable
    │   └── type
    ├── HDMIDPpcm4Jack
    │   ├── jackin_inject
    │   ├── kctl_id
    │   ├── mask_bits
    │   ├── status
    │   ├── sw_inject_enable
    │   └── type
    ├── HDMIDPpcm5Jack
    │   ├── jackin_inject
    │   ├── kctl_id
    │   ├── mask_bits
    │   ├── status
    │   ├── sw_inject_enable
    │   └── type
    ├── HeadphoneJack
    │   ├── jackin_inject
    │   ├── kctl_id
    │   ├── mask_bits
    │   ├── status
    │   ├── sw_inject_enable
    │   └── type
    └── MicJack
        ├── jackin_inject
        ├── kctl_id
        ├── mask_bits
        ├── status
        ├── sw_inject_enable
        └── type

/sys/kernel/debug# cat sound-core/card0/HeadphoneJack/kctl_id 
Headphone Jack
/sys/kernel/debug# cat sound-core/card0/HeadphoneJack/mask_bits 
0x0001 HEADPHONE(0x0001)
/sys/kernel/debug# cat sound-core/card0/HeadphoneJack/status 
0x0000
/sys/kernel/debug# cat sound-core/card0/HeadphoneJack/sw_inject_enable 
Jack: Headphone Jack		Inject Enabled: 0
/sys/kernel/debug# cat sound-core/card0/HeadphoneJack/type 
0x0001 HEADPHONE(0x0001)








the changes in the V2:
 - using debugfs instead of sysfs
 - using jack_ctrl to create a folder instead of snd_jack, since ASoC drivers
   could create multi jack_ctrls within a snd_jack
 - create a folder for each jack_ctrl instead for all jack_ctrls

[ snip ]

Hui Wang (4):
  alsa: jack: implement software jack injection via debugfs
  alsa: jack: adjust jack_kctl debugfs folder's name
  alsa: jack: add more jack_kctl debugfs nodes
  alsa: jack: implement save-and-restore for jack's hw status

 include/sound/core.h |   2 +
 include/sound/jack.h |   1 +
 sound/core/init.c    |   7 +
 sound/core/jack.c    | 351 ++++++++++++++++++++++++++++++++++++++++++-
 sound/core/sound.c   |   8 +
 5 files changed, 365 insertions(+), 4 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs
  2020-12-28  7:59 [RFC][PATCH v3 0/4] design a way to change audio Jack state by software Hui Wang
@ 2020-12-28  8:00 ` Hui Wang
  2021-01-04 15:04   ` Jaroslav Kysela
  2021-01-04 15:20   ` Jaroslav Kysela
  2020-12-28  8:00 ` [RFC][PATCH v3 2/4] alsa: jack: adjust jack_kctl debugfs folder's name Hui Wang
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 9+ messages in thread
From: Hui Wang @ 2020-12-28  8:00 UTC (permalink / raw)
  To: alsa-devel, tiwai, perex, kai.vehmanen

We want to perform remote audio auto test, need the audio jack to
change from plugout to plugin or vice versa by software ways.

Here the design is creating a sound-core root folder in the debugfs
dir, and each sound card will create a folder cardN under sound-core,
then the sound jack will create folders by jack_ctrl->ctrl->id.name,
and will create 2 file nodes jackin_inject and sw_inject_enable in
the folder, this is the layout of folder on a machine with 2 sound
cards:
$tree $debugfs_mount_dir/sound-core
sound-core/
├── card0
│   ├── HDMI!DP,pcm=10 Jack
│   │   ├── jackin_inject
│   │   └── sw_inject_enable
│   ├── HDMI!DP,pcm=11 Jack
│   │   ├── jackin_inject
│   │   └── sw_inject_enable
│   ├── HDMI!DP,pcm=3 Jack
│   │   ├── jackin_inject
│   │   └── sw_inject_enable
│   ├── HDMI!DP,pcm=7 Jack
│   │   ├── jackin_inject
│   │   └── sw_inject_enable
│   ├── HDMI!DP,pcm=8 Jack
│   │   ├── jackin_inject
│   │   └── sw_inject_enable
│   └── HDMI!DP,pcm=9 Jack
│       ├── jackin_inject
│       └── sw_inject_enable
└── card1
    ├── HDMI!DP,pcm=3 Jack
    │   ├── jackin_inject
    │   └── sw_inject_enable
    ├── HDMI!DP,pcm=4 Jack
    │   ├── jackin_inject
    │   └── sw_inject_enable
    ├── HDMI!DP,pcm=5 Jack
    │   ├── jackin_inject
    │   └── sw_inject_enable
    ├── Headphone Jack
    │   ├── jackin_inject
    │   └── sw_inject_enable
    ├── Headset Jack
    │   ├── jackin_inject
    │   └── sw_inject_enable
    └── Mic Jack
        ├── jackin_inject
        └── sw_inject_enable

Suppose users want to enable jack injection for Headphone, they need
to run $sudo sh -c 'echo 1 > 'Headphone Jack'/sw_inject_enable', then
users could change the Headphone Jack state through jackin_inject and
this Jack's state will not be changed by non-injection ways anymore
until users echo 0 to sw_inject_enable.

Users could run $sudo sh -c 'echo 1 > 'Headphone Jack'/jackin_inject'
to trigger the Headphone jack to plugin or echo 0 to trigger it to
plugout.

If users finish their test, they could run
$sudo sh -c 'echo 0 > 'Headphone Jack'/sw_inject_enable' to disable
injection and let non-injection ways control this Jack.

For the jack event report, the hw jack event will call
snd_jack_report(), it will avoid to report the events if a jack_kctl's
sw_inject is enabled, also the related input_dev's events will not be
reproted.

If users inject a event, it will call snd_jack_inject_report(), it
only reports the events for this jack_kctl and reports the related
input_dev's events.

Signed-off-by: Hui Wang <hui.wang@canonical.com>
---
 include/sound/core.h |   2 +
 sound/core/init.c    |   7 ++
 sound/core/jack.c    | 181 ++++++++++++++++++++++++++++++++++++++++++-
 sound/core/sound.c   |   8 ++
 4 files changed, 194 insertions(+), 4 deletions(-)

diff --git a/include/sound/core.h b/include/sound/core.h
index 0462c577d7a3..11d61e4248ee 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -122,6 +122,7 @@ struct snd_card {
 
 	size_t total_pcm_alloc_bytes;	/* total amount of allocated buffers */
 	struct mutex memory_mutex;	/* protection for the above */
+	struct dentry *debugfs_root;    /* debugfs root for card */
 
 #ifdef CONFIG_PM
 	unsigned int power_state;	/* power state */
@@ -180,6 +181,7 @@ static inline struct device *snd_card_get_device_link(struct snd_card *card)
 extern int snd_major;
 extern int snd_ecards_limit;
 extern struct class *sound_class;
+extern struct dentry *sound_core_debugfs_root;
 
 void snd_request_card(int card);
 
diff --git a/sound/core/init.c b/sound/core/init.c
index 764dbe673d48..a9ceaf788019 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -13,6 +13,7 @@
 #include <linux/time.h>
 #include <linux/ctype.h>
 #include <linux/pm.h>
+#include <linux/debugfs.h>
 #include <linux/completion.h>
 
 #include <sound/core.h>
@@ -163,6 +164,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
 {
 	struct snd_card *card;
 	int err;
+	char name[8];
 
 	if (snd_BUG_ON(!card_ret))
 		return -EINVAL;
@@ -246,6 +248,10 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
 		dev_err(parent, "unable to create card info\n");
 		goto __error_ctl;
 	}
+
+	sprintf(name, "card%d", idx);
+	card->debugfs_root = debugfs_create_dir(name, sound_core_debugfs_root);
+
 	*card_ret = card;
 	return 0;
 
@@ -418,6 +424,7 @@ int snd_card_disconnect(struct snd_card *card)
 	/* notify all devices that we are disconnected */
 	snd_device_disconnect_all(card);
 
+	debugfs_remove(card->debugfs_root);
 	snd_info_card_disconnect(card);
 	if (card->registered) {
 		device_del(&card->card_dev);
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 503c8af79d55..0f232a806c3a 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -8,6 +8,8 @@
 #include <linux/input.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
 #include <sound/jack.h>
 #include <sound/core.h>
 #include <sound/control.h>
@@ -16,6 +18,9 @@ struct snd_jack_kctl {
 	struct snd_kcontrol *kctl;
 	struct list_head list;  /* list of controls belong to the same jack */
 	unsigned int mask_bits; /* only masked status bits are reported via kctl */
+	struct snd_jack *jack;  /* pointer to struct snd_jack */
+	bool sw_inject_enable;  /* allow to inject plug event via debugfs */
+	struct dentry *jack_debugfs_root; /* jack_kctl debugfs root */
 };
 
 #ifdef CONFIG_SND_JACK_INPUT_DEV
@@ -109,12 +114,172 @@ static int snd_jack_dev_register(struct snd_device *device)
 }
 #endif /* CONFIG_SND_JACK_INPUT_DEV */
 
+#ifdef CONFIG_DEBUG_FS
+static void snd_jack_inject_report(struct snd_jack_kctl *jack_kctl, int status)
+{
+	struct snd_jack *jack;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+	int i;
+#endif
+	if (!jack_kctl)
+		return;
+
+	jack = jack_kctl->jack;
+
+	if (jack_kctl->sw_inject_enable)
+		snd_kctl_jack_report(jack->card, jack_kctl->kctl,
+				     status & jack_kctl->mask_bits);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+	if (!jack->input_dev)
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
+		int testbit = ((SND_JACK_BTN_0 >> i) & jack_kctl->mask_bits);
+
+		if (jack->type & testbit)
+			input_report_key(jack->input_dev, jack->key[i],
+					 status & testbit);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
+		int testbit = ((1 << i) & jack_kctl->mask_bits);
+
+		if (jack->type & testbit)
+			input_report_switch(jack->input_dev,
+					    jack_switch_types[i],
+					    status & testbit);
+	}
+
+	input_sync(jack->input_dev);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
+}
+
+static ssize_t sw_inject_enable_read(struct file *file,
+				     char __user *to, size_t count, loff_t *ppos)
+{
+	struct snd_jack_kctl *jack_kctl = file->private_data;
+	char *buf;
+	int len, ret;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len = scnprintf(buf, PAGE_SIZE, "%s: %s\t\t%s: %i\n", "Jack", jack_kctl->kctl->id.name,
+			"Inject Enabled", jack_kctl->sw_inject_enable);
+	ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t sw_inject_enable_write(struct file *file,
+				      const char __user *from, size_t count, loff_t *ppos)
+{
+	struct snd_jack_kctl *jack_kctl = file->private_data;
+	char *buf;
+	int ret, err;
+	unsigned long enable;
+
+	buf = kzalloc(count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = simple_write_to_buffer(buf, count, ppos, from, count);
+	err = kstrtoul(buf, 0, &enable);
+	if (err) {
+		ret = err;
+		goto exit;
+	}
+
+	jack_kctl->sw_inject_enable = !!enable;
+
+ exit:
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t jackin_inject_write(struct file *file,
+				   const char __user *from, size_t count, loff_t *ppos)
+{
+	struct snd_jack_kctl *jack_kctl = file->private_data;
+	char *buf;
+	int ret, err;
+	unsigned long enable;
+
+	if (!jack_kctl->sw_inject_enable)
+		return -EINVAL;
+
+	buf = kzalloc(count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = simple_write_to_buffer(buf, count, ppos, from, count);
+	err = kstrtoul(buf, 0, &enable);
+	if (err) {
+		ret = err;
+		goto exit;
+	}
+
+	snd_jack_inject_report(jack_kctl, !!enable ? jack_kctl->mask_bits : 0);
+
+ exit:
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations sw_inject_enable_fops = {
+	.open = simple_open,
+	.read = sw_inject_enable_read,
+	.write = sw_inject_enable_write,
+	.llseek = default_llseek,
+};
+
+static const struct file_operations jackin_inject_fops = {
+	.open = simple_open,
+	.write = jackin_inject_write,
+	.llseek = default_llseek,
+};
+
+static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
+					    struct snd_jack_kctl *jack_kctl)
+{
+	char *tname;
+
+	/* the folder's name can't contains '/', need to replace it with '!'
+	 * as lib/kobject.c does
+	 */
+	tname = kstrdup(jack_kctl->kctl->id.name, GFP_KERNEL);
+	if (!tname)
+		return -ENOMEM;
+	strreplace(tname, '/', '!');
+	jack_kctl->jack_debugfs_root = debugfs_create_dir(tname, jack->card->debugfs_root);
+	kfree(tname);
+
+	debugfs_create_file("sw_inject_enable", 0644, jack_kctl->jack_debugfs_root, jack_kctl,
+			    &sw_inject_enable_fops);
+
+	debugfs_create_file("jackin_inject", 0200, jack_kctl->jack_debugfs_root, jack_kctl,
+			    &jackin_inject_fops);
+
+	return 0;
+}
+#else /* CONFIG_DEBUG_FS */
+static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
+					    struct snd_jack_kctl *jack_kctl)
+{
+	return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
 static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
 {
 	struct snd_jack_kctl *jack_kctl;
 
 	jack_kctl = kctl->private_data;
 	if (jack_kctl) {
+		debugfs_remove(jack_kctl->jack_debugfs_root);
 		list_del(&jack_kctl->list);
 		kfree(jack_kctl);
 	}
@@ -122,7 +287,10 @@ static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
 
 static void snd_jack_kctl_add(struct snd_jack *jack, struct snd_jack_kctl *jack_kctl)
 {
+	jack_kctl->jack = jack;
 	list_add_tail(&jack_kctl->list, &jack->kctl_list);
+	if (!strstr(jack_kctl->kctl->id.name, "Phantom"))
+		snd_jack_debugfs_add_inject_node(jack, jack_kctl);
 }
 
 static struct snd_jack_kctl * snd_jack_kctl_new(struct snd_card *card, const char *name, unsigned int mask)
@@ -340,6 +508,7 @@ EXPORT_SYMBOL(snd_jack_set_key);
 void snd_jack_report(struct snd_jack *jack, int status)
 {
 	struct snd_jack_kctl *jack_kctl;
+	unsigned int mask_bits = 0;
 #ifdef CONFIG_SND_JACK_INPUT_DEV
 	int i;
 #endif
@@ -348,15 +517,18 @@ void snd_jack_report(struct snd_jack *jack, int status)
 		return;
 
 	list_for_each_entry(jack_kctl, &jack->kctl_list, list)
-		snd_kctl_jack_report(jack->card, jack_kctl->kctl,
-					    status & jack_kctl->mask_bits);
+		if (jack_kctl->sw_inject_enable)
+			mask_bits |= jack_kctl->mask_bits;
+		else
+			snd_kctl_jack_report(jack->card, jack_kctl->kctl,
+					     status & jack_kctl->mask_bits);
 
 #ifdef CONFIG_SND_JACK_INPUT_DEV
 	if (!jack->input_dev)
 		return;
 
 	for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
-		int testbit = SND_JACK_BTN_0 >> i;
+		int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);
 
 		if (jack->type & testbit)
 			input_report_key(jack->input_dev, jack->key[i],
@@ -364,7 +536,8 @@ void snd_jack_report(struct snd_jack *jack, int status)
 	}
 
 	for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
-		int testbit = 1 << i;
+		int testbit = ((1 << i) & ~mask_bits);
+
 		if (jack->type & testbit)
 			input_report_switch(jack->input_dev,
 					    jack_switch_types[i],
diff --git a/sound/core/sound.c b/sound/core/sound.c
index b75f78f2c4b8..3039e317df93 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -9,6 +9,7 @@
 #include <linux/time.h>
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/debugfs.h>
 #include <sound/core.h>
 #include <sound/minors.h>
 #include <sound/info.h>
@@ -39,6 +40,9 @@ MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);
 int snd_ecards_limit;
 EXPORT_SYMBOL(snd_ecards_limit);
 
+struct dentry *sound_core_debugfs_root;
+EXPORT_SYMBOL_GPL(sound_core_debugfs_root);
+
 static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
 static DEFINE_MUTEX(sound_mutex);
 
@@ -395,6 +399,9 @@ static int __init alsa_sound_init(void)
 		unregister_chrdev(major, "alsa");
 		return -ENOMEM;
 	}
+
+	sound_core_debugfs_root = debugfs_create_dir("sound-core", NULL);
+
 #ifndef MODULE
 	pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
 #endif
@@ -403,6 +410,7 @@ static int __init alsa_sound_init(void)
 
 static void __exit alsa_sound_exit(void)
 {
+	debugfs_remove(sound_core_debugfs_root);
 	snd_info_done();
 	unregister_chrdev(major, "alsa");
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC][PATCH v3 2/4] alsa: jack: adjust jack_kctl debugfs folder's name
  2020-12-28  7:59 [RFC][PATCH v3 0/4] design a way to change audio Jack state by software Hui Wang
  2020-12-28  8:00 ` [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs Hui Wang
@ 2020-12-28  8:00 ` Hui Wang
  2020-12-28  8:00 ` [RFC][PATCH v3 3/4] alsa: jack: add more jack_kctl debugfs nodes Hui Wang
  2020-12-28  8:00 ` [RFC][PATCH v3 4/4] alsa: jack: implement save-and-restore for jack's hw status Hui Wang
  3 siblings, 0 replies; 9+ messages in thread
From: Hui Wang @ 2020-12-28  8:00 UTC (permalink / raw)
  To: alsa-devel, tiwai, perex, kai.vehmanen

We used jack_kctl->kctl.id as the folder's name, but there are some
characters which are not suitable for foler's name, for example, a
HDMI/DP audio jack id contains '/', ',', '=' and ' ', this patch will
remove them from folder's name.

Before applying patch, the folders look like:
'HDMI!DP,pcm=3 Jack'  'Headphone Jack'  'Mic Jack'

After applying the patch, the folders look like:
HDMIDPpcm3Jack  HeadphoneJack  MicJack

Signed-off-by: Hui Wang <hui.wang@canonical.com>
---
 sound/core/jack.c | 26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)

diff --git a/sound/core/jack.c b/sound/core/jack.c
index 0f232a806c3a..62e9215fa0f0 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -242,18 +242,36 @@ static const struct file_operations jackin_inject_fops = {
 	.llseek = default_llseek,
 };
 
+/* The substrings in the jack's name but not suitable for folder's name */
+static const char * const dropped_chars[] = {
+	"/", "=", ",", " ",
+};
+
+static char *strremove(char *s, const char *c)
+{
+	char *p;
+
+	while ((p = strstr(s, c))) {
+		*p = '\0';
+		strcat(s, p+strlen(c));
+	}
+
+	return s;
+}
+
 static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
 					    struct snd_jack_kctl *jack_kctl)
 {
 	char *tname;
+	int i;
 
-	/* the folder's name can't contains '/', need to replace it with '!'
-	 * as lib/kobject.c does
-	 */
 	tname = kstrdup(jack_kctl->kctl->id.name, GFP_KERNEL);
 	if (!tname)
 		return -ENOMEM;
-	strreplace(tname, '/', '!');
+
+	for (i = 0; i < ARRAY_SIZE(dropped_chars); i++)
+		tname = strremove(tname, dropped_chars[i]);
+
 	jack_kctl->jack_debugfs_root = debugfs_create_dir(tname, jack->card->debugfs_root);
 	kfree(tname);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC][PATCH v3 3/4] alsa: jack: add more jack_kctl debugfs nodes
  2020-12-28  7:59 [RFC][PATCH v3 0/4] design a way to change audio Jack state by software Hui Wang
  2020-12-28  8:00 ` [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs Hui Wang
  2020-12-28  8:00 ` [RFC][PATCH v3 2/4] alsa: jack: adjust jack_kctl debugfs folder's name Hui Wang
@ 2020-12-28  8:00 ` Hui Wang
  2020-12-28  8:00 ` [RFC][PATCH v3 4/4] alsa: jack: implement save-and-restore for jack's hw status Hui Wang
  3 siblings, 0 replies; 9+ messages in thread
From: Hui Wang @ 2020-12-28  8:00 UTC (permalink / raw)
  To: alsa-devel, tiwai, perex, kai.vehmanen

Adding 4 more debugfs nodes, users could get more information about
the jack_kctl from them:
 - kctl_id, read-only, get jack_kctl->kctl's id
   sound-core/card0/HeadphoneJack# cat kctl_id
   Headphone Jack

 - mask_bits, read-only, get jack_kctl's events mask_bits
   sound-core/card0/HeadphoneJack# cat mask_bits
   0x0001 HEADPHONE(0x0001)

 - status, read-only, get jack_kctl's current status
   headphone unplugged:
   sound-core/card0/HeadphoneJack# cat status
   0x0000
   headphone plugged:
   sound-core/card0/HeadphoneJack# cat status
   0x0001 HEADPHONE(0x0001)

 - type, read-only, get jack's supported events type
   sound-core/card0/HeadphoneJack# cat type
   0x0001 HEADPHONE(0x0001)

Signed-off-by: Hui Wang <hui.wang@canonical.com>
---
 sound/core/jack.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 144 insertions(+)

diff --git a/sound/core/jack.c b/sound/core/jack.c
index 62e9215fa0f0..31c80883db2c 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -229,6 +229,119 @@ static ssize_t jackin_inject_write(struct file *file,
 	return ret;
 }
 
+static ssize_t jack_kctl_id_read(struct file *file,
+				 char __user *to, size_t count, loff_t *ppos)
+{
+	struct snd_jack_kctl *jack_kctl = file->private_data;
+	char *buf;
+	int len, ret;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len = scnprintf(buf, PAGE_SIZE, "%s\n", jack_kctl->kctl->id.name);
+	ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+	kfree(buf);
+	return ret;
+}
+
+/* the bit definition is aligned with snd_jack_types in jack.h */
+static const char * const jack_events_name[] = {
+	"HEADPHONE(0x0001)", "MICROPHONE(0x0002)", "LINEOUT(0x0004)",
+	"MECHANICAL(0x0008)", "VIDEOOUT(0x0010)", "LINEIN(0x0020)",
+	"", "", "", "BTN_5(0x0200)", "BTN_4(0x0400)", "BTN_3(0x0800)",
+	"BTN_2(0x1000)", "BTN_1(0x2000)", "BTN_0(0x4000)", "",
+};
+
+static int parse_mask_bits(unsigned int mask_bits, char *s)
+{
+	char *buf;
+	int len, i;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len = scnprintf(buf, PAGE_SIZE, "0x%04x", mask_bits);
+
+	for (i = 0; i < 16; i++)
+		if (mask_bits & (1 << i))
+			len += scnprintf(buf + strlen(buf), PAGE_SIZE - strlen(buf),
+					 " %s", jack_events_name[i]);
+
+	len += scnprintf(buf + strlen(buf), PAGE_SIZE - strlen(buf), "\n");
+
+	strcpy(s, buf);
+
+	kfree(buf);
+
+	return len;
+}
+
+static ssize_t jack_kctl_mask_bits_read(struct file *file,
+					char __user *to, size_t count, loff_t *ppos)
+{
+	struct snd_jack_kctl *jack_kctl = file->private_data;
+	char *buf;
+	int len, ret;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len = parse_mask_bits(jack_kctl->mask_bits, buf);
+	ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t jack_kctl_status_read(struct file *file,
+				     char __user *to, size_t count, loff_t *ppos)
+{
+	struct snd_jack_kctl *jack_kctl = file->private_data;
+	char *buf;
+	int len, ret;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len = parse_mask_bits(jack_kctl->kctl->private_value, buf);
+	ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+	kfree(buf);
+	return ret;
+}
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+static ssize_t jack_type_read(struct file *file,
+			      char __user *to, size_t count, loff_t *ppos)
+{
+	struct snd_jack_kctl *jack_kctl = file->private_data;
+	char *buf;
+	int len, ret;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len = parse_mask_bits(jack_kctl->jack->type, buf);
+	ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations jack_type_fops = {
+	.open = simple_open,
+	.read = jack_type_read,
+	.llseek = default_llseek,
+};
+#endif
+
 static const struct file_operations sw_inject_enable_fops = {
 	.open = simple_open,
 	.read = sw_inject_enable_read,
@@ -242,6 +355,24 @@ static const struct file_operations jackin_inject_fops = {
 	.llseek = default_llseek,
 };
 
+static const struct file_operations jack_kctl_id_fops = {
+	.open = simple_open,
+	.read = jack_kctl_id_read,
+	.llseek = default_llseek,
+};
+
+static const struct file_operations jack_kctl_mask_bits_fops = {
+	.open = simple_open,
+	.read = jack_kctl_mask_bits_read,
+	.llseek = default_llseek,
+};
+
+static const struct file_operations jack_kctl_status_fops = {
+	.open = simple_open,
+	.read = jack_kctl_status_read,
+	.llseek = default_llseek,
+};
+
 /* The substrings in the jack's name but not suitable for folder's name */
 static const char * const dropped_chars[] = {
 	"/", "=", ",", " ",
@@ -281,6 +412,19 @@ static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
 	debugfs_create_file("jackin_inject", 0200, jack_kctl->jack_debugfs_root, jack_kctl,
 			    &jackin_inject_fops);
 
+	debugfs_create_file("kctl_id", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+			    &jack_kctl_id_fops);
+
+	debugfs_create_file("mask_bits", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+			    &jack_kctl_mask_bits_fops);
+
+	debugfs_create_file("status", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+			    &jack_kctl_status_fops);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+	debugfs_create_file("type", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+			    &jack_type_fops);
+#endif
 	return 0;
 }
 #else /* CONFIG_DEBUG_FS */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC][PATCH v3 4/4] alsa: jack: implement save-and-restore for jack's hw status
  2020-12-28  7:59 [RFC][PATCH v3 0/4] design a way to change audio Jack state by software Hui Wang
                   ` (2 preceding siblings ...)
  2020-12-28  8:00 ` [RFC][PATCH v3 3/4] alsa: jack: add more jack_kctl debugfs nodes Hui Wang
@ 2020-12-28  8:00 ` Hui Wang
  3 siblings, 0 replies; 9+ messages in thread
From: Hui Wang @ 2020-12-28  8:00 UTC (permalink / raw)
  To: alsa-devel, tiwai, perex, kai.vehmanen

Once we enable the sw_inject for a jack_kctl, the hw status change
will be blocked, but the hw status still could be reported to
snd_jack_report() and be saved to hw_status_cache.

After the sw_inject is disabled, we use the last saved hw_status_cache
to restore jack's status.

Signed-off-by: Hui Wang <hui.wang@canonical.com>
---
 include/sound/jack.h | 1 +
 sound/core/jack.c    | 8 ++++++++
 2 files changed, 9 insertions(+)

diff --git a/include/sound/jack.h b/include/sound/jack.h
index 9eb2b5ec1ec4..1181f536557e 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -67,6 +67,7 @@ struct snd_jack {
 	char name[100];
 	unsigned int key[6];   /* Keep in sync with definitions above */
 #endif /* CONFIG_SND_JACK_INPUT_DEV */
+	int hw_status_cache;
 	void *private_data;
 	void (*private_free)(struct snd_jack *);
 };
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 31c80883db2c..2c7fef94823d 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -193,8 +193,14 @@ static ssize_t sw_inject_enable_write(struct file *file,
 		goto exit;
 	}
 
+	if (jack_kctl->sw_inject_enable == (!!enable))
+		goto exit;
+
 	jack_kctl->sw_inject_enable = !!enable;
 
+	if (!jack_kctl->sw_inject_enable)
+		snd_jack_report(jack_kctl->jack, jack_kctl->jack->hw_status_cache);
+
  exit:
 	kfree(buf);
 	return ret;
@@ -678,6 +684,8 @@ void snd_jack_report(struct snd_jack *jack, int status)
 	if (!jack)
 		return;
 
+	jack->hw_status_cache = status;
+
 	list_for_each_entry(jack_kctl, &jack->kctl_list, list)
 		if (jack_kctl->sw_inject_enable)
 			mask_bits |= jack_kctl->mask_bits;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs
  2020-12-28  8:00 ` [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs Hui Wang
@ 2021-01-04 15:04   ` Jaroslav Kysela
  2021-01-05  9:28     ` Hui Wang
  2021-01-04 15:20   ` Jaroslav Kysela
  1 sibling, 1 reply; 9+ messages in thread
From: Jaroslav Kysela @ 2021-01-04 15:04 UTC (permalink / raw)
  To: Hui Wang, alsa-devel, tiwai, kai.vehmanen

Dne 28. 12. 20 v 9:00 Hui Wang napsal(a):
> We want to perform remote audio auto test, need the audio jack to
> change from plugout to plugin or vice versa by software ways.
> 
> Here the design is creating a sound-core root folder in the debugfs
> dir, and each sound card will create a folder cardN under sound-core,
> then the sound jack will create folders by jack_ctrl->ctrl->id.name,
> and will create 2 file nodes jackin_inject and sw_inject_enable in
> the folder, this is the layout of folder on a machine with 2 sound
> cards:
> $tree $debugfs_mount_dir/sound-core
> sound-core/
> ├── card0
> │   ├── HDMI!DP,pcm=10 Jack
> │   │   ├── jackin_inject
> │   │   └── sw_inject_enable

> +	sound_core_debugfs_root = debugfs_create_dir("sound-core", NULL);

I would just use "sound" directory name here. Why "core" suffix?

					Jaroslav

-- 
Jaroslav Kysela <perex@perex.cz>
Linux Sound Maintainer; ALSA Project; Red Hat, Inc.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs
  2020-12-28  8:00 ` [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs Hui Wang
  2021-01-04 15:04   ` Jaroslav Kysela
@ 2021-01-04 15:20   ` Jaroslav Kysela
  2021-01-05 10:09     ` Hui Wang
  1 sibling, 1 reply; 9+ messages in thread
From: Jaroslav Kysela @ 2021-01-04 15:20 UTC (permalink / raw)
  To: Hui Wang, alsa-devel, tiwai, kai.vehmanen

Dne 28. 12. 20 v 9:00 Hui Wang napsal(a):

> +static ssize_t sw_inject_enable_write(struct file *file,
> +				      const char __user *from, size_t count, loff_t *ppos)
> +{
> +	struct snd_jack_kctl *jack_kctl = file->private_data;
> +	char *buf;
> +	int ret, err;
> +	unsigned long enable;
> +
> +	buf = kzalloc(count, GFP_KERNEL);

Debugfs fops allocations should use kvzalloc() / kvfree().

					Jaroslav

-- 
Jaroslav Kysela <perex@perex.cz>
Linux Sound Maintainer; ALSA Project; Red Hat, Inc.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs
  2021-01-04 15:04   ` Jaroslav Kysela
@ 2021-01-05  9:28     ` Hui Wang
  0 siblings, 0 replies; 9+ messages in thread
From: Hui Wang @ 2021-01-05  9:28 UTC (permalink / raw)
  To: Jaroslav Kysela, alsa-devel, tiwai, kai.vehmanen


On 1/4/21 11:04 PM, Jaroslav Kysela wrote:
> Dne 28. 12. 20 v 9:00 Hui Wang napsal(a):
>> We want to perform remote audio auto test, need the audio jack to
>> change from plugout to plugin or vice versa by software ways.
>>
>> Here the design is creating a sound-core root folder in the debugfs
>> dir, and each sound card will create a folder cardN under sound-core,
>> then the sound jack will create folders by jack_ctrl->ctrl->id.name,
>> and will create 2 file nodes jackin_inject and sw_inject_enable in
>> the folder, this is the layout of folder on a machine with 2 sound
>> cards:
>> $tree $debugfs_mount_dir/sound-core
>> sound-core/
>> ├── card0
>> │   ├── HDMI!DP,pcm=10 Jack
>> │   │   ├── jackin_inject
>> │   │   └── sw_inject_enable
>> +	sound_core_debugfs_root = debugfs_create_dir("sound-core", NULL);
> I would just use "sound" directory name here. Why "core" suffix?

OK, will change it to sound.

thx.

>
> 					Jaroslav
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs
  2021-01-04 15:20   ` Jaroslav Kysela
@ 2021-01-05 10:09     ` Hui Wang
  0 siblings, 0 replies; 9+ messages in thread
From: Hui Wang @ 2021-01-05 10:09 UTC (permalink / raw)
  To: Jaroslav Kysela, alsa-devel, tiwai, kai.vehmanen


On 1/4/21 11:20 PM, Jaroslav Kysela wrote:
> Dne 28. 12. 20 v 9:00 Hui Wang napsal(a):
>
>> +static ssize_t sw_inject_enable_write(struct file *file,
>> +				      const char __user *from, size_t count, loff_t *ppos)
>> +{
>> +	struct snd_jack_kctl *jack_kctl = file->private_data;
>> +	char *buf;
>> +	int ret, err;
>> +	unsigned long enable;
>> +
>> +	buf = kzalloc(count, GFP_KERNEL);
> Debugfs fops allocations should use kvzalloc() / kvfree().

OK, got it. Will change to use them in the next version.

Thanks.

>
> 					Jaroslav
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-01-05 10:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-28  7:59 [RFC][PATCH v3 0/4] design a way to change audio Jack state by software Hui Wang
2020-12-28  8:00 ` [RFC][PATCH v3 1/4] alsa: jack: implement software jack injection via debugfs Hui Wang
2021-01-04 15:04   ` Jaroslav Kysela
2021-01-05  9:28     ` Hui Wang
2021-01-04 15:20   ` Jaroslav Kysela
2021-01-05 10:09     ` Hui Wang
2020-12-28  8:00 ` [RFC][PATCH v3 2/4] alsa: jack: adjust jack_kctl debugfs folder's name Hui Wang
2020-12-28  8:00 ` [RFC][PATCH v3 3/4] alsa: jack: add more jack_kctl debugfs nodes Hui Wang
2020-12-28  8:00 ` [RFC][PATCH v3 4/4] alsa: jack: implement save-and-restore for jack's hw status Hui Wang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).