All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Ismaïl Bahloul" <i.bahloul01@gmail.com>, linux-sound@vger.kernel.org
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH v7 4/8] ALSA: usb: babyfacepro: add routing flags and varispeed pitch
Date: Tue, 27 Oct 2026 11:47:01 +0000	[thread overview]
Message-ID: <sashiko-outbox-186473@kernel.org> (raw)
In-Reply-To: <20261027113140.134257-5-i.bahloul01@gmail.com>

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] ALSA: usb: babyfacepro: bf_state_apply_flags() unmutes AN1/2 inputs on probe
- [High] ALSA: usb: babyfacepro: bf_ms_put() restores wrong crosspoints to AN1/2
- [High] ALSA: usb: babyfacepro: DIM and Phones master/mute states clobber each other
- [Medium] ALSA: usb: babyfacepro: preamp flag commits clear Boost ref level state
- [Medium] ALSA: usb: babyfacepro: bf_loopback_put() omits notify on cleared outputs
- [Low] ALSA: usb: babyfacepro: missing enum bounds and long checks in flag puts
- [Low] ALSA: usb: babyfacepro: unused dim_kctl pointer in babyface_create_flags()
--

commit 43e9e598414a501c1d77575b85841ffbf7ea280c
Author: Ismaïl Bahloul <i.bahloul01@gmail.com>

ALSA: usb: babyfacepro: add routing flags and varispeed pitch

This commit adds ALSA mixer controls and state restoration for loopback,
AN 1>2, sample clock source, optical output format, AN1/2 input link, MS
processor, DIM, stereo width, FX send, and varispeed pitch.

> diff --git a/sound/usb/babyfacepro/babyfacepro-ctl.c b/sound/usb/babyfacepro/babyfacepro-ctl.c
> index 60604242be883..39584ec719b23 100644
> --- a/sound/usb/babyfacepro/babyfacepro-ctl.c
> +++ b/sound/usb/babyfacepro/babyfacepro-ctl.c

[ ... ]

> @@ -377,6 +381,11 @@ static int bf_master_put(struct snd_kcontrol *kctl,
>  
>  	chip->master[out][0] = l;
>  	chip->master[out][1] = r;
> +	/* A Phones change while DIM is engaged re-bases the restore point. */
> +	if (chip->dim && out == 1) {
> +		chip->dim_saved[0] = l;
> +		chip->dim_saved[1] = r;
> +	}
>  	ret = 1;
>  out:
>  	mutex_unlock(&chip->mutex);

[Severity: High]
Does changing the Phones master volume while DIM is engaged cancel the DIM
attenuation on the hardware?

In bf_master_put(), when chip->dim is true and out == 1, the code updates
chip->dim_saved[0..1], but only after writing wire_l and wire_r (the full
undimmed volume) to BF_REG_MASTER_8 + 2 * out and BF_REG_MASTER_16 + 2 * out.
That drives the Phones output to the undimmed level while chip->dim remains
true.

Similarly, in bf_mute_put(), unmuting out == 1 while chip->dim is true writes
chip->master[1][0..1] to the Phones master registers instead of the dimmed
BF_MASTER_MINUS20_8 and BF_MASTER_MINUS20_16 levels.

> @@ -1280,3 +1289,699 @@ int babyface_create_preamp(struct snd_usb_babyface *chip)

[ ... ]

> +static int bf_pitch_put(struct snd_kcontrol *kctl,
> +			struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_usb_babyface *chip = snd_kcontrol_chip(kctl);
> +	int p = ucontrol->value.integer.value[0];
> +	int ret = 0;
> +
> +	if (p < -50 || p > 50)
> +		return -EINVAL;

[Severity: Low]
Can ucontrol->value.integer.value[0] wrap when narrowed from long to int here
(and in bf_width_put() below) before the range check, unlike bf_master_put()
and bf_fx_send_put() which validate the long value prior to narrowing?

[ ... ]

> +static int bf_loopback_put(struct snd_kcontrol *kctl,
> +			   struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_usb_babyface *chip = snd_kcontrol_chip(kctl);
> +	int out = kctl->private_value;
> +	bool on = ucontrol->value.integer.value[0];
> +	int ret = 0;
> +
> +	mutex_lock(&chip->mutex);
> +	if (on == chip->loopback[out])
> +		goto out;
> +	ret = bf_loopback_write_map(chip, out, on);
> +	if (ret < 0)
> +		goto out;
> +	/* Single-active model (TotalMix writes one pair at 0x0001, the
> +	 * rest 0x0000): toggling one output clears the others.
> +	 */
> +	memset(chip->loopback, 0, sizeof(chip->loopback));
> +	chip->loopback[out] = on;
> +	ret = 1;
> +out:
> +	mutex_unlock(&chip->mutex);
> +	return ret;
> +}

[Severity: Medium]
When enabling Loopback Switch on one output clears a previously active
chip->loopback[] entry via memset(), should this notify user space with
snd_ctl_notify() for the cleared control (similar to how bf_trim_put()
notifies its sibling control)?

Also, babyface_create_flags() registers Loopback Switch using bf_mute_info
(which declares count = 2 channels), whereas bf_loopback_put() only inspects
ucontrol->value.integer.value[0]. Should Loopback Switch use bf_switch_info
(count = 1) or handle both channels?

[ ... ]

> +static int bf_an12_put(struct snd_kcontrol *kctl,
> +		       struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_usb_babyface *chip = snd_kcontrol_chip(kctl);
> +	bool an12 = ucontrol->value.integer.value[0];
> +	u16 v;
> +	int ret = 0;
> +
> +	mutex_lock(&chip->mutex);
> +	if (an12 == chip->an12)
> +		goto out;
> +	v = (chip->linked ? 0x0400 : 0x0000) | (an12 ? 0x1000 : 0x0000);
> +	ret = bf_vendor_write(chip, BF_REQ_PREAMP, v, 0x1000);
> +	if (ret < 0)
> +		goto out;
> +	ret = bf_vendor_write(chip, BF_REQ_PREAMP_COMMIT, 0x0000, 0x0000);

[Severity: Medium]
Does sending BF_REQ_PREAMP_COMMIT with a hardcoded 0x0000 here (as well as in
bf_link_put() and bf_state_apply_flags()) clear the Instrument 3/4 Boost
reference level on the hardware?

In bf_preamp_state_write(), Boost is distinguished from -10dBV by passing
wValue = 0x0003 instead of 0x0000 to BF_REQ_PREAMP_COMMIT:

    u16 commit = chip->ref_level == BF_REF_LEVEL_BOOST ? 0x0003 : 0x0000;

When chip->ref_level == BF_REF_LEVEL_BOOST, toggling AN 1>2 Switch or AN1/2
Link Switch, or running bf_state_apply_flags() after bf_preamp_state_write(),
sends 0x0000 and drops the hardware back to -10dBV while chip->ref_level still
reports Boost.

[ ... ]

> +static int bf_clock_put(struct snd_kcontrol *kctl,
> +			struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_usb_babyface *chip = snd_kcontrol_chip(kctl);
> +	bool optical = ucontrol->value.enumerated.item[0] != 0;
> +	int ret = 0;

[ ... ]

> +static int bf_optical_put(struct snd_kcontrol *kctl,
> +			  struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_usb_babyface *chip = snd_kcontrol_chip(kctl);
> +	bool spdif = ucontrol->value.enumerated.item[0] != 0;
> +	int ret = 0;

[Severity: Low]
Should bf_clock_put() and bf_optical_put() reject ucontrol->value.enumerated.item[0] >= 2
with -EINVAL when CONFIG_SND_CTL_INPUT_VALIDATION is not enabled, matching
bf_reflevel_put()?

[ ... ]

> +static int bf_ms_put(struct snd_kcontrol *kctl,
> +		     struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_usb_babyface *chip = snd_kcontrol_chip(kctl);
> +	bool on = ucontrol->value.integer.value[0];
> +	int ret = 0;
> +
> +	mutex_lock(&chip->mutex);
> +	if (on == chip->ms_proc)
> +		goto out;
> +	if (on) {
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, 0x0000, 0x0035);
> +		if (ret < 0)
> +			goto out;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, 0x0000, 0x004f);
> +		if (ret < 0)
> +			goto out;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, 0x0000, 0x0001);
> +		if (ret < 0)
> +			goto out;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, 0x0000, 0x001b);
> +		if (ret < 0)
> +			goto out;
> +	} else {
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT,
> +				      chip->xpoint[1][1][0], 0x0001);
> +		if (ret < 0)
> +			goto out;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT,
> +				      chip->xpoint[1][1][0], 0x0035);
> +		if (ret < 0)
> +			goto out;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT,
> +				      chip->xpoint[1][1][1], 0x001b);
> +		if (ret < 0)
> +			goto out;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT,
> +				      chip->xpoint[1][1][1], 0x004f);
> +		if (ret < 0)
> +			goto out;
> +	}

