From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
To: "Chia-Lin Kao (AceLan)" <acelan.kao@canonical.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Andrei Kuchynski <akuchynski@chromium.org>,
Johan Hovold <johan@kernel.org>,
Jameson Thies <jthies@google.com>,
Pooja Katiyar <pooja.katiyar@intel.com>,
Hsin-Te Yuan <yuanhsinte@chromium.org>,
Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>,
linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v7 1/3] usb: typec: ucsi: Detect and skip duplicate altmodes from buggy firmware
Date: Mon, 13 Jul 2026 12:53:59 +0300 [thread overview]
Message-ID: <alS1tywaoP_0ECVl@kuha> (raw)
In-Reply-To: <20260713084323.287516-1-acelan.kao@canonical.com>
On Mon, Jul 13, 2026 at 04:43:21PM +0800, Chia-Lin Kao (AceLan) wrote:
> Some firmware implementations incorrectly return the same altmode
> multiple times at different offsets when queried via
> UCSI_GET_ALTERNATE_MODES. This causes sysfs duplicate filename errors
> and kernel call traces when the driver attempts to register the same
> altmode twice:
>
> sysfs: cannot create duplicate filename '/devices/.../typec/port0/port0.0/partner'
> typec-thunderbolt port0-partner.1: failed to create symlinks
> typec-thunderbolt port0-partner.1: probe with driver typec-thunderbolt failed with error -17
>
> The matching rules differ by recipient:
>
> - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug):
> Two altmodes with identical SVID and VDO are byte-for-byte
> duplicates and the second has no observable function, so drop it.
>
> - UCSI_RECIPIENT_SOP (partner):
> The typec class binds each partner altmode to a port altmode of
> the same SVID via altmode_match()/device_find_child(), which
> returns the first port altmode with a matching SVID. If the
> partner advertises more altmodes for SVID X than the port
> advertises, the surplus partner altmode(s) collapse onto an
> already-paired port altmode and trigger the
> "duplicate filename .../partner" sysfs error during
> typec_altmode_create_links(). Use the port-side altmode count for
> SVID X as the authoritative cap and reject any partner altmode
> that would exceed it. This preserves legitimate multi-Mode
> partner altmodes (vendor SVIDs that the port really does
> advertise more than once) while filtering the firmware-generated
> duplicates that have no port counterpart, and is therefore
> stricter than a plain SVID+VDO comparison (which still admits the
> Thunderbolt case where firmware reports the same SVID twice with
> different VDOs) without being over-broad like a plain SVID match
> (which would falsely drop legitimate vendor multi-Mode entries).
>
> If a duplicate is detected, skip it and emit a clean warning instead
> of generating a kernel call trace:
>
> ucsi_acpi USBC000:00: con2: Firmware bug: duplicate partner altmode SVID 0x8087 at offset 1, ignoring.
> ucsi_acpi USBC000:00: con2: VDO mismatch: 0x8087a043 vs 0x00000001
>
> The duplicate detection logic lives in a reusable helper
> ucsi_altmode_is_duplicate() and is invoked from
> ucsi_register_altmodes(). It applies to all three recipient types:
> partner (SOP), port (CON), and plug (SOP_P) altmodes.
>
> Fixes: a79f16efcd00 ("usb: typec: ucsi: Add support for the partner USB Modes")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> --
> v7. - Rephrase the warning message ", ignoring." to ", ignoring but
> please contact the BIOS vendor to fix this issue."
> v6. - New helper ucsi_altmode_count_svid() counts altmodes with a given
> SVID in any of the per-connector altmode arrays.
> - ucsi_altmode_is_duplicate() for UCSI_RECIPIENT_SOP no longer asks
> "have I seen this SVID before". It now checks whether
> partner_count_for(svid) >= port_count_for(svid) and rejects only the
> surplus.
> - For UCSI_RECIPIENT_CON and UCSI_RECIPIENT_SOP_P, behavior is
> unchanged: still SVID+VDO exact-dup match.
> - Commit message rewritten to spell out the per-recipient rules and
> why partner needs the cardinality cap (avoids both the SVID-only
> false positives that hurt the legitimate Dell 0x413c case and the
> SVID+VDO false negatives that let the Thunderbolt 0x8087 firmware
> bug through).
> ---
> drivers/usb/typec/ucsi/ucsi.c | 132 ++++++++++++++++++++++++++++++++++
> 1 file changed, 132 insertions(+)
>
> diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
> index 642acc09d861..b9343bf67357 100644
> --- a/drivers/usb/typec/ucsi/ucsi.c
> +++ b/drivers/usb/typec/ucsi/ucsi.c
> @@ -550,6 +550,129 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
> return ret;
> }
>
> +static void ucsi_dump_duplicate_altmode(struct ucsi_connector *con,
> + u8 recipient, u16 svid,
> + u32 existing_vdo, u32 new_vdo,
> + int offset)
> +{
> + static const char * const recipient_names[] = {
> + [UCSI_RECIPIENT_CON] = "port",
> + [UCSI_RECIPIENT_SOP] = "partner",
> + [UCSI_RECIPIENT_SOP_P] = "plug",
> + [UCSI_RECIPIENT_SOP_PP] = "cable plug prime",
> + };
> +
> + dev_warn(con->ucsi->dev,
> + "con%d: Firmware bug: duplicate %s altmode SVID 0x%04x at offset %d, ignoring but please contact the BIOS vendor to fix this issue.\n",
> + con->num, recipient_names[recipient], svid, offset);
> +
> + if (existing_vdo != new_vdo)
> + dev_warn(con->ucsi->dev,
> + "con%d: VDO mismatch: 0x%08x vs 0x%08x\n",
> + con->num, existing_vdo, new_vdo);
> +}
> +
> +/* Count altmodes in @altmodes that advertise @svid. */
> +static int ucsi_altmode_count_svid(struct typec_altmode **altmodes, u16 svid)
> +{
> + int count = 0;
> + int k;
> +
> + for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
> + if (!altmodes[k])
> + break;
> + if (altmodes[k]->svid == svid)
> + count++;
> + }
> +
> + return count;
> +}
> +
> +/*
> + * Check if an altmode is a duplicate. Some firmware implementations
> + * incorrectly return the same altmode multiple times, causing sysfs errors.
> + * Returns true if the altmode should be skipped.
> + *
> + * The matching rules differ by recipient:
> + *
> + * - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug):
> + * Two altmodes with identical SVID and VDO are byte-for-byte duplicates
> + * and the second has no observable function. Drop them.
> + *
> + * - UCSI_RECIPIENT_SOP (partner):
> + * The typec class binds each partner altmode to a port altmode of the
> + * same SVID via altmode_match()/device_find_child(), which returns the
> + * first port altmode with a matching SVID. If the partner advertises
> + * more altmodes for SVID X than the port advertises, the surplus
> + * partner altmode(s) collapse onto an already-paired port altmode and
> + * trigger a "duplicate filename .../partner" sysfs error during
> + * typec_altmode_create_links(). Use the port-side altmode count for
> + * SVID X as the authoritative cap and reject any partner altmode that
> + * would exceed it. This preserves legitimate multi-Mode partner
> + * altmodes (e.g. vendor SVIDs that the port really does advertise
> + * twice) while filtering the firmware-generated duplicates that have
> + * no port counterpart.
> + */
> +static bool ucsi_altmode_is_duplicate(struct ucsi_connector *con, u8 recipient,
> + const struct ucsi_altmode *alt_batch, int batch_idx,
> + u16 svid, u32 vdo, int offset)
> +{
> + struct typec_altmode **altmodes;
> + int port_count, partner_count;
> + int k;
> +
> + /* Check for duplicates within the current batch first */
> + for (k = 0; k < batch_idx; k++) {
> + if (alt_batch[k].svid == svid && alt_batch[k].mid == vdo) {
> + ucsi_dump_duplicate_altmode(con, recipient, svid,
> + vdo, vdo, offset);
> + return true;
> + }
> + }
> +
> + switch (recipient) {
> + case UCSI_RECIPIENT_SOP:
> + /*
> + * Cap partner altmodes per SVID by the port-side count:
> + * any further partner altmode for that SVID would alias an
> + * already-paired port altmode and break typec sysfs.
> + */
> + port_count = ucsi_altmode_count_svid(con->port_altmode, svid);
> + partner_count = ucsi_altmode_count_svid(con->partner_altmode,
> + svid);
> + if (port_count && partner_count >= port_count) {
> + ucsi_dump_duplicate_altmode(con, recipient, svid,
> + con->partner_altmode[partner_count - 1]->vdo,
> + vdo, offset);
> + return true;
> + }
> + return false;
> + case UCSI_RECIPIENT_CON:
> + altmodes = con->port_altmode;
> + break;
> + case UCSI_RECIPIENT_SOP_P:
> + altmodes = con->plug_altmode;
> + break;
> + default:
> + return false;
> + }
> +
> + /* CON and SOP_P: drop only exact SVID+VDO duplicates. */
> + for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
> + if (!altmodes[k])
> + break;
> +
> + if (altmodes[k]->svid != svid || altmodes[k]->vdo != vdo)
> + continue;
> +
> + ucsi_dump_duplicate_altmode(con, recipient, svid,
> + altmodes[k]->vdo, vdo, offset);
> + return true;
> + }
> +
> + return false;
> +}
> +
> static int
> ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
> {
> @@ -674,6 +797,15 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
> if (!alt[j].svid)
> return 0;
>
> + /*
> + * Check for duplicates in current batch and already
> + * registered altmodes. Skip if duplicate found.
> + */
> + if (ucsi_altmode_is_duplicate(con, recipient, alt, j,
> + alt[j].svid, alt[j].mid,
> + i - num + j))
> + continue;
> +
> memset(&desc, 0, sizeof(desc));
> desc.vdo = alt[j].mid;
> desc.svid = alt[j].svid;
> --
> 2.53.0
>
--
heikki
prev parent reply other threads:[~2026-07-13 9:54 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-13 8:43 [PATCH v7 1/3] usb: typec: ucsi: Detect and skip duplicate altmodes from buggy firmware Chia-Lin Kao (AceLan)
2026-07-13 8:43 ` [PATCH v7 2/3] usb: typec: ucsi: Add duplicate detection to nvidia registration path Chia-Lin Kao (AceLan)
2026-07-13 8:43 ` [PATCH v7 3/3] usb: typec: ucsi: yoga_c630: Remove redundant duplicate altmode handling Chia-Lin Kao (AceLan)
2026-07-13 9:53 ` Heikki Krogerus [this message]
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=alS1tywaoP_0ECVl@kuha \
--to=heikki.krogerus@linux.intel.com \
--cc=acelan.kao@canonical.com \
--cc=akuchynski@chromium.org \
--cc=dmitry.baryshkov@oss.qualcomm.com \
--cc=gregkh@linuxfoundation.org \
--cc=johan@kernel.org \
--cc=jthies@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=pooja.katiyar@intel.com \
--cc=yuanhsinte@chromium.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox