summaryrefslogtreecommitdiff
path: root/common/usbc/usb_pe_drp_sm.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usbc/usb_pe_drp_sm.c')
-rw-r--r--common/usbc/usb_pe_drp_sm.c141
1 files changed, 140 insertions, 1 deletions
diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c
index 6c8c1f3356..d144f07ba7 100644
--- a/common/usbc/usb_pe_drp_sm.c
+++ b/common/usbc/usb_pe_drp_sm.c
@@ -237,6 +237,7 @@ enum usb_pe_state {
PE_VDM_IDENTITY_REQUEST_CBL,
PE_INIT_PORT_VDM_IDENTITY_REQUEST,
PE_INIT_VDM_SVIDS_REQUEST,
+ PE_INIT_VDM_MODES_REQUEST,
PE_VDM_REQUEST,
PE_VDM_ACKED,
PE_VDM_RESPONSE,
@@ -323,6 +324,7 @@ static const char * const pe_state_names[] = {
[PE_INIT_PORT_VDM_IDENTITY_REQUEST] =
"PE_INIT_PORT_VDM_Identity_Request",
[PE_INIT_VDM_SVIDS_REQUEST] = "PE_INIT_VDM_SVIDs_Request",
+ [PE_INIT_VDM_MODES_REQUEST] = "PE_INIT_VDM_Modes_Request",
[PE_VDM_REQUEST] = "PE_VDM_Request",
[PE_VDM_ACKED] = "PE_VDM_Acked",
[PE_VDM_RESPONSE] = "PE_VDM_Response",
@@ -1296,6 +1298,12 @@ static bool pe_attempt_port_discovery(int port)
pe[port].tx_type = TCPC_TX_SOP;
set_state_pe(port, PE_INIT_VDM_SVIDS_REQUEST);
return true;
+ } else if (pd_get_modes_discovery(port, TCPC_TX_SOP) ==
+ PD_DISC_NEEDED &&
+ pe_can_send_sop_vdm(port, CMD_DISCOVER_MODES)) {
+ pe[port].tx_type = TCPC_TX_SOP;
+ set_state_pe(port, PE_INIT_VDM_MODES_REQUEST);
+ return true;
/*
* Note: determine if next VDM can be sent by taking advantage
* of discovery following the VDM command enum ordering.
@@ -4528,7 +4536,7 @@ static void pe_init_vdm_svids_request_run(int port)
ext == 0) {
/*
* Valid Discover SVIDs ACKs should have at least 2 objects
- * (header, SVID VDO).
+ * (VDM header, SVID VDO).
*/
if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_ACK &&
cnt >= 2) {
@@ -4577,6 +4585,131 @@ static void pe_init_vdm_svids_request_exit(int port)
{
/* Invalidate TX type so that it must be set before next call */
pe[port].tx_type = TCPC_TX_INVALID;
+
+ /*
+ * No need to mark modes discovery as failed. If no SVIDs were
+ * discovered, mode discovery is trivially complete.
+ */
+}
+
+/**
+ * PE_INIT_VDM_Modes_Request
+ *
+ * Used for SOP and SOP' requests, selected by pe[port].tx_type prior to entry.
+ */
+static void pe_init_vdm_modes_request_entry(int port)
+{
+ uint32_t *msg = (uint32_t *)tx_emsg[port].buf;
+ const struct svid_mode_data *mode_data =
+ pd_get_next_mode(port, pe[port].tx_type);
+ uint16_t svid;
+ /*
+ * The caller should have checked that there was something to discover
+ * before entering this state.
+ */
+ assert(mode_data);
+ assert(mode_data->discovery == PD_DISC_NEEDED);
+ svid = mode_data->svid;
+
+ print_current_state(port);
+
+ if (pe[port].tx_type == TCPC_TX_INVALID) {
+ CPRINTS("C%d: TX type expected to be set, returning", port);
+ set_state_pe(port, get_last_state_pe(port));
+ return;
+ }
+
+ msg[0] = VDO((uint16_t) svid, 1,
+ VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) |
+ CMD_DISCOVER_MODES);
+ tx_emsg[port].len = sizeof(uint32_t);
+
+ prl_send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF);
+}
+
+static void pe_init_vdm_modes_request_run(int port)
+{
+ uint32_t *payload;
+ int sop;
+ uint8_t type;
+ uint8_t cnt;
+ uint8_t ext;
+ struct svid_mode_data *mode_data =
+ pd_get_next_mode(port, pe[port].tx_type);
+ uint16_t requested_svid;
+
+ /* No message received */
+ if (!PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED))
+ return;
+
+ assert(mode_data);
+ assert(mode_data->discovery == PD_DISC_NEEDED);
+ requested_svid = mode_data->svid;
+
+ /* Retrieve the message information */
+ payload = (uint32_t *)rx_emsg[port].buf;
+ sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
+ type = PD_HEADER_TYPE(rx_emsg[port].header);
+ cnt = PD_HEADER_CNT(rx_emsg[port].header);
+ ext = PD_HEADER_EXT(rx_emsg[port].header);
+
+ PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
+
+ if (sop == pe[port].tx_type && type == PD_DATA_VENDOR_DEF &&
+ cnt >= 2 && ext == 0) {
+ /*
+ * Valid Discover Modes responses should have at least 2 objects
+ * (VDM header and at least one mode VDO).
+ */
+ if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_ACK) {
+ /* PE_INIT_VDM_Modes_ACKed embedded here */
+ dfp_consume_modes(port, sop, cnt, payload);
+ /*
+ * TODO(b:152419850): Fake vdm_cmd for now to
+ * ensure existing discovery process continues.
+ */
+ pe[port].vdm_cmd = DISCOVER_MODES;
+ } else if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_NAK) {
+ /* PE_INIT_VDM_Modes_NAKed embedded here */
+ pd_set_modes_discovery(port, sop,
+ requested_svid, PD_DISC_FAIL);
+ } else if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_BUSY) {
+ /*
+ * Don't fill in the discovery field so we
+ * re-probe in tVDMBusy
+ */
+ CPRINTS("C%d: Partner Busy, Discover Modes will be "
+ "re-tried", port);
+ pe[port].discover_identity_timer =
+ get_time().val + PD_T_VDM_BUSY;
+ } else {
+ /*
+ * Partner gave us an incorrect size or command,
+ * mark discovery as failed
+ */
+ pd_set_modes_discovery(port, sop,
+ requested_svid, PD_DISC_FAIL);
+ CPRINTS("C%d: Unexpected Discover Modes response: "
+ "0x%04x 0x%04x",
+ port, rx_emsg[port].header,
+ payload[0]);
+ }
+ } else {
+ /*
+ * Unexpected Message Received. Src.Ready or Snk.Ready can
+ * handle it.
+ */
+ PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED);
+ }
+
+ /* Return to calling state (PE_{SRC,SNK}_Ready) */
+ set_state_pe(port, get_last_state_pe(port));
+}
+
+static void pe_init_vdm_modes_request_exit(int port)
+{
+ /* Invalidate TX type so it must be set before next call */
+ pe[port].tx_type = TCPC_TX_INVALID;
}
/**
@@ -5664,6 +5797,12 @@ static const struct usb_state pe_states[] = {
.exit = pe_init_vdm_svids_request_exit,
.parent = &pe_states[PE_VDM_SEND_REQUEST],
},
+ [PE_INIT_VDM_MODES_REQUEST] = {
+ .entry = pe_init_vdm_modes_request_entry,
+ .run = pe_init_vdm_modes_request_run,
+ .exit = pe_init_vdm_modes_request_exit,
+ .parent = &pe_states[PE_VDM_SEND_REQUEST],
+ },
[PE_VDM_REQUEST] = {
.entry = pe_vdm_request_entry,
.run = pe_vdm_request_run,