[Severity: High]
When disengaging MS Processor Switch (on == false), does this write output 1's
(PH3/4) cached AN2 crosspoints into output 0's (AN1/2) registers?

Registers 0x0001 (L) and 0x001b (R) belong to output pair 0 (AN1/2), whose
cached AN2 levels are in chip->xpoint[0][1][0] and chip->xpoint[0][1][1],
whereas chip->xpoint[1][1][0..1] holds output pair 1 (PH3/4, registers 0x0035
and 0x004f). Writing chip->xpoint[1][1][0..1] to 0x0001 and 0x001b overwrites
output 0's AN2 routing with output 1's fader values and leaves hardware out of
sync with chip->xpoint[0][1].

[ ... ]

> +static int bf_dim_apply(struct snd_usb_babyface *chip, bool on)
> +{
> +	u16 flag;
> +	int ret;
> +
> +	lockdep_assert_held(&chip->mutex);
> +	if (on) {
> +		chip->dim_saved[0] = chip->master[1][0];
> +		chip->dim_saved[1] = chip->master[1][1];
> +		ret = bf_vendor_write(chip, BF_REQ_GAIN, BF_MASTER_MINUS20_8,
> +				      BF_REG_MASTER_8 + 2 * 1);
> +		if (ret < 0)
> +			return ret;
> +		ret = bf_vendor_write(chip, BF_REQ_GAIN, BF_MASTER_MINUS20_8,
> +				      BF_REG_MASTER_8 + 2 * 1 + 1);
> +		if (ret < 0)
> +			return ret;
> +		flag = bf_flag_cycle[chip->flag_cnt];
> +		chip->flag_cnt = (chip->flag_cnt + 1) & 3;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT,
> +				      BF_MASTER_MINUS20_16,
> +				      (BF_REG_MASTER_16 + 2 * 1) | flag);
> +		if (ret < 0)
> +			return ret;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT,
> +				      BF_MASTER_MINUS20_16,
> +				      (BF_REG_MASTER_16 + 2 * 1 + 1) | flag);
> +		if (ret < 0)
> +			return ret;
> +		ret = bf_vendor_write(chip, BF_REQ_PREAMP, 0x2000, 0x2000);
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		ret = bf_vendor_write(chip, BF_REQ_GAIN,
> +				      bf_master_reg8(chip->dim_saved[0]),
> +				      BF_REG_MASTER_8 + 2 * 1);
> +		if (ret < 0)
> +			return ret;
> +		ret = bf_vendor_write(chip, BF_REQ_GAIN,
> +				      bf_master_reg8(chip->dim_saved[1]),
> +				      BF_REG_MASTER_8 + 2 * 1 + 1);
> +		if (ret < 0)
> +			return ret;
> +		flag = bf_flag_cycle[chip->flag_cnt];
> +		chip->flag_cnt = (chip->flag_cnt + 1) & 3;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT,
> +				      bf_master_reg16(chip->dim_saved[0]),
> +				      (BF_REG_MASTER_16 + 2 * 1) | flag);
> +		if (ret < 0)
> +			return ret;
> +		ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT,
> +				      bf_master_reg16(chip->dim_saved[1]),
> +				      (BF_REG_MASTER_16 + 2 * 1 + 1) | flag);
> +		if (ret < 0)
> +			return ret;
> +		ret = bf_vendor_write(chip, BF_REQ_PREAMP, 0x0000, 0x2000);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	chip->dim = on;
> +	return 0;
> +}

