Commit a3542d1b authored by Cássio Gabriel's avatar Cássio Gabriel Committed by Takashi Iwai
Browse files

ALSA: caiaq: Fix control_put() result and cache rollback



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: 8e3cd08e ("[ALSA] caiaq - add control API and more input features")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarCássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260417-caiaq-control-put-v1-1-c37826e92447@gmail.com


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 4ff036f9
Loading
Loading
Loading
Loading
+36 −16
Original line number Diff line number Diff line
@@ -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,9 +118,10 @@ 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,
			ret = usb_bulk_msg(cdev->chip.dev,
					   usb_sndbulkpipe(cdev->chip.dev, 8),
				     cdev->ep8_out_buf, sizeof(cdev->ep8_out_buf),
					   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,
			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;
}