Linux USB
 help / color / mirror / Atom feed
From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
To: RD Babiera <rdbabiera@google.com>
Cc: badhri@google.com, gregkh@linuxfoundation.org,
	linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4] usb: typec: tcpm: implement retry mechanism for Discover Identity VDMs
Date: Tue, 14 Jul 2026 13:02:05 +0300	[thread overview]
Message-ID: <alYJHaXFR7q0q_Xq@kuha> (raw)
In-Reply-To: <20260710200308.1401321-2-rdbabiera@google.com>

On Fri, Jul 10, 2026 at 08:03:08PM +0000, RD Babiera wrote:
> The current mechanism for sending Discover Identity in the ready state
> presents a flaw where tcpm_queue_vdm can collide with non interruptible
> AMSes such as GET_SINK_CAP or VCONN_SWAP. vdm_run_state_machine will hit
> the VDM_STATE_BUSY state, and Discover SVIDs or Discover Modes will not
> retry.
> 
> This patch introduces a state machine under the enum vdm_discovery_states.
> The tcpm_port field vdm_discovery_state tracks which step of the Discover
> Identity process has been completed.
> 
> The current TCPM implementation utilizes the send_discover and
> send_discover_prime booleans to queue Discover Identity in the
> aforementioned collision case. These booleans are removed in place of
> vdm_discovery_state and send_discover_work is replaced by
> vdm_discovery_work, which runs unconditionally in the ready state.
> When the Discovery process is complete, the port will move to the
> VDM_DISCOVERY_COMPLETE state and vdm_discovery_work becomes a no-op.
> When there are still Discovery VDMs to be sent, vdm_discovery_work will
> continue based on the last received response from the port partner or
> cable.
> 
> Signed-off-by: RD Babiera <rdbabiera@google.com>
> ---
> Changes since v1:
> * fixed lint error regarding vdm_discovery_state_strings declaration
> 
> Changes since v2:
> * Convert declaration of vdm_discovery_state into FOREACH_ macro
> * Remove added newline from SNK_READY state in run_state_machine
> * tcpm_reset_port now puts the VDM discovery state machine in
>   VDM_DISCOVERY_UNKNOWN and cancels any pending work as opposed to
>   VDM_DISCOVERY_COMPLETE.
> * Removed brackets when logging states in tcpm_update_vdm_discovery_state.
> * Small logging nit (added colon) to tcpm_vdm_discovery_work and
>   tcpm_update_vdm_discovery_state.
> 
> Changes since v3:
> * vdm_discovery_state_strings type changed across versions, sorry for
>   the nit repatch here.
> * Checkpatch raises an error for #define FOREACH_VDM_DISCOVERY(S),
>   sending patch despite the error, as macro is consistent with rest of
>   driver.

This looks good to me but let's wait for Badhri. If you do v5, there
is one nitpick below. Otherwise:

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>

<snip>