[Severity: High]
What happens in bf_dim_apply() (and in the chip->dim restore block in
bf_state_apply_flags()) if the Phones output is muted (chip->muted[1] == true)?

Both paths write non-muted gain values (BF_MASTER_MINUS20_8 /
BF_MASTER_MINUS20_16 on engage, or chip->dim_saved[0..1] on release) to the
Phones master registers without checking chip->muted[1]. If Phones was muted,
toggling Dim Switch or running bf_state_apply_flags() unmutes the headphones
on the hardware while chip->muted[1] remains true.

[ ... ]

> +	kctl = snd_ctl_new1(&(struct snd_kcontrol_new){
> +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
> +		.name = "Dim Switch",
> +		.info = bf_switch_info,
> +		.get = bf_dim_get,
> +		.put = bf_dim_put,
> +	}, chip);
> +	chip->dim_kctl = kctl;
> +	err = snd_ctl_add(chip->card, kctl);
> +	if (err < 0)
> +		return err;

[Severity: Low]
This isn't a bug, but chip->dim_kctl is assigned here in
babyface_create_flags() and never read anywhere in the driver (a later patch
adds a separate chip->dim_press_kctl for the front-panel button). Can
dim_kctl be dropped from struct snd_usb_babyface?

> diff --git a/sound/usb/babyfacepro/babyfacepro.c b/sound/usb/babyfacepro/babyfacepro.c
> index c2835e2d64851..ed4555c711064 100644
> --- a/sound/usb/babyfacepro/babyfacepro.c
> +++ b/sound/usb/babyfacepro/babyfacepro.c

[ ... ]

> @@ -300,14 +317,111 @@ int babyface_restore_state(struct snd_usb_babyface *chip)
>  	return bf_pitch_write(chip, chip->pitch);
>  }
>  
> -/* Re-apply the per-input state that sits on top of the crosspoints
> - * (phase invert, stereo split, trim) after a state restore.  The write
> - * patterns mirror the corresponding _put() handlers.  Caller holds
> - * chip->mutex.
> +/* Re-apply the state that sits on top of the masters and crosspoints
> + * (the routing flags, DIM, phase invert, stereo split, trim) after a
> + * state restore.  The write patterns mirror the corresponding _put()
> + * handlers.  Caller holds chip->mutex.
>   */
>  int bf_state_apply_flags(struct snd_usb_babyface *chip)
>  {
> -	int ret;
> +	int out, ret, on_out = -1;
> +	u16 l, r;
> +
> +	/* Loopback: the full 30-channel map from the cached state (the
> +	 * single-active invariant keeps at most one pair at 0x0001).
> +	 */
> +	for (out = 0; out < 6; out++) {
> +		if (chip->loopback[out]) {
> +			on_out = out;
> +			break;
> +		}
> +	}
> +	ret = bf_loopback_write_map(chip, on_out, on_out >= 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = bf_vendor_write(chip, BF_REQ_PREAMP,
> +			      (chip->linked ? 0x0400 : 0x0000) |
> +			      (chip->an12 ? 0x1000 : 0x0000), 0x1000);
> +	if (ret < 0)
> +		return ret;
> +	ret = bf_vendor_write(chip, BF_REQ_PREAMP_COMMIT, 0x0000, 0x0000);
> +	if (ret < 0)
> +		return ret;
> +
> +	l = (u16)(((0x2000 * (100 + chip->width) / 2) + 50) / 100);
> +	r = 0x2000 - l;
> +	ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, l, 0x0000);
> +	if (ret < 0)
> +		return ret;
> +	ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, r, 0x001a);
> +	if (ret < 0)
> +		return ret;
> +	ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, r, 0x0001);
> +	if (ret < 0)
> +		return ret;
> +	ret = bf_vendor_write(chip, BF_REQ_CROSSPOINT, l, 0x001b);
> +	if (ret < 0)
> +		return ret;

