All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] kselftest: alsa: Add test case for writing invalid values
@ 2022-01-24 15:14 ` Mark Brown
  0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2022-01-24 15:14 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Shuah Khan
  Cc: linux-kselftest, alsa-devel, Mark Brown

Attempt to write various invalid values for control types we know about and
check that something sensible happens. The ABI isn't quite as clearly
defined as one might like, rather than generating an error when an invalid
value is written many devices will silently rewrite the value into one that
is valid for the control. The exact value chosen is not predictable so in
the case the write succeeds we just check that the value we read back is
one that is valid for the control.

Signed-off-by: Mark Brown <broonie@kernel.org>
---
 tools/testing/selftests/alsa/mixer-test.c | 222 +++++++++++++++++++++-
 1 file changed, 221 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
index 17f158d7a767..15e05b241468 100644
--- a/tools/testing/selftests/alsa/mixer-test.c
+++ b/tools/testing/selftests/alsa/mixer-test.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
+#include <limits.h>
 #include <string.h>
 #include <getopt.h>
 #include <stdarg.h>
@@ -26,7 +27,7 @@
 
 #include "../kselftest.h"
 
-#define TESTS_PER_CONTROL 3
+#define TESTS_PER_CONTROL 4
 
 struct card_data {
 	snd_ctl_t *handle;
@@ -679,6 +680,224 @@ void test_ctl_write_valid(struct ctl_data *ctl)
 			 ctl->card->card, ctl->elem);
 }
 
+bool test_ctl_write_invalid_value(struct ctl_data *ctl,
+				  snd_ctl_elem_value_t *val)
+{
+	int err;
+	long val_read;
+
+	/* Ideally this will fail... */
+	err = snd_ctl_elem_write(ctl->card->handle, val);
+	if (err < 0)
+		return false;
+
+	/* ...but some devices will clamp to an in range value */
+	err = snd_ctl_elem_read(ctl->card->handle, val);
+	if (err < 0) {
+		ksft_print_msg("%s failed to read: %s\n",
+			       ctl->name, snd_strerror(err));
+		return true;
+	}
+
+	return !ctl_value_valid(ctl, val);
+}
+
+bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
+{
+	int err, i;
+	long val_read;
+	bool fail = false;
+	snd_ctl_elem_value_t *val;
+	snd_ctl_elem_value_alloca(&val);
+
+	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+		snd_ctl_elem_value_copy(val, ctl->def_val);
+		snd_ctl_elem_value_set_boolean(val, i, 2);
+
+		if (test_ctl_write_invalid_value(ctl, val))
+			fail = true;
+	}
+
+	return !fail;
+}
+
+bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
+{
+	int i;
+	bool fail = false;
+	snd_ctl_elem_value_t *val;
+	snd_ctl_elem_value_alloca(&val);
+
+	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+		if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
+			/* Just under range */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i,
+			       snd_ctl_elem_info_get_min(ctl->info) - 1);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+
+			/* Minimum representable value */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+		}
+
+		if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
+			/* Just over range */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i,
+			       snd_ctl_elem_info_get_max(ctl->info) + 1);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+
+			/* Maximum representable value */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+		}
+	}
+
+	return !fail;
+}
+
+bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
+{
+	int i;
+	bool fail = false;
+	snd_ctl_elem_value_t *val;
+	snd_ctl_elem_value_alloca(&val);
+
+	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+		if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
+			/* Just under range */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer64(val, i,
+				snd_ctl_elem_info_get_min64(ctl->info) - 1);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+
+			/* Minimum representable value */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i, LLONG_MIN);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+		}
+
+		if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
+			/* Just over range */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer64(val, i,
+				snd_ctl_elem_info_get_max64(ctl->info) + 1);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+
+			/* Maximum representable value */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i, LLONG_MAX);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+		}
+	}
+
+	return !fail;
+}
+
+bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
+{
+	int err, i;
+	unsigned int val_read;
+	bool fail = false;
+	snd_ctl_elem_value_t *val;
+	snd_ctl_elem_value_alloca(&val);
+
+	snd_ctl_elem_value_set_id(val, ctl->id);
+
+	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+		/* One beyond maximum */
+		snd_ctl_elem_value_copy(val, ctl->def_val);
+		snd_ctl_elem_value_set_enumerated(val, i,
+				  snd_ctl_elem_info_get_items(ctl->info));
+
+		if (test_ctl_write_invalid_value(ctl, val))
+			fail = true;
+
+		/* Maximum representable value */
+		snd_ctl_elem_value_copy(val, ctl->def_val);
+		snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
+
+		if (test_ctl_write_invalid_value(ctl, val))
+			fail = true;
+
+	}
+
+	return !fail;
+}
+
+
+void test_ctl_write_invalid(struct ctl_data *ctl)
+{
+	bool pass;
+	int err;
+
+	/* If the control is turned off let's be polite */
+	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
+		ksft_print_msg("%s is inactive\n", ctl->name);
+		ksft_test_result_skip("write_invalid.%d.%d\n",
+				      ctl->card->card, ctl->elem);
+		return;
+	}
+
+	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
+		ksft_print_msg("%s is not writeable\n", ctl->name);
+		ksft_test_result_skip("write_invalid.%d.%d\n",
+				      ctl->card->card, ctl->elem);
+		return;
+	}
+
+	switch (snd_ctl_elem_info_get_type(ctl->info)) {
+	case SND_CTL_ELEM_TYPE_BOOLEAN:
+		pass = test_ctl_write_invalid_boolean(ctl);
+		break;
+
+	case SND_CTL_ELEM_TYPE_INTEGER:
+		pass = test_ctl_write_invalid_integer(ctl);
+		break;
+
+	case SND_CTL_ELEM_TYPE_INTEGER64:
+		pass = test_ctl_write_invalid_integer64(ctl);
+		break;
+
+	case SND_CTL_ELEM_TYPE_ENUMERATED:
+		pass = test_ctl_write_invalid_enumerated(ctl);
+		break;
+
+	default:
+		/* No tests for this yet */
+		ksft_test_result_skip("write_invalid.%d.%d\n",
+				      ctl->card->card, ctl->elem);
+		return;
+	}
+
+	/* Restore the default value to minimise disruption */
+	err = write_and_verify(ctl, ctl->def_val, NULL);
+	if (err < 0)
+		pass = false;
+
+	ksft_test_result(pass, "write_invalid.%d.%d\n",
+			 ctl->card->card, ctl->elem);
+}
+
 int main(void)
 {
 	struct ctl_data *ctl;
@@ -697,6 +916,7 @@ int main(void)
 		test_ctl_get_value(ctl);
 		test_ctl_write_default(ctl);
 		test_ctl_write_valid(ctl);
+		test_ctl_write_invalid(ctl);
 	}
 
 	ksft_exit_pass();
-- 
2.30.2


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

* [PATCH] kselftest: alsa: Add test case for writing invalid values
@ 2022-01-24 15:14 ` Mark Brown
  0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2022-01-24 15:14 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai, Shuah Khan
  Cc: alsa-devel, Mark Brown, linux-kselftest

