/* 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); DECLARE_STATE(prl, tx_phy_layer_reset, NOOP_EXIT); DECLARE_STATE(prl, tx_wait_for_message_request, NOOP_EXIT); DECLARE_STATE(prl, tx_layer_reset_for_transmit, NOOP_EXIT); DECLARE_STATE(prl, tx_wait_for_phy_response, WITH_EXIT); DECLARE_STATE(prl, tx_src_source_tx, NOOP_EXIT); DECLARE_STATE(prl, tx_snk_start_ams, NOOP_EXIT); /* Source Protocol Layser Message Transmission */ DECLARE_STATE(prl, tx_src_pending, NOOP_EXIT); /* Sink Protocol Layer Message Transmission */ DECLARE_STATE(prl, tx_snk_pending, NOOP_EXIT); DECLARE_STATE(prl, tx_discard_message, NOOP_EXIT); /* Protocol Layer Message Reception */ static unsigned int prl_rx_wait_for_phy_message(int port, int evt); /* Hard Reset Operation */ DECLARE_STATE(prl, hr_wait_for_request, NOOP_EXIT); DECLARE_STATE(prl, hr_reset_layer, NOOP_EXIT); DECLARE_STATE(prl, hr_wait_for_phy_hard_reset_complete, NOOP_EXIT); DECLARE_STATE(prl, hr_wait_for_pe_hard_reset_complete, WITH_EXIT); /* Chunked Rx */ DECLARE_STATE(rch, wait_for_message_from_protocol_layer, NOOP_EXIT); DECLARE_STATE(rch, processing_extended_message, NOOP_EXIT); DECLARE_STATE(rch, requesting_chunk, NOOP_EXIT); DECLARE_STATE(rch, waiting_chunk, NOOP_EXIT); DECLARE_STATE(rch, report_error, NOOP_EXIT); /* Chunked Tx */ DECLARE_STATE(tch, wait_for_message_request_from_pe, NOOP_EXIT); DECLARE_STATE(tch, wait_for_transmission_complete, NOOP_EXIT); DECLARE_STATE(tch, construct_chunked_message, NOOP_EXIT); DECLARE_STATE(tch, sending_chunked_message, NOOP_EXIT); DECLARE_STATE(tch, wait_chunk_request, NOOP_EXIT); DECLARE_STATE(tch, message_received, NOOP_EXIT); 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; }