Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: typec: tcpm: Properly handle Alert and Status Messages

When receiving Alert Message, if it is not unexpected but is
unsupported for some reason, the port should return Not_Supported
Message response.

Also, according to PD3.0 Spec 6.5.2.1.4 Event Flags Field, the
OTP/OVP/OCP flags in the Event Flags field in Status Message no longer
require Get_PPS_Status Message to clear them. Thus remove it when
receiving Status Message with those flags being set.

In addition, add the missing AMS operations for Status Message.

Fixes: 64f7c494a3c0 ("typec: tcpm: Add support for sink PPS related messages")
Fixes: 0908c5aca31e ("usb: typec: tcpm: AMS and Collision Avoidance")
Signed-off-by: Kyle Tso <kyletso@google.com>
Link: https://lore.kernel.org/r/20210531164928.2368606-1-kyletso@google.com
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Kyle Tso and committed by
Greg Kroah-Hartman
063933f4 4d2aa178

+27 -29
+27 -25
drivers/usb/typec/tcpm/tcpm.c
··· 2188 2188 2189 2189 if (!type) { 2190 2190 tcpm_log(port, "Alert message received with no type"); 2191 + tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP); 2191 2192 return; 2192 2193 } 2193 2194 2194 2195 /* Just handling non-battery alerts for now */ 2195 2196 if (!(type & USB_PD_ADO_TYPE_BATT_STATUS_CHANGE)) { 2196 - switch (port->state) { 2197 - case SRC_READY: 2198 - case SNK_READY: 2197 + if (port->pwr_role == TYPEC_SOURCE) { 2198 + port->upcoming_state = GET_STATUS_SEND; 2199 + tcpm_ams_start(port, GETTING_SOURCE_SINK_STATUS); 2200 + } else { 2201 + /* 2202 + * Do not check SinkTxOk here in case the Source doesn't set its Rp to 2203 + * SinkTxOk in time. 2204 + */ 2205 + port->ams = GETTING_SOURCE_SINK_STATUS; 2199 2206 tcpm_set_state(port, GET_STATUS_SEND, 0); 2200 - break; 2201 - default: 2202 - tcpm_queue_message(port, PD_MSG_CTRL_WAIT); 2203 - break; 2204 2207 } 2208 + } else { 2209 + tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP); 2205 2210 } 2206 2211 } 2207 2212 ··· 2450 2445 tcpm_pd_handle_state(port, BIST_RX, BIST, 0); 2451 2446 break; 2452 2447 case PD_DATA_ALERT: 2453 - tcpm_handle_alert(port, msg->payload, cnt); 2448 + if (port->state != SRC_READY && port->state != SNK_READY) 2449 + tcpm_pd_handle_state(port, port->pwr_role == TYPEC_SOURCE ? 2450 + SRC_SOFT_RESET_WAIT_SNK_TX : SNK_SOFT_RESET, 2451 + NONE_AMS, 0); 2452 + else 2453 + tcpm_handle_alert(port, msg->payload, cnt); 2454 2454 break; 2455 2455 case PD_DATA_BATT_STATUS: 2456 2456 case PD_DATA_GET_COUNTRY_INFO: ··· 2779 2769 2780 2770 switch (type) { 2781 2771 case PD_EXT_STATUS: 2782 - /* 2783 - * If PPS related events raised then get PPS status to clear 2784 - * (see USB PD 3.0 Spec, 6.5.2.4) 2785 - */ 2786 - if (msg->ext_msg.data[USB_PD_EXT_SDB_EVENT_FLAGS] & 2787 - USB_PD_EXT_SDB_PPS_EVENTS) 2788 - tcpm_pd_handle_state(port, GET_PPS_STATUS_SEND, 2789 - GETTING_SOURCE_SINK_STATUS, 0); 2790 - 2791 - else 2792 - tcpm_pd_handle_state(port, ready_state(port), NONE_AMS, 0); 2793 - break; 2794 2772 case PD_EXT_PPS_STATUS: 2795 - /* 2796 - * For now the PPS status message is used to clear events 2797 - * and nothing more. 2798 - */ 2799 - tcpm_pd_handle_state(port, ready_state(port), NONE_AMS, 0); 2773 + if (port->ams == GETTING_SOURCE_SINK_STATUS) { 2774 + tcpm_ams_finish(port); 2775 + tcpm_set_state(port, ready_state(port), 0); 2776 + } else { 2777 + /* unexpected Status or PPS_Status Message */ 2778 + tcpm_pd_handle_state(port, port->pwr_role == TYPEC_SOURCE ? 2779 + SRC_SOFT_RESET_WAIT_SNK_TX : SNK_SOFT_RESET, 2780 + NONE_AMS, 0); 2781 + } 2800 2782 break; 2801 2783 case PD_EXT_SOURCE_CAP_EXT: 2802 2784 case PD_EXT_GET_BATT_CAP:
-4
include/linux/usb/pd_ext_sdb.h
··· 24 24 #define USB_PD_EXT_SDB_EVENT_OVP BIT(3) 25 25 #define USB_PD_EXT_SDB_EVENT_CF_CV_MODE BIT(4) 26 26 27 - #define USB_PD_EXT_SDB_PPS_EVENTS (USB_PD_EXT_SDB_EVENT_OCP | \ 28 - USB_PD_EXT_SDB_EVENT_OTP | \ 29 - USB_PD_EXT_SDB_EVENT_OVP) 30 - 31 27 #endif /* __LINUX_USB_PD_EXT_SDB_H */