Attempt to write various invalid values for control types we know about and
check that something sensible happens. The ABI isn't quite as clearly
defined as one might like, rather than generating an error when an invalid
value is written many devices will silently rewrite the value into one that
is valid for the control. The exact value chosen is not predictable so in
the case the write succeeds we just check that the value we read back is
one that is valid for the control.

Signed-off-by: Mark Brown <broonie@kernel.org>
---
 tools/testing/selftests/alsa/mixer-test.c | 222 +++++++++++++++++++++-
 1 file changed, 221 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
index 17f158d7a767..15e05b241468 100644
--- a/tools/testing/selftests/alsa/mixer-test.c
+++ b/tools/testing/selftests/alsa/mixer-test.c
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
+#include <limits.h>
 #include <string.h>
 #include <getopt.h>
 #include <stdarg.h>
@@ -26,7 +27,7 @@
 
 #include "../kselftest.h"
 
-#define TESTS_PER_CONTROL 3
+#define TESTS_PER_CONTROL 4
 
 struct card_data {
 	snd_ctl_t *handle;
@@ -679,6 +680,224 @@ void test_ctl_write_valid(struct ctl_data *ctl)
 			 ctl->card->card, ctl->elem);
 }
 
+bool test_ctl_write_invalid_value(struct ctl_data *ctl,
+				  snd_ctl_elem_value_t *val)
+{
+	int err;
+	long val_read;
+
+	/* Ideally this will fail... */
+	err = snd_ctl_elem_write(ctl->card->handle, val);
+	if (err < 0)
+		return false;
+
+	/* ...but some devices will clamp to an in range value */
+	err = snd_ctl_elem_read(ctl->card->handle, val);
+	if (err < 0) {
+		ksft_print_msg("%s failed to read: %s\n",
+			       ctl->name, snd_strerror(err));
+		return true;
+	}
+
+	return !ctl_value_valid(ctl, val);
+}
+
+bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
+{
+	int err, i;
+	long val_read;
+	bool fail = false;
+	snd_ctl_elem_value_t *val;
+	snd_ctl_elem_value_alloca(&val);
+
+	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+		snd_ctl_elem_value_copy(val, ctl->def_val);
+		snd_ctl_elem_value_set_boolean(val, i, 2);
+
+		if (test_ctl_write_invalid_value(ctl, val))
+			fail = true;
+	}
+
+	return !fail;
+}
+
+bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
+{
+	int i;
+	bool fail = false;
+	snd_ctl_elem_value_t *val;
+	snd_ctl_elem_value_alloca(&val);
+
+	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+		if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
+			/* Just under range */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i,
+			       snd_ctl_elem_info_get_min(ctl->info) - 1);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+
+			/* Minimum representable value */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+		}
+
+		if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
+			/* Just over range */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i,
+			       snd_ctl_elem_info_get_max(ctl->info) + 1);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+
+			/* Maximum representable value */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+		}
+	}
+
+	return !fail;
+}
+
+bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
+{
+	int i;
+	bool fail = false;
+	snd_ctl_elem_value_t *val;
+	snd_ctl_elem_value_alloca(&val);
+
+	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+		if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
+			/* Just under range */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer64(val, i,
+				snd_ctl_elem_info_get_min64(ctl->info) - 1);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+
+			/* Minimum representable value */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i, LLONG_MIN);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+		}
+
+		if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
+			/* Just over range */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer64(val, i,
+				snd_ctl_elem_info_get_max64(ctl->info) + 1);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+
+			/* Maximum representable value */
+			snd_ctl_elem_value_copy(val, ctl->def_val);
+			snd_ctl_elem_value_set_integer(val, i, LLONG_MAX);
+
+			if (test_ctl_write_invalid_value(ctl, val))
+				fail = true;
+		}
+	}
+
+	return !fail;
+}
+
+bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
+{
+	int err, i;
+	unsigned int val_read;
+	bool fail = false;
+	snd_ctl_elem_value_t *val;
+	snd_ctl_elem_value_alloca(&val);
+
+	snd_ctl_elem_value_set_id(val, ctl->id);
+
+	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+		/* One beyond maximum */
+		snd_ctl_elem_value_copy(val, ctl->def_val);
+		snd_ctl_elem_value_set_enumerated(val, i,
+				  snd_ctl_elem_info_get_items(ctl->info));
+
+		if (test_ctl_write_invalid_value(ctl, val))
+			fail = true;
+
+		/* Maximum representable value */
+		snd_ctl_elem_value_copy(val, ctl->def_val);
+		snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
+
+		if (test_ctl_write_invalid_value(ctl, val))
+			fail = true;
+
+	}
+
+	return !fail;
+}
+
+
+void test_ctl_write_invalid(struct ctl_data *ctl)
+{
+	bool pass;
+	int err;
+
+	/* If the control is turned off let's be polite */
+	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
+		ksft_print_msg("%s is inactive\n", ctl->name);
+		ksft_test_result_skip("write_invalid.%d.%d\n",
+				      ctl->card->card, ctl->elem);
+		return;
+	}
+
+	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
+		ksft_print_msg("%s is not writeable\n", ctl->name);
+		ksft_test_result_skip("write_invalid.%d.%d\n",
+				      ctl->card->card, ctl->elem);
+		return;
+	}
+
+	switch (snd_ctl_elem_info_get_type(ctl->info)) {
+	case SND_CTL_ELEM_TYPE_BOOLEAN:
+		pass = test_ctl_write_invalid_boolean(ctl);
+		break;
+
+	case SND_CTL_ELEM_TYPE_INTEGER:
+		pass = test_ctl_write_invalid_integer(ctl);
+		break;
+
+	case SND_CTL_ELEM_TYPE_INTEGER64:
+		pass = test_ctl_write_invalid_integer64(ctl);
+		break;
+
+	case SND_CTL_ELEM_TYPE_ENUMERATED:
+		pass = test_ctl_write_invalid_enumerated(ctl);
+		break;
+
+	default:
+		/* No tests for this yet */
+		ksft_test_result_skip("write_invalid.%d.%d\n",
+				      ctl->card->card, ctl->elem);
+		return;
+	}
+
+	/* Restore the default value to minimise disruption */
+	err = write_and_verify(ctl, ctl->def_val, NULL);
+	if (err < 0)
+		pass = false;
+
+	ksft_test_result(pass, "write_invalid.%d.%d\n",
+			 ctl->card->card, ctl->elem);
+}
+
 int main(void)
 {
 	struct ctl_data *ctl;
@@ -697,6 +916,7 @@ int main(void)
 		test_ctl_get_value(ctl);
 		test_ctl_write_default(ctl);
 		test_ctl_write_valid(ctl);
+		test_ctl_write_invalid(ctl);
 	}
 
 	ksft_exit_pass();
