diff options
Diffstat (limited to 'common/usb_prl_sm.c')
-rw-r--r-- | common/usb_prl_sm.c | 2163 |
1 files changed, 2163 insertions, 0 deletions
diff --git a/common/usb_prl_sm.c b/common/usb_prl_sm.c new file mode 100644 index 0000000000..42b4ee3c24 --- /dev/null +++ b/common/usb_prl_sm.c @@ -0,0 +1,2163 @@ +/* Copyright 2019 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "battery.h" +#include "battery_smart.h" +#include "board.h" +#include "charge_manager.h" +#include "charge_state.h" +#include "chipset.h" +#include "common.h" +#include "console.h" +#include "ec_commands.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "tcpm.h" +#include "util.h" +#include "usb_charge.h" +#include "usb_mux.h" +#include "usb_pd.h" +#include "usb_pe_sm.h" +#include "usb_prl_sm.h" +#include "usb_tc_sm.h" +#include "usb_emsg.h" +#include "usb_sm.h" +#include "vpd_api.h" +#include "version.h" + +/* Protocol Layer Flags */ +#define PRL_FLAGS_TX_COMPLETE BIT(0) +#define PRL_FLAGS_START_AMS BIT(1) +#define PRL_FLAGS_END_AMS BIT(2) +#define PRL_FLAGS_TX_ERROR BIT(3) +#define PRL_FLAGS_PE_HARD_RESET BIT(4) +#define PRL_FLAGS_HARD_RESET_COMPLETE BIT(5) +#define PRL_FLAGS_PORT_PARTNER_HARD_RESET BIT(6) +#define PRL_FLAGS_MSG_XMIT BIT(7) +#define PRL_FLAGS_MSG_RECEIVED BIT(8) +#define PRL_FLAGS_ABORT BIT(9) +#define PRL_FLAGS_CHUNKING BIT(10) + +/* PD counter definitions */ +#define PD_MESSAGE_ID_COUNT 7 + +#define RCH_OBJ(port) (SM_OBJ(rch[port])) +#define TCH_OBJ(port) (SM_OBJ(tch[port])) +#define PRL_TX_OBJ(port) (SM_OBJ(prl_tx[port])) +#define PRL_HR_OBJ(port) (SM_OBJ(prl_hr[port])) + +#define RCH_TEST_OBJ(port) (SM_OBJ(rch[(port)].obj)) +#define TCH_TEST_OBJ(port) (SM_OBJ(tch[(port)].obj)) +#define PRL_TX_TEST_OBJ(port) (SM_OBJ(prl_tx[(port)].obj)) +#define PRL_HR_TEST_OBJ(port) (SM_OBJ(prl_hr[(port)].obj)) + +static enum sm_local_state local_state[CONFIG_USB_PD_PORT_COUNT] = {SM_INIT}; + +/* Chunked Rx State Machine Object */ +static struct rx_chunked { + /* struct sm_obj must be first. */ + struct sm_obj obj; + /* state id */ + enum rch_state_id state_id; + /* PRL_FLAGS */ + uint32_t flags; + /* protocol timer */ + uint64_t chunk_sender_response_timer; +} rch[CONFIG_USB_PD_PORT_COUNT]; + +/* Chunked Tx State Machine Object */ +static struct tx_chunked { + /* struct sm_obj must be first. */ + struct sm_obj obj; + /* state id */ + enum tch_state_id state_id; + /* state machine flags */ + uint32_t flags; + /* protocol timer */ + uint64_t chunk_sender_request_timer; +} tch[CONFIG_USB_PD_PORT_COUNT]; + +/* Message Reception State Machine Object */ +static struct protocol_layer_rx { + /* message ids for all valid port partners */ + int msg_id[NUM_XMIT_TYPES]; +} prl_rx[CONFIG_USB_PD_PORT_COUNT]; + +/* Message Transmission State Machine Object */ +static struct protocol_layer_tx { + /* struct sm_obj must be first. */ + struct sm_obj obj; + /* state id */ + enum prl_tx_state_id state_id; + /* state machine flags */ + uint32_t flags; + /* protocol timer */ + uint64_t sink_tx_timer; + /* tcpc transmit timeout */ + uint64_t tcpc_tx_timeout; + /* Last SOP* we transmitted to */ + uint8_t sop; + /* message id counters for all 6 port partners */ + uint32_t msg_id_counter[NUM_XMIT_TYPES]; + /* message retry counter */ + uint32_t retry_counter; + /* transmit status */ + int xmit_status; +} prl_tx[CONFIG_USB_PD_PORT_COUNT]; + +/* Hard Reset State Machine Object */ +static struct protocol_hard_reset { + /* struct sm_obj must be first. */ + struct sm_obj obj; + /* state id */ + enum prl_hr_state_id state_id; + /* state machine flags */ + uint32_t flags; + /* protocol timer */ + uint64_t hard_reset_complete_timer; +} prl_hr[CONFIG_USB_PD_PORT_COUNT]; + +/* Chunking Message Object */ +static struct pd_message { + /* message status flags */ + uint32_t status_flags; + + /* SOP* */ + enum tcpm_transmit_type xmit_type; + /* type of message */ + uint8_t msg_type; + /* extended message */ + uint8_t ext; + /* PD revision */ + enum pd_rev_type rev; + /* Number of 32-bit objects in chk_buf */ + uint16_t data_objs; + /* temp chunk buffer */ + uint32_t chk_buf[7]; + uint32_t chunk_number_expected; + uint32_t num_bytes_received; + uint32_t chunk_number_to_send; + uint32_t send_offset; +} pdmsg[CONFIG_USB_PD_PORT_COUNT]; + +struct extended_msg emsg[CONFIG_USB_PD_PORT_COUNT]; + +/* Protocol Layer States */ +/* Common Protocol Layer Message Transmission */ +static void prl_tx_construct_message(int port); + +static unsigned int prl_tx_phy_layer_reset(int port, enum signal sig); +static unsigned int prl_tx_phy_layer_reset_entry(int port); +static unsigned int prl_tx_phy_layer_reset_run(int port); + +static unsigned int prl_tx_wait_for_message_request(int port, enum signal sig); +static unsigned int prl_tx_wait_for_message_request_entry(int port); +static unsigned int prl_tx_wait_for_message_request_run(int port); + +static unsigned int prl_tx_layer_reset_for_transmit(int port, enum signal sig); +static unsigned int prl_tx_layer_reset_for_transmit_entry(int port); +static unsigned int prl_tx_layer_reset_for_transmit_run(int port); + +static unsigned int prl_tx_wait_for_phy_response(int port, enum signal sig); +static unsigned int prl_tx_wait_for_phy_response_entry(int port); +static unsigned int prl_tx_wait_for_phy_response_run(int port); +static unsigned int prl_tx_wait_for_phy_response_exit(int port); + +static unsigned int prl_tx_src_source_tx(int port, enum signal sig); +static unsigned int prl_tx_src_source_tx_entry(int port); +static unsigned int prl_tx_src_source_tx_run(int port); + +static unsigned int prl_tx_snk_start_ams(int port, enum signal sig); +static unsigned int prl_tx_snk_start_ams_entry(int port); +static unsigned int prl_tx_snk_start_ams_run(int port); + +/* Source Protocol Layser Message Transmission */ +static unsigned int prl_tx_src_pending(int port, enum signal sig); +static unsigned int prl_tx_src_pending_entry(int port); +static unsigned int prl_tx_src_pending_run(int port); + +/* Sink Protocol Layer Message Transmission */ +static unsigned int prl_tx_snk_pending(int port, enum signal sig); +static unsigned int prl_tx_snk_pending_entry(int port); +static unsigned int prl_tx_snk_pending_run(int port); + +static unsigned int prl_tx_discard_message(int port, enum signal sig); +static unsigned int prl_tx_discard_message_entry(int port); +static unsigned int prl_tx_discard_message_run(int port); + +/* Protocol Layer Message Reception */ +static unsigned int prl_rx_wait_for_phy_message(int port, int evt); + +/* Hard Reset Operation */ +static unsigned int prl_hr_wait_for_request(int port, enum signal sig); +static unsigned int prl_hr_wait_for_request_entry(int port); +static unsigned int prl_hr_wait_for_request_run(int port); + +static unsigned int prl_hr_reset_layer(int port, enum signal sig); +static unsigned int prl_hr_reset_layer_entry(int port); +static unsigned int prl_hr_reset_layer_run(int port); + +static unsigned int + prl_hr_wait_for_phy_hard_reset_complete(int port, enum signal sig); +static unsigned int prl_hr_wait_for_phy_hard_reset_complete_entry(int port); +static unsigned int prl_hr_wait_for_phy_hard_reset_complete_run(int port); + +static unsigned int + prl_hr_wait_for_pe_hard_reset_complete(int port, enum signal sig); +static unsigned int prl_hr_wait_for_pe_hard_reset_complete_entry(int port); +static unsigned int prl_hr_wait_for_pe_hard_reset_complete_run(int port); +static unsigned int prl_hr_wait_for_pe_hard_reset_complete_exit(int port); + +/* Chunked Rx */ +static unsigned int + rch_wait_for_message_from_protocol_layer(int port, enum signal sig); +static unsigned int rch_wait_for_message_from_protocol_layer_entry(int port); +static unsigned int rch_wait_for_message_from_protocol_layer_run(int port); + +static unsigned int rch_processing_extended_message(int port, enum signal sig); +static unsigned int rch_processing_extended_message_entry(int port); +static unsigned int rch_processing_extended_message_run(int port); + +static unsigned int rch_requesting_chunk(int port, enum signal sig); +static unsigned int rch_requesting_chunk_entry(int port); +static unsigned int rch_requesting_chunk_run(int port); + +static unsigned int rch_waiting_chunk(int port, enum signal sig); +static unsigned int rch_waiting_chunk_entry(int port); +static unsigned int rch_waiting_chunk_run(int port); + +static unsigned int rch_report_error(int port, enum signal sig); +static unsigned int rch_report_error_entry(int port); +static unsigned int rch_report_error_run(int port); + +/* Chunked Tx */ +static unsigned int + tch_wait_for_message_request_from_pe(int port, enum signal sig); +static unsigned int tch_wait_for_message_request_from_pe_entry(int port); +static unsigned int tch_wait_for_message_request_from_pe_run(int port); + +static unsigned int + tch_wait_for_transmission_complete(int port, enum signal sig); +static unsigned int tch_wait_for_transmission_complete_entry(int port); +static unsigned int tch_wait_for_transmission_complete_run(int port); + +static unsigned int tch_construct_chunked_message(int port, enum signal sig); +static unsigned int tch_construct_chunked_message_entry(int port); +static unsigned int tch_construct_chunked_message_run(int port); + +static unsigned int tch_sending_chunked_message(int port, enum signal sig); +static unsigned int tch_sending_chunked_message_entry(int port); +static unsigned int tch_sending_chunked_message_run(int port); + +static unsigned int tch_wait_chunk_request(int port, enum signal sig); +static unsigned int tch_wait_chunk_request_entry(int port); +static unsigned int tch_wait_chunk_request_run(int port); + +static unsigned int tch_message_received(int port, enum signal sig); +static unsigned int tch_message_received_entry(int port); +static unsigned int tch_message_received_run(int port); + +static unsigned int do_nothing_exit(int port); +static unsigned int get_super_state(int port); + +static const state_sig prl_tx_phy_layer_reset_sig[] = { + prl_tx_phy_layer_reset_entry, + prl_tx_phy_layer_reset_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig prl_tx_wait_for_message_request_sig[] = { + prl_tx_wait_for_message_request_entry, + prl_tx_wait_for_message_request_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig prl_tx_layer_reset_for_transmit_sig[] = { + prl_tx_layer_reset_for_transmit_entry, + prl_tx_layer_reset_for_transmit_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig prl_tx_wait_for_phy_response_sig[] = { + prl_tx_wait_for_phy_response_entry, + prl_tx_wait_for_phy_response_run, + prl_tx_wait_for_phy_response_exit, + get_super_state +}; + +static const state_sig prl_tx_src_source_tx_sig[] = { + prl_tx_src_source_tx_entry, + prl_tx_src_source_tx_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig prl_tx_snk_start_ams_sig[] = { + prl_tx_snk_start_ams_entry, + prl_tx_snk_start_ams_run, + do_nothing_exit, + get_super_state +}; + +/* Source Protocol Layser Message Transmission */ +static const state_sig prl_tx_src_pending_sig[] = { + prl_tx_src_pending_entry, + prl_tx_src_pending_run, + do_nothing_exit, + get_super_state +}; + +/* Sink Protocol Layer Message Transmission */ +static const state_sig prl_tx_snk_pending_sig[] = { + prl_tx_snk_pending_entry, + prl_tx_snk_pending_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig prl_tx_discard_message_sig[] = { + prl_tx_discard_message_entry, + prl_tx_discard_message_run, + do_nothing_exit, + get_super_state +}; + +/* Hard Reset Operation */ +static const state_sig prl_hr_wait_for_request_sig[] = { + prl_hr_wait_for_request_entry, + prl_hr_wait_for_request_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig prl_hr_reset_layer_sig[] = { + prl_hr_reset_layer_entry, + prl_hr_reset_layer_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig prl_hr_wait_for_phy_hard_reset_complete_sig[] = { + prl_hr_wait_for_phy_hard_reset_complete_entry, + prl_hr_wait_for_phy_hard_reset_complete_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig prl_hr_wait_for_pe_hard_reset_complete_sig[] = { + prl_hr_wait_for_pe_hard_reset_complete_entry, + prl_hr_wait_for_pe_hard_reset_complete_run, + prl_hr_wait_for_pe_hard_reset_complete_exit, + get_super_state +}; + +/* Chunked Rx */ +static const state_sig rch_wait_for_message_from_protocol_layer_sig[] = { + rch_wait_for_message_from_protocol_layer_entry, + rch_wait_for_message_from_protocol_layer_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig rch_processing_extended_message_sig[] = { + rch_processing_extended_message_entry, + rch_processing_extended_message_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig rch_requesting_chunk_sig[] = { + rch_requesting_chunk_entry, + rch_requesting_chunk_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig rch_waiting_chunk_sig[] = { + rch_waiting_chunk_entry, + rch_waiting_chunk_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig rch_report_error_sig[] = { + rch_report_error_entry, + rch_report_error_run, + do_nothing_exit, + get_super_state +}; + +/* Chunked Tx */ +static const state_sig tch_wait_for_message_request_from_pe_sig[] = { + tch_wait_for_message_request_from_pe_entry, + tch_wait_for_message_request_from_pe_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig tch_wait_for_transmission_complete_sig[] = { + tch_wait_for_transmission_complete_entry, + tch_wait_for_transmission_complete_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig tch_construct_chunked_message_sig[] = { + tch_construct_chunked_message_entry, + tch_construct_chunked_message_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig tch_sending_chunked_message_sig[] = { + tch_sending_chunked_message_entry, + tch_sending_chunked_message_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig tch_wait_chunk_request_sig[] = { + tch_wait_chunk_request_entry, + tch_wait_chunk_request_run, + do_nothing_exit, + get_super_state +}; + +static const state_sig tch_message_received_sig[] = { + tch_message_received_entry, + tch_message_received_run, + do_nothing_exit, + get_super_state +}; + +void pd_transmit_complete(int port, int status) +{ + prl_tx[port].xmit_status = status; +} + +void pd_execute_hard_reset(int port) +{ + /* Only allow async. function calls when state machine is running */ + if (local_state[port] != SM_RUN) + return; + + prl_hr[port].flags |= PRL_FLAGS_PORT_PARTNER_HARD_RESET; + set_state(port, PRL_HR_OBJ(port), prl_hr_reset_layer); + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); +} + +void prl_execute_hard_reset(int port) +{ + /* Only allow async. function calls when state machine is running */ + if (local_state[port] != SM_RUN) + return; + + prl_hr[port].flags |= PRL_FLAGS_PE_HARD_RESET; + set_state(port, PRL_HR_OBJ(port), prl_hr_reset_layer); + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); +} + +void prl_init(int port) +{ + int i; + + prl_tx[port].flags = 0; + prl_tx[port].xmit_status = TCPC_TX_UNSET; + + tch[port].flags = 0; + rch[port].flags = 0; + + /* + * Initialize to highest revision supported. If the port partner + * doesn't support this revision, the Protocol Engine will lower + * this value to the revision supported by the port partner. + */ + pdmsg[port].rev = PD_REV30; + pdmsg[port].status_flags = 0; + + prl_hr[port].flags = 0; + + for (i = 0; i < NUM_XMIT_TYPES; i++) { + prl_rx[port].msg_id[i] = -1; + prl_tx[port].msg_id_counter[i] = 0; + } + + init_state(port, PRL_TX_OBJ(port), prl_tx_phy_layer_reset); + init_state(port, RCH_OBJ(port), + rch_wait_for_message_from_protocol_layer); + init_state(port, TCH_OBJ(port), tch_wait_for_message_request_from_pe); + init_state(port, PRL_HR_OBJ(port), prl_hr_wait_for_request); +} + +enum rch_state_id get_rch_state_id(int port) +{ + return rch[port].state_id; +} + +enum tch_state_id get_tch_state_id(int port) +{ + return tch[port].state_id; +} + +enum prl_tx_state_id get_prl_tx_state_id(int port) +{ + return prl_tx[port].state_id; +} + +enum prl_hr_state_id get_prl_hr_state_id(int port) +{ + return prl_hr[port].state_id; +} + +void prl_start_ams(int port) +{ + prl_tx[port].flags |= PRL_FLAGS_START_AMS; +} + +void prl_end_ams(int port) +{ + prl_tx[port].flags |= PRL_FLAGS_END_AMS; +} + +void prl_hard_reset_complete(int port) +{ + prl_hr[port].flags |= PRL_FLAGS_HARD_RESET_COMPLETE; + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); +} + +void prl_send_ctrl_msg(int port, + enum tcpm_transmit_type type, + enum pd_ctrl_msg_type msg) +{ + pdmsg[port].xmit_type = type; + pdmsg[port].msg_type = msg; + pdmsg[port].ext = 0; + emsg[port].len = 0; + + tch[port].flags |= PRL_FLAGS_MSG_XMIT; + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); +} + +void prl_send_data_msg(int port, + enum tcpm_transmit_type type, + enum pd_data_msg_type msg) +{ + pdmsg[port].xmit_type = type; + pdmsg[port].msg_type = msg; + pdmsg[port].ext = 0; + + tch[port].flags |= PRL_FLAGS_MSG_XMIT; + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); +} + +void prl_send_ext_data_msg(int port, + enum tcpm_transmit_type type, + enum pd_ext_msg_type msg) +{ + pdmsg[port].xmit_type = type; + pdmsg[port].msg_type = msg; + pdmsg[port].ext = 1; + + tch[port].flags |= PRL_FLAGS_MSG_XMIT; + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); +} + +void prl_reset(int port) +{ + local_state[port] = SM_INIT; +} + +void protocol_layer(int port, int evt, int en) +{ + switch (local_state[port]) { + case SM_INIT: + prl_init(port); + local_state[port] = SM_RUN; + /* fall through */ + case SM_RUN: + /* If disabling, wait until message is sent. */ + if (!en && tch[port].state_id == + TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE) { + /* Disable RX */ +#if defined(CONFIG_USB_TYPEC_CTVPD) || defined(CONFIG_USB_TYPEC_VPD) + vpd_rx_enable(0); +#else + tcpm_set_rx_enable(port, 0); +#endif + local_state[port] = SM_PAUSED; + break; + } + + /* Run Protocol Layer Message Reception */ + prl_rx_wait_for_phy_message(port, evt); + + /* Run RX Chunked state machine */ + exe_state(port, RCH_OBJ(port), RUN_SIG); + + /* Run TX Chunked state machine */ + exe_state(port, TCH_OBJ(port), RUN_SIG); + + /* Run Protocol Layer Message Transmission state machine */ + exe_state(port, PRL_TX_OBJ(port), RUN_SIG); + + /* Run Protocol Layer Hard Reset state machine */ + exe_state(port, PRL_HR_OBJ(port), RUN_SIG); + break; + case SM_PAUSED: + if (en) + local_state[port] = SM_INIT; + break; + } +} + +enum sm_local_state prl_get_local_state(int port) +{ + return local_state[port]; +} + +void prl_set_rev(int port, enum pd_rev_type rev) +{ + pdmsg[port].rev = rev; +} + +enum pd_rev_type prl_get_rev(int port) +{ + return pdmsg[port].rev; +} + +/* Common Protocol Layer Message Transmission */ +static unsigned int prl_tx_phy_layer_reset(int port, enum signal sig) +{ + int ret; + + ret = (*prl_tx_phy_layer_reset_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_tx_phy_layer_reset_entry(int port) +{ + prl_tx[port].state_id = PRL_TX_PHY_LAYER_RESET; + +#if defined(CONFIG_USB_TYPEC_CTVPD) || defined(CONFIG_USB_TYPEC_VPD) + vpd_rx_enable(1); +#else + tcpm_init(port); + tcpm_set_rx_enable(port, 1); +#endif + + return 0; +} + +static unsigned int prl_tx_phy_layer_reset_run(int port) +{ + set_state(port, PRL_TX_OBJ(port), + prl_tx_wait_for_message_request); + return 0; +} + +static unsigned int prl_tx_wait_for_message_request(int port, enum signal sig) +{ + int ret; + + ret = (*prl_tx_wait_for_message_request_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_tx_wait_for_message_request_entry(int port) +{ + prl_tx[port].state_id = PRL_TX_WAIT_FOR_MESSAGE_REQUEST; + + /* Reset RetryCounter */ + prl_tx[port].retry_counter = 0; + + return 0; +} + +static unsigned int prl_tx_wait_for_message_request_run(int port) +{ + if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) { + prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT; + /* + * Soft Reset Message Message pending + */ + if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && + (emsg[port].len == 0)) { + set_state(port, PRL_TX_OBJ(port), + prl_tx_layer_reset_for_transmit); + } + /* + * Message pending (except Soft Reset) + */ + else { + /* NOTE: PRL_TX_Construct_Message State embedded here */ + prl_tx_construct_message(port); + set_state(port, PRL_TX_OBJ(port), + prl_tx_wait_for_phy_response); + } + + return 0; + } else if ((pdmsg[port].rev == PD_REV30) && + (prl_tx[port].flags & + (PRL_FLAGS_START_AMS | PRL_FLAGS_END_AMS))) { + if (tc_get_power_role(port) == PD_ROLE_SOURCE) { + /* + * Start of AMS notification received from + * Policy Engine + */ + if (prl_tx[port].flags & PRL_FLAGS_START_AMS) { + prl_tx[port].flags &= ~PRL_FLAGS_START_AMS; + set_state(port, PRL_TX_OBJ(port), + prl_tx_src_source_tx); + return 0; + } + /* + * End of AMS notification received from + * Policy Engine + */ + else if (prl_tx[port].flags & PRL_FLAGS_END_AMS) { + prl_tx[port].flags &= ~PRL_FLAGS_END_AMS; + /* Set Rp = SinkTxOk */ + tcpm_select_rp_value(port, SINK_TX_OK); + tcpm_set_cc(port, TYPEC_CC_RP); + prl_tx[port].retry_counter = 0; + prl_tx[port].flags = 0; + } + } else { + if (prl_tx[port].flags & PRL_FLAGS_START_AMS) { + prl_tx[port].flags &= ~PRL_FLAGS_START_AMS; + /* + * First Message in AMS notification + * received from Policy Engine. + */ + set_state(port, PRL_TX_OBJ(port), + prl_tx_snk_start_ams); + return 0; + } + } + } + + return RUN_SUPER; +} + +static void increment_msgid_counter(int port) +{ + prl_tx[port].msg_id_counter[prl_tx[port].sop] = + (prl_tx[port].msg_id_counter[prl_tx[port].sop] + 1) & + PD_MESSAGE_ID_COUNT; +} + +/* + * PrlTxDiscard + */ +static unsigned int prl_tx_discard_message(int port, enum signal sig) +{ + int ret; + + ret = (*prl_tx_discard_message_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_tx_discard_message_entry(int port) +{ + prl_tx[port].state_id = PRL_TX_DISCARD_MESSAGE; + + /* Increment msgidCounter */ + increment_msgid_counter(port); + set_state(port, PRL_TX_OBJ(port), prl_tx_phy_layer_reset); + + return 0; +} + +static unsigned int prl_tx_discard_message_run(int port) +{ + return RUN_SUPER; +} + +/* + * PrlTxSrcSourceTx + */ +static unsigned int prl_tx_src_source_tx(int port, enum signal sig) +{ + int ret; + + ret = (*prl_tx_src_source_tx_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_tx_src_source_tx_entry(int port) +{ + prl_tx[port].state_id = PRL_TX_SRC_SOURCE_TX; + + /* Set Rp = SinkTxNG */ + tcpm_select_rp_value(port, SINK_TX_NG); + tcpm_set_cc(port, TYPEC_CC_RP); + + return 0; +} + +static unsigned int prl_tx_src_source_tx_run(int port) +{ + if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) { + prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT; + + set_state(port, PRL_TX_OBJ(port), prl_tx_src_pending); + } + + return RUN_SUPER; +} + +/* + * PrlTxSnkStartAms + */ +static unsigned int prl_tx_snk_start_ams(int port, enum signal sig) +{ + int ret; + + ret = (*prl_tx_snk_start_ams_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_tx_snk_start_ams_entry(int port) +{ + prl_tx[port].state_id = PRL_TX_SNK_START_OF_AMS; + return 0; +} + +static unsigned int prl_tx_snk_start_ams_run(int port) +{ + if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) { + prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT; + + set_state(port, PRL_TX_OBJ(port), prl_tx_snk_pending); + return 0; + } + + return RUN_SUPER; +} + +/* + * PrlTxLayerResetForTransmit + */ +static unsigned int prl_tx_layer_reset_for_transmit(int port, enum signal sig) +{ + int ret; + + ret = (*prl_tx_layer_reset_for_transmit_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_tx_layer_reset_for_transmit_entry(int port) +{ + int i; + + prl_tx[port].state_id = PRL_TX_LAYER_RESET_FOR_TRANSMIT; + + /* Reset MessageIdCounters */ + for (i = 0; i < NUM_XMIT_TYPES; i++) + prl_tx[port].msg_id_counter[i] = 0; + + return 0; +} + +static unsigned int prl_tx_layer_reset_for_transmit_run(int port) +{ + /* NOTE: PRL_Tx_Construct_Message State embedded here */ + prl_tx_construct_message(port); + set_state(port, PRL_TX_OBJ(port), prl_tx_wait_for_phy_response); + + return 0; +} + +static void prl_tx_construct_message(int port) +{ + uint32_t header = PD_HEADER( + pdmsg[port].msg_type, + tc_get_power_role(port), + tc_get_data_role(port), + prl_tx[port].msg_id_counter[pdmsg[port].xmit_type], + pdmsg[port].data_objs, + pdmsg[port].rev, + pdmsg[port].ext); + + /* Save SOP* so the correct msg_id_counter can be incremented */ + prl_tx[port].sop = pdmsg[port].xmit_type; + + /* Pass message to PHY Layer */ + tcpm_transmit(port, pdmsg[port].xmit_type, header, + pdmsg[port].chk_buf); +} + +/* + * PrlTxWaitForPhyResponse + */ +static unsigned int prl_tx_wait_for_phy_response(int port, enum signal sig) +{ + int ret; + + ret = (*prl_tx_wait_for_phy_response_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_tx_wait_for_phy_response_entry(int port) +{ + prl_tx[port].state_id = PRL_TX_WAIT_FOR_PHY_RESPONSE; + + prl_tx[port].tcpc_tx_timeout = get_time().val + PD_T_TCPC_TX_TIMEOUT; + return 0; +} + +static unsigned int prl_tx_wait_for_phy_response_run(int port) +{ + /* Wait until TX is complete */ + + /* + * NOTE: The TCPC will set xmit_status to TCPC_TX_COMPLETE_DISCARDED + * when a GoodCRC containing an incorrect MessageID is received. + * This condition satifies the PRL_Tx_Match_MessageID state + * requirement. + */ + + if (get_time().val > prl_tx[port].tcpc_tx_timeout || + prl_tx[port].xmit_status == TCPC_TX_COMPLETE_FAILED || + prl_tx[port].xmit_status == TCPC_TX_COMPLETE_DISCARDED) { + + /* NOTE: PRL_Tx_Check_RetryCounter State embedded here. */ + + /* Increment check RetryCounter */ + prl_tx[port].retry_counter++; + + /* + * (RetryCounter > nRetryCount) | Large Extended Message + */ + if (prl_tx[port].retry_counter > N_RETRY_COUNT || + (pdmsg[port].ext && + PD_EXT_HEADER_DATA_SIZE(GET_EXT_HEADER( + pdmsg[port].chk_buf[0]) > 26))) { + + /* + * NOTE: PRL_Tx_Transmission_Error State embedded + * here. + */ + + /* + * State tch_wait_for_transmission_complete will + * inform policy engine of error + */ + pdmsg[port].status_flags |= PRL_FLAGS_TX_ERROR; + + /* Increment message id counter */ + increment_msgid_counter(port); + set_state(port, PRL_TX_OBJ(port), + prl_tx_wait_for_message_request); + return 0; + } + + /* Try to resend the message. */ + /* NOTE: PRL_TX_Construct_Message State embedded here. */ + prl_tx_construct_message(port); + return 0; + } + + if (prl_tx[port].xmit_status == TCPC_TX_COMPLETE_SUCCESS) { + + /* NOTE: PRL_TX_Message_Sent State embedded here. */ + + /* Increment messageId counter */ + increment_msgid_counter(port); + /* Inform Policy Engine Message was sent */ + pdmsg[port].status_flags |= PRL_FLAGS_TX_COMPLETE; + set_state(port, PRL_TX_OBJ(port), + prl_tx_wait_for_message_request); + return 0; + } + + return RUN_SUPER; +} + +static unsigned int prl_tx_wait_for_phy_response_exit(int port) +{ + prl_tx[port].xmit_status = TCPC_TX_UNSET; + return 0; +} + +/* Source Protocol Layer Message Transmission */ +/* + * PrlTxSrcPending + */ +static unsigned int prl_tx_src_pending(int port, enum signal sig) +{ + int ret; + + ret = (*prl_tx_src_pending_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_tx_src_pending_entry(int port) +{ + prl_tx[port].state_id = PRL_TX_SRC_PENDING; + + /* Start SinkTxTimer */ + prl_tx[port].sink_tx_timer = get_time().val + PD_T_SINK_TX; + + return 0; +} + +static unsigned int prl_tx_src_pending_run(int port) +{ + + if (get_time().val > prl_tx[port].sink_tx_timer) { + /* + * Soft Reset Message pending & + * SinkTxTimer timeout + */ + if ((emsg[port].len == 0) && + (pdmsg[port].msg_type == PD_CTRL_SOFT_RESET)) { + set_state(port, PRL_TX_OBJ(port), + prl_tx_layer_reset_for_transmit); + } + /* Message pending (except Soft Reset) & + * SinkTxTimer timeout + */ + else { + prl_tx_construct_message(port); + set_state(port, PRL_TX_OBJ(port), + prl_tx_wait_for_phy_response); + } + + return 0; + } + + return RUN_SUPER; +} + +/* + * PrlTxSnkPending + */ +static unsigned int prl_tx_snk_pending(int port, enum signal sig) +{ + int ret; + + ret = (*prl_tx_snk_pending_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_tx_snk_pending_entry(int port) +{ + prl_tx[port].state_id = PRL_TX_SNK_PENDING; + return 0; +} + +static unsigned int prl_tx_snk_pending_run(int port) +{ + int cc1; + int cc2; + + tcpm_get_cc(port, &cc1, &cc2); + if (cc1 == TYPEC_CC_VOLT_RP_3_0 || cc2 == TYPEC_CC_VOLT_RP_3_0) { + /* + * Soft Reset Message Message pending & + * Rp = SinkTxOk + */ + if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && + (emsg[port].len == 0)) { + set_state(port, PRL_TX_OBJ(port), + prl_tx_layer_reset_for_transmit); + } + /* + * Message pending (except Soft Reset) & + * Rp = SinkTxOk + */ + else { + prl_tx_construct_message(port); + set_state(port, PRL_TX_OBJ(port), + prl_tx_wait_for_phy_response); + } + return 0; + } + + return RUN_SUPER; +} + +/* Hard Reset Operation */ + +static unsigned int prl_hr_wait_for_request(int port, enum signal sig) +{ + int ret; + + ret = (*prl_hr_wait_for_request_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_hr_wait_for_request_entry(int port) +{ + prl_hr[port].state_id = PRL_HR_WAIT_FOR_REQUEST; + + prl_hr[port].flags = 0; + return 0; +} + +static unsigned int prl_hr_wait_for_request_run(int port) +{ + if (prl_hr[port].flags & PRL_FLAGS_PE_HARD_RESET || + prl_hr[port].flags & PRL_FLAGS_PORT_PARTNER_HARD_RESET) { + set_state(port, PRL_HR_OBJ(port), prl_hr_reset_layer); + } + + return 0; +} + +/* + * PrlHrResetLayer + */ +static unsigned int prl_hr_reset_layer(int port, enum signal sig) +{ + int ret; + + ret = (*prl_hr_reset_layer_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_hr_reset_layer_entry(int port) +{ + int i; + + prl_hr[port].state_id = PRL_HR_RESET_LAYER; + + /* reset messageIDCounters */ + for (i = 0; i < NUM_XMIT_TYPES; i++) + prl_tx[port].msg_id_counter[i] = 0; + /* + * Protocol Layer message transmission transitions to + * PRL_Tx_Wait_For_Message_Request state. + */ + set_state(port, PRL_TX_OBJ(port), + prl_tx_wait_for_message_request); + + return 0; +} + +static unsigned int prl_hr_reset_layer_run(int port) +{ + /* + * Protocol Layer reset Complete & + * Hard Reset was initiated by Policy Engine + */ + if (prl_hr[port].flags & PRL_FLAGS_PE_HARD_RESET) { + /* Request PHY to perform a Hard Reset */ + prl_send_ctrl_msg(port, TCPC_TX_HARD_RESET, 0); + set_state(port, PRL_HR_OBJ(port), + prl_hr_wait_for_phy_hard_reset_complete); + } + /* + * Protocol Layer reset complete & + * Hard Reset was initiated by Port Partner + */ + else { + /* Inform Policy Engine of the Hard Reset */ + pe_got_hard_reset(port); + set_state(port, PRL_HR_OBJ(port), + prl_hr_wait_for_pe_hard_reset_complete); + } + + return 0; +} + +/* + * PrlHrWaitForPhyHardResetComplete + */ +static unsigned int + prl_hr_wait_for_phy_hard_reset_complete(int port, enum signal sig) +{ + int ret; + + ret = (*prl_hr_wait_for_phy_hard_reset_complete_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_hr_wait_for_phy_hard_reset_complete_entry(int port) +{ + prl_hr[port].state_id = PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE; + + /* Start HardResetCompleteTimer */ + prl_hr[port].hard_reset_complete_timer = + get_time().val + PD_T_PS_HARD_RESET; + + return 0; +} + +static unsigned int prl_hr_wait_for_phy_hard_reset_complete_run(int port) +{ + /* + * Wait for hard reset from PHY + * or timeout + */ + if ((pdmsg[port].status_flags & PRL_FLAGS_TX_COMPLETE) || + (get_time().val > prl_hr[port].hard_reset_complete_timer)) { + /* PRL_HR_PHY_Hard_Reset_Requested */ + + /* Inform Policy Engine Hard Reset was sent */ + pe_hard_reset_sent(port); + set_state(port, PRL_HR_OBJ(port), + prl_hr_wait_for_pe_hard_reset_complete); + + return 0; + } + + return RUN_SUPER; +} + +/* + * PrlHrWaitForPeHardResetComplete + */ +static unsigned int + prl_hr_wait_for_pe_hard_reset_complete(int port, enum signal sig) +{ + int ret; + + ret = (*prl_hr_wait_for_pe_hard_reset_complete_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int prl_hr_wait_for_pe_hard_reset_complete_entry(int port) +{ + prl_hr[port].state_id = PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE; + return 0; +} + +static unsigned int prl_hr_wait_for_pe_hard_reset_complete_run(int port) +{ + /* + * Wait for Hard Reset complete indication from Policy Engine + */ + if (prl_hr[port].flags & PRL_FLAGS_HARD_RESET_COMPLETE) + set_state(port, PRL_HR_OBJ(port), prl_hr_wait_for_request); + + return RUN_SUPER; +} + +static unsigned int prl_hr_wait_for_pe_hard_reset_complete_exit(int port) +{ + /* Exit from Hard Reset */ + + set_state(port, PRL_TX_OBJ(port), prl_tx_phy_layer_reset); + set_state(port, RCH_OBJ(port), + rch_wait_for_message_from_protocol_layer); + set_state(port, TCH_OBJ(port), tch_wait_for_message_request_from_pe); + + return 0; +} + +static void copy_chunk_to_ext(int port) +{ + /* Calculate number of bytes */ + pdmsg[port].num_bytes_received = (PD_HEADER_CNT(emsg[port].header) * 4); + + /* Copy chunk into extended message */ + memcpy((uint8_t *)emsg[port].buf, (uint8_t *)pdmsg[port].chk_buf, + pdmsg[port].num_bytes_received); + + /* Set extended message length */ + emsg[port].len = pdmsg[port].num_bytes_received; +} + +/* + * Chunked Rx State Machine + */ +static unsigned int + rch_wait_for_message_from_protocol_layer(int port, enum signal sig) +{ + int ret; + + ret = (*rch_wait_for_message_from_protocol_layer_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static inline void rch_clear_abort_set_chunking(int port) +{ + /* Clear Abort flag */ + pdmsg[port].status_flags &= ~PRL_FLAGS_ABORT; + + /* All Messages are chunked */ + rch[port].flags = PRL_FLAGS_CHUNKING; +} + +static unsigned int rch_wait_for_message_from_protocol_layer_entry(int port) +{ + rch[port].state_id = RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER; + rch_clear_abort_set_chunking(port); + return 0; +} + +static unsigned int rch_wait_for_message_from_protocol_layer_run(int port) +{ + if (rch[port].flags & PRL_FLAGS_MSG_RECEIVED) { + rch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED; + /* + * Are we communicating with a PD3.0 device and is + * this an extended message? + */ + if (pdmsg[port].rev == PD_REV30 && + PD_HEADER_EXT(emsg[port].header)) { + uint16_t exhdr = GET_EXT_HEADER(*pdmsg[port].chk_buf); + uint8_t chunked = PD_EXT_HEADER_CHUNKED(exhdr); + + /* + * Received Extended Message & + * (Chunking = 1 & Chunked = 1) + */ + if ((rch[port].flags & PRL_FLAGS_CHUNKING) && + chunked) { + set_state(port, RCH_OBJ(port), + rch_processing_extended_message); + return 0; + } + /* + * (Received Extended Message & + * (Chunking = 0 & Chunked = 0)) + */ + else if (!(rch[port].flags & + PRL_FLAGS_CHUNKING) && !chunked) { + /* Copy chunk to extended buffer */ + copy_chunk_to_ext(port); + /* Pass Message to Policy Engine */ + pe_pass_up_message(port); + /* Clear Abort flag and set Chunking */ + rch_clear_abort_set_chunking(port); + } + /* + * Chunked != Chunking + */ + else { + set_state(port, RCH_OBJ(port), + rch_report_error); + return 0; + } + } + /* + * Received Non-Extended Message + */ + else if (!PD_HEADER_EXT(emsg[port].header)) { + /* Copy chunk to extended buffer */ + copy_chunk_to_ext(port); + /* Pass Message to Policy Engine */ + pe_pass_up_message(port); + /* Clear Abort flag and set Chunking */ + rch_clear_abort_set_chunking(port); + } + /* + * Received an Extended Message while communicating at a + * revision lower than PD3.0 + */ + else { + set_state(port, RCH_OBJ(port), + rch_report_error); + return 0; + } + } + + return RUN_SUPER; +} + +/* + * RchProcessingExtendedMessage + */ +static unsigned int rch_processing_extended_message(int port, enum signal sig) +{ + int ret; + + ret = (*rch_processing_extended_message_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int rch_processing_extended_message_entry(int port) +{ + uint32_t header = emsg[port].header; + uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]); + uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr); + + rch[port].state_id = RCH_PROCESSING_EXTENDED_MESSAGE; + + /* + * If first chunk: + * Set Chunk_number_expected = 0 and + * Num_Bytes_Received = 0 + */ + if (chunk_num == 0) { + pdmsg[port].chunk_number_expected = 0; + pdmsg[port].num_bytes_received = 0; + pdmsg[port].msg_type = PD_HEADER_TYPE(header); + } + + return 0; +} + +static unsigned int rch_processing_extended_message_run(int port) +{ + uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]); + uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr); + uint32_t data_size = PD_EXT_HEADER_DATA_SIZE(exhdr); + uint32_t byte_num; + + /* + * Abort Flag Set + */ + if (pdmsg[port].status_flags & PRL_FLAGS_ABORT) { + set_state(port, RCH_OBJ(port), + rch_wait_for_message_from_protocol_layer); + } + /* + * If expected Chunk Number: + * Append data to Extended_Message_Buffer + * Increment Chunk_number_Expected + * Adjust Num Bytes Received + */ + else if (chunk_num == pdmsg[port].chunk_number_expected) { + byte_num = data_size - pdmsg[port].num_bytes_received; + + if (byte_num > 25) + byte_num = 26; + + /* Make sure extended message buffer does not overflow */ + if (pdmsg[port].num_bytes_received + + byte_num > EXTENDED_BUFFER_SIZE) { + set_state(port, RCH_OBJ(port), rch_report_error); + return 0; + } + + /* Append data */ + /* Add 2 to chk_buf to skip over extended message header */ + memcpy(((uint8_t *)emsg[port].buf + + pdmsg[port].num_bytes_received), + (uint8_t *)pdmsg[port].chk_buf + 2, byte_num); + /* increment chunk number expected */ + pdmsg[port].chunk_number_expected++; + /* adjust num bytes received */ + pdmsg[port].num_bytes_received += byte_num; + + /* Was that the last chunk? */ + if (pdmsg[port].num_bytes_received >= data_size) { + emsg[port].len = pdmsg[port].num_bytes_received; + /* Pass Message to Policy Engine */ + pe_pass_up_message(port); + set_state(port, RCH_OBJ(port), + rch_wait_for_message_from_protocol_layer); + } + /* + * Message not Complete + */ + else + set_state(port, RCH_OBJ(port), rch_requesting_chunk); + } + /* + * Unexpected Chunk Number + */ + else + set_state(port, RCH_OBJ(port), rch_report_error); + + return 0; +} + +/* + * RchRequestingChunk + */ +static unsigned int rch_requesting_chunk(int port, enum signal sig) +{ + int ret; + + ret = (*rch_requesting_chunk_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int rch_requesting_chunk_entry(int port) +{ + rch[port].state_id = RCH_REQUESTING_CHUNK; + + /* + * Send Chunk Request to Protocol Layer + * with chunk number = Chunk_Number_Expected + */ + pdmsg[port].chk_buf[0] = PD_EXT_HEADER( + pdmsg[port].chunk_number_expected, + 1, /* Request Chunk */ + 0 /* Data Size */ + ); + + pdmsg[port].data_objs = 1; + pdmsg[port].ext = 1; + prl_tx[port].flags |= PRL_FLAGS_MSG_XMIT; + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX, 0); + + return 0; +} + +static unsigned int rch_requesting_chunk_run(int port) +{ + /* + * Transmission Error from Protocol Layer or + * Message Received From Protocol Layer + */ + if (rch[port].flags & PRL_FLAGS_MSG_RECEIVED || + pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) { + /* + * Leave PRL_FLAGS_MSG_RECEIVED flag set. It'll be + * cleared in rch_report_error state + */ + set_state(port, RCH_OBJ(port), rch_report_error); + } + /* + * Message Transmitted received from Protocol Layer + */ + else if (pdmsg[port].status_flags & PRL_FLAGS_TX_COMPLETE) { + pdmsg[port].status_flags &= ~PRL_FLAGS_TX_COMPLETE; + set_state(port, RCH_OBJ(port), rch_waiting_chunk); + } else + return RUN_SUPER; + + return 0; +} + +/* + * RchWaitingChunk + */ +static unsigned int rch_waiting_chunk(int port, enum signal sig) +{ + int ret; + + ret = (*rch_waiting_chunk_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int rch_waiting_chunk_entry(int port) +{ + rch[port].state_id = RCH_WAITING_CHUNK; + + /* + * Start ChunkSenderResponseTimer + */ + rch[port].chunk_sender_response_timer = + get_time().val + PD_T_CHUNK_SENDER_RESPONSE; + + return 0; +} + +static unsigned int rch_waiting_chunk_run(int port) +{ + if ((rch[port].flags & PRL_FLAGS_MSG_RECEIVED)) { + /* + * Leave PRL_FLAGS_MSG_RECEIVED flag set just in case an error + * is detected. If an error is detected, PRL_FLAGS_MSG_RECEIVED + * will be cleared in rch_report_error state. + */ + + if (PD_HEADER_EXT(emsg[port].header)) { + uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]); + /* + * Other Message Received from Protocol Layer + */ + if (PD_EXT_HEADER_REQ_CHUNK(exhdr) || + !PD_EXT_HEADER_CHUNKED(exhdr)) { + set_state(port, RCH_OBJ(port), + rch_report_error); + } + /* + * Chunk response Received from Protocol Layer + */ + else { + /* + * No error wad detected, so clear + * PRL_FLAGS_MSG_RECEIVED flag. + */ + rch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED; + set_state(port, RCH_OBJ(port), + rch_processing_extended_message); + } + + return 0; + } + } + /* + * ChunkSenderResponseTimer Timeout + */ + else if (get_time().val > rch[port].chunk_sender_response_timer) { + set_state(port, RCH_OBJ(port), rch_report_error); + return 0; + } + + return RUN_SUPER; +} + +/* + * RchReportError + */ +static unsigned int rch_report_error(int port, enum signal sig) +{ + int ret; + + ret = (*rch_report_error_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int rch_report_error_entry(int port) +{ + rch[port].state_id = RCH_REPORT_ERROR; + + /* + * If the state was entered because a message was received, + * this message is passed to the Policy Engine. + */ + if (rch[port].flags & PRL_FLAGS_MSG_RECEIVED) { + rch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED; + + /* Copy chunk to extended buffer */ + copy_chunk_to_ext(port); + /* Pass Message to Policy Engine */ + pe_pass_up_message(port); + /* Report error */ + pe_report_error(port, ERR_RCH_MSG_REC); + } else { + /* Report error */ + pe_report_error(port, ERR_RCH_CHUNKED); + } + + return 0; +} + +static unsigned int rch_report_error_run(int port) +{ + set_state(port, RCH_OBJ(port), + rch_wait_for_message_from_protocol_layer); + + return 0; +} + +/* + * Chunked Tx State Machine + */ +static unsigned int + tch_wait_for_message_request_from_pe(int port, enum signal sig) +{ + int ret; + + ret = (*tch_wait_for_message_request_from_pe_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static inline void tch_clear_abort_set_chunking(int port) +{ + /* Clear Abort flag */ + pdmsg[port].status_flags &= ~PRL_FLAGS_ABORT; + + /* All Messages are chunked */ + tch[port].flags = PRL_FLAGS_CHUNKING; +} + +static unsigned int tch_wait_for_message_request_from_pe_entry(int port) +{ + tch[port].state_id = TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE; + tch_clear_abort_set_chunking(port); + return 0; +} + +static unsigned int tch_wait_for_message_request_from_pe_run(int port) +{ + /* + * Any message received and not in state TCH_Wait_Chunk_Request + */ + if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) { + tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED; + set_state(port, TCH_OBJ(port), tch_message_received); + return 0; + } else if (tch[port].flags & PRL_FLAGS_MSG_XMIT) { + tch[port].flags &= ~PRL_FLAGS_MSG_XMIT; + /* + * Rx Chunking State != RCH_Wait_For_Message_From_Protocol_Layer + * & Abort Supported + * + * Discard the Message + */ + if (rch[port].state_id != + RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER) { + /* Report Error To Policy Engine */ + pe_report_error(port, ERR_TCH_XMIT); + tch_clear_abort_set_chunking(port); + } else { + /* + * Extended Message Request & Chunking + */ + if ((pdmsg[port].rev == PD_REV30) && pdmsg[port].ext && + (tch[port].flags & PRL_FLAGS_CHUNKING)) { + pdmsg[port].send_offset = 0; + pdmsg[port].chunk_number_to_send = 0; + set_state(port, TCH_OBJ(port), + tch_construct_chunked_message); + } else + /* + * Non-Extended Message Request + */ + { + /* Make sure buffer doesn't overflow */ + if (emsg[port].len > BUFFER_SIZE) { + /* Report Error To Policy Engine */ + pe_report_error(port, ERR_TCH_XMIT); + tch_clear_abort_set_chunking(port); + return 0; + } + + /* Copy message to chunked buffer */ + memset((uint8_t *)pdmsg[port].chk_buf, + 0, BUFFER_SIZE); + memcpy((uint8_t *)pdmsg[port].chk_buf, + (uint8_t *)emsg[port].buf, + emsg[port].len); + /* + * Pad length to 4-byte boundery and + * convert to number of 32-bit objects. + * Since the value is shifted right by 2, + * no need to explicitly clear the lower + * 2-bits. + */ + pdmsg[port].data_objs = + (emsg[port].len + 3) >> 2; + /* Pass Message to Protocol Layer */ + prl_tx[port].flags |= PRL_FLAGS_MSG_XMIT; + set_state(port, TCH_OBJ(port), + tch_wait_for_transmission_complete); + } + + return 0; + } + } + + return RUN_SUPER; +} + +/* + * TchWaitForTransmissionComplete + */ +static unsigned int + tch_wait_for_transmission_complete(int port, enum signal sig) +{ + int ret; + + ret = (*tch_wait_for_transmission_complete_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int tch_wait_for_transmission_complete_entry(int port) +{ + tch[port].state_id = TCH_WAIT_FOR_TRANSMISSION_COMPLETE; + return 0; +} + +static unsigned int tch_wait_for_transmission_complete_run(int port) +{ + /* + * Any message received and not in state TCH_Wait_Chunk_Request + */ + if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) { + tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED; + set_state(port, TCH_OBJ(port), tch_message_received); + return 0; + } + + /* + * Inform Policy Engine that Message was sent. + */ + if (pdmsg[port].status_flags & PRL_FLAGS_TX_COMPLETE) { + pdmsg[port].status_flags &= ~PRL_FLAGS_TX_COMPLETE; + set_state(port, TCH_OBJ(port), + tch_wait_for_message_request_from_pe); + + /* Tell PE message was sent */ + pe_message_sent(port); + } + /* + * Inform Policy Engine of Tx Error + */ + else if (pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) { + pdmsg[port].status_flags &= ~PRL_FLAGS_TX_ERROR; + /* Tell PE an error occurred */ + pe_report_error(port, ERR_TCH_XMIT); + set_state(port, TCH_OBJ(port), + tch_wait_for_message_request_from_pe); + } + + return 0; +} + +/* + * TchConstructChunkedMessage + */ +static unsigned int tch_construct_chunked_message(int port, enum signal sig) +{ + int ret; + + ret = (*tch_construct_chunked_message_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int tch_construct_chunked_message_entry(int port) +{ + uint16_t *ext_hdr; + uint8_t *data; + uint16_t num; + + tch[port].state_id = TCH_CONSTRUCT_CHUNKED_MESSAGE; + + /* + * Any message received and not in state TCH_Wait_Chunk_Request + */ + if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) { + tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED; + set_state(port, TCH_OBJ(port), tch_message_received); + return 0; + } + /* Prepare to copy chunk into chk_buf */ + + ext_hdr = (uint16_t *)pdmsg[port].chk_buf; + data = ((uint8_t *)pdmsg[port].chk_buf + 2); + num = emsg[port].len - pdmsg[port].send_offset; + + if (num > 26) + num = 26; + + /* Set the chunks extended header */ + *ext_hdr = PD_EXT_HEADER(pdmsg[port].chunk_number_to_send, + 0, /* Chunk Request */ + emsg[port].len); + + /* Copy the message chunk into chk_buf */ + memset(data, 0, 28); + memcpy(data, emsg[port].buf + pdmsg[port].send_offset, num); + pdmsg[port].send_offset += num; + + /* + * Add in 2 bytes for extended header + * pad out to 4-byte boundary + * convert to number of 4-byte words + * Since the value is shifted right by 2, + * no need to explicitly clear the lower + * 2-bits. + */ + pdmsg[port].data_objs = (num + 2 + 3) >> 2; + + /* Pass message chunk to Protocol Layer */ + prl_tx[port].flags |= PRL_FLAGS_MSG_XMIT; + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); + + return 0; +} + +static unsigned int tch_construct_chunked_message_run(int port) +{ + if (pdmsg[port].status_flags & PRL_FLAGS_ABORT) + set_state(port, TCH_OBJ(port), + tch_wait_for_message_request_from_pe); + else + set_state(port, TCH_OBJ(port), + tch_sending_chunked_message); + return 0; +} + +/* + * TchSendingChunkedMessage + */ +static unsigned int tch_sending_chunked_message(int port, enum signal sig) +{ + int ret; + + ret = (*tch_sending_chunked_message_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int tch_sending_chunked_message_entry(int port) +{ + tch[port].state_id = TCH_SENDING_CHUNKED_MESSAGE; + return 0; +} + +static unsigned int tch_sending_chunked_message_run(int port) +{ + /* + * Any message received and not in state TCH_Wait_Chunk_Request + */ + if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) { + tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED; + set_state(port, TCH_OBJ(port), tch_message_received); + return 0; + } + + /* + * Transmission Error + */ + if (pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) { + pe_report_error(port, ERR_TCH_XMIT); + set_state(port, TCH_OBJ(port), + tch_wait_for_message_request_from_pe); + } + /* + * Message Transmitted from Protocol Layer & + * Last Chunk + */ + else if (emsg[port].len == pdmsg[port].send_offset) { + set_state(port, TCH_OBJ(port), + tch_wait_for_message_request_from_pe); + + /* Tell PE message was sent */ + pe_message_sent(port); + } + /* + * Message Transmitted from Protocol Layer & + * Not Last Chunk + */ + else + set_state(port, TCH_OBJ(port), tch_wait_chunk_request); + + return 0; +} + +/* + * TchWaitChunkRequest + */ +static unsigned int tch_wait_chunk_request(int port, enum signal sig) +{ + int ret; + + ret = (*tch_wait_chunk_request_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int tch_wait_chunk_request_entry(int port) +{ + tch[port].state_id = TCH_WAIT_CHUNK_REQUEST; + + /* Increment Chunk Number to Send */ + pdmsg[port].chunk_number_to_send++; + /* Start Chunk Sender Request Timer */ + tch[port].chunk_sender_request_timer = + get_time().val + PD_T_CHUNK_SENDER_REQUEST; + return 0; +} + +static unsigned int tch_wait_chunk_request_run(int port) +{ + if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) { + tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED; + + if (PD_HEADER_EXT(emsg[port].header)) { + uint16_t exthdr; + + exthdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]); + if (PD_EXT_HEADER_REQ_CHUNK(exthdr)) { + /* + * Chunk Request Received & + * Chunk Number = Chunk Number to Send + */ + if (PD_EXT_HEADER_CHUNK_NUM(exthdr) == + pdmsg[port].chunk_number_to_send) { + set_state(port, TCH_OBJ(port), + tch_construct_chunked_message); + } + /* + * Chunk Request Received & + * Chunk Number != Chunk Number to Send + */ + else { + pe_report_error(port, ERR_TCH_CHUNKED); + set_state(port, TCH_OBJ(port), + tch_wait_for_message_request_from_pe); + } + return 0; + } + } + + /* + * Other message received + */ + set_state(port, TCH_OBJ(port), tch_message_received); + } + /* + * ChunkSenderRequestTimer timeout + */ + else if (get_time().val >= + tch[port].chunk_sender_request_timer) { + set_state(port, TCH_OBJ(port), + tch_wait_for_message_request_from_pe); + + /* Tell PE message was sent */ + pe_message_sent(port); + } + + return 0; +} + +/* + * TchMessageReceived + */ +static unsigned int tch_message_received(int port, enum signal sig) +{ + int ret; + + ret = (*tch_message_received_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int tch_message_received_entry(int port) +{ + tch[port].state_id = TCH_MESSAGE_RECEIVED; + + /* Pass message to chunked Rx */ + rch[port].flags |= PRL_FLAGS_MSG_RECEIVED; + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); + + return 0; +} + +static unsigned int tch_message_received_run(int port) +{ + set_state(port, TCH_OBJ(port), + tch_wait_for_message_request_from_pe); + + return 0; +} + +/* + * Protocol Layer Message Reception State Machine + */ +static unsigned int prl_rx_wait_for_phy_message(int port, int evt) +{ + uint32_t header; + uint8_t type; + uint8_t cnt; + uint8_t sop; + int8_t msid; + int ret; + + /* process any potential incoming message */ + if (tcpm_has_pending_message(port)) { + ret = tcpm_dequeue_message(port, pdmsg[port].chk_buf, &header); + if (ret == 0) { + emsg[port].header = header; + type = PD_HEADER_TYPE(header); + cnt = PD_HEADER_CNT(header); + msid = PD_HEADER_ID(header); + sop = PD_HEADER_GET_SOP(header); + + if (cnt == 0 && type == PD_CTRL_SOFT_RESET) { + int i; + + for (i = 0; i < NUM_XMIT_TYPES; i++) { + /* Clear MessageIdCounter */ + prl_tx[port].msg_id_counter[i] = 0; + /* Clear stored MessageID value */ + prl_rx[port].msg_id[i] = -1; + } + + /* Inform Policy Engine of Soft Reset */ + pe_got_soft_reset(port); + + /* Soft Reset occurred */ + set_state(port, PRL_TX_OBJ(port), + prl_tx_phy_layer_reset); + set_state(port, RCH_OBJ(port), + rch_wait_for_message_from_protocol_layer); + set_state(port, TCH_OBJ(port), + tch_wait_for_message_request_from_pe); + } + + /* + * Ignore if this is a duplicate message. + */ + if (prl_rx[port].msg_id[sop] != msid) { + /* + * Discard any pending tx message if this is + * not a ping message + */ + if ((pdmsg[port].rev == PD_REV30) && + (cnt == 0) && type != PD_CTRL_PING) { + if (prl_tx[port].state_id == + PRL_TX_SRC_PENDING || + prl_tx[port].state_id == + PRL_TX_SNK_PENDING) { + set_state(port, + PRL_TX_OBJ(port), + prl_tx_discard_message); + } + } + + /* Store Message Id */ + prl_rx[port].msg_id[sop] = msid; + + /* RTR Chunked Message Router States. */ + /* + * Received Ping from Protocol Layer + */ + if (cnt == 0 && type == PD_CTRL_PING) { + /* NOTE: RTR_PING State embedded + * here. + */ + emsg[port].len = 0; + pe_pass_up_message(port); + return 0; + } + /* + * Message (not Ping) Received from + * Protocol Layer & Doing Tx Chunks + */ + else if (tch[port].state_id != + TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE) { + /* NOTE: RTR_TX_CHUNKS State embedded + * here. + */ + /* + * Send Message to Tx Chunk + * Chunk State Machine + */ + tch[port].flags |= + PRL_FLAGS_MSG_RECEIVED; + } + /* + * Message (not Ping) Received from + * Protocol Layer & Not Doing Tx Chunks + */ + else { + /* + * NOTE: RTR_RX_CHUNKS State embedded + * here. + */ + /* + * Send Message to Rx + * Chunk State Machine + */ + rch[port].flags |= + PRL_FLAGS_MSG_RECEIVED; + } + + task_set_event(PD_PORT_TO_TASK_ID(port), + PD_EVENT_SM, 0); + } + } + } + + return 0; +} + +static unsigned int do_nothing_exit(int port) +{ + return 0; +} + +static unsigned int get_super_state(int port) +{ + return RUN_SUPER; +} |