in typec/tcpm/tcpm.c [2543:2803]
static void tcpm_pd_ctrl_request(struct tcpm_port *port,
const struct pd_message *msg)
{
enum pd_ctrl_msg_type type = pd_header_type_le(msg->header);
enum tcpm_state next_state;
/*
* Stop VDM state machine if interrupted by other Messages while NOT_SUPP is allowed in
* VDM AMS if waiting for VDM responses and will be handled later.
*/
if (tcpm_vdm_ams(port) && type != PD_CTRL_NOT_SUPP && type != PD_CTRL_GOOD_CRC) {
port->vdm_state = VDM_STATE_ERR_BUSY;
tcpm_ams_finish(port);
mod_vdm_delayed_work(port, 0);
}
switch (type) {
case PD_CTRL_GOOD_CRC:
case PD_CTRL_PING:
break;
case PD_CTRL_GET_SOURCE_CAP:
tcpm_pd_handle_msg(port, PD_MSG_DATA_SOURCE_CAP, GET_SOURCE_CAPABILITIES);
break;
case PD_CTRL_GET_SINK_CAP:
tcpm_pd_handle_msg(port, PD_MSG_DATA_SINK_CAP, GET_SINK_CAPABILITIES);
break;
case PD_CTRL_GOTO_MIN:
break;
case PD_CTRL_PS_RDY:
switch (port->state) {
case SNK_TRANSITION_SINK:
if (port->vbus_present) {
tcpm_set_current_limit(port,
port->req_current_limit,
port->req_supply_voltage);
port->explicit_contract = true;
tcpm_set_auto_vbus_discharge_threshold(port,
TYPEC_PWR_MODE_PD,
port->pps_data.active,
port->supply_voltage);
tcpm_set_state(port, SNK_READY, 0);
} else {
/*
* Seen after power swap. Keep waiting for VBUS
* in a transitional state.
*/
tcpm_set_state(port,
SNK_TRANSITION_SINK_VBUS, 0);
}
break;
case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED:
tcpm_set_state(port, PR_SWAP_SRC_SNK_SINK_ON, 0);
break;
case PR_SWAP_SNK_SRC_SINK_OFF:
tcpm_set_state(port, PR_SWAP_SNK_SRC_SOURCE_ON, 0);
break;
case VCONN_SWAP_WAIT_FOR_VCONN:
tcpm_set_state(port, VCONN_SWAP_TURN_OFF_VCONN, 0);
break;
case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
tcpm_set_state(port, FR_SWAP_SNK_SRC_NEW_SINK_READY, 0);
break;
default:
tcpm_pd_handle_state(port,
port->pwr_role == TYPEC_SOURCE ?
SRC_SOFT_RESET_WAIT_SNK_TX :
SNK_SOFT_RESET,
NONE_AMS, 0);
break;
}
break;
case PD_CTRL_REJECT:
case PD_CTRL_WAIT:
case PD_CTRL_NOT_SUPP:
switch (port->state) {
case SNK_NEGOTIATE_CAPABILITIES:
/* USB PD specification, Figure 8-43 */
if (port->explicit_contract)
next_state = SNK_READY;
else
next_state = SNK_WAIT_CAPABILITIES;
/* Threshold was relaxed before sending Request. Restore it back. */
tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
port->pps_data.active,
port->supply_voltage);
tcpm_set_state(port, next_state, 0);
break;
case SNK_NEGOTIATE_PPS_CAPABILITIES:
/* Revert data back from any requested PPS updates */
port->pps_data.req_out_volt = port->supply_voltage;
port->pps_data.req_op_curr = port->current_limit;
port->pps_status = (type == PD_CTRL_WAIT ?
-EAGAIN : -EOPNOTSUPP);
/* Threshold was relaxed before sending Request. Restore it back. */
tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
port->pps_data.active,
port->supply_voltage);
tcpm_set_state(port, SNK_READY, 0);
break;
case DR_SWAP_SEND:
port->swap_status = (type == PD_CTRL_WAIT ?
-EAGAIN : -EOPNOTSUPP);
tcpm_set_state(port, DR_SWAP_CANCEL, 0);
break;
case PR_SWAP_SEND:
port->swap_status = (type == PD_CTRL_WAIT ?
-EAGAIN : -EOPNOTSUPP);
tcpm_set_state(port, PR_SWAP_CANCEL, 0);
break;
case VCONN_SWAP_SEND:
port->swap_status = (type == PD_CTRL_WAIT ?
-EAGAIN : -EOPNOTSUPP);
tcpm_set_state(port, VCONN_SWAP_CANCEL, 0);
break;
case FR_SWAP_SEND:
tcpm_set_state(port, FR_SWAP_CANCEL, 0);
break;
case GET_SINK_CAP:
port->sink_cap_done = true;
tcpm_set_state(port, ready_state(port), 0);
break;
case SRC_READY:
case SNK_READY:
if (port->vdm_state > VDM_STATE_READY) {
port->vdm_state = VDM_STATE_DONE;
if (tcpm_vdm_ams(port))
tcpm_ams_finish(port);
mod_vdm_delayed_work(port, 0);
break;
}
fallthrough;
default:
tcpm_pd_handle_state(port,
port->pwr_role == TYPEC_SOURCE ?
SRC_SOFT_RESET_WAIT_SNK_TX :
SNK_SOFT_RESET,
NONE_AMS, 0);
break;
}
break;
case PD_CTRL_ACCEPT:
switch (port->state) {
case SNK_NEGOTIATE_CAPABILITIES:
port->pps_data.active = false;
tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
break;
case SNK_NEGOTIATE_PPS_CAPABILITIES:
port->pps_data.active = true;
port->pps_data.min_volt = port->pps_data.req_min_volt;
port->pps_data.max_volt = port->pps_data.req_max_volt;
port->pps_data.max_curr = port->pps_data.req_max_curr;
port->req_supply_voltage = port->pps_data.req_out_volt;
port->req_current_limit = port->pps_data.req_op_curr;
power_supply_changed(port->psy);
tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
break;
case SOFT_RESET_SEND:
if (port->ams == SOFT_RESET_AMS)
tcpm_ams_finish(port);
if (port->pwr_role == TYPEC_SOURCE) {
port->upcoming_state = SRC_SEND_CAPABILITIES;
tcpm_ams_start(port, POWER_NEGOTIATION);
} else {
tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
}
break;
case DR_SWAP_SEND:
tcpm_set_state(port, DR_SWAP_CHANGE_DR, 0);
break;
case PR_SWAP_SEND:
tcpm_set_state(port, PR_SWAP_START, 0);
break;
case VCONN_SWAP_SEND:
tcpm_set_state(port, VCONN_SWAP_START, 0);
break;
case FR_SWAP_SEND:
tcpm_set_state(port, FR_SWAP_SNK_SRC_TRANSITION_TO_OFF, 0);
break;
default:
tcpm_pd_handle_state(port,
port->pwr_role == TYPEC_SOURCE ?
SRC_SOFT_RESET_WAIT_SNK_TX :
SNK_SOFT_RESET,
NONE_AMS, 0);
break;
}
break;
case PD_CTRL_SOFT_RESET:
port->ams = SOFT_RESET_AMS;
tcpm_set_state(port, SOFT_RESET, 0);
break;
case PD_CTRL_DR_SWAP:
/*
* XXX
* 6.3.9: If an alternate mode is active, a request to swap
* alternate modes shall trigger a port reset.
*/
if (port->typec_caps.data != TYPEC_PORT_DRD) {
tcpm_pd_handle_msg(port,
port->negotiated_rev < PD_REV30 ?
PD_MSG_CTRL_REJECT :
PD_MSG_CTRL_NOT_SUPP,
NONE_AMS);
} else {
if (port->send_discover) {
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
break;
}
tcpm_pd_handle_state(port, DR_SWAP_ACCEPT, DATA_ROLE_SWAP, 0);
}
break;
case PD_CTRL_PR_SWAP:
if (port->port_type != TYPEC_PORT_DRP) {
tcpm_pd_handle_msg(port,
port->negotiated_rev < PD_REV30 ?
PD_MSG_CTRL_REJECT :
PD_MSG_CTRL_NOT_SUPP,
NONE_AMS);
} else {
if (port->send_discover) {
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
break;
}
tcpm_pd_handle_state(port, PR_SWAP_ACCEPT, POWER_ROLE_SWAP, 0);
}
break;
case PD_CTRL_VCONN_SWAP:
if (port->send_discover) {
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
break;
}
tcpm_pd_handle_state(port, VCONN_SWAP_ACCEPT, VCONN_SWAP, 0);
break;
case PD_CTRL_GET_SOURCE_CAP_EXT:
case PD_CTRL_GET_STATUS:
case PD_CTRL_FR_SWAP:
case PD_CTRL_GET_PPS_STATUS:
case PD_CTRL_GET_COUNTRY_CODES:
/* Currently not supported */
tcpm_pd_handle_msg(port,
port->negotiated_rev < PD_REV30 ?
PD_MSG_CTRL_REJECT :
PD_MSG_CTRL_NOT_SUPP,
NONE_AMS);
break;
default:
tcpm_pd_handle_msg(port,
port->negotiated_rev < PD_REV30 ?
PD_MSG_CTRL_REJECT :
PD_MSG_CTRL_NOT_SUPP,
NONE_AMS);
tcpm_log(port, "Unrecognized ctrl message type %#x", type);
break;
}
}