> -static void tcpm_send_discover_work(struct kthread_work *work)
> +static void tcpm_vdm_discovery_work(struct kthread_work *work)
>  {
> -	struct tcpm_port *port = container_of(work, struct tcpm_port, send_discover_work);
> +	struct tcpm_port *port = container_of(work, struct tcpm_port, vdm_discovery_work);
> +	enum tcpm_transmit_type tx_sop_type = TCPC_TX_SOP;
> +	struct typec_port *typec = port->typec_port;
> +	struct pd_mode_data *modep, *modep_prime;
> +	u32 msg[2] = { };
> +	int svdm_version;
>  
>  	mutex_lock(&port->lock);
> -	/* No need to send DISCOVER_IDENTITY anymore */
> -	if (!port->send_discover && !port->send_discover_prime)
> +
> +	tcpm_log_force(port, "%s state [%s]", __func__,
> +		       vdm_discovery_state_strings[port->vdm_discovery_state]);
> +
> +	/* No need to perform work if Discovery process is complete */
> +	if (port->vdm_discovery_state == VDM_DISCOVERY_COMPLETE)
>  		goto unlock;
>  
> -	if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) {
> -		port->send_discover = false;
> -		port->send_discover_prime = false;
> +	/* Retry if the port is not idle */
> +	if (!tcpm_can_send_vdm(port->state) || port->vdm_sm_running) {
> +		mod_vdm_discovery_delayed_work(port, SEND_DISCOVERY_VDM_RETRY_MS);
>  		goto unlock;
>  	}
>  
> -	/* Retry if the port is not idle */
> -	if ((port->state != SRC_READY && port->state != SNK_READY &&
> -	     port->state != SRC_VDM_IDENTITY_REQUEST) || port->vdm_sm_running) {
> -		mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS);
> +	modep = &port->mode_data;
> +	modep_prime = &port->mode_data_prime;
> +
> +	svdm_version = typec_get_negotiated_svdm_version(typec);
> +
> +	switch (port->vdm_discovery_state) {
> +	/*
> +	 * The port has not received a Discover Identity response from the port partner.
> +	 *
> +	 * 1. The port will send Discover Identity to the partner over SOP in the SRC_READY and
> +	 *    SNK_READY states if there is an explicit contract
> +	 * 2. The port will send Discover Identity to the cable over SOP' in the
> +	 *    SRC_VDM_IDENTITY_REQUEST state if capable of doing so.
> +	 */
> +	case VDM_DISCOVERY_UNKNOWN:
> +		/* Can't send Discover Identity, VDM discovery is complete */
> +		if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) {
> +			tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
> +			goto unlock;
> +		}
> +
> +		if (port->state == SRC_VDM_IDENTITY_REQUEST) {
> +			tx_sop_type = TCPC_TX_SOP_PRIME;
> +			svdm_version = SVDM_VER_MAX;
> +		}
> +
> +		msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT);
> +		break;
> +	/*
> +	 * The port has received a Discover Identity ACK from the port partner.
> +	 *
> +	 * 1. The port will send Discover Identity to the cable over SOP' in the SRC_READY and
> +	 *    SNK_READY states if it did not previously discover the cable but is capable of doing
> +	 *    so.
> +	 * 2. The port will send Discover SVIDs to the partner over SOP in the SRC_READY and
> +	 *    SNK_READY states otherwise.
> +	 */
> +	case VDM_DISCOVERY_PARTNER_IDENT:
> +		if (tcpm_can_communicate_sop_prime(port) && !port->cable) {
> +			tx_sop_type = TCPC_TX_SOP_PRIME;
> +			msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT);
> +		} else {
> +			if (tcpm_can_communicate_sop_prime(port))
> +				tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT);
> +			msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID);
> +		}
> +		break;
> +	/*
> +	 * The port has received a Discover Identity ACK from the cable.
> +	 *
> +	 * 1. The port will send Discover SVIDs to the partner over SOP.
> +	 */
> +	case VDM_DISCOVERY_CABLE_IDENT:
> +		msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID);
> +		break;
> +	/*
> +	 * The port has received a Discover SVIDs ACK from the partner or the last SVIDs supported
> +	 * by the partner.
> +	 *
> +	 * 1. The port will send Discover Modes for the first SVID over SOP if the partner supports
> +	 *    modal operation and valid SVIDs were registered.
> +	 * 2. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise.
> +	 */
> +	case VDM_DISCOVERY_PARTNER_SVIDS:
> +		if (modep->nsvids && supports_modal(port)) {
> +			msg[0] = VDO(modep->svids[0], 1, svdm_version, CMD_DISCOVER_MODES);
> +		} else {
> +			tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
> +			goto unlock;
> +		}
> +		break;
> +	/*
> +	 * The port has received a Discover Modes ACK from the partner for any mode.
> +	 *
> +	 * 1. The port will send Discover Modes for the next SVID that has not been discovered to
> +	 *    the port partner over SOP.
> +	 * 2. The port will send Discover SVIDs over SOP' if the port can communicate over SOP'
> +	 *    and the cable supports VDMs.
> +	 * 3. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise.
> +	 */
> +	case VDM_DISCOVERY_PARTNER_MODES:
> +		/* Not all modes have been discovered yet */
> +		if (modep->svid_index < modep->nsvids) {
> +			msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version,
> +				     CMD_DISCOVER_MODES);
> +		} else if (tcpm_can_communicate_sop_prime(port) && tcpm_cable_vdm_supported(port)) {
> +			tx_sop_type = TCPC_TX_SOP_PRIME;
> +			svdm_version = typec_get_cable_svdm_version(typec);
> +			msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID);
> +		} else {
> +			tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
> +			goto unlock;
> +		}
> +		break;
> +	/*
> +	 * The port has received a Discover SVIDs ACK from the cable over SOP'.
> +	 *
> +	 * 1. The port will send Discover Modes for the first SVID over SOP'.
> +	 */
> +	case VDM_DISCOVERY_CABLE_SVIDS:
> +		if (modep_prime->nsvids) {
> +			tx_sop_type = TCPC_TX_SOP_PRIME;
> +			svdm_version = typec_get_cable_svdm_version(typec);
> +			msg[0] = VDO(modep_prime->svids[0], 1, svdm_version, CMD_DISCOVER_MODES);
> +		} else {
> +			goto unlock;
> +		}
> +		break;
> +	/*
> +	 * The port has received a Discover Modes ACK from the cable for any mode.
> +	 *
> +	 * 1. The port will send Discover Modes for the next SVID that has not been discovered to
> +	 *    the cable over SOP'.
> +	 */
> +	case VDM_DISCOVERY_CABLE_MODES:
> +		if (modep_prime->svid_index < modep_prime->nsvids) {
> +			tx_sop_type = TCPC_TX_SOP_PRIME;
> +			svdm_version = typec_get_cable_svdm_version(typec);
> +			msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version,
> +				     CMD_DISCOVER_MODES);
> +		} else {
> +			goto unlock;
> +		}
> +		break;
> +	default:
>  		goto unlock;
>  	}
>  
> -	tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0, port->tx_sop_type);
> +	/* No port partner exists, and Discover Identity */
> +	if (svdm_version < 0)
> +		goto unlock;
>  
> +	tcpm_queue_vdm(port, msg[0], &msg[1], 0, tx_sop_type);

I think you could trim the above a little with this (is 0 a valid
version?):

        if (svdm_version >= 0)
        	tcpm_queue_vdm(port, msg[0], &msg[1], 0, tx_sop_type);

>  unlock:
>  	mutex_unlock(&port->lock);
>  }

thanks,

-- 
heikki

      reply	other threads:[~2026-07-14 10:02 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-10 20:03 [PATCH v4] usb: typec: tcpm: implement retry mechanism for Discover Identity VDMs RD Babiera
2026-07-14 10:02 ` 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=alYJHaXFR7q0q_Xq@kuha \
    --to=heikki.krogerus@linux.intel.com \
    --cc=badhri@google.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=rdbabiera@google.com \
    /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