/* 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 static enum sm_local_state local_state[CONFIG_USB_PD_PORT_COUNT]; /* Protocol Transmit States (Section 6.11.2.2) */ enum usb_prl_tx_state { PRL_TX_PHY_LAYER_RESET, PRL_TX_WAIT_FOR_MESSAGE_REQUEST, PRL_TX_LAYER_RESET_FOR_TRANSMIT, PRL_TX_WAIT_FOR_PHY_RESPONSE, PRL_TX_SRC_SOURCE_TX, PRL_TX_SNK_START_AMS, PRL_TX_SRC_PENDING, PRL_TX_SNK_PENDING, PRL_TX_DISCARD_MESSAGE, }; /* Protocol Hard Reset States (Section 6.11.2.4) */ enum usb_prl_hr_state { PRL_HR_WAIT_FOR_REQUEST, PRL_HR_RESET_LAYER, PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE, }; /* Chunked Rx states (Section 6.11.2.1.2) */ enum usb_rch_state { RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER, RCH_PASS_UP_MESSAGE, RCH_PROCESSING_EXTENDED_MESSAGE, RCH_REQUESTING_CHUNK, RCH_WAITING_CHUNK, RCH_REPORT_ERROR, }; /* Chunked Tx states (Section 6.11.2.1.3) */ enum usb_tch_state { TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE, TCH_WAIT_FOR_TRANSMISSION_COMPLETE, TCH_CONSTRUCT_CHUNKED_MESSAGE, TCH_SENDING_CHUNKED_MESSAGE, TCH_WAIT_CHUNK_REQUEST, TCH_MESSAGE_RECEIVED, }; /* Forward declare full list of states. Index by above enums. */ static const struct usb_state prl_tx_states[]; static const struct usb_state prl_hr_states[]; static const struct usb_state rch_states[]; static const struct usb_state tch_states[]; /* Chunked Rx State Machine Object */ static struct rx_chunked { /* state machine context */ struct sm_ctx ctx; /* 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 { /* state machine context */ struct sm_ctx ctx; /* 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 { /* state machine context */ struct sm_ctx ctx; /* 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 { /* state machine context */ struct sm_ctx ctx; /* 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]; /* Common Protocol Layer Message Transmission */ static void prl_tx_construct_message(int port); static void prl_rx_wait_for_phy_message(const int port, int evt); /* Set the protocol transmit statemachine to a new state. */ static void set_state_prl_tx(const int port, const enum usb_prl_tx_state new_state) { set_state(port, &prl_tx[port].ctx, &prl_tx_states[new_state]); } /* Get the protocol transmit statemachine's current state. */ test_export_static enum usb_prl_tx_state prl_tx_get_state(const int port) { return prl_tx[port].ctx.current - &prl_tx_states[0]; } /* Set the hard reset statemachine to a new state. */ static void set_state_prl_hr(const int port, const enum usb_prl_hr_state new_state) { set_state(port, &prl_hr[port].ctx, &prl_hr_states[new_state]); } #ifdef TEST_BUILD /* Get the hard reset statemachine's current state. */ enum usb_prl_hr_state prl_hr_get_state(const int port) { return prl_hr[port].ctx.current - &prl_hr_states[0]; } #endif /* Set the chunked Rx statemachine to a new state. */ static void set_state_rch(const int port, const enum usb_rch_state new_state) { set_state(port, &rch[port].ctx, &rch_states[new_state]); } /* Get the chunked Rx statemachine's current state. */ test_export_static enum usb_rch_state rch_get_state(const int port) { return rch[port].ctx.current - &rch_states[0]; } /* Set the chunked Tx statemachine to a new state. */ static void set_state_tch(const int port, const enum usb_tch_state new_state) { set_state(port, &tch[port].ctx, &tch_states[new_state]); } /* Get the chunked Tx statemachine's current state. */ test_export_static enum usb_tch_state tch_get_state(const int port) { return tch[port].ctx.current - &tch_states[0]; } 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_prl_hr(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_prl_hr(port, PRL_HR_RESET_LAYER); task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); } static void prl_init(int port) { int i; const struct sm_ctx cleared = {}; 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; } /* Clear state machines and set initial states */ prl_tx[port].ctx = cleared; set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); rch[port].ctx = cleared; set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); tch[port].ctx = cleared; set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); prl_hr[port].ctx = cleared; set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); } 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_run(int port, int evt, int en) { switch (local_state[port]) { case SM_PAUSED: if (!en) break; /* fall through */ 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_get_state(port) == TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE) { /* Disable RX */ if (IS_ENABLED(CONFIG_USB_TYPEC_CTVPD) || IS_ENABLED(CONFIG_USB_TYPEC_VPD)) vpd_rx_enable(0); else tcpm_set_rx_enable(port, 0); 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[port].ctx); /* Run TX Chunked state machine */ exe_state(port, &tch[port].ctx); /* Run Protocol Layer Message Transmission state machine */ exe_state(port, &prl_tx[port].ctx); /* Run Protocol Layer Hard Reset state machine */ exe_state(port, &prl_hr[port].ctx); break; } } 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 void prl_tx_phy_layer_reset_entry(const int port) { if (IS_ENABLED(CONFIG_USB_TYPEC_CTVPD) || IS_ENABLED(CONFIG_USB_TYPEC_VPD)) { vpd_rx_enable(1); } else { tcpm_init(port); tcpm_set_rx_enable(port, 1); } } static void prl_tx_phy_layer_reset_run(const int port) { set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); } static void prl_tx_wait_for_message_request_entry(const int port) { /* Reset RetryCounter */ prl_tx[port].retry_counter = 0; } static void prl_tx_wait_for_message_request_run(const 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_prl_tx(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_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); } return; } 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_prl_tx(port, PRL_TX_SRC_SOURCE_TX); return; } /* * 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_prl_tx(port, PRL_TX_SNK_START_AMS); return; } } } } 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 void prl_tx_discard_message_entry(const int port) { /* Increment msgidCounter */ increment_msgid_counter(port); set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); } /* * PrlTxSrcSourceTx */ static void prl_tx_src_source_tx_entry(const int port) { /* Set Rp = SinkTxNG */ tcpm_select_rp_value(port, SINK_TX_NG); tcpm_set_cc(port, TYPEC_CC_RP); } static void prl_tx_src_source_tx_run(const int port) { if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) { prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT; set_state_prl_tx(port, PRL_TX_SRC_PENDING); } } /* * PrlTxSnkStartAms */ static void prl_tx_snk_start_ams_run(const int port) { if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) { prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT; set_state_prl_tx(port, PRL_TX_SNK_PENDING); } } /* * PrlTxLayerResetForTransmit */ static void prl_tx_layer_reset_for_transmit_entry(const int port) { int i; /* Reset MessageIdCounters */ for (i = 0; i < NUM_XMIT_TYPES; i++) prl_tx[port].msg_id_counter[i] = 0; } static void prl_tx_layer_reset_for_transmit_run(const int port) { /* NOTE: PRL_Tx_Construct_Message State embedded here */ prl_tx_construct_message(port); set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); } 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 void prl_tx_wait_for_phy_response_entry(const int port) { prl_tx[port].tcpc_tx_timeout = get_time().val + PD_T_TCPC_TX_TIMEOUT; } static void prl_tx_wait_for_phy_response_run(const 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_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); return; } /* Try to resend the message. */ /* NOTE: PRL_TX_Construct_Message State embedded here. */ prl_tx_construct_message(port); return; } 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_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); return; } } static void prl_tx_wait_for_phy_response_exit(const int port) { prl_tx[port].xmit_status = TCPC_TX_UNSET; } /* Source Protocol Layer Message Transmission */ /* * PrlTxSrcPending */ static void prl_tx_src_pending_entry(const int port) { /* Start SinkTxTimer */ prl_tx[port].sink_tx_timer = get_time().val + PD_T_SINK_TX; } static void prl_tx_src_pending_run(const 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_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); } /* Message pending (except Soft Reset) & * SinkTxTimer timeout */ else { prl_tx_construct_message(port); set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); } return; } } /* * PrlTxSnkPending */ static void prl_tx_snk_pending_run(const int port) { enum tcpc_cc_voltage_status cc1, 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_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); } /* * Message pending (except Soft Reset) & * Rp = SinkTxOk */ else { prl_tx_construct_message(port); set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); } return; } } /* Hard Reset Operation */ static void prl_hr_wait_for_request_entry(const int port) { prl_hr[port].flags = 0; } static void prl_hr_wait_for_request_run(const int port) { if (prl_hr[port].flags & PRL_FLAGS_PE_HARD_RESET || prl_hr[port].flags & PRL_FLAGS_PORT_PARTNER_HARD_RESET) { set_state_prl_hr(port, PRL_HR_RESET_LAYER); } } /* * PrlHrResetLayer */ static void prl_hr_reset_layer_entry(const int port) { int i; /* 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_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); } static void prl_hr_reset_layer_run(const 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_prl_hr(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_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); } } /* * PrlHrWaitForPhyHardResetComplete */ static void prl_hr_wait_for_phy_hard_reset_complete_entry(const int port) { /* Start HardResetCompleteTimer */ prl_hr[port].hard_reset_complete_timer = get_time().val + PD_T_PS_HARD_RESET; } static void prl_hr_wait_for_phy_hard_reset_complete_run(const 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_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); return; } } /* * PrlHrWaitForPeHardResetComplete */ static void prl_hr_wait_for_pe_hard_reset_complete_run(const int port) { /* * Wait for Hard Reset complete indication from Policy Engine */ if (prl_hr[port].flags & PRL_FLAGS_HARD_RESET_COMPLETE) set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); } static void prl_hr_wait_for_pe_hard_reset_complete_exit(const int port) { /* Exit from Hard Reset */ set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } 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 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 void rch_wait_for_message_from_protocol_layer_entry(const int port) { rch_clear_abort_set_chunking(port); } static void rch_wait_for_message_from_protocol_layer_run(const 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_rch(port, RCH_PROCESSING_EXTENDED_MESSAGE); return; } /* * (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); set_state_rch(port, RCH_PASS_UP_MESSAGE); } /* * Chunked != Chunking */ else { set_state_rch(port, RCH_REPORT_ERROR); return; } } /* * Received Non-Extended Message */ else if (!PD_HEADER_EXT(emsg[port].header)) { /* Copy chunk to extended buffer */ copy_chunk_to_ext(port); set_state_rch(port, RCH_PASS_UP_MESSAGE); } /* * Received an Extended Message while communicating at a * revision lower than PD3.0 */ else { set_state_rch(port, RCH_REPORT_ERROR); return; } } } /* * RchPassUpMessage */ static void rch_pass_up_message_entry(const int port) { /* Pass Message to Policy Engine */ pe_pass_up_message(port); set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); } /* * RchProcessingExtendedMessage */ static void rch_processing_extended_message_entry(const 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); /* * 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); } } static void rch_processing_extended_message_run(const 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_rch(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_rch(port, RCH_REPORT_ERROR); return; } /* 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 */ set_state_rch(port, RCH_PASS_UP_MESSAGE); } /* * Message not Complete */ else set_state_rch(port, RCH_REQUESTING_CHUNK); } /* * Unexpected Chunk Number */ else set_state_rch(port, RCH_REPORT_ERROR); } /* * RchRequestingChunk */ static void rch_requesting_chunk_entry(const int port) { /* * 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); } static void rch_requesting_chunk_run(const 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_rch(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_rch(port, RCH_WAITING_CHUNK); } } /* * RchWaitingChunk */ static void rch_waiting_chunk_entry(const int port) { /* * Start ChunkSenderResponseTimer */ rch[port].chunk_sender_response_timer = get_time().val + PD_T_CHUNK_SENDER_RESPONSE; } static void rch_waiting_chunk_run(const 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_rch(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_rch(port, RCH_PROCESSING_EXTENDED_MESSAGE); } } } /* * ChunkSenderResponseTimer Timeout */ else if (get_time().val > rch[port].chunk_sender_response_timer) { set_state_rch(port, RCH_REPORT_ERROR); } } /* * RchReportError */ static void rch_report_error_entry(const int port) { /* * 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); } } static void rch_report_error_run(const int port) { set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); } /* * Chunked Tx State Machine */ 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 void tch_wait_for_message_request_from_pe_entry(const int port) { tch_clear_abort_set_chunking(port); } static void tch_wait_for_message_request_from_pe_run(const 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_tch(port, TCH_MESSAGE_RECEIVED); return; } 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_get_state(port) != 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_tch(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; } /* 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_tch(port, TCH_WAIT_FOR_TRANSMISSION_COMPLETE); } return; } } } /* * TchWaitForTransmissionComplete */ static void tch_wait_for_transmission_complete_run(const 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_tch(port, TCH_MESSAGE_RECEIVED); return; } /* * 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_tch(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_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } } /* * TchConstructChunkedMessage */ static void tch_construct_chunked_message_entry(const int port) { uint16_t *ext_hdr; uint8_t *data; uint16_t num; /* * 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_tch(port, TCH_MESSAGE_RECEIVED); return; } /* 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); } static void tch_construct_chunked_message_run(const int port) { if (pdmsg[port].status_flags & PRL_FLAGS_ABORT) set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); else set_state_tch(port, TCH_SENDING_CHUNKED_MESSAGE); } /* * TchSendingChunkedMessage */ static void tch_sending_chunked_message_run(const 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_tch(port, TCH_MESSAGE_RECEIVED); return; } /* * Transmission Error */ if (pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) { pe_report_error(port, ERR_TCH_XMIT); set_state_tch(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_tch(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_tch(port, TCH_WAIT_CHUNK_REQUEST); } /* * TchWaitChunkRequest */ static void tch_wait_chunk_request_entry(const int port) { /* 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; } static void tch_wait_chunk_request_run(const 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_tch(port, TCH_CONSTRUCT_CHUNKED_MESSAGE); } /* * Chunk Request Received & * Chunk Number != Chunk Number to Send */ else { pe_report_error(port, ERR_TCH_CHUNKED); set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } return; } } /* * Other message received */ set_state_tch(port, TCH_MESSAGE_RECEIVED); } /* * ChunkSenderRequestTimer timeout */ else if (get_time().val >= tch[port].chunk_sender_request_timer) { set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); /* Tell PE message was sent */ pe_message_sent(port); } } /* * TchMessageReceived */ static void tch_message_received_entry(const int port) { /* 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); } static void tch_message_received_run(const int port) { set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } /* * Protocol Layer Message Reception State Machine */ static void prl_rx_wait_for_phy_message(const int port, int evt) { uint32_t header; uint8_t type; uint8_t cnt; uint8_t sop; int8_t msid; /* If we don't have any message, just stop processing now. */ if (!tcpm_has_pending_message(port) || tcpm_dequeue_message(port, pdmsg[port].chk_buf, &header)) return; 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_prl_tx(port, PRL_TX_PHY_LAYER_RESET); set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } /* * Ignore if this is a duplicate message. Stop processing. */ if (prl_rx[port].msg_id[sop] == msid) return; /* * 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_get_state(port) == PRL_TX_SRC_PENDING || prl_tx_get_state(port) == PRL_TX_SNK_PENDING) set_state_prl_tx(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; } /* * Message (not Ping) Received from * Protocol Layer & Doing Tx Chunks */ else if (tch_get_state(port) != 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); } /* All necessary Protocol Transmit States (Section 6.11.2.2) */ static const struct usb_state prl_tx_states[] = { [PRL_TX_PHY_LAYER_RESET] = { .entry = prl_tx_phy_layer_reset_entry, .run = prl_tx_phy_layer_reset_run, }, [PRL_TX_WAIT_FOR_MESSAGE_REQUEST] = { .entry = prl_tx_wait_for_message_request_entry, .run = prl_tx_wait_for_message_request_run, }, [PRL_TX_LAYER_RESET_FOR_TRANSMIT] = { .entry = prl_tx_layer_reset_for_transmit_entry, .run = prl_tx_layer_reset_for_transmit_run, }, [PRL_TX_WAIT_FOR_PHY_RESPONSE] = { .entry = prl_tx_wait_for_phy_response_entry, .run = prl_tx_wait_for_phy_response_run, .exit = prl_tx_wait_for_phy_response_exit, }, [PRL_TX_SRC_SOURCE_TX] = { .entry = prl_tx_src_source_tx_entry, .run = prl_tx_src_source_tx_run, }, [PRL_TX_SNK_START_AMS] = { .run = prl_tx_snk_start_ams_run, }, [PRL_TX_SRC_PENDING] = { .entry = prl_tx_src_pending_entry, .run = prl_tx_src_pending_run, }, [PRL_TX_SNK_PENDING] = { .run = prl_tx_snk_pending_run, }, [PRL_TX_DISCARD_MESSAGE] = { .entry = prl_tx_discard_message_entry, }, }; /* All necessary Protocol Hard Reset States (Section 6.11.2.4) */ static const struct usb_state prl_hr_states[] = { [PRL_HR_WAIT_FOR_REQUEST] = { .entry = prl_hr_wait_for_request_entry, .run = prl_hr_wait_for_request_run, }, [PRL_HR_RESET_LAYER] = { .entry = prl_hr_reset_layer_entry, .run = prl_hr_reset_layer_run, }, [PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE] = { .entry = prl_hr_wait_for_phy_hard_reset_complete_entry, .run = prl_hr_wait_for_phy_hard_reset_complete_run, }, [PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE] = { .run = prl_hr_wait_for_pe_hard_reset_complete_run, .exit = prl_hr_wait_for_pe_hard_reset_complete_exit, }, }; /* All necessary Chunked Rx states (Section 6.11.2.1.2) */ static const struct usb_state rch_states[] = { [RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER] = { .entry = rch_wait_for_message_from_protocol_layer_entry, .run = rch_wait_for_message_from_protocol_layer_run, }, [RCH_PASS_UP_MESSAGE] = { .entry = rch_pass_up_message_entry, }, [RCH_PROCESSING_EXTENDED_MESSAGE] = { .entry = rch_processing_extended_message_entry, .run = rch_processing_extended_message_run, }, [RCH_REQUESTING_CHUNK] = { .entry = rch_requesting_chunk_entry, .run = rch_requesting_chunk_run, }, [RCH_WAITING_CHUNK] = { .entry = rch_waiting_chunk_entry, .run = rch_waiting_chunk_run, }, [RCH_REPORT_ERROR] = { .entry = rch_report_error_entry, .run = rch_report_error_run, }, }; /* All necessary Chunked Tx states (Section 6.11.2.1.3) */ static const struct usb_state tch_states[] = { [TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE] = { .entry = tch_wait_for_message_request_from_pe_entry, .run = tch_wait_for_message_request_from_pe_run, }, [TCH_WAIT_FOR_TRANSMISSION_COMPLETE] = { .run = tch_wait_for_transmission_complete_run, }, [TCH_CONSTRUCT_CHUNKED_MESSAGE] = { .entry = tch_construct_chunked_message_entry, .run = tch_construct_chunked_message_run, }, [TCH_SENDING_CHUNKED_MESSAGE] = { .run = tch_sending_chunked_message_run, }, [TCH_WAIT_CHUNK_REQUEST] = { .entry = tch_wait_chunk_request_entry, .run = tch_wait_chunk_request_run, }, [TCH_MESSAGE_RECEIVED] = { .entry = tch_message_received_entry, .run = tch_message_received_run, }, }; #ifdef TEST_BUILD const struct test_sm_data test_prl_sm_data[] = { { .base = prl_tx_states, .size = ARRAY_SIZE(prl_tx_states), }, { .base = prl_hr_states, .size = ARRAY_SIZE(prl_hr_states), }, { .base = rch_states, .size = ARRAY_SIZE(rch_states), }, { .base = tch_states, .size = ARRAY_SIZE(tch_states), }, }; const int test_prl_sm_data_size = ARRAY_SIZE(test_prl_sm_data); #endif