-- 
2.30.2


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

* Re: [PATCH] kselftest: alsa: Add test case for writing invalid values
  2022-01-24 15:14 ` Mark Brown
@ 2022-01-24 21:35   ` Shuah Khan
  -1 siblings, 0 replies; 10+ messages in thread
From: Shuah Khan @ 2022-01-24 21:35 UTC (permalink / raw)
  To: Mark Brown, Jaroslav Kysela, Takashi Iwai, Shuah Khan
  Cc: alsa-devel, linux-kselftest, Shuah Khan

On 1/24/22 8:14 AM, Mark Brown wrote:
> Attempt to write various invalid values for control types we know about and
> check that something sensible happens. The ABI isn't quite as clearly
> defined as one might like, rather than generating an error when an invalid
> value is written many devices will silently rewrite the value into one that
> is valid for the control. The exact value chosen is not predictable so in
> the case the write succeeds we just check that the value we read back is
> one that is valid for the control.
> 
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
>   tools/testing/selftests/alsa/mixer-test.c | 222 +++++++++++++++++++++-
>   1 file changed, 221 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
> index 17f158d7a767..15e05b241468 100644
> --- a/tools/testing/selftests/alsa/mixer-test.c
> +++ b/tools/testing/selftests/alsa/mixer-test.c
> @@ -13,6 +13,7 @@
>   #include <stdio.h>
>   #include <stdlib.h>
>   #include <stdbool.h>
> +#include <limits.h>
>   #include <string.h>
>   #include <getopt.h>
>   #include <stdarg.h>
> @@ -26,7 +27,7 @@
>   
>   #include "../kselftest.h"
>   
> -#define TESTS_PER_CONTROL 3
> +#define TESTS_PER_CONTROL 4
>   
>   struct card_data {
>   	snd_ctl_t *handle;
> @@ -679,6 +680,224 @@ void test_ctl_write_valid(struct ctl_data *ctl)
>   			 ctl->card->card, ctl->elem);
>   }
>   
> +bool test_ctl_write_invalid_value(struct ctl_data *ctl,
> +				  snd_ctl_elem_value_t *val)
> +{
> +	int err;
> +	long val_read;
> +
> +	/* Ideally this will fail... */
> +	err = snd_ctl_elem_write(ctl->card->handle, val);
> +	if (err < 0)
> +		return false;
> +
> +	/* ...but some devices will clamp to an in range value */
> +	err = snd_ctl_elem_read(ctl->card->handle, val);
> +	if (err < 0) {
> +		ksft_print_msg("%s failed to read: %s\n",
> +			       ctl->name, snd_strerror(err));
> +		return true;
> +	}
> +
> +	return !ctl_value_valid(ctl, val);
> +}
> +
> +bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
> +{
> +	int err, i;
> +	long val_read;
> +	bool fail = false;
> +	snd_ctl_elem_value_t *val;
> +	snd_ctl_elem_value_alloca(&val);
> +
> +	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> +		snd_ctl_elem_value_copy(val, ctl->def_val);
> +		snd_ctl_elem_value_set_boolean(val, i, 2);
> +
> +		if (test_ctl_write_invalid_value(ctl, val))
> +			fail = true;
> +	}
> +
> +	return !fail;
> +}
> +
> +bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
> +{
> +	int i;
> +	bool fail = false;
> +	snd_ctl_elem_value_t *val;
> +	snd_ctl_elem_value_alloca(&val);
> +
> +	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> +		if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
> +			/* Just under range */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i,
> +			       snd_ctl_elem_info_get_min(ctl->info) - 1);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +
> +			/* Minimum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +		}
> +
> +		if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
> +			/* Just over range */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i,
> +			       snd_ctl_elem_info_get_max(ctl->info) + 1);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +
> +			/* Maximum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +		}
> +	}
> +
> +	return !fail;
> +}
> +
> +bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
> +{
> +	int i;
> +	bool fail = false;
> +	snd_ctl_elem_value_t *val;
> +	snd_ctl_elem_value_alloca(&val);
> +
> +	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> +		if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
> +			/* Just under range */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer64(val, i,
> +				snd_ctl_elem_info_get_min64(ctl->info) - 1);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +
> +			/* Minimum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LLONG_MIN);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +		}
> +
> +		if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
> +			/* Just over range */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer64(val, i,
> +				snd_ctl_elem_info_get_max64(ctl->info) + 1);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +
> +			/* Maximum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LLONG_MAX);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +		}
> +	}
> +
> +	return !fail;
> +}
> +
> +bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
> +{
> +	int err, i;
> +	unsigned int val_read;
> +	bool fail = false;
> +	snd_ctl_elem_value_t *val;
> +	snd_ctl_elem_value_alloca(&val);
> +
> +	snd_ctl_elem_value_set_id(val, ctl->id);
> +
> +	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> +		/* One beyond maximum */
> +		snd_ctl_elem_value_copy(val, ctl->def_val);
> +		snd_ctl_elem_value_set_enumerated(val, i,
> +				  snd_ctl_elem_info_get_items(ctl->info));
> +
> +		if (test_ctl_write_invalid_value(ctl, val))
> +			fail = true;
> +
> +		/* Maximum representable value */
> +		snd_ctl_elem_value_copy(val, ctl->def_val);
> +		snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
> +
> +		if (test_ctl_write_invalid_value(ctl, val))
> +			fail = true;
> +
> +	}
> +
> +	return !fail;
> +}
> +
> +
> +void test_ctl_write_invalid(struct ctl_data *ctl)
> +{
> +	bool pass;
> +	int err;
> +
> +	/* If the control is turned off let's be polite */
> +	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
> +		ksft_print_msg("%s is inactive\n", ctl->name);
> +		ksft_test_result_skip("write_invalid.%d.%d\n",
> +				      ctl->card->card, ctl->elem);
> +		return;
> +	}
> +
> +	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
> +		ksft_print_msg("%s is not writeable\n", ctl->name);
> +		ksft_test_result_skip("write_invalid.%d.%d\n",
> +				      ctl->card->card, ctl->elem);
> +		return;
> +	}
> +
> +	switch (snd_ctl_elem_info_get_type(ctl->info)) {
> +	case SND_CTL_ELEM_TYPE_BOOLEAN:
> +		pass = test_ctl_write_invalid_boolean(ctl);
> +		break;
> +
> +	case SND_CTL_ELEM_TYPE_INTEGER:
> +		pass = test_ctl_write_invalid_integer(ctl);
> +		break;
> +
> +	case SND_CTL_ELEM_TYPE_INTEGER64:
> +		pass = test_ctl_write_invalid_integer64(ctl);
> +		break;
> +
> +	case SND_CTL_ELEM_TYPE_ENUMERATED:
> +		pass = test_ctl_write_invalid_enumerated(ctl);
> +		break;
> +
> +	default:
> +		/* No tests for this yet */
> +		ksft_test_result_skip("write_invalid.%d.%d\n",
> +				      ctl->card->card, ctl->elem);
> +		return;
> +	}
> +
> +	/* Restore the default value to minimise disruption */
> +	err = write_and_verify(ctl, ctl->def_val, NULL);

Why not read the value and then restore the value, in case the value
before the write test is different from the ctl->def_val?

> +	if (err < 0)
> +		pass = false;
> +
> +	ksft_test_result(pass, "write_invalid.%d.%d\n",
> +			 ctl->card->card, ctl->elem);
> +}
> +
>   int main(void)
>   {
>   	struct ctl_data *ctl;
> @@ -697,6 +916,7 @@ int main(void)
>   		test_ctl_get_value(ctl);
>   		test_ctl_write_default(ctl);
>   		test_ctl_write_valid(ctl);
> +		test_ctl_write_invalid(ctl);
>   	}
>   
>   	ksft_exit_pass();
> 

thanks,
-- Shuah

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

* Re: [PATCH] kselftest: alsa: Add test case for writing invalid values
@ 2022-01-24 21:35   ` Shuah Khan
  0 siblings, 0 replies; 10+ messages in thread
From: Shuah Khan @ 2022-01-24 21:35 UTC (permalink / raw)
  To: Mark Brown, Jaroslav Kysela, Takashi Iwai, Shuah Khan
  Cc: linux-kselftest, alsa-devel, Shuah Khan

On 1/24/22 8:14 AM, Mark Brown wrote:
> Attempt to write various invalid values for control types we know about and
> check that something sensible happens. The ABI isn't quite as clearly
> defined as one might like, rather than generating an error when an invalid
> value is written many devices will silently rewrite the value into one that
> is valid for the control. The exact value chosen is not predictable so in
> the case the write succeeds we just check that the value we read back is
> one that is valid for the control.
> 
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
>   tools/testing/selftests/alsa/mixer-test.c | 222 +++++++++++++++++++++-
>   1 file changed, 221 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
> index 17f158d7a767..15e05b241468 100644
> --- a/tools/testing/selftests/alsa/mixer-test.c
> +++ b/tools/testing/selftests/alsa/mixer-test.c
> @@ -13,6 +13,7 @@
>   #include <stdio.h>
>   #include <stdlib.h>
>   #include <stdbool.h>
> +#include <limits.h>
>   #include <string.h>
>   #include <getopt.h>
>   #include <stdarg.h>
> @@ -26,7 +27,7 @@
>   
>   #include "../kselftest.h"
>   
> -#define TESTS_PER_CONTROL 3
> +#define TESTS_PER_CONTROL 4
>   
>   struct card_data {
>   	snd_ctl_t *handle;
> @@ -679,6 +680,224 @@ void test_ctl_write_valid(struct ctl_data *ctl)
>   			 ctl->card->card, ctl->elem);
>   }
>   
> +bool test_ctl_write_invalid_value(struct ctl_data *ctl,
> +				  snd_ctl_elem_value_t *val)
> +{
> +	int err;
> +	long val_read;
> +
> +	/* Ideally this will fail... */
> +	err = snd_ctl_elem_write(ctl->card->handle, val);
> +	if (err < 0)
> +		return false;
> +
> +	/* ...but some devices will clamp to an in range value */
> +	err = snd_ctl_elem_read(ctl->card->handle, val);
> +	if (err < 0) {
> +		ksft_print_msg("%s failed to read: %s\n",
> +			       ctl->name, snd_strerror(err));
> +		return true;
> +	}
> +
> +	return !ctl_value_valid(ctl, val);
> +}
> +
> +bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
> +{
> +	int err, i;
> +	long val_read;
> +	bool fail = false;
> +	snd_ctl_elem_value_t *val;
> +	snd_ctl_elem_value_alloca(&val);
> +
> +	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> +		snd_ctl_elem_value_copy(val, ctl->def_val);
> +		snd_ctl_elem_value_set_boolean(val, i, 2);
> +
> +		if (test_ctl_write_invalid_value(ctl, val))
> +			fail = true;
> +	}
> +
> +	return !fail;
> +}
> +
> +bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
> +{
> +	int i;
> +	bool fail = false;
> +	snd_ctl_elem_value_t *val;
> +	snd_ctl_elem_value_alloca(&val);
> +
> +	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> +		if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
> +			/* Just under range */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i,
> +			       snd_ctl_elem_info_get_min(ctl->info) - 1);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +
> +			/* Minimum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +		}
> +
> +		if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
> +			/* Just over range */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i,
> +			       snd_ctl_elem_info_get_max(ctl->info) + 1);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +
> +			/* Maximum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +		}
> +	}
> +
> +	return !fail;
> +}
> +
> +bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
> +{
> +	int i;
> +	bool fail = false;
> +	snd_ctl_elem_value_t *val;
> +	snd_ctl_elem_value_alloca(&val);
> +
> +	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> +		if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
> +			/* Just under range */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer64(val, i,
> +				snd_ctl_elem_info_get_min64(ctl->info) - 1);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +
> +			/* Minimum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LLONG_MIN);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +		}
> +
> +		if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
> +			/* Just over range */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer64(val, i,
> +				snd_ctl_elem_info_get_max64(ctl->info) + 1);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +
> +			/* Maximum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LLONG_MAX);
> +
> +			if (test_ctl_write_invalid_value(ctl, val))
> +				fail = true;
> +		}
> +	}
> +
> +	return !fail;
> +}
> +
> +bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
> +{
> +	int err, i;
> +	unsigned int val_read;
> +	bool fail = false;
> +	snd_ctl_elem_value_t *val;
> +	snd_ctl_elem_value_alloca(&val);
> +
> +	snd_ctl_elem_value_set_id(val, ctl->id);
> +
> +	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
> +		/* One beyond maximum */
> +		snd_ctl_elem_value_copy(val, ctl->def_val);
> +		snd_ctl_elem_value_set_enumerated(val, i,
> +				  snd_ctl_elem_info_get_items(ctl->info));
> +
> +		if (test_ctl_write_invalid_value(ctl, val))
> +			fail = true;
> +
> +		/* Maximum representable value */
> +		snd_ctl_elem_value_copy(val, ctl->def_val);
> +		snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
> +
> +		if (test_ctl_write_invalid_value(ctl, val))
> +			fail = true;
> +
> +	}
> +
> +	return !fail;
> +}
> +
> +
> +void test_ctl_write_invalid(struct ctl_data *ctl)
> +{
> +	bool pass;
> +	int err;
> +
> +	/* If the control is turned off let's be polite */
> +	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
> +		ksft_print_msg("%s is inactive\n", ctl->name);
> +		ksft_test_result_skip("write_invalid.%d.%d\n",
> +				      ctl->card->card, ctl->elem);
> +		return;
> +	}
> +
> +	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
> +		ksft_print_msg("%s is not writeable\n", ctl->name);
> +		ksft_test_result_skip("write_invalid.%d.%d\n",
> +				      ctl->card->card, ctl->elem);
> +		return;
> +	}
> +
> +	switch (snd_ctl_elem_info_get_type(ctl->info)) {
> +	case SND_CTL_ELEM_TYPE_BOOLEAN:
> +		pass = test_ctl_write_invalid_boolean(ctl);
> +		break;
> +
> +	case SND_CTL_ELEM_TYPE_INTEGER:
> +		pass = test_ctl_write_invalid_integer(ctl);
> +		break;
> +
> +	case SND_CTL_ELEM_TYPE_INTEGER64:
> +		pass = test_ctl_write_invalid_integer64(ctl);
> +		break;
> +
> +	case SND_CTL_ELEM_TYPE_ENUMERATED:
> +		pass = test_ctl_write_invalid_enumerated(ctl);
> +		break;
> +
> +	default:
> +		/* No tests for this yet */
> +		ksft_test_result_skip("write_invalid.%d.%d\n",
> +				      ctl->card->card, ctl->elem);
> +		return;
> +	}
> +
> +	/* Restore the default value to minimise disruption */
> +	err = write_and_verify(ctl, ctl->def_val, NULL);

Why not read the value and then restore the value, in case the value
before the write test is different from the ctl->def_val?

> +	if (err < 0)
> +		pass = false;
> +
> +	ksft_test_result(pass, "write_invalid.%d.%d\n",
> +			 ctl->card->card, ctl->elem);
> +}
> +
>   int main(void)
>   {
>   	struct ctl_data *ctl;
> @@ -697,6 +916,7 @@ int main(void)
>   		test_ctl_get_value(ctl);
>   		test_ctl_write_default(ctl);
>   		test_ctl_write_valid(ctl);
> +		test_ctl_write_invalid(ctl);
>   	}
>   
>   	ksft_exit_pass();
> 

thanks,
-- Shuah

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

* Re: [PATCH] kselftest: alsa: Add test case for writing invalid values
  2022-01-24 21:35   ` Shuah Khan
