Module Name:    src
Committed By:   isaki
Date:           Wed Jul 21 06:18:32 UTC 2021

Modified Files:
        src/tests/dev/audio: audiotest.c

Log Message:
Add AUDIO_SETINFO_gain_balance test.
The test checks whether AUDIO_SETINFO can change the gain and the balance
at the same time (if MD driver has the capability).  See PR kern/56308.


To generate a diff of this commit:
cvs rdiff -u -r1.13 -r1.14 src/tests/dev/audio/audiotest.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/tests/dev/audio/audiotest.c
diff -u src/tests/dev/audio/audiotest.c:1.13 src/tests/dev/audio/audiotest.c:1.14
--- src/tests/dev/audio/audiotest.c:1.13	Tue Oct 13 09:00:17 2020
+++ src/tests/dev/audio/audiotest.c	Wed Jul 21 06:18:32 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: audiotest.c,v 1.13 2020/10/13 09:00:17 rin Exp $	*/
+/*	$NetBSD: audiotest.c,v 1.14 2021/07/21 06:18:32 isaki Exp $	*/
 
 /*
  * Copyright (C) 2019 Tetsuya Isaki. All rights reserved.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: audiotest.c,v 1.13 2020/10/13 09:00:17 rin Exp $");
+__RCSID("$NetBSD: audiotest.c,v 1.14 2021/07/21 06:18:32 isaki Exp $");
 
 #include <errno.h>
 #include <fcntl.h>
@@ -5573,6 +5573,294 @@ DEF(AUDIO_SETINFO_gain)
 	XP_SYS_EQ(0, r);
 }
 
+/*
+ * Look if there are any (non-zero) gain values that can be changed.
+ * If any gain can be set, it is set to gain[0].
+ * If another gain can be set, it is set to gain[1], otherwise gain[1] = -1.
+ * This is for AUDIO_SETINFO_gain_balance.
+ */
+static void
+get_changeable_gain(int fd, int *gain, const char *dir, int offset)
+{
+	struct audio_info ai;
+	int *ai_gain;
+	int hi;
+	int lo;
+	int r;
+
+	/* A hack to handle ai.{play,record}.gain in the same code.. */
+	ai_gain = (int *)(((char *)&ai) + offset);
+
+	/* Try to set the maximum gain */
+	AUDIO_INITINFO(&ai);
+	*ai_gain = AUDIO_MAX_GAIN;
+	r = IOCTL(fd, AUDIO_SETINFO, &ai, "%s.gain=%d", dir, *ai_gain);
+	XP_SYS_EQ(0, r);
+	/* Get again.  The value you set is not always used as is. */
+	AUDIO_INITINFO(&ai);
+	r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai");
+	XP_SYS_EQ(0, r);
+	hi = *ai_gain;
+
+	/* Look for next configurable value. */
+	for (lo = hi - 1; lo >= 0; lo--) {
+		AUDIO_INITINFO(&ai);
+		*ai_gain = lo;
+		r = IOCTL(fd, AUDIO_SETINFO, &ai, "%s.gain=%d", dir, *ai_gain);
+		XP_SYS_EQ(0, r);
+		/* Get again */
+		r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai");
+		XP_SYS_EQ(0, r);
+		if (*ai_gain != hi) {
+			lo = *ai_gain;
+			break;
+		}
+	}
+
+	/* Now gain is lo(=gain[0]). */
+
+	/*
+	 * hi  lo
+	 * --- ---
+	 *  <0  <0          : not available.
+	 * >=0  <0          : available but not changeable.
+	 * >=0 >=0 (hi!=lo) : available and changeable.
+	 */
+	if (hi < 0) {
+		gain[0] = -1;
+		gain[1] = -1;
+		DPRINTF("  > %s.gain cannot be set\n", dir);
+	} else if (lo < 0) {
+		gain[0] = hi;
+		gain[1] = -1;
+		DPRINTF("  > %s.gain can only be set %d\n", dir, gain[0]);
+	} else {
+		gain[0] = lo;
+		gain[1] = hi;
+		DPRINTF("  > %s.gain can be set %d, %d\n",
+		    dir, gain[0], gain[1]);
+	}
+}
+
+/*
+ * Look if there are any balance values that can be changed.
+ * If any balance value can be set, it is set to balance[0].
+ * If another balance value can be set, it is set to balance[1],
+ * otherwise balance[1] = -1.
+ * This is for AUDIO_SETINFO_gain_balance.
+ */
+static void
+get_changeable_balance(int fd, int *balance, const char *dir, int offset)
+{
+	struct audio_info ai;
+	u_char *ai_balance;
+	u_char left;
+	u_char right;
+	int r;
+
+	/* A hack to handle ai.{play,record}.balance in the same code.. */
+	ai_balance = ((u_char *)&ai) + offset;
+
+	/* Look for the right side configurable value. */
+	AUDIO_INITINFO(&ai);
+	*ai_balance = AUDIO_RIGHT_BALANCE;
+	r = IOCTL(fd, AUDIO_SETINFO, &ai, "%s.balance=%d", dir, *ai_balance);
+	XP_SYS_EQ(0, r);
+	/* Get again.  The value you set is not always used as is. */
+	r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai");
+	XP_SYS_EQ(0, r);
+	right = *ai_balance;
+
+	/* Look for the left side configurable value. */
+	AUDIO_INITINFO(&ai);
+	*ai_balance = AUDIO_LEFT_BALANCE;
+	r = IOCTL(fd, AUDIO_SETINFO, &ai, "%s.balance=%d", dir, *ai_balance);
+	XP_SYS_EQ(0, r);
+	/* Get again */
+	r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai");
+	XP_SYS_EQ(0, r);
+	left = *ai_balance;
+
+	/* Now balance is the left(=balance[0]). */
+
+	if (left == right) {
+		/* The driver has no balance feature. */
+		balance[0] = left;
+		balance[1] = -1;
+		DPRINTF("  > %s.balance can only be set %d\n",
+		    dir, balance[0]);
+	} else {
+		balance[0] = left;
+		balance[1] = right;
+		DPRINTF("  > %s.balance can be set %d, %d\n",
+		    dir, balance[0], balance[1]);
+	}
+}
+
+/*
+ * Check whether gain and balance can be set at the same time.
+ * PR kern/56308
+ */
+DEF(AUDIO_SETINFO_gain_balance)
+{
+	struct audio_info oai;
+	struct audio_info ai;
+	int i;
+	int mode;
+	int fd;
+	int r;
+	int pgain[2];
+	int pbalance[2];
+	int rgain[2];
+	int rbalance[2];
+	bool ptest;
+	bool rtest;
+
+	TEST("AUDIO_SETINFO_gain_balance");
+
+	mode = openable_mode();
+	fd = OPEN(devaudio, mode);
+	REQUIRED_SYS_OK(fd);
+
+	/* Backup current gain and balance */
+	r = IOCTL(fd, AUDIO_GETINFO, &oai, "&oai");
+	XP_SYS_EQ(0, r);
+
+	if (debug) {
+		printf("  > old play.gain      = %d\n", oai.play.gain);
+		printf("  > old play.balance   = %d\n", oai.play.balance);
+		printf("  > old record.gain    = %d\n", oai.record.gain);
+		printf("  > old record.balance = %d\n", oai.record.balance);
+	}
+
+	for (i = 0; i < 2; i++) {
+		pgain[i]    = -1;
+		pbalance[i] = -1;
+		rgain[i]    = -1;
+		rbalance[i] = -1;
+	}
+
+	/*
+	 * First, check each one separately can be changed.
+	 *
+	 * The simplest two different gain values are zero and non-zero.
+	 * But some device drivers seem to process balance differently
+	 * when the gain is high enough and when the gain is zero or near.
+	 * So I needed to select two different "non-zero (and high if
+	 * possible)" gains.
+	 */
+	if (hw_canplay()) {
+		get_changeable_gain(fd, pgain, "play",
+		    offsetof(struct audio_info, play.gain));
+		get_changeable_balance(fd, pbalance, "play",
+		    offsetof(struct audio_info, play.balance));
+	}
+	if (hw_canrec()) {
+		get_changeable_gain(fd, rgain, "record",
+		    offsetof(struct audio_info, record.gain));
+		get_changeable_balance(fd, rbalance, "record",
+		    offsetof(struct audio_info, record.balance));
+	}
+
+	/*
+	 * [0] [1]
+	 * --- ---
+	 *  -1  *  : not available.
+	 * >=0  -1 : available but not changeable.
+	 * >=0 >=0 : available and changeable.  It can be tested.
+	 */
+	ptest = (pgain[0]    >= 0 && pgain[1]    >= 0 &&
+	         pbalance[0] >= 0 && pbalance[1] >= 0);
+	rtest = (rgain[0]    >= 0 && rgain[1]    >= 0 &&
+	         rbalance[0] >= 0 && rbalance[1] >= 0);
+
+	if (ptest == false && rtest == false) {
+		XP_SKIP(
+		    "The test requires changeable gain and changeable balance");
+
+		/* Restore as possible */
+		AUDIO_INITINFO(&ai);
+		ai.play.gain      = oai.play.gain;
+		ai.play.balance   = oai.play.balance;
+		ai.record.gain    = oai.record.gain;
+		ai.record.balance = oai.record.balance;
+		r = IOCTL(fd, AUDIO_SETINFO, &ai, "restore all");
+		XP_SYS_EQ(0, r);
+
+		r = CLOSE(fd);
+		XP_SYS_EQ(0, r);
+		return;
+	}
+
+	/*
+	 * If both play.gain and play.balance are changeable,
+	 * it should be able to set both at the same time.
+	 */
+	if (ptest) {
+		AUDIO_INITINFO(&ai);
+		ai.play.gain    = pgain[1];
+		ai.play.balance = pbalance[1];
+		r = IOCTL(fd, AUDIO_SETINFO, &ai, "play.gain=%d/balance=%d",
+		    ai.play.gain, ai.play.balance);
+		XP_SYS_EQ(0, r);
+
+		AUDIO_INITINFO(&ai);
+		r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai");
+		XP_SYS_EQ(0, r);
+
+		DPRINTF("  > setting play.gain=%d/balance=%d: "
+		    "result gain=%d/balance=%d\n",
+		    pgain[1], pbalance[1], ai.play.gain, ai.play.balance);
+		XP_EQ(ai.play.gain,    pgain[1]);
+		XP_EQ(ai.play.balance, pbalance[1]);
+	}
+	/*
+	 * If both record.gain and record.balance are changeable,
+	 * it should be able to set both at the same time.
+	 */
+	if (rtest) {
+		AUDIO_INITINFO(&ai);
+		ai.record.gain    = rgain[1];
+		ai.record.balance = rbalance[1];
+		r = IOCTL(fd, AUDIO_SETINFO, &ai, "record.gain=%d/balance=%d",
+		    ai.record.gain, ai.record.balance);
+		XP_SYS_EQ(0, r);
+
+		AUDIO_INITINFO(&ai);
+		r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai");
+		XP_SYS_EQ(0, r);
+
+		DPRINTF("  > setting record.gain=%d/balance=%d: "
+		    "result gain=%d/balance=%d\n",
+		    rgain[1], rbalance[1], ai.record.gain, ai.record.balance);
+		XP_EQ(ai.record.gain,    rgain[1]);
+		XP_EQ(ai.record.balance, rbalance[1]);
+	}
+
+	/*
+	 * Restore all values as possible at the same time.
+	 * This restore is also a test.
+	 */
+	AUDIO_INITINFO(&ai);
+	ai.play.gain      = oai.play.gain;
+	ai.play.balance   = oai.play.balance;
+	ai.record.gain    = oai.record.gain;
+	ai.record.balance = oai.record.balance;
+	r = IOCTL(fd, AUDIO_SETINFO, &ai, "restore all");
+	XP_SYS_EQ(0, r);
+
+	AUDIO_INITINFO(&ai);
+	r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai");
+	XP_SYS_EQ(0, r);
+	XP_EQ(oai.play.gain,      ai.play.gain);
+	XP_EQ(oai.play.balance,   ai.play.balance);
+	XP_EQ(oai.record.gain,    ai.record.gain);
+	XP_EQ(oai.record.balance, ai.record.balance);
+
+	r = CLOSE(fd);
+	XP_SYS_EQ(0, r);
+}
+
 #define NENC	(AUDIO_ENCODING_AC3 + 1)
 #define NPREC	(5)
 /*
@@ -6368,6 +6656,7 @@ struct testentry testtable[] = {
 	ENT(AUDIO_SETINFO_pause_RDWR_2),
 	ENT(AUDIO_SETINFO_pause_RDWR_3),
 	ENT(AUDIO_SETINFO_gain),
+	ENT(AUDIO_SETINFO_gain_balance),
 	ENT(AUDIO_GETENC_range),
 	ENT(AUDIO_GETENC_error),
 	ENT(AUDIO_ERROR_RDONLY),

Reply via email to