diff options
Diffstat (limited to 'sound')
| -rw-r--r-- | sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 3 | ||||
| -rw-r--r-- | sound/usb/endpoint.c | 6 | ||||
| -rw-r--r-- | sound/usb/fcp.c | 10 | ||||
| -rw-r--r-- | sound/usb/midi2.c | 9 | ||||
| -rw-r--r-- | sound/usb/mixer.c | 60 | ||||
| -rw-r--r-- | sound/usb/mixer.h | 1 | ||||
| -rw-r--r-- | sound/usb/quirks.c | 7 | ||||
| -rw-r--r-- | sound/usb/usbaudio.h | 13 | ||||
| -rw-r--r-- | sound/usb/usx2y/us144mkii_capture.c | 1 | ||||
| -rw-r--r-- | sound/usb/usx2y/usX2Yhwdep.c | 2 | ||||
| -rw-r--r-- | sound/usb/usx2y/usx2yhwdeppcm.c | 2 |
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); |
