summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-08-19 18:20:32 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-08-19 18:20:32 +0200
commite915bbe928c500d29db12f4a2d3fd31e4c6b07be (patch)
tree9694f7588d6bb4a3443974ffc397ce6e80b90b1a /sound
parente2466b32e02a49318b99520c40b397062c65c84c (diff)
parentffc82ed665314ccf141abc4710830f3f424d98ea (diff)
downloadlinux-rolling-stable.tar.gz
linux-rolling-stable.zip
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_spi.c3
-rw-r--r--sound/usb/endpoint.c6
-rw-r--r--sound/usb/fcp.c10
-rw-r--r--sound/usb/midi2.c9
-rw-r--r--sound/usb/mixer.c60
-rw-r--r--sound/usb/mixer.h1
-rw-r--r--sound/usb/quirks.c7
-rw-r--r--sound/usb/usbaudio.h13
-rw-r--r--sound/usb/usx2y/us144mkii_capture.c1
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c2
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c2
11 files changed, 100 insertions, 14 deletions
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
index d243baff95a7..5b38e4e9f228 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
@@ -344,6 +344,8 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda,
strscpy(p->dev_name, hid, sizeof(p->dev_name));
physdev = get_device(acpi_get_first_physical_node(adev));
acpi_dev_put(adev);
+ if (!physdev)
+ return -ENODEV;
property = "ti,dev-index";
ret = device_property_count_u32(physdev, property);
@@ -386,7 +388,6 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda,
err:
dev_err(p->dev, "read acpi error, ret: %d\n", ret);
put_device(physdev);
- acpi_dev_put(adev);
return ret;
}
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index dca06ba1c67e..a1d449f2a342 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -385,13 +385,15 @@ static int prepare_inbound_urb(struct snd_usb_endpoint *ep,
case SND_USB_ENDPOINT_TYPE_DATA:
offs = 0;
for (i = 0; i < urb_ctx->packets; i++) {
+ if (offs + ep->curpacksize > urb_ctx->buffer_size)
+ break;
urb->iso_frame_desc[i].offset = offs;
urb->iso_frame_desc[i].length = ep->curpacksize;
offs += ep->curpacksize;
}
urb->transfer_buffer_length = offs;
- urb->number_of_packets = urb_ctx->packets;
+ urb->number_of_packets = i;
break;
case SND_USB_ENDPOINT_TYPE_SYNC:
@@ -1243,10 +1245,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
u->index = i;
u->ep = ep;
u->packets = urb_packs;
- u->buffer_size = maxsize * u->packets;
if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
u->packets++; /* for transfer delimiter */
+ u->buffer_size = maxsize * u->packets;
u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
if (!u->urb)
goto out_of_memory;
diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c
index c45dbe4d4532..ec1ddfa4d0ce 100644
--- a/sound/usb/fcp.c
+++ b/sound/usb/fcp.c
@@ -129,6 +129,10 @@ struct fcp_data {
#define FCP_SEGMENT_APP_GOLD 0
+#define FCP_MAX_METER_MAP_SIZE \
+ (sizeof_field(struct snd_ctl_elem_value, value.integer.value) / \
+ sizeof(long))
+
/* Forward declarations */
static int fcp_init(struct usb_mixer_interface *mixer,
void *step0_resp, void *step2_resp);
@@ -410,6 +414,9 @@ static int fcp_meter_ctl_get(struct snd_kcontrol *kctl,
if (err < 0)
return err;
+ if (WARN_ON_ONCE(elem->channels > FCP_MAX_METER_MAP_SIZE))
+ return -EINVAL;
+
/* copy & translate from resp[] using meter_level_map[] */
for (i = 0; i < elem->channels; i++) {
int idx = private->meter_level_map[i];
@@ -636,7 +643,8 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer,
}
/* Validate the map size */
- if (map.map_size < 1 || map.map_size > 255 ||
+ if (map.map_size < 1 ||
+ map.map_size > FCP_MAX_METER_MAP_SIZE ||
map.meter_slots < 1 || map.meter_slots > 255)
return -EINVAL;
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
index 83980fb83ac8..1dc78f536ef9 100644
--- a/sound/usb/midi2.c
+++ b/sound/usb/midi2.c
@@ -677,6 +677,14 @@ static int parse_midi_2_0_endpoints(struct snd_usb_midi2_interface *umidi)
return 0;
}
+static void free_ump_private_data(struct snd_ump_endpoint *ump)
+{
+ struct snd_usb_midi2_ump *rmidi = ump->private_data;
+
+ if (rmidi)
+ rmidi->ump = NULL;
+}
+
static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi)
{
struct snd_usb_midi2_ump *rmidi;
@@ -727,6 +735,7 @@ static int create_midi2_ump(struct snd_usb_midi2_interface *umidi,
ump->private_data = rmidi;
ump->ops = &snd_usb_midi_v2_ump_ops;
+ ump->private_free = free_ump_private_data;
rmidi->eps[STR_IN] = ep_in;
rmidi->eps[STR_OUT] = ep_out;
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index dbcefcaf7c36..142f5ca7a72d 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -434,6 +434,11 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval,
*value = cval->cache_val[index];
return 0;
}
+
+ /* The current value is always provided by the cache after initialization. */
+ if (cval->get_cur_broken)
+ return -ENXIO;
+
err = get_cur_mix_raw(cval, channel, value);
if (err < 0) {
if (!cval->head.mixer->ignore_ctl_error)
@@ -1234,7 +1239,7 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
err = snd_usb_get_cur_mix_value(cval, ch, idx, &val);
if (!err)
return;
- if (!cval->head.mixer->ignore_ctl_error)
+ if (!cval->head.mixer->ignore_ctl_error && !cval->get_cur_broken)
usb_audio_warn(cval->head.mixer->chip,
"%d:%d: failed to get current value for ch %d (%d)\n",
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
@@ -1248,8 +1253,16 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
* Some devices' volume control mixers are sticky, which accept SET_CUR but
* do absolutely nothing.
*
- * Prevent sticky mixers from being registered, otherwise they confuses
- * userspace and results in ineffective volume control.
+ * Check the return values of GET_CUR with different SET_CUR values. Consider
+ * the mixer as sticky if GET_CUR always returns a constant value.
+ *
+ * Some devices have effective SET_CUR despite GET_CUR being constant. Do not
+ * consider the mixer as sticky if a quirk flag indicates that.
+ *
+ * Gate the registration of sticky mixers to prevent confusing userspace, so
+ * that they won't cause ineffective volume control. However, for mixers with
+ * effective SET_CUR but broken GET_CUR, the registration can continue normally
+ * but further GET_CUR requests will be gated.
*/
static int check_sticky_volume_control(struct usb_mixer_elem_info *cval,
int channel, int saved)
@@ -1269,10 +1282,22 @@ static int check_sticky_volume_control(struct usb_mixer_elem_info *cval,
return 0;
}
+ if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_BROKEN) {
+ usb_audio_info(cval->head.mixer->chip,
+ "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n",
+ cval->head.id, mixer_ctrl_intf(cval->head.mixer),
+ cval->min, cval->max, cval->res, saved);
+
+ cval->get_cur_broken = 1;
+ return -ENXIO;
+ }
+
usb_audio_err(cval->head.mixer->chip,
"%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n",
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
cval->min, cval->max, cval->res, saved);
+ usb_audio_info(cval->head.mixer->chip,
+ "check MIXER_GET_CUR_BROKEN if you believe the mixer is non-sticky");
return -ENODEV;
}
@@ -1315,7 +1340,7 @@ static void check_volume_control_res(struct usb_mixer_elem_info *cval,
static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
int default_min, struct snd_kcontrol *kctl)
{
- int i, idx, ret;
+ int i, idx, ret = 0;
/* for failsafe */
cval->min = default_min;
@@ -1371,8 +1396,10 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
goto no_checks;
ret = check_sticky_volume_control(cval, minchn, saved);
- if (ret < 0)
+ if (ret == -ENODEV)
goto sticky;
+ if (ret)
+ goto no_checks;
if (cval->min + cval->res < cval->max)
check_volume_control_res(cval, minchn, saved);
@@ -1381,6 +1408,16 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
}
no_checks:
+ /*
+ * Got a non-fatal failure during sanity checks.
+ *
+ * Do not propagate mixer values written by sanity checks.
+ * Instead, rely on init_cur_mix_raw() to initialize the mixer
+ * properly.
+ */
+ if (ret)
+ cval->cached = 0;
+
cval->initialized = 1;
}
@@ -3538,7 +3575,8 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
continue;
info = mixer_elem_list_to_info(list);
/* invalidate cache, so the value is read from the device */
- info->cached = 0;
+ if (!info->get_cur_broken)
+ info->cached = 0;
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&list->kctl->id);
}
@@ -3635,10 +3673,12 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
switch (attribute) {
case UAC2_CS_CUR:
/* invalidate cache, so the value is read from the device */
- if (channel)
- info->cached &= ~BIT(channel);
- else /* master channel */
- info->cached = 0;
+ if (!info->get_cur_broken) {
+ if (channel)
+ info->cached &= ~BIT(channel);
+ else /* master channel */
+ info->cached = 0;
+ }
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&info->head.kctl->id);
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index afbb3dd9f177..3fa1bd96f858 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -94,6 +94,7 @@ struct usb_mixer_elem_info {
int cache_val[MAX_CHANNELS];
u8 initialized;
u8 min_mute;
+ u8 get_cur_broken;
void *private_data;
};
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 33eb8be48af8..354866a80605 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2249,6 +2249,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
DEVICE_FLG(0x046d, 0x0a8f, /* Logitech H390 headset */
QUIRK_FLAG_CTL_MSG_DELAY_1M |
QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
+ DEVICE_FLG(0x046d, 0x0af7, /* Logitech PRO X 2 LIGHTSPEED */
+ QUIRK_FLAG_MIXER_GET_CUR_BROKEN),
DEVICE_FLG(0x0499, 0x1506, /* Yamaha THR5 */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */
@@ -2318,6 +2320,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x0763, 0x2080, /* M-Audio Fast Track Ultra */
+ QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x0763, 0x2081, /* M-Audio Fast Track Ultra */
+ QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */
@@ -2611,6 +2617,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = {
QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL),
QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL),
QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY),
+ QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN),
NULL
};
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 9afcad8f143a..e472aef6eb87 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -242,6 +242,17 @@ extern bool snd_usb_skip_validation;
* from snd_usb_handle_sync_urb. Instead fall through and enqueue a
* packet_info containing only size-0 packets, so the OUT ring keeps
* moving (emits silence). Needed by Behringer Flow 8 (1397:050c).
+ * QUIRK_FLAG_MIXER_GET_CUR_BROKEN
+ * Some mixers are sticky, which means that setting their current volume is a
+ * no-op, and reading the current volume returns a constant value. The sticky
+ * check disables these mixers to prevent confusing userspace. However, some
+ * devices do have a tunable volume despite the reported current volume being
+ * constant. As the sticky check can't distinguish between the two categories,
+ * setting this flag tells that the device should fall into the second
+ * category when GET_CUR returns a constant value, resulting in the sticky
+ * check being non-fatal and only disabling GET_CUR instead of the whole mixer.
+ * The current volume will then be provided by the internal cache that stores
+ * the last set volume
*/
enum {
@@ -275,6 +286,7 @@ enum {
QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27,
QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28,
QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29,
+ QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30,
/* Please also edit snd_usb_audio_quirk_flag_names */
};
@@ -310,5 +322,6 @@ enum {
#define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL)
#define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL)
#define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY)
+#define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN)
#endif /* __USBAUDIO_H */
diff --git a/sound/usb/usx2y/us144mkii_capture.c b/sound/usb/usx2y/us144mkii_capture.c
index fa01da98151a..d7cdf9e0f685 100644
--- a/sound/usb/usx2y/us144mkii_capture.c
+++ b/sound/usb/usx2y/us144mkii_capture.c
@@ -302,6 +302,7 @@ void capture_urb_complete(struct urb *urb)
}
usb_get_urb(urb);
+ usb_anchor_urb(urb, &tascam->capture_anchor);
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) {
dev_err_ratelimited(tascam->card->dev,
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 4d7925184826..7f153a2fcdd8 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -25,6 +25,8 @@ static vm_fault_t snd_us428ctls_vm_fault(struct vm_fault *vmf)
void *vaddr;
offset = vmf->pgoff << PAGE_SHIFT;
+ if (offset >= US428_SHAREDMEM_PAGES)
+ return VM_FAULT_SIGBUS;
vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset;
page = virt_to_page(vaddr);
get_page(page);
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index 7c90214485d9..f1d5b4b797ad 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -672,6 +672,8 @@ static vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
void *vaddr;
offset = vmf->pgoff << PAGE_SHIFT;
+ if (offset >= USX2Y_HWDEP_PCM_PAGES)
+ return VM_FAULT_SIGBUS;
vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
vmf->page = virt_to_page(vaddr);
get_page(vmf->page);