@ 2022-01-24 21:48     ` Mark Brown
  -1 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2022-01-24 21:48 UTC (permalink / raw)
  To: Shuah Khan; +Cc: alsa-devel, Shuah Khan, Takashi Iwai, linux-kselftest

[-- Attachment #1: Type: text/plain, Size: 633 bytes --]

On Mon, Jan 24, 2022 at 02:35:53PM -0700, Shuah Khan wrote:
> On 1/24/22 8:14 AM, Mark Brown wrote:

> > +	/* Restore the default value to minimise disruption */
> > +	err = write_and_verify(ctl, ctl->def_val, NULL);

> Why not read the value and then restore the value, in case the value
> before the write test is different from the ctl->def_val?

That's what we're doing - def_val is the value read from the device at
the start of the test program before it tries to do any writes, we then
restore it at the end of every test to minimise the chances that the
device configuration will be messed up if the test program falls over.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH] kselftest: alsa: Add test case for writing invalid values
@ 2022-01-24 21:48     ` Mark Brown
  0 siblings, 0 replies; 10+ messages in thread
From: Mark Brown @ 2022-01-24 21:48 UTC (permalink / raw)
  To: Shuah Khan
  Cc: Jaroslav Kysela, Takashi Iwai, Shuah Khan, linux-kselftest, alsa-devel

