Linux Sound subsystem development
 help / color / mirror / Atom feed
From: Will Porter <mrwillporter@gmail.com>
To: Christian Ruppert <arc@gmx.li>
Cc: Takashi Iwai <tiwai@suse.com>, Jaroslav Kysela <perex@perex.cz>,
	linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
	Neil Andrews <neil@androos.io>
Subject: [RFC PATCH 2/6] ALSA: mixer_audient: Use the cache of usb_mixer_elem_info
Date: Wed, 11 Nov 2026 20:37:16 -0600	[thread overview]
Message-ID: <20261112023720.1503814-3-mrwillporter@gmail.com> (raw)
In-Reply-To: <20261112023720.1503814-1-mrwillporter@gmail.com>

A cached control keeps its values in an array that is allocated after
the element and reached through elem->private_data. Every struct
usb_mixer_elem_info already has fields for this: the cached bitmap and
the cache_val[] array.

Use bit i of elem->cached and elem->cache_val[i] for element channel i.
The bit means what the valid flag meant: a transfer confirmed the
value. A successful read or write records the value and sets its bit.
A failed transfer leaves the cached value and the bit unchanged. Remove
the cache struct, its accessor and the extra allocation.

The generic cache holds MAX_CHANNELS values, so refuse a control with
more hardware channels. No control in the tables has more than two.

The generic notify paths clear the cache of an element unless
get_cur_broken is set. A write-only control cannot read its value back
after that, so set get_cur_broken for the cached FORCE_SET controls.
The notify paths do not reach these elements today, because the
elements do not set their unit id.

No functional change for the controls in the tables.

Assisted-by: Claude:claude-fable-5-2
Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Will Porter <mrwillporter@gmail.com>
---
 sound/usb/mixer_audient.c | 131 +++++++++++++++++---------------------
 1 file changed, 60 insertions(+), 71 deletions(-)

diff --git a/sound/usb/mixer_audient.c b/sound/usb/mixer_audient.c
index df1e25a55a7a9..35b562b3aac51 100644
--- a/sound/usb/mixer_audient.c
+++ b/sound/usb/mixer_audient.c
@@ -555,24 +555,13 @@ static inline u16 snd_audient_kctl_channels(struct snd_kcontrol *kctl)
 }
 
 /*
- * A cached value is valid once a transfer confirmed it: a read, or a write
- * that succeeded. A probe transfer that fails leaves it invalid. CACHE |
- * FORCE_SET without INIT_HW keeps the earlier behaviour: the init value is
- * assumed, without a transfer.
+ * A cached control keeps its values in the cache of struct
+ * usb_mixer_elem_info: bit i of elem->cached and elem->cache_val[i] for
+ * element channel i. The bit is set once a transfer confirmed the value: a
+ * read, or a write that succeeded. A probe transfer that fails leaves it
+ * clear. CACHE | FORCE_SET without INIT_HW keeps the earlier behaviour: the
+ * init value is assumed, without a transfer.
  */
-struct snd_audient_cache_entry {
-	u32 value;
-	bool valid;
-};
-
-/* Cache entry of element channel i, or NULL for an uncached control. */
-static inline struct snd_audient_cache_entry *
-snd_audient_cache_slot(struct usb_mixer_elem_info *elem, unsigned int i)
-{
-	struct snd_audient_cache_entry *const cache = elem->private_data;
-
-	return cache ? cache + i : NULL;
-}
 
 
 #define AUDIENT_REQTYPE_SET (USB_DIR_OUT | USB_TYPE_CLASS \
@@ -621,26 +610,28 @@ static inline int snd_audient_set_cur(struct usb_mixer_interface *mixer,
 }
 
 