[Severity: High]
Does running bf_state_apply_flags() unconditionally unmute hardware inputs AN1
and AN2 into the main outputs (AN1/2) on probe and state restore?

In babyface_probe(), babyface_write_default_mixer() intentionally writes 0 to
all hardware input crosspoints (src < BF_SRC_PB1, which includes AN1 at
0x0000/0x001a and AN2 at 0x0001/0x001b on output 0) and caches 0 in
chip->xpoint[0][0..1][0..1] so live inputs are not routed out of the box.

Immediately afterward, babyface_probe() calls bf_state_apply_flags(). Even
when chip->width == 0, this block computes l = 0x1000 and r = 0x1000 and
writes them to registers 0x0000, 0x001a, 0x0001, and 0x001b, routing AN1 and
AN2 at 0 dB into output 0 while chip->xpoint[0][0..1][0..1] remains 0. Because
the cache still holds 0, a subsequent alsactl restore or mixer write of 0 to
those crosspoints hits the early return in bf_xpoint_put() and cannot mute
them.

Also, when chip->width is non-zero, bf_width_put() writes both the low map
(0x0000, 0x001a, 0x0001, 0x001b) and the block-0 map (0x0034, 0x004e, 0x0035,
0x004f), whereas bf_state_apply_flags() only writes the low map. Should the
width writes in bf_state_apply_flags() be guarded by if (chip->width) and
include both maps?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261027113140.134257-1-i.bahloul01@gmail.com?part=4

  reply	other threads:[~2026-10-27 11:47 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 11:31 [PATCH v7 0/8] ALSA: usb: add RME Babyface Pro driver (proprietary mode) Ismaïl Bahloul
2026-10-27 11:31 ` [PATCH v7 1/8] ALSA: usb: add RME Babyface Pro driver core (probe, PCM stream) Ismaïl Bahloul
2026-10-27 12:02   ` sashiko-bot
2026-10-27 11:31 ` [PATCH v7 2/8] ALSA: usb: babyfacepro: add output masters and crosspoint routing Ismaïl Bahloul
2026-10-27 11:48   ` sashiko-bot
2026-10-27 11:31 ` [PATCH v7 3/8] ALSA: usb: babyfacepro: add mic preamp, phantom/pad and input trim Ismaïl Bahloul
2026-10-27 11:48   ` sashiko-bot
2026-10-27 11:31 ` [PATCH v7 4/8] ALSA: usb: babyfacepro: add routing flags and varispeed pitch Ismaïl Bahloul
2026-10-27 11:47   ` sashiko-bot [this message]
2026-10-27 11:31 ` [PATCH v7 5/8] ALSA: usb: babyfacepro: add S3 suspend/resume Ismaïl Bahloul
2026-10-27 11:53   ` sashiko-bot
2026-10-27 11:31 ` [PATCH v7 6/8] ALSA: usb: babyfacepro: add the front-panel poll and controls Ismaïl Bahloul
2026-10-27 11:58   ` sashiko-bot
2026-10-27 11:31 ` [PATCH v7 7/8] ALSA: usb: babyfacepro: add the hardware DSP EQ Ismaïl Bahloul
2026-10-27 11:46   ` sashiko-bot
2026-10-27 11:31 ` [PATCH v7 8/8] Documentation: sound: add the Babyface Pro proprietary-mode design doc Ismaïl Bahloul
2026-10-27 11:37   ` sashiko-bot

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=sashiko-outbox-186473@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=i.bahloul01@gmail.com \
    --cc=linux-sound@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.