[-- Attachment #1: Type: text/plain, Size: 633 bytes --]

On Mon, Jan 24, 2022 at 02:35:53PM -0700, Shuah Khan wrote:
> On 1/24/22 8:14 AM, Mark Brown wrote:

> > +	/* Restore the default value to minimise disruption */
> > +	err = write_and_verify(ctl, ctl->def_val, NULL);

> Why not read the value and then restore the value, in case the value
> before the write test is different from the ctl->def_val?

That's what we're doing - def_val is the value read from the device at
the start of the test program before it tries to do any writes, we then
restore it at the end of every test to minimise the chances that the
device configuration will be messed up if the test program falls over.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH] kselftest: alsa: Add test case for writing invalid values
  2022-01-24 21:48     ` Mark Brown
@ 2022-01-24 22:12       ` Shuah Khan
  -1 siblings, 0 replies; 10+ messages in thread
From: Shuah Khan @ 2022-01-24 22:12 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, Takashi Iwai, linux-kselftest, Shuah Khan, Shuah Khan

On 1/24/22 2:48 PM, Mark Brown wrote:
> On Mon, Jan 24, 2022 at 02:35:53PM -0700, Shuah Khan wrote:
>> On 1/24/22 8:14 AM, Mark Brown wrote:
> 
>>> +	/* Restore the default value to minimise disruption */
>>> +	err = write_and_verify(ctl, ctl->def_val, NULL);
> 
>> Why not read the value and then restore the value, in case the value
>> before the write test is different from the ctl->def_val?
> 
> That's what we're doing - def_val is the value read from the device at
> the start of the test program before it tries to do any writes, we then
> restore it at the end of every test to minimise the chances that the
> device configuration will be messed up if the test program falls over.
> 

Missed that. Sounds good. In case this is going through sound tree

Reviewed-by: Shuah Khan <skhan@linuxfoundation.org>

thanks,
-- Shuah

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

* Re: [PATCH] kselftest: alsa: Add test case for writing invalid values
@ 2022-01-24 22:12       ` Shuah Khan
  0 siblings, 0 replies; 10+ messages in thread
From: Shuah Khan @ 2022-01-24 22:12 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Shuah Khan, linux-kselftest,
	alsa-devel, Shuah Khan

On 1/24/22 2:48 PM, Mark Brown wrote:
> On Mon, Jan 24, 2022 at 02:35:53PM -0700, Shuah Khan wrote:
>> On 1/24/22 8:14 AM, Mark Brown wrote:
> 
>>> +	/* Restore the default value to minimise disruption */
>>> +	err = write_and_verify(ctl, ctl->def_val, NULL);
> 
>> Why not read the value and then restore the value, in case the value
>> before the write test is different from the ctl->def_val?
> 
> That's what we're doing - def_val is the value read from the device at
> the start of the test program before it tries to do any writes, we then
> restore it at the end of every test to minimise the chances that the
> device configuration will be messed up if the test program falls over.
> 

Missed that. Sounds good. In case this is going through sound tree

Reviewed-by: Shuah Khan <skhan@linuxfoundation.org>

thanks,
-- Shuah

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

* Re: [PATCH] kselftest: alsa: Add test case for writing invalid values
  2022-01-24 15:14 ` Mark Brown
@ 2022-01-25 14:14   ` Takashi Iwai
  -1 siblings, 0 replies; 10+ messages in thread
From: Takashi Iwai @ 2022-01-25 14:14 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Shuah Khan, Takashi Iwai, linux-kselftest

On Mon, 24 Jan 2022 16:14:10 +0100,
Mark Brown wrote:
> +bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
> +{
....
> +			/* Minimum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LLONG_MIN);

This should be snd_ctl_elem_value_set_integer64()?

> +			/* Maximum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LLONG_MAX);

Ditto.


thanks,

Takashi

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

* Re: [PATCH] kselftest: alsa: Add test case for writing invalid values
@ 2022-01-25 14:14   ` Takashi Iwai
  0 siblings, 0 replies; 10+ messages in thread
From: Takashi Iwai @ 2022-01-25 14:14 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jaroslav Kysela, Takashi Iwai, Shuah Khan, linux-kselftest, alsa-devel

On Mon, 24 Jan 2022 16:14:10 +0100,
Mark Brown wrote:
> +bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
> +{
....
> +			/* Minimum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LLONG_MIN);

This should be snd_ctl_elem_value_set_integer64()?

> +			/* Maximum representable value */
> +			snd_ctl_elem_value_copy(val, ctl->def_val);
> +			snd_ctl_elem_value_set_integer(val, i, LLONG_MAX);

Ditto.


thanks,

Takashi

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

end of thread, other threads:[~2022-01-25 14:16 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-24 15:14 [PATCH] kselftest: alsa: Add test case for writing invalid values Mark Brown
2022-01-24 15:14 ` Mark Brown
2022-01-24 21:35 ` Shuah Khan
2022-01-24 21:35   ` Shuah Khan
2022-01-24 21:48   ` Mark Brown
2022-01-24 21:48     ` Mark Brown
2022-01-24 22:12     ` Shuah Khan
2022-01-24 22:12       ` Shuah Khan
2022-01-25 14:14 ` Takashi Iwai
2022-01-25 14:14   ` Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.