-static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
+static int snd_audient_raw_get_one(struct usb_mixer_elem_info *const elem,
 			const struct snd_audient_ctrl_type *const ctype,
-			struct snd_audient_cache_entry *const slot,
-			const u8 cidx, u32 *const value)
+			const unsigned int ch, const u8 cidx,
+			u32 *const value)
 {
+	struct usb_mixer_interface *const mixer = elem->head.mixer;
+	const bool cache = ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE;
 	int ret;
 	__le32 buf = 0;
 	s32 val;
 
-	if (slot && slot->valid) {
-		*value = slot->value;
+	if (cache && test_bit(ch, elem->cached)) {
+		*value = elem->cache_val[ch];
 
 		return ctype->len;
 	}
 
 	/*
-	 * Cached FORCE_SET controls are treated as write-only: an invalid
-	 * cache entry does not trigger a GET.
+	 * Cached FORCE_SET controls are treated as write-only: a clear
+	 * cache bit does not trigger a GET.
 	 */
-	if (slot && (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET))
+	if (cache && (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET))
 		return -ENODATA;
 
 	ret = snd_audient_get_cur(mixer, ctype, cidx, &buf);
@@ -654,27 +645,27 @@ static int snd_audient_raw_get_one(struct usb_mixer_interface *const mixer,
 
 	*value = val;
 
-	if (slot) {
-		slot->value = val;
-		slot->valid = true;
+	if (cache) {
+		elem->cache_val[ch] = val;
+		set_bit(ch, elem->cached);
 	}
 
 	return ret;
 }
 
-static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
+static int snd_audient_raw_set_one(struct usb_mixer_elem_info *const elem,
 			const struct snd_audient_ctrl_type *const ctype,
-			struct snd_audient_cache_entry *const slot,
-			const u8 cidx, const u32 value,
-			bool force)
+			const unsigned int ch, const u8 cidx,
+			const u32 value, bool force)
 {
+	struct usb_mixer_interface *const mixer = elem->head.mixer;
 	int ret;
 	u32 val;
 
 	if (force) {
 		val = !value;		/* just make it different */
 	} else {
-		ret = snd_audient_raw_get_one(mixer, ctype, slot, cidx, &val);
+		ret = snd_audient_raw_get_one(elem, ctype, ch, cidx, &val);
 		if (ret == -ENODATA)	/* FORCE_SET and not cached yet */
 			val = !value;	/* just make it different */
 		else if (ret < 0)
@@ -690,9 +681,9 @@ static int snd_audient_raw_set_one(struct usb_mixer_interface *const mixer,
 		if (ret < 0)
 			return ret;
 
-		if (slot) {
-			slot->value = value;
-			slot->valid = true;
+		if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE) {
+			elem->cache_val[ch] = value;
+			set_bit(ch, elem->cached);
 		}
 	}
 
@@ -724,9 +715,8 @@ static int snd_ctl_audient_boolean_get(struct snd_kcontrol *kctl,
 		u32 buf;
 		int ret;
 
-		ret = snd_audient_raw_get_one(mixer, ctype,
-					      snd_audient_cache_slot(elem, i),
-					      base + i, &buf);
+		ret = snd_audient_raw_get_one(elem, ctype, i, base + i,
+					      &buf);
 		if (ret == -ENODATA)	/* unknown write-only value: off */
 			buf = ctype->min.integer;
 		else if (ret < 0)
@@ -767,9 +757,8 @@ static int snd_ctl_audient_boolean_set(struct snd_kcontrol *kctl,
 							  : ctype->min.integer;
 		int ret;
 
-		ret = snd_audient_raw_set_one(mixer, ctype,
-					      snd_audient_cache_slot(elem, i),
-					      base + i, buf, false);
+		ret = snd_audient_raw_set_one(elem, ctype, i, base + i,
+					      buf, false);
 		if (ret < 0)
 			return ret;
 
@@ -839,9 +828,8 @@ static int snd_ctl_audient_integer_get(struct snd_kcontrol *kctl,
 		s32 val;
 		int ret;
 
-		ret = snd_audient_raw_get_one(mixer, ctype,
-					      snd_audient_cache_slot(elem, i),
-					      base + i, &val);
+		ret = snd_audient_raw_get_one(elem, ctype, i, base + i,
+					      &val);
 		if (ret == -ENODATA)	/* unknown write-only value: minimum */
 			val = ctype->min.integer;
 		else if (ret < 0)
@@ -890,9 +878,8 @@ static int snd_ctl_audient_integer_set(struct snd_kcontrol *kctl,
 						   ctl_val->value.integer.value[i]);
 		int ret;
 
-		ret = snd_audient_raw_set_one(mixer, ctype,
-					      snd_audient_cache_slot(elem, i),
-					      base + i, val, false);
+		ret = snd_audient_raw_set_one(elem, ctype, i, base + i,
+					      val, false);
 		if (ret < 0)
 			return ret;
 
@@ -940,9 +927,8 @@ static int snd_ctl_audient_enum_get(struct snd_kcontrol *kctl,
 		int item;
 		int ret;
 
-		ret = snd_audient_raw_get_one(mixer, ctype,
-					      snd_audient_cache_slot(elem, i),
-					      base + i, &val);
+		ret = snd_audient_raw_get_one(elem, ctype, i, base + i,
+					      &val);
 		if (ret == -ENODATA)	/* unknown write-only value: init */
 			val = ctype->init;
 		else if (ret < 0)
@@ -990,9 +976,8 @@ static int snd_ctl_audient_enum_set(struct snd_kcontrol *kctl,
 		const unsigned int item = ctl_val->value.enumerated.item[i];
 		int ret;
 
-		ret = snd_audient_raw_set_one(mixer, ctype,
-					      snd_audient_cache_slot(elem, i),
-					      base + i, e->values[item], false);
+		ret = snd_audient_raw_set_one(elem, ctype, i, base + i,
+					      e->values[item], false);
 		if (ret < 0)
 			return ret;
 
@@ -1072,24 +1057,28 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
 		return -EINVAL;
 	}
 
-	elem = kzalloc(sizeof(*elem)
-		       + ((ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
-			  ? (sizeof(struct snd_audient_cache_entry)
-			     * ctrl->hwchannels)
-			  : 0),
-		       GFP_KERNEL);
+	if (ctrl->hwchannels > MAX_CHANNELS)
+		return -EINVAL;
+
+	elem = kzalloc_obj(*elem);
 	if (!elem)
 		return -ENOMEM;
 
 	elem->head.mixer = mixer;
 	elem->channels = ctrl->channels;
 
-	if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE)
-		elem->private_data = elem + 1;
+	/*
+	 * The generic notify paths clear the cache of an element unless
+	 * get_cur_broken is set. A write-only control cannot read its value
+	 * back after that.
+	 */
+	if ((ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE) &&
+	    (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET))
+		elem->get_cur_broken = 1;
 
 	/*
 	 * A failed probe transfer must not fail the card: warn and keep the
-	 * control. Its cache entry stays invalid, so a readable control reads
+	 * control. Its cache bit stays clear, so a readable control reads
 	 * the device again on its next get, and a write-only control shows an
 	 * unknown value until a put succeeds.
 	 */
@@ -1097,8 +1086,7 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
 		int i;
 
 		for (i = 0; i < ctrl->hwchannels; i++) {
-			ret = snd_audient_raw_set_one(mixer, ctype,
-						      snd_audient_cache_slot(elem, i),
+			ret = snd_audient_raw_set_one(elem, ctype, i,
 						      ctrl->val_offs + i,
 						      ctype->init, true);
 			if (ret < 0)
@@ -1106,20 +1094,21 @@ static int snd_audient_add_ctrl(struct usb_mixer_interface *mixer,
 					       "%s: cannot set channel %d (%d)\n",
 					       ctrl->name, i, ret);
 		}
-	} else if (elem->private_data) {
-		struct snd_audient_cache_entry *const cache = elem->private_data;
+	} else if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_CACHE) {
 		int i;
 
 		for (i = 0; i < ctrl->hwchannels; i++) {
+			u32 val;
+
 			if (ctype->flags & SND_AUDIENT_CTYPE_FLAGS_FORCE_SET) {
-				cache[i].value = ctype->init;
-				cache[i].valid = true;
+				elem->cache_val[i] = ctype->init;
+				set_bit(i, elem->cached);
 				continue;
 			}
 
-			ret = snd_audient_raw_get_one(mixer, ctype, cache + i,
+			ret = snd_audient_raw_get_one(elem, ctype, i,
 						      ctrl->val_offs + i,
-						      &cache[i].value);
+						      &val);
 			if (ret < 0)
 				usb_audio_warn(mixer->chip,
 					       "%s: cannot read channel %d (%d)\n",
-- 
2.47.3


  parent reply	other threads:[~2026-11-12  2:37 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-19 23:01 [PATCH 0/5] ALSA: usb-audio: Audient iD14 MkI vendor controls Will Porter
2026-10-19 23:01 ` [PATCH 1/5] ALSA: usb-audio: Give the Audient iD14 MkI its own mixer map Will Porter
2026-10-19 23:01 ` [PATCH 2/5] ALSA: usb-audio: Add Audient iD14 MkI monitor and routing controls Will Porter
2026-10-19 23:01 ` [PATCH 3/5] ALSA: usb-audio: Add Audient iD14 MkI mixer matrix Will Porter
2026-10-19 23:01 ` [PATCH 4/5] ALSA: usb-audio: Add Audient iD14 MkI write-only controls Will Porter
2026-10-19 23:01 ` [PATCH 5/5] ALSA: usb-audio: Replace the Audient iD14 MkI FU 12 control Will Porter
2026-10-20 13:10 ` [PATCH 0/5] ALSA: usb-audio: Audient iD14 MkI vendor controls Takashi Iwai
2026-10-26 18:36 ` Christian Ruppert
2026-10-26 21:44   ` Will Porter
2026-10-27  6:35     ` Takashi Iwai
2026-10-27 23:21     ` Christian Ruppert
2026-10-28  2:36       ` Will Porter
2026-11-01  0:00         ` [RFC PATCH 00/11] " Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 01/11] ALSA: rename mixer_evo to mixer_audient Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 02/11] ALSA: mixer_audient: explicitly define full control names Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 03/11] ALSA: mixer_audient: clarify USB control message definition Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 04/11] ALSA: mixer_audient: Add length check to USB transfer Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 05/11] ALSA: mixer_audient: make USB control transfer size configurable Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 06/11] ALSA: mixer_audient: Allow arbitrary boolean control values Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 07/11] ALSA: mixer_audient: Add integer range checks Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 08/11] ALSA: mixer_audient: Add control initialization at probe Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 09/11] ALSA: mixer_audient: Add readback-free parameter set Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 10/11] ALSA: mixer_audient: add control value caching Christian Ruppert
2026-11-01  0:00           ` [RFC PATCH 11/11] ALSA: mixer_audient: Create matrix programmatically Christian Ruppert
2026-11-01 17:35           ` [RFC PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor controls Will Porter
2026-11-02  0:32             ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 01/11] ALSA: rename mixer_evo to mixer_audient Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 02/11] ALSA: mixer_audient: explicitly define full control names Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 03/11] ALSA: mixer_audient: clarify USB control message definition Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 04/11] ALSA: mixer_audient: Add length check to USB transfer Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 05/11] ALSA: mixer_audient: make USB control transfer size configurable Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 06/11] ALSA: mixer_audient: Allow arbitrary boolean control values Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 07/11] ALSA: mixer_audient: Add integer range checks Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 08/11] ALSA: mixer_audient: Add control initialization at probe Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 09/11] ALSA: mixer_audient: Add readback-free parameter set Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 10/11] ALSA: mixer_audient: add control value caching Christian Ruppert
2026-11-02  0:32               ` [RFC V2 PATCH 11/11] ALSA: mixer_audient: Create matrix programmatically Christian Ruppert
2026-11-03  2:45               ` [RFC V2 PATCH 00/11] Re: ALSA: usb-audio: Audient iD14 MkI vendor Will Porter
2026-11-03 23:01                 ` Christian Ruppert
2026-11-06 16:31                   ` [RFC PATCH 00/16] ALSA: usb-audio: Audient driver and iD14 MkI support Will Porter
2026-11-06 16:31                     ` [RFC PATCH 01/16] ALSA: rename mixer_evo to mixer_audient Will Porter
2026-11-06 16:31                     ` [RFC PATCH 02/16] ALSA: mixer_audient: explicitly define full control names Will Porter
2026-11-06 16:31                     ` [RFC PATCH 03/16] ALSA: mixer_audient: clarify USB control message definition Will Porter
2026-11-06 16:31                     ` [RFC PATCH 04/16] ALSA: mixer_audient: Add length check to USB transfer Will Porter
2026-11-06 16:31                     ` [RFC PATCH 05/16] ALSA: mixer_audient: make USB control transfer size configurable Will Porter
2026-11-06 16:31                     ` [RFC PATCH 06/16] ALSA: mixer_audient: Allow arbitrary boolean control values Will Porter
2026-11-06 16:31                     ` [RFC PATCH 07/16] ALSA: mixer_audient: Add integer range checks Will Porter
2026-11-06 16:31                     ` [RFC PATCH 08/16] ALSA: mixer_audient: Add control initialization at probe Will Porter
2026-11-06 16:31                     ` [RFC PATCH 09/16] ALSA: mixer_audient: Add readback-free parameter set Will Porter
2026-11-06 16:31                     ` [RFC PATCH 10/16] ALSA: mixer_audient: add control value caching Will Porter
2026-11-06 16:31                     ` [RFC PATCH 11/16] ALSA: mixer_audient: Create matrix programmatically Will Porter
2026-11-06 16:31                     ` [RFC PATCH 12/16] ALSA: mixer_audient: Add a step size to integer controls Will Porter
2026-11-06 16:31                     ` [RFC PATCH 13/16] ALSA: mixer_audient: Add enumerated controls Will Porter
2026-11-06 16:53                       ` sashiko-bot
2026-11-06 20:29                         ` Will Porter
2026-11-09 22:30                           ` Christian Ruppert
2026-11-10  2:13                             ` Will Porter
2026-11-10 20:57                               ` Christian Ruppert
2026-11-12  2:37                                 ` [RFC PATCH 0/6] ALSA: mixer_audient: Cache rework and simplifications Will Porter
2026-11-12  2:37                                   ` [RFC PATCH 1/6] ALSA: mixer_audient: Fold in the review changes to patches 12-16 Will Porter
2026-11-12  2:37                                   ` Will Porter [this message]
2026-11-12  2:37                                   ` [RFC PATCH 3/6] ALSA: mixer_audient: Replace three flags with an access mode Will Porter
2026-11-12  2:37                                   ` [RFC PATCH 4/6] ALSA: mixer_audient: Fill the cache on first use Will Porter
2026-11-12  2:37                                   ` [RFC PATCH 5/6] ALSA: mixer_audient: Use one get and one put for all control types Will Porter
2026-11-12  2:37                                   ` [RFC PATCH 6/6] ALSA: mixer_audient: Use the same cache for the iD14 MkI FU 12 control Will Porter
2026-11-06 16:31                     ` [RFC PATCH 14/16] ALSA: usb-audio: Give the Audient iD14 MkI its own mixer map Will Porter
2026-11-06 16:31                     ` [RFC PATCH 15/16] ALSA: mixer_audient: Add Audient iD14 MkI controls Will Porter
2026-11-06 16:31                     ` [RFC PATCH 16/16] ALSA: mixer_audient: Replace the Audient iD14 MkI FU 12 control Will Porter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261112023720.1503814-3-mrwillporter@gmail.com \
    --to=mrwillporter@gmail.com \
    --cc=arc@gmx.li \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=neil@androos.io \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox