]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: caiaq: Fix control_put() result and cache rollback
authorCássio Gabriel <cassiogabrielcontato@gmail.com>
Fri, 17 Apr 2026 13:41:33 +0000 (10:41 -0300)
committerTakashi Iwai <tiwai@suse.de>
Fri, 17 Apr 2026 15:52:00 +0000 (17:52 +0200)
control_put() always returns 1 and updates cdev->control_state[]
before sending the USB command. It also ignores transport errors
from usb_bulk_msg(), snd_usb_caiaq_send_command(), and
snd_usb_caiaq_send_command_bank().

That breaks the ALSA .put() contract and can leave control_get()
reporting a cached value the device never accepted.

Return 0 for unchanged values, propagate transport failures,
and restore the cached byte when the write fails.

Fixes: 8e3cd08ed8e59 ("[ALSA] caiaq - add control API and more input features")
Cc: stable@vger.kernel.org
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260417-caiaq-control-put-v1-1-c37826e92447@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/caiaq/control.c

index af459c49baf4c235e1a9b0792c7e147334e2f0cb..4598fb7e8be0252d99d6a3c20d7cb0ca927ba75a 100644 (file)
@@ -87,6 +87,7 @@ static int control_put(struct snd_kcontrol *kcontrol,
        struct snd_usb_caiaqdev *cdev = caiaqdev(chip->card);
        int pos = kcontrol->private_value;
        int v = ucontrol->value.integer.value[0];
+       int ret;
        unsigned char cmd;
 
        switch (cdev->chip.usb_id) {
@@ -103,6 +104,10 @@ static int control_put(struct snd_kcontrol *kcontrol,
 
        if (pos & CNT_INTVAL) {
                int i = pos & ~CNT_INTVAL;
+               unsigned char old = cdev->control_state[i];
+
+               if (old == v)
+                       return 0;
 
                cdev->control_state[i] = v;
 
@@ -113,10 +118,11 @@ static int control_put(struct snd_kcontrol *kcontrol,
                        cdev->ep8_out_buf[0] = i;
                        cdev->ep8_out_buf[1] = v;
 
-                       usb_bulk_msg(cdev->chip.dev,
-                                    usb_sndbulkpipe(cdev->chip.dev, 8),
-                                    cdev->ep8_out_buf, sizeof(cdev->ep8_out_buf),
-                                    &actual_len, 200);
+                       ret = usb_bulk_msg(cdev->chip.dev,
+                                          usb_sndbulkpipe(cdev->chip.dev, 8),
+                                          cdev->ep8_out_buf,
+                                          sizeof(cdev->ep8_out_buf),
+                                          &actual_len, 200);
                } else if (cdev->chip.usb_id ==
                        USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER)) {
 
@@ -128,21 +134,36 @@ static int control_put(struct snd_kcontrol *kcontrol,
                                offset = MASCHINE_BANK_SIZE;
                        }
 
-                       snd_usb_caiaq_send_command_bank(cdev, cmd, bank,
-                                       cdev->control_state + offset,
-                                       MASCHINE_BANK_SIZE);
+                       ret = snd_usb_caiaq_send_command_bank(cdev, cmd, bank,
+                                                             cdev->control_state + offset,
+                                                             MASCHINE_BANK_SIZE);
                } else {
-                       snd_usb_caiaq_send_command(cdev, cmd,
-                                       cdev->control_state, sizeof(cdev->control_state));
+                       ret = snd_usb_caiaq_send_command(cdev, cmd,
+                                                        cdev->control_state,
+                                                        sizeof(cdev->control_state));
+               }
+
+               if (ret < 0) {
+                       cdev->control_state[i] = old;
+                       return ret;
                }
        } else {
-               if (v)
-                       cdev->control_state[pos / 8] |= 1 << (pos % 8);
-               else
-                       cdev->control_state[pos / 8] &= ~(1 << (pos % 8));
+               int idx = pos / 8;
+               unsigned char mask = 1 << (pos % 8);
+               unsigned char old = cdev->control_state[idx];
+               unsigned char val = v ? (old | mask) : (old & ~mask);
 
-               snd_usb_caiaq_send_command(cdev, cmd,
-                               cdev->control_state, sizeof(cdev->control_state));
+               if (old == val)
+                       return 0;
+
+               cdev->control_state[idx] = val;
+               ret = snd_usb_caiaq_send_command(cdev, cmd,
+                                                cdev->control_state,
+                                                sizeof(cdev->control_state));
+               if (ret < 0) {
+                       cdev->control_state[idx] = old;
+                       return ret;
+               }
        }
 
        return 1;
@@ -640,4 +661,3 @@ int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *cdev)
 
        return ret;
 }
-