/* * fusb302 usb phy driver for type-c and PD * * Copyright (C) 2015, 2016 Fairchild Semiconductor Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Seee the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * */ #ifdef FSC_DEBUG #include "Log.h" #endif // FSC_DEBUG #include #include "PD_Types.h" #include "PDPolicy.h" #include "PDProtocol.h" #include "TypeC.h" #include "fusb30X.h" #ifdef FSC_HAVE_VDM #include "vdm/vdm_callbacks.h" #include "vdm/vdm_callbacks_defs.h" #include "vdm/vdm.h" #include "vdm/vdm_types.h" #include "vdm/bitfield_translators.h" #include "vdm/DisplayPort/dp_types.h" #include "vdm/DisplayPort/dp.h" #include "vdm/DisplayPort/interface_dp.h" #endif // FSC_HAVE_VDM ///////////////////////////////////////////////////////////////////////////// // Variables for use with the USB PD state machine ///////////////////////////////////////////////////////////////////////////// #ifdef FSC_DEBUG StateLog PDStateLog; // Log for tracking state transitions and times extern volatile FSC_U16 Timer_S; // Tracks seconds elapsed for log timestamp extern volatile FSC_U16 Timer_tms; // Tracks tenths of milliseconds elapsed for log timestamp extern FSC_U8 manualRetries; // Set to 1 to enable manual retries extern FSC_U8 nTries; // Number of tries for manual retry #endif // FSC_DEBUG extern FSC_BOOL g_Idle; // Puts state machine into Idle state extern USBTypeCCurrent SourceCurrent; // TypeC advertised current // Device Policy Manager Variables FSC_BOOL USBPDTxFlag; // Flag to indicate that we need to send a message (set by device policy manager) FSC_BOOL IsHardReset; // Variable indicating that a Hard Reset is occurring FSC_BOOL IsPRSwap; // Variable indicating that a PRSwap is occurring FSC_BOOL IsVCONNSource; // Indicates who is the VCONN source sopMainHeader_t PDTransmitHeader = {0}; // Definition of the PD packet to send sopMainHeader_t CapsHeaderSink = {0}; // Definition of the sink capabilities of the device sopMainHeader_t CapsHeaderSource = {0}; // Definition of the source capabilities of the device sopMainHeader_t CapsHeaderReceived = {0}; // Last capabilities header received (source or sink) doDataObject_t PDTransmitObjects[7] = {{0}}; // Data objects to send doDataObject_t CapsSink[7] = {{0}}; // Power object definitions of the sink capabilities of the device doDataObject_t CapsSource[7] = {{0}}; // Power object definitions of the source capabilities of the device doDataObject_t CapsReceived[7] = {{0}}; // Last power objects received (source or sink) doDataObject_t USBPDContract = {0}; // Current USB PD contract (request object) doDataObject_t SinkRequest = {0}; // Sink request message FSC_U32 SinkRequestMaxVoltage; // Maximum voltage that the sink will request FSC_U32 SinkRequestMaxPower; // Maximum power the sink will request (used to calculate current as well) FSC_U32 SinkRequestOpPower; // Operating power the sink will request (used to calculate current as well) FSC_U32 SinkRequestMaxCurrent; // Maximum current the sink will request FSC_BOOL SinkGotoMinCompatible; // Whether the sink will respond to the GotoMin command FSC_BOOL SinkUSBSuspendOperation; // Whether the sink wants to continue operation during USB suspend FSC_BOOL SinkUSBCommCapable; // Whether the sink is USB communications capable doDataObject_t PartnerCaps = {0}; // Partner's Sink Capabilities #ifdef FSC_DEBUG FSC_BOOL SourceCapsUpdated; // Flag to indicate whether we have updated the source caps (for the GUI) #endif // FSC_DEBUG // Policy Variables // removing static qualifier so PolicyState is visible to other code blocks. // re-org coming soon! PolicyState_t PolicyState; // State variable for Policy Engine PolicyState_t LastPolicyState; // State variable for Policy Engine FSC_U8 PolicySubIndex; // Sub index for policy states FSC_BOOL PolicyIsSource; // Flag to indicate whether we are acting as a source or a sink FSC_BOOL PolicyIsDFP; // Flag to indicate whether we are acting as a UFP or DFP FSC_BOOL PolicyHasContract; // Flag to indicate whether there is a contract in place FSC_U32 VbusTransitionTime; // Time to wait for VBUS switch to transition static FSC_U8 CollisionCounter; // Collision counter for the policy engine static FSC_U8 HardResetCounter; // The number of times a hard reset has been generated static FSC_U8 CapsCounter; // Number of capabilities messages sent volatile FSC_U32 PolicyStateTimer; // Multi-function timer for the different policy states volatile FSC_U32 NoResponseTimer; // Policy engine no response timer volatile static FSC_U32 SwapSourceStartTimer; // Delay after power role swap before starting source PD sopMainHeader_t PolicyRxHeader = {0}; // Header object for USB PD messages received sopMainHeader_t PolicyTxHeader = {0}; // Header object for USB PD messages to send doDataObject_t PolicyRxDataObj[7] = {{0}}; // Buffer for data objects received doDataObject_t PolicyTxDataObj[7] = {{0}}; // Buffer for data objects to send static FSC_BOOL isContractValid; // Is PD Contract Valid #ifdef FSC_HAVE_VDM // VDM Manager object extern VdmManager vdmm; VdmDiscoveryState_t AutoVdmState; FSC_U32 vdm_msg_length; doDataObject_t vdm_msg_obj[7] = {{0}}; PolicyState_t vdm_next_ps; FSC_BOOL sendingVdmData; volatile FSC_U32 VdmTimer; FSC_BOOL VdmTimerStarted; FSC_U16 auto_mode_disc_tracker; extern FSC_BOOL mode_entered; extern SvidInfo core_svid_info; #endif // FSC_HAVE_VDM #ifdef FSC_HAVE_DP extern FSC_U32 DpModeEntered; extern FSC_S32 AutoDpModeEntryObjPos; #endif // FSC_HAVE_DP extern FSC_BOOL ProtocolCheckRxBeforeTx; extern FSC_U8 loopCounter; // Used to count the number of Unattach<->AttachWait loops ///////////////////////////////////////////////////////////////////////////// // Timer Interrupt service routine ///////////////////////////////////////////////////////////////////////////// void PolicyTick( void ) { if( !USBPDActive ) return; if (PolicyStateTimer) // If the PolicyStateTimer is greater than zero... PolicyStateTimer--; // Decrement it if ((NoResponseTimer < T_TIMER_DISABLE) && (NoResponseTimer > 0)) // If the timer is enabled and hasn't expired NoResponseTimer--; // Decrement it if (SwapSourceStartTimer) SwapSourceStartTimer--; #ifdef FSC_HAVE_VDM if (VdmTimer) VdmTimer--; #endif // FSC_HAVE_VDM } void InitializePDPolicyVariables(void) { SwapSourceStartTimer = 0; #ifdef FSC_HAVE_SNK SinkRequestMaxVoltage = 180; // Maximum voltage that the sink will request (9V) SinkRequestMaxPower = 36000; // Maximum power the sink will request (18W, used to calculate current as well) SinkRequestOpPower = 36000; // Operating power the sink will request (18W, sed to calculate current as well) SinkGotoMinCompatible = FALSE; // Whether the sink will respond to the GotoMin command SinkUSBSuspendOperation = FALSE; // Whether the sink wants to continue operation during USB suspend SinkUSBCommCapable = FALSE; // Whether the sink is USB communications capable SinkRequestMaxCurrent = 300; CapsHeaderSink.NumDataObjects = 3; // Set the number of power objects to 2 CapsHeaderSink.PortDataRole = 0; // Set the data role to UFP by default CapsHeaderSink.PortPowerRole = 0; // By default, set the device to be a sink CapsHeaderSink.SpecRevision = 1; // Set the spec revision to 2.0 CapsHeaderSink.Reserved0 = 0; CapsHeaderSink.Reserved1 = 0; CapsSink[0].FPDOSink.Voltage = 100; // Set 5V for the first supply option CapsSink[0].FPDOSink.OperationalCurrent = 300; // Set that our device will consume 3000mA for this object CapsSink[0].FPDOSink.DataRoleSwap = 1; // By default, enable DR_SWAP CapsSink[0].FPDOSink.USBCommCapable = 0; // By default, USB communications is not allowed CapsSink[0].FPDOSink.ExternallyPowered = 0; // By default, we are not externally powered CapsSink[0].FPDOSink.HigherCapability = FALSE; // By default, don't require more than vSafe5V CapsSink[0].FPDOSink.DualRolePower = 1; // By default, enable PR_SWAP CapsSink[0].FPDOSink.Reserved = 0; CapsSink[1].FPDOSink.Voltage = 180; // Set 9V for the second supply option CapsSink[1].FPDOSink.OperationalCurrent = 200; // Set that our device will consume 2000mA for this object CapsSink[1].FPDOSink.DataRoleSwap = 0; // Not used CapsSink[1].FPDOSink.USBCommCapable = 0; // Not used CapsSink[1].FPDOSink.ExternallyPowered = 0; // Not used CapsSink[1].FPDOSink.HigherCapability = 0; // Not used CapsSink[1].FPDOSink.DualRolePower = 0; // Not used CapsSink[2].BPDO.MaxPower = 72; // Set 18W for the third supply option CapsSink[2].BPDO.MinVoltage = 80; // Set 4V for minimum voltage CapsSink[2].BPDO.MaxVoltage = 200; // Set 10V for maximum voltage CapsSink[2].BPDO.SupplyType = pdoTypeBattery; #endif // FSC_HAVE_SNK #ifdef FSC_HAVE_SRC #ifdef FM150911A CapsHeaderSource.NumDataObjects = 2; // Set the number of power objects to 2 #else CapsHeaderSource.NumDataObjects = 1; // Set the number of power objects to 1 #endif // FM150911A CapsHeaderSource.PortDataRole = 0; // Set the data role to UFP by default CapsHeaderSource.PortPowerRole = 1; // By default, set the device to be a source CapsHeaderSource.SpecRevision = 1; // Set the spec revision to 2.0 CapsHeaderSource.Reserved0 = 0; CapsHeaderSource.Reserved1 = 0; CapsSource[0].FPDOSupply.Voltage = 100; // Set 5V for the first supply option CapsSource[0].FPDOSupply.MaxCurrent = 90; // Set 900mA for the first supply option CapsSource[0].FPDOSupply.PeakCurrent = 0; // Set peak equal to max CapsSource[0].FPDOSupply.DataRoleSwap = TRUE; // By default, don't enable DR_SWAP CapsSource[0].FPDOSupply.USBCommCapable = FALSE; // By default, USB communications is not allowed CapsSource[0].FPDOSupply.ExternallyPowered = FALSE; // By default, state that we are not externally powered CapsSource[0].FPDOSupply.USBSuspendSupport = FALSE; // By default, allow USB Suspend CapsSource[0].FPDOSupply.DualRolePower = TRUE; // By default, enable PR_SWAP CapsSource[0].FPDOSupply.SupplyType = 0; // Fixed supply CapsSource[0].FPDOSupply.Reserved = 0; // Clearing reserved bits #ifdef FM150911A CapsSource[1].FPDOSupply.Voltage = 240; // Set 12V for the second supply option CapsSource[1].FPDOSupply.MaxCurrent = 150; // Set 500mA for the second supply option CapsSource[1].FPDOSupply.PeakCurrent = 0; // Set peak equal to max CapsSource[1].FPDOSupply.DataRoleSwap = 0; // Not used... set to zero CapsSource[1].FPDOSupply.USBCommCapable = 0; // Not used... set to zero CapsSource[1].FPDOSupply.ExternallyPowered = 0; // Not used... set to zero CapsSource[1].FPDOSupply.USBSuspendSupport = 0; // Not used... set to zero CapsSource[1].FPDOSupply.DualRolePower = 0; // Allows PR_SWAP CapsSource[1].FPDOSupply.SupplyType = 0; // Fixed supply #endif // FM150911A #endif // FSC_HAVE_SRC #ifdef FSC_DEBUG SourceCapsUpdated = FALSE; // Set the flag to indicate to the GUI that our source caps have been updated #endif // FSC_DEBUG VbusTransitionTime = tFPF2498Transition; // Default VBUS transition time 20ms #ifdef FSC_HAVE_VDM InitializeVdmManager(); // Initialize VDM Manager vdmInitDpm(); AutoVdmState = AUTO_VDM_INIT; auto_mode_disc_tracker = 0; #endif // FSC_HAVE_VDM #ifdef FSC_HAVE_DP AutoDpModeEntryObjPos = -1; #endif // FSC_HAVE_DP ProtocolCheckRxBeforeTx = FALSE; isContractValid = FALSE; Registers.Slice.SDAC = SDAC_DEFAULT; // Set the SDAC threshold to "default" Registers.Slice.SDAC_HYS = 0b01; // Set hysteresis to 85mV DeviceWrite(regSlice, 1, &Registers.Slice.byte); #ifdef FSC_DEBUG InitializeStateLog(&PDStateLog); #endif // FSC_DEBUG } // ##################### USB PD Enable / Disable Routines ################### // void USBPDEnable(FSC_BOOL DeviceUpdate, SourceOrSink TypeCDFP) { FSC_U8 data[5]; IsHardReset = FALSE; IsPRSwap = FALSE; HardResetCounter = 0; pr_debug("FUSB %s: IsPRSwap=%d\n", __func__, IsPRSwap); if (USBPDEnabled == TRUE) { if (blnCCPinIsCC1) { // If the CC pin is on CC1 Registers.Switches.TXCC1 = 1; // Enable the BMC transmitter on CC1 Registers.Switches.MEAS_CC1 = 1; Registers.Switches.TXCC2 = 0; // Disable the BMC transmitter on CC2 Registers.Switches.MEAS_CC2 = 0; } else if (blnCCPinIsCC2) { // If the CC pin is on CC2 Registers.Switches.TXCC2 = 1; // Enable the BMC transmitter on CC2 Registers.Switches.MEAS_CC2 = 1; Registers.Switches.TXCC1 = 0; // Disable the BMC transmitter on CC1 Registers.Switches.MEAS_CC1 = 0; } if (blnCCPinIsCC1 || blnCCPinIsCC2) // If we know what pin the CC signal is... { USBPDActive = TRUE; // Set the active flag ResetProtocolLayer(FALSE); // Reset the protocol layer by default NoResponseTimer = T_TIMER_DISABLE; // Disable the no response timer by default PolicyIsSource = TypeCDFP; // Set whether we should be initially a source or sink PolicyIsDFP = TypeCDFP; IsVCONNSource = TypeCDFP; // Set the initial data port direction if (PolicyIsSource) // If we are a source... { PolicyState = peSourceStartup; // initialize the policy engine state to source startup PolicySubIndex = 0; LastPolicyState = peDisabled; Registers.Switches.POWERROLE = 1; // Initialize to a SRC Registers.Switches.DATAROLE = 1; // Initialize to a DFP } else // Otherwise we are a sink... { PolicyState = peSinkStartup; // initialize the policy engine state to sink startup PolicySubIndex = 0; PolicyStateTimer =0; LastPolicyState = peDisabled; Registers.Switches.POWERROLE = 0; // Initialize to a SNK Registers.Switches.DATAROLE = 0; // Initialize to a UFP Registers.Control.ENSOP1 = 0; Registers.Control.ENSOP1DP = 0; Registers.Control.ENSOP2 = 0; Registers.Control.ENSOP2DB = 0; } Registers.Switches.AUTO_CRC = 0; // Disable auto CRC until startup Registers.Control.AUTO_PRE = 0; // Disable AUTO_PRE since we are going to use AUTO_CRC Registers.Control.N_RETRIES = 3; // Set the number of retries to 3 Registers.Power.PWR = 0xF; // Enable the internal oscillator for USB PD DeviceWrite(regPower, 1, &Registers.Power.byte); // Commit the power setting #ifdef FSC_DEBUG if(manualRetries) { Registers.Control.N_RETRIES = 0; // Set the number of retries to 0 } #endif // FSC_DEBUG Registers.Control.AUTO_RETRY = 1; // Enable AUTO_RETRY to use the I_TXSENT interrupt - needed for auto-CRC to work data[0] = Registers.Slice.byte; // Set the slice byte (building one transaction) data[1] = Registers.Control.byte[0] | 0x40; // Set the Control0 byte and set the TX_FLUSH bit (auto-clears) data[2] = Registers.Control.byte[1] | 0x04; // Set the Control1 byte and set the RX_FLUSH bit (auto-clears) data[3] = Registers.Control.byte[2]; data[4] = Registers.Control.byte[3]; DeviceWrite(regControl0, 4, &data[1]); if (DeviceUpdate) { DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); // Commit the switch1 setting } Registers.Power.PWR = 0xF; // Enable the internal oscillator for USB PD DeviceWrite(regPower, 1, &Registers.Power.byte); // Commit the power setting #ifdef FSC_DEBUG StoreUSBPDToken(TRUE, pdtAttach); // Store the PD attach token #endif // FSC_DEBUG } #ifdef FSC_INTERRUPT_TRIGGERED g_Idle = FALSE; // Go into active mode platform_enable_timer(TRUE); #endif // FSC_INTERRUPT_TRIGGERED } } void USBPDDisable(FSC_BOOL DeviceUpdate) { IsHardReset = FALSE; #ifdef FSC_DEBUG if (USBPDActive == TRUE) // If we were previously active... StoreUSBPDToken(TRUE, pdtDetach); // Store the PD detach token SourceCapsUpdated = TRUE; // Set the source caps updated flag to trigger an update of the GUI #endif // FSC_DEBUG USBPDActive = FALSE; // Clear the USB PD active flag ProtocolState = PRLDisabled; // Set the protocol layer state to disabled PolicyState = peDisabled; // Set the policy engine state to disabled PDTxStatus = txIdle; // Reset the transmitter status PolicyIsSource = FALSE; // Clear the is source flag until we connect again PolicyHasContract = FALSE; // Clear the has contract flag platform_notify_pd_contract(FALSE); if (DeviceUpdate) { Registers.Switches.TXCC1 = 0; // Disable the BMC transmitter (both CC1 & CC2) Registers.Switches.TXCC2 = 0; Registers.Switches.AUTO_CRC = 0; // turn off Auto CRC DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); // Commit the switch setting } Registers.Power.PWR = 0x7; // Disable the internal oscillator for USB PD DeviceWrite(regPower, 1, &Registers.Power.byte); // Commit the power setting ProtocolFlushRxFIFO(); ProtocolFlushTxFIFO(); #ifdef FSC_INTERRUPT_TRIGGERED Registers.Mask.M_COLLISION = 1; // Mask PD Interrupts DeviceWrite(regMask, 1, &Registers.Mask.byte); Registers.MaskAdv.M_RETRYFAIL = 1; Registers.MaskAdv.M_TXSENT = 1; Registers.MaskAdv.M_HARDRST = 1; DeviceWrite(regMaska, 1, &Registers.MaskAdv.byte[0]); Registers.MaskAdv.M_GCRCSENT = 1; DeviceWrite(regMaskb, 1, &Registers.MaskAdv.byte[1]); #endif // FSC_INTERRUPT_TRIGGERED } // ##################### USB PD Policy Engine Routines ###################### // void USBPDPolicyEngine(void) { #ifdef FSC_DEBUG if (LastPolicyState != PolicyState) // Log Policy State for Debugging { WriteStateLog(&PDStateLog, PolicyState, Timer_tms, Timer_S); } #endif // FSC_DEBUG LastPolicyState = PolicyState; switch (PolicyState) { case peDisabled: break; case peErrorRecovery: PolicyErrorRecovery(); break; // ###################### Source States ##################### // #ifdef FSC_HAVE_SRC case peSourceSendHardReset: PolicySourceSendHardReset(); break; case peSourceSendSoftReset: PolicySourceSendSoftReset(); break; case peSourceSoftReset: PolicySourceSoftReset(); break; case peSourceStartup: PolicySourceStartup(); break; case peSourceDiscovery: PolicySourceDiscovery(); break; case peSourceSendCaps: PolicySourceSendCaps(); break; case peSourceDisabled: PolicySourceDisabled(); break; case peSourceTransitionDefault: PolicySourceTransitionDefault(); break; case peSourceNegotiateCap: PolicySourceNegotiateCap(); break; case peSourceCapabilityResponse: PolicySourceCapabilityResponse(); break; case peSourceTransitionSupply: PolicySourceTransitionSupply(); break; case peSourceReady: PolicySourceReady(); break; case peSourceGiveSourceCaps: PolicySourceGiveSourceCap(); break; case peSourceGetSinkCaps: PolicySourceGetSinkCap(); break; case peSourceSendPing: PolicySourceSendPing(); break; case peSourceGotoMin: PolicySourceGotoMin(); break; case peSourceGiveSinkCaps: PolicySourceGiveSinkCap(); break; case peSourceGetSourceCaps: PolicySourceGetSourceCap(); break; case peSourceSendDRSwap: PolicySourceSendDRSwap(); break; case peSourceEvaluateDRSwap: PolicySourceEvaluateDRSwap(); break; case peSourceSendVCONNSwap: PolicySourceSendVCONNSwap(); break; case peSourceSendPRSwap: PolicySourceSendPRSwap(); break; case peSourceEvaluatePRSwap: PolicySourceEvaluatePRSwap(); break; case peSourceWaitNewCapabilities: PolicySourceWaitNewCapabilities(); break; case peSourceEvaluateVCONNSwap: PolicySourceEvaluateVCONNSwap(); break; #endif // FSC_HAVE_SRC // ###################### Sink States ####################### // #ifdef FSC_HAVE_SNK case peSinkStartup: PolicySinkStartup(); break; case peSinkSendHardReset: PolicySinkSendHardReset(); break; case peSinkSoftReset: PolicySinkSoftReset(); break; case peSinkSendSoftReset: PolicySinkSendSoftReset(); break; case peSinkTransitionDefault: PolicySinkTransitionDefault(); break; case peSinkDiscovery: PolicySinkDiscovery(); break; case peSinkWaitCaps: PolicySinkWaitCaps(); break; case peSinkEvaluateCaps: PolicySinkEvaluateCaps(); break; case peSinkSelectCapability: PolicySinkSelectCapability(); break; case peSinkTransitionSink: PolicySinkTransitionSink(); break; case peSinkReady: PolicySinkReady(); break; case peSinkGiveSinkCap: PolicySinkGiveSinkCap(); break; case peSinkGetSourceCap: PolicySinkGetSourceCap(); break; case peSinkGetSinkCap: PolicySinkGetSinkCap(); break; case peSinkGiveSourceCap: PolicySinkGiveSourceCap(); break; case peSinkSendDRSwap: PolicySinkSendDRSwap(); break; case peSinkEvaluateDRSwap: PolicySinkEvaluateDRSwap(); break; case peSinkEvaluateVCONNSwap: PolicySinkEvaluateVCONNSwap(); break; case peSinkSendPRSwap: PolicySinkSendPRSwap(); break; case peSinkEvaluatePRSwap: PolicySinkEvaluatePRSwap(); break; #endif // FSC_HAVE_SNK #ifdef FSC_HAVE_VDM case peGiveVdm: PolicyGiveVdm(); break; #endif // FSC_HAVE_VDM // ---------- BIST Receive Mode --------------------- // case PE_BIST_Receive_Mode: // Bist Receive Mode policyBISTReceiveMode(); break; case PE_BIST_Frame_Received: // Test Frame received by Protocol layer policyBISTFrameReceived(); break; // ---------- BIST Carrier Mode and Eye Pattern ----- // case PE_BIST_Carrier_Mode_2: // BIST Carrier Mode 2 policyBISTCarrierMode2(); break; case PE_BIST_Test_Data: policyBISTTestData(); break; default: #ifdef FSC_HAVE_VDM if ((PolicyState >= FIRST_VDM_STATE) && (PolicyState <= LAST_VDM_STATE) ) { // valid VDM state PolicyVdm(); } else #endif // FSC_HAVE_VDM { // invalid state, reset PolicyInvalidState(); } break; } } // ############################# Source States ############################# // void PolicyErrorRecovery(void) { SetStateErrorRecovery(); } #ifdef FSC_HAVE_SRC void PolicySourceSendHardReset(void) { PolicySendHardReset(peSourceTransitionDefault, 0); } void PolicySourceSoftReset(void) { PolicySendCommand(CMTAccept, peSourceSendCaps, 0); } void PolicySourceSendSoftReset(void) { switch (PolicySubIndex) { case 0: if (PolicySendCommand(CMTSoftReset, peSourceSendSoftReset, 1) == STAT_SUCCESS) // Send the soft reset command to the protocol layer { PolicyStateTimer = tSenderResponse; // Start the sender response timer to wait for an accept message once successfully sent } break; default: if (ProtocolMsgRx) // If we have received a message { ProtocolMsgRx = FALSE; // Reset the message received flag since we've handled it here if ((PolicyRxHeader.NumDataObjects == 0) && (PolicyRxHeader.MessageType == CMTAccept)) // And it was the Accept... { PolicyState = peSourceSendCaps; // Go to the send caps state } else // Otherwise it was a message that we didn't expect, so... PolicyState = peSourceSendHardReset; // Go to the hard reset state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Clear the transmitter status } else if (!PolicyStateTimer) // If we didn't get a response to our request before timing out... { PolicyState = peSourceSendHardReset; // Go to the hard reset state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; } } void PolicySourceStartup(void) { #ifdef FSC_HAVE_VDM FSC_S32 i; #endif // FSC_HAVE_VDM // Set masks for PD Registers.Mask.M_COLLISION = 0; DeviceWrite(regMask, 1, &Registers.Mask.byte); Registers.MaskAdv.M_RETRYFAIL = 0; Registers.MaskAdv.M_TXSENT = 0; Registers.MaskAdv.M_HARDRST = 0; DeviceWrite(regMaska, 1, &Registers.MaskAdv.byte[0]); Registers.MaskAdv.M_GCRCSENT = 0; DeviceWrite(regMaskb, 1, &Registers.MaskAdv.byte[1]); if(Registers.DeviceID.VERSION_ID == VERSION_302B) { if(Registers.Control.BIST_TMODE == 1) { Registers.Control.BIST_TMODE = 0; // Disable auto-flush RxFIFO DeviceWrite(regControl3, 1, &Registers.Control.byte[3]); } } else { if(Registers.Control.RX_FLUSH == 1) // Disable Rx flushing if it has been enabled { Registers.Control.RX_FLUSH = 0; } } switch (PolicySubIndex) { case 0: USBPDContract.object = 0; // Clear the USB PD contract (output power to 5V default) PartnerCaps.object = 0; // Clear partner sink caps IsPRSwap = FALSE; pr_debug("FUSB %s: IsPRSwap=%d\n", __func__, IsPRSwap); PolicyIsSource = TRUE; // Set the flag to indicate that we are a source (PRSwaps) Registers.Switches.POWERROLE = PolicyIsSource; DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); ResetProtocolLayer(TRUE); // Reset the protocol layer PRSwapTimer = 0; // Clear the swap timer CapsCounter = 0; // Clear the caps counter CollisionCounter = 0; // Reset the collision counter PolicyStateTimer = 150; // 150ms timeout for VBUS PolicySubIndex++; break; case 1: if ((isVBUSOverVoltage(VBUS_MDAC_4P62) && (SwapSourceStartTimer == 0)) || (PolicyStateTimer == 0)) // Wait until we reach vSafe0V and delay if coming from PR Swap { PolicySubIndex++; } break; case 2: PolicyStateTimer = 0; // Reset the policy state timer PolicyState = peSourceSendCaps; // Go to the source caps PolicySubIndex = 0; // Reset the sub index #ifdef FSC_HAVE_VDM AutoVdmState = AUTO_VDM_INIT; mode_entered = FALSE; auto_mode_disc_tracker = 0; core_svid_info.num_svids = 0; for (i = 0; i < MAX_NUM_SVIDS; i++) { core_svid_info.svids[i] = 0; } #endif // FSC_HAVE_VDM #ifdef FSC_HAVE_DP AutoDpModeEntryObjPos = -1; resetDp(); #endif // FSC_HAVE_DP break; default: PolicySubIndex = 0; break; } } void PolicySourceDiscovery(void) { switch (PolicySubIndex) { case 0: PolicyStateTimer = tTypeCSendSourceCap; // Initialize the SourceCapabilityTimer PolicySubIndex++; // Increment the sub index break; default: if ((HardResetCounter > nHardResetCount) && (NoResponseTimer == 0) && (PolicyHasContract == TRUE)) { // If we previously had a contract in place... PolicyState = peErrorRecovery; // Go to the error recovery state since something went wrong PolicySubIndex = 0; } else if ((HardResetCounter > nHardResetCount) && (NoResponseTimer == 0) && (PolicyHasContract == FALSE)) { // Otherwise... PolicyState = peSourceDisabled; // Go to the disabled state since we are assuming that there is no PD sink attached PolicySubIndex = 0; // Reset the sub index for the next state } if (PolicyStateTimer == 0) // Once the timer expires... { if (CapsCounter > nCapsCount) // If we have sent the maximum number of capabilities messages... PolicyState = peSourceDisabled; // Go to the disabled state, no PD sink connected else // Otherwise... PolicyState = peSourceSendCaps; // Go to the send source caps state to send a source caps message PolicySubIndex = 0; // Reset the sub index for the next state } break; } } void PolicySourceSendCaps(void) { if ((HardResetCounter > nHardResetCount) && (NoResponseTimer == 0)) // Check our higher level timeout { if (PolicyHasContract) // If USB PD was previously established... PolicyState = peErrorRecovery; // Need to go to the error recovery state else // Otherwise... PolicyState = peSourceDisabled; // We are disabling PD and leaving the Type-C connections alone } else // If we haven't timed out and maxed out on hard resets... { switch (PolicySubIndex) { case 0: if (PolicySendData(DMTSourceCapabilities, CapsHeaderSource.NumDataObjects, &CapsSource[0], peSourceSendCaps, 1, SOP_TYPE_SOP) == STAT_SUCCESS) { HardResetCounter = 0; // Clear the hard reset counter CapsCounter = 0; // Clear the caps counter NoResponseTimer = T_TIMER_DISABLE; // Stop the no response timer PolicyStateTimer = tSenderResponse - tHardResetOverhead; // Set the sender response timer } break; default: if (ProtocolMsgRx) // If we have received a message { ProtocolMsgRx = FALSE; // Reset the message ready flag since we're handling it here if ((PolicyRxHeader.NumDataObjects == 1) && (PolicyRxHeader.MessageType == DMTRequest)) // Was this a valid request message? PolicyState = peSourceNegotiateCap; // If so, go to the negotiate capabilities state else // Otherwise it was a message that we didn't expect, so... PolicyState = peSourceSendSoftReset; // Go onto issuing a soft reset PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Clear the transmitter status } else if (!PolicyStateTimer) // If we didn't get a response to our request before timing out... { ProtocolMsgRx = FALSE; // Reset the message ready flag since we've timed out PolicyState = peSourceSendHardReset; // Go to the hard reset state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; } } } void PolicySourceDisabled(void) { USBPDContract.object = 0; // Clear the USB PD contract (output power to 5V default) // Wait for a hard reset or detach... #ifdef FSC_INTERRUPT_TRIGGERED if(loopCounter == 0) { g_Idle = TRUE; // Idle until COMP or HARDRST or GCRCSENT platform_enable_timer(FALSE); } #endif // FSC_INTERRUPT_TRIGGERED } void PolicySourceTransitionDefault(void) { switch (PolicySubIndex) { case 0: if(PolicyStateTimer == 0) { PolicyHasContract = FALSE; platform_notify_pd_contract(FALSE); PolicySubIndex++; } break; case 1: platform_set_vbus_lvl_enable(VBUS_LVL_ALL, FALSE, FALSE); // Disable VBUS output platform_set_vbus_discharge(TRUE); // Enabled VBUS discharge path if(!PolicyIsDFP) // Make sure date role is DFP { PolicyIsDFP = TRUE;; // Set the current data role Registers.Switches.DATAROLE = PolicyIsDFP; // Update the data role DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); // Commit the data role in the 302 platform_notify_attached_source(PolicyIsDFP, true); } if(IsVCONNSource) // Disable VCONN if VCONN Source { Registers.Switches.VCONN_CC1 = 0; Registers.Switches.VCONN_CC2 = 0; DeviceWrite(regSwitches0, 1, &Registers.Switches.byte[0]); platform_set_vconn_enable(FALSE); } PolicySubIndex++; // Adjust output if necessary and start timer prior to going to startup state? break; case 2: if(VbusVSafe0V()) // Allow the voltage to drop to 0 { platform_set_vbus_discharge(FALSE); // Disable VBUS discharge path PolicyStateTimer = tSrcRecover; PolicySubIndex++; } break; case 3: if(PolicyStateTimer == 0) // Wait tSrcRecover to turn VBUS on { PolicySubIndex++; } break; default: platform_set_vbus_lvl_enable(VBUS_LVL_5V, TRUE, FALSE); // Enable the 5V source platform_set_vconn_enable(TRUE); if(blnCCPinIsCC1) { Registers.Switches.VCONN_CC2 = 1; } else { Registers.Switches.VCONN_CC1 = 1; } DeviceWrite(regSwitches0, 1, &Registers.Switches.byte[0]); IsVCONNSource = TRUE; NoResponseTimer = tNoResponse; // Initialize the no response timer PolicyState = peSourceStartup; // Go to the startup state PolicySubIndex = 0; break; } } void PolicySourceNegotiateCap(void) { // This state evaluates if the sink request can be met or not and sets the next state accordingly FSC_BOOL reqAccept = FALSE; // Set a flag that indicates whether we will accept or reject the request FSC_U8 objPosition; // Get the requested object position objPosition = PolicyRxDataObj[0].FVRDO.ObjectPosition; // Get the object position reference if ((objPosition > 0) && (objPosition <= CapsHeaderSource.NumDataObjects)) // Make sure the requested object number if valid, continue validating request { if ((PolicyRxDataObj[0].FVRDO.OpCurrent <= CapsSource[objPosition-1].FPDOSupply.MaxCurrent)) // Ensure the default power/current request is available reqAccept = TRUE; // If the source can supply the request, set the flag to respond } if (reqAccept) // If we have received a valid request... { PolicyState = peSourceTransitionSupply; // Go to the transition supply state } else // Otherwise the request was invalid... PolicyState = peSourceCapabilityResponse; // Go to the capability response state to send a reject/wait message } void PolicySourceTransitionSupply(void) { FSC_U8 sourceVoltage = 0; switch (PolicySubIndex) { case 0: PolicySendCommand(CMTAccept, peSourceTransitionSupply, 1); // Send the Accept message break; case 1: PolicyStateTimer = tSrcTransition; // Initialize the timer to allow for the sink to transition PolicySubIndex++; // Increment to move to the next sub state break; case 2: if (!PolicyStateTimer) // If the timer has expired (the sink is ready)... PolicySubIndex++; // Increment to move to the next sub state break; case 3: PolicyHasContract = TRUE; // Set the flag to indicate that a contract is in place USBPDContract.object = PolicyRxDataObj[0].object; // Set the contract to the sink request //TODO: More robust selection of PDO sourceVoltage = CapsSource[USBPDContract.FVRDO.ObjectPosition-1].FPDOSupply.Voltage; if (sourceVoltage == 100) // If the chosen object is 5V { if(platform_get_vbus_lvl_enable(VBUS_LVL_5V)) // If the supply is already enabled, go to PS_READY { PolicySubIndex = 5; } else { platform_set_vbus_lvl_enable(VBUS_LVL_5V, TRUE, FALSE); PolicyStateTimer = t5To12VTransition; // Set the policy state timer to allow the load switch time to turn off so we don't short our supplies PolicySubIndex++; } } #ifdef FM150911A else if (sourceVoltage == 240) // If the chosen object is 12V { if(platform_get_vbus_lvl_enable(VBUS_LVL_12V)) // If the supply is already enabled, go to PS_READY { PolicySubIndex = 5; } else { platform_set_vbus_lvl_enable(VBUS_LVL_12V, TRUE, FALSE); PolicyStateTimer = t5To12VTransition; // Set the policy state timer to allow the load switch time to turn off so we don't short our supplies PolicySubIndex++; } } #endif // FM150911A else // Default to vSafe5V { if(platform_get_vbus_lvl_enable(VBUS_LVL_5V)) // If the supply is already enabled, go to PS_READY { PolicySubIndex = 5; } else { platform_set_vbus_lvl_enable(VBUS_LVL_5V, TRUE, FALSE); PolicyStateTimer = t5To12VTransition; // Set the policy state timer to allow the load switch time to turn off so we don't short our supplies PolicySubIndex++; } } break; case 4: // Validate the output is ready prior to sending the ready message (only using a timer for now, could validate using an ADC as well) if (PolicyStateTimer == 0) { if(CapsSource[USBPDContract.FVRDO.ObjectPosition-1].FPDOSupply.Voltage == 100) { platform_set_vbus_lvl_enable(VBUS_LVL_5V, TRUE, TRUE); // Disable the "other" vbus outputs } #ifdef FM150911A else if(CapsSource[USBPDContract.FVRDO.ObjectPosition-1].FPDOSupply.Voltage == 240) { platform_set_vbus_lvl_enable(VBUS_LVL_12V, TRUE, TRUE); // Disable the "other" vbus outputs } #endif // FM150911A else { platform_set_vbus_lvl_enable(VBUS_LVL_5V, TRUE, TRUE); // Disable the "other" vbus outputs } PolicyStateTimer = tSourceRiseTimeout; // Source rise timeout PolicySubIndex++; // Increment to move to the next sub state } break; case 5: if(CapsSource[USBPDContract.FVRDO.ObjectPosition-1].FPDOSupply.Voltage == 100) { if((!isVBUSOverVoltage(VBUS_MDAC_5P04) && isVBUSOverVoltage(VBUS_MDAC_4P62)) || (PolicyStateTimer == 0)) // Check that VBUS is between 4.6 and 5.5 V { PolicySubIndex++; } } #ifdef FM150911A else if(CapsSource[USBPDContract.FVRDO.ObjectPosition-1].FPDOSupply.Voltage == 240) { if((isVBUSOverVoltage(VBUS_MDAC_11P76)) || (PolicyStateTimer == 0)) // Check that VBUS is over 11.8V { PolicySubIndex++; } } #endif // FM150911A else if(PolicyStateTimer == 0) { PolicySubIndex++; } break; default: if (PolicySendCommand(CMTPS_RDY, peSourceReady, 0) == STAT_SUCCESS) // Send the PS_RDY message and move onto the Source Ready state { if (Registers.Control.HOST_CUR == 0b01) // Host current must be set to 1.5A or 3.0A in explicit contract { Registers.Control.HOST_CUR = 0b10; DeviceWrite(regControl0, 1, &Registers.Control.byte[0]); } platform_notify_pd_contract(TRUE); } break; } } void PolicySourceCapabilityResponse(void) { if (PolicyHasContract) // If we currently have a contract, issue the reject and move back to the ready state { if(isContractValid) { PolicySendCommand(CMTReject, peSourceReady, 0); // Send the message and continue onto the ready state } else { PolicySendCommand(CMTReject, peSourceSendHardReset, 0); // Send the message and continue onto the ready state } } else // If there is no contract in place, issue a hard reset { PolicySendCommand(CMTReject, peSourceWaitNewCapabilities, 0); // Send Reject and continue onto the Source Wait New Capabilities state after success } } void PolicySourceReady(void) { if (ProtocolMsgRx) // Have we received a message from the sink? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTGetSourceCap: PolicyState = peSourceGiveSourceCaps; // Send out the caps PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTGetSinkCap: // If we receive a get sink capabilities message... PolicyState = peSourceGiveSinkCaps; // Go evaluate whether we are going to send sink caps or reject PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTDR_Swap: // If we get a DR_Swap message... PolicyState = peSourceEvaluateDRSwap; // Go evaluate whether we are going to accept or reject the swap PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTPR_Swap: PolicyState = peSourceEvaluatePRSwap; // Go evaluate whether we are going to accept or reject the swap PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTVCONN_Swap: // If we get a VCONN_Swap message... PolicyState = peSourceEvaluateVCONNSwap; // Go evaluate whether we are going to accept or reject the swap PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTSoftReset: PolicyState = peSourceSoftReset; // Go to the soft reset state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; default: // Send a reject message for all other commands break; } } else // If we received a data message... for now just send a soft reset { switch (PolicyRxHeader.MessageType) { case DMTRequest: PolicyState = peSourceNegotiateCap; // If we've received a request object, go to the negotiate capabilities state break; #ifdef FSC_HAVE_VDM case DMTVenderDefined: convertAndProcessVdmMessage(ProtocolMsgRxSop); break; #endif // FSC_HAVE_VDM case DMTBIST: processDMTBIST(); break; default: // Otherwise we've received a message we don't know how to handle yet break; } PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } } else if (USBPDTxFlag) // Has the device policy manager requested us to send a message? { if (PDTransmitHeader.NumDataObjects == 0) { switch (PDTransmitHeader.MessageType) // Determine which command we need to send { case CMTGetSinkCap: PolicyState = peSourceGetSinkCaps; // Go to the get sink caps state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTGetSourceCap: PolicyState = peSourceGetSourceCaps; // Go to the get source caps state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTPing: PolicyState = peSourceSendPing; // Go to the send ping state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTGotoMin: PolicyState = peSourceGotoMin; // Go to the source goto min state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; #ifdef FSC_HAVE_DRP case CMTPR_Swap: PolicyState = peSourceSendPRSwap; // Issue a PR_Swap message PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; #endif // FSC_HAVE_DRP case CMTDR_Swap: PolicyState = peSourceSendDRSwap; // Issue a DR_Swap message PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTVCONN_Swap: PolicyState = peSourceSendVCONNSwap; // Issue a VCONN_Swap message PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTSoftReset: PolicyState = peSourceSendSoftReset; // Go to the soft reset state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; default: // Don't send any commands we don't know how to handle yet break; } } else { switch (PDTransmitHeader.MessageType) { case DMTSourceCapabilities: PolicyState = peSourceSendCaps; PolicySubIndex = 0; PDTxStatus = txIdle; break; case DMTVenderDefined: PolicySubIndex = 0; #ifdef FSC_HAVE_VDM doVdmCommand(); #endif // FSC_HAVE_VDM break; default: break; } } USBPDTxFlag = FALSE; } else if(PartnerCaps.object == 0) { PolicyState = peSourceGetSinkCaps; // Go to the get sink caps state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } #ifdef FSC_HAVE_VDM else if(PolicyIsDFP && (AutoVdmState != AUTO_VDM_DONE) #ifdef FSC_DEBUG && (GetUSBPDBufferNumBytes() == 0) #endif // FSC_DEBUG ) { autoVdmDiscovery(); } #endif // FSC_HAVE_VDM else { #ifdef FSC_INTERRUPT_TRIGGERED if(loopCounter == 0) { g_Idle = TRUE; // Idle until COMP or HARDRST or GCRCSENT platform_enable_timer(FALSE); } #endif // FSC_INTERRUPT_TRIGGERED } } void PolicySourceGiveSourceCap(void) { PolicySendData(DMTSourceCapabilities, CapsHeaderSource.NumDataObjects, &CapsSource[0], peSourceReady, 0, SOP_TYPE_SOP); } void PolicySourceGetSourceCap(void) { PolicySendCommand(CMTGetSourceCap, peSourceReady, 0); } void PolicySourceGetSinkCap(void) { switch (PolicySubIndex) { case 0: if (PolicySendCommand(CMTGetSinkCap, peSourceGetSinkCaps, 1) == STAT_SUCCESS) // Send the get sink caps command upon entering state PolicyStateTimer = tSenderResponse; // Start the sender response timer upon receiving the good CRC message break; default: if (ProtocolMsgRx) // If we have received a message { ProtocolMsgRx = FALSE; // Reset the message ready flag since we're handling it here if ((PolicyRxHeader.NumDataObjects > 0) && (PolicyRxHeader.MessageType == DMTSinkCapabilities)) { UpdateCapabilitiesRx(FALSE); PolicyState = peSourceReady; // Go onto the source ready state } else // If we didn't receive a valid sink capabilities message... { PolicyState = peSourceSendHardReset; // Go onto issuing a hard reset } PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Clear the transmitter status } else if (!PolicyStateTimer) // If we didn't get a response to our request before timing out... { PolicyState = peSourceSendHardReset; // Go to the hard reset state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; } } void PolicySourceGiveSinkCap(void) { #ifdef FSC_HAVE_DRP if (PortType == USBTypeC_DRP) PolicySendData(DMTSinkCapabilities, CapsHeaderSink.NumDataObjects, &CapsSink[0], peSourceReady, 0, SOP_TYPE_SOP); else #endif // FSC_HAVE_DRP PolicySendCommand(CMTReject, peSourceReady, 0); // Send the reject message and continue onto the ready state } void PolicySourceSendPing(void) { PolicySendCommand(CMTPing, peSourceReady, 0); } void PolicySourceGotoMin(void) { if (ProtocolMsgRx) { ProtocolMsgRx = FALSE; // Reset the message ready flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a control message... { switch(PolicyRxHeader.MessageType) // Determine the message type { case CMTSoftReset: PolicyState = peSourceSoftReset; // Go to the soft reset state if we received a reset command PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status break; default: // If we receive any other command (including Reject & Wait), just go back to the ready state without changing break; } } } else { switch (PolicySubIndex) { case 0: PolicySendCommand(CMTGotoMin, peSourceGotoMin, 1); // Send the GotoMin message break; case 1: PolicyStateTimer = tSrcTransition; // Initialize the timer to allow for the sink to transition PolicySubIndex++; // Increment to move to the next sub state break; case 2: if (!PolicyStateTimer) // If the timer has expired (the sink is ready)... PolicySubIndex++; // Increment to move to the next sub state break; case 3: // Adjust the power supply if necessary... PolicySubIndex++; // Increment to move to the next sub state break; case 4: // Validate the output is ready prior to sending the ready message PolicySubIndex++; // Increment to move to the next sub state break; default: PolicySendCommand(CMTPS_RDY, peSourceReady, 0); // Send the PS_RDY message and move onto the Source Ready state break; } } } void PolicySourceSendDRSwap(void) { FSC_U8 Status; switch (PolicySubIndex) { case 0: Status = PolicySendCommandNoReset(CMTDR_Swap, peSourceSendDRSwap, 1); // Send the DR_Swap message if (Status == STAT_SUCCESS) { // If we received the good CRC message... pr_info("FUSB %s: send data role swap, status(%d)\n", __func__, Status); PolicyStateTimer = tSenderResponse; // Initialize for SenderResponseTimer } else if (Status == STAT_ERROR) { // If there was an error... pr_err("FUSB %s: send data role swap, status(%d)\n", __func__, Status); PolicyState = peErrorRecovery; // Go directly to the error recovery state } break; default: if (ProtocolMsgRx) { ProtocolMsgRx = FALSE; // Reset the message ready flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a control message... { switch(PolicyRxHeader.MessageType) // Determine the message type { case CMTAccept: PolicyIsDFP = (PolicyIsDFP == TRUE) ? FALSE : TRUE; // Flip the current data role Registers.Switches.DATAROLE = PolicyIsDFP; // Update the data role DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); // Commit the data role in the 302 for the auto CRC PolicyState = peSourceReady; // Source ready state pr_info("FUSB %s: accept, PolicyIsDFP(%d)\n", __func__, PolicyIsDFP); platform_notify_attached_source(PolicyIsDFP, true); break; case CMTSoftReset: PolicyState = peSourceSoftReset; // Go to the soft reset state if we received a reset command break; default: // If we receive any other command (including Reject & Wait), just go back to the ready state without changing PolicyState = peSourceReady; // Go to the source ready state break; } } else // Otherwise we received a data message... { PolicyState = peSourceReady; // Go to the sink ready state if we received a unexpected data message (ignoring message) } PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } else if (PolicyStateTimer == 0) // If the sender response timer times out... { PolicyState = peSourceReady; // Go to the source ready state if the SenderResponseTimer times out PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } break; } } void PolicySourceEvaluateDRSwap(void) { FSC_U8 Status; #ifdef FSC_HAVE_VDM if (mode_entered == TRUE) // If were are in modal operation, send a hard reset { PolicyState = peSourceSendHardReset; PolicySubIndex = 0; return; } #endif // FSC_HAVE_VDM pr_info("FUSB %s: enter\n", __func__); Status = PolicySendCommandNoReset(CMTAccept, peSourceReady, 0); // Send the Accept message and wait for the good CRC if (Status == STAT_SUCCESS) // If we received the good CRC... { PolicyIsDFP = (PolicyIsDFP == TRUE) ? FALSE : TRUE; // We're not really doing anything except flipping the bit Registers.Switches.DATAROLE = PolicyIsDFP; // Update the data role DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); // Commit the data role in the 302 for the auto CRC pr_info("FUSB %s: accept, PolicyIsDFP(%d)\n", __func__, PolicyIsDFP); platform_notify_attached_source(PolicyIsDFP, true); } else if (Status == STAT_ERROR) // If we didn't receive the good CRC... { PolicyState = peErrorRecovery; // Go to the error recovery state PolicySubIndex = 0; // Clear the sub-index PDTxStatus = txIdle; // Clear the transmitter status } } void PolicySourceSendVCONNSwap(void) { switch(PolicySubIndex) { case 0: if (PolicySendCommand(CMTVCONN_Swap, peSourceSendVCONNSwap, 1) == STAT_SUCCESS) // Send the VCONN_Swap message and wait for the good CRC PolicyStateTimer = tSenderResponse; // Once we receive the good CRC, set the sender response timer break; case 1: if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTAccept: // If we get the Accept message... PolicySubIndex++; // Increment the subindex to move onto the next step break; case CMTWait: // If we get either the reject or wait message... case CMTReject: PolicyState = peSourceReady; // Go back to the source ready state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the SenderResponseTimer times out... { PolicyState = peSourceReady; // Go back to the source ready state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; case 2: if (IsVCONNSource) // If we are currently sourcing VCONN... { PolicyStateTimer = tVCONNSourceOn; // Enable the VCONNOnTimer and wait for a PS_RDY message PolicySubIndex++; // Increment the subindex to move to waiting for a PS_RDY message } else // Otherwise we need to start sourcing VCONN { platform_set_vconn_enable(TRUE); if (blnCCPinIsCC1) // If the CC pin is CC1... Registers.Switches.VCONN_CC2 = 1; // Enable VCONN for CC2 else // Otherwise the CC pin is CC2 Registers.Switches.VCONN_CC1 = 1; // so enable VCONN on CC1 DeviceWrite(regSwitches0, 1, &Registers.Switches.byte[0]); // Commit the register setting to the device IsVCONNSource = TRUE; PolicyStateTimer = VbusTransitionTime; // Allow time for the FPF2498 to enable... PolicySubIndex = 4; // Skip the next state and move onto sending the PS_RDY message after the timer expires } } break; case 3: if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTPS_RDY: // If we get the PS_RDY message... Registers.Switches.VCONN_CC1 = 0; // Disable the VCONN source Registers.Switches.VCONN_CC2 = 0; // Disable the VCONN source DeviceWrite(regSwitches0, 1, &Registers.Switches.byte[0]); // Commit the register setting to the device platform_set_vconn_enable(FALSE); IsVCONNSource = FALSE; PolicyState = peSourceReady; // Move onto the Sink Ready state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the VCONNOnTimer times out... { PolicyState = peSourceSendHardReset; // Issue a hard reset PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; default: if (!PolicyStateTimer) { PolicySendCommand(CMTPS_RDY, peSourceReady, 0); // Send the Accept message and wait for the good CRC } break; } } void PolicySourceSendPRSwap(void) { #ifdef FSC_HAVE_DRP FSC_U8 Status; switch(PolicySubIndex) { case 0: // Send the PRSwap command pr_info("FUSB %s: send PR_Swap command\n", __func__); if (PolicySendCommand(CMTPR_Swap, peSourceSendPRSwap, 1) == STAT_SUCCESS) // Send the PR_Swap message and wait for the good CRC PolicyStateTimer = tSenderResponse; // Once we receive the good CRC, set the sender response timer break; case 1: // Require Accept message to move on or go back to ready state if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTAccept: // If we get the Accept message... IsPRSwap = TRUE; pr_debug("FUSB %s(%d): IsPRSwap=%d\n", __func__, __LINE__, IsPRSwap); PolicyHasContract = FALSE; platform_notify_pd_contract(FALSE); PRSwapTimer = tPRSwapBailout; // Initialize the PRSwapTimer to indicate we are in the middle of a swap PolicyStateTimer = tSrcTransition; // Start the sink transition timer PolicySubIndex++; // Increment the subindex to move onto the next step break; case CMTWait: // If we get either the reject or wait message... case CMTReject: PolicyState = peSourceReady; // Go back to the source ready state PolicySubIndex = 0; // Clear the sub index IsPRSwap = FALSE; pr_debug("FUSB %s(%d): IsPRSwap=%d\n", __func__, __LINE__, IsPRSwap); PDTxStatus = txIdle; // Clear the transmitter status break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the SenderResponseTimer times out... { PolicyState = peSourceReady; // Go back to the source ready state PolicySubIndex = 0; // Clear the sub index IsPRSwap = FALSE; pr_debug("FUSB %s(%d): IsPRSwap=%d\n", __func__, __LINE__, IsPRSwap); PDTxStatus = txIdle; // Clear the transmitter status } break; case 2: // Wait for tSrcTransition and then turn off power (and Rd on/Rp off) if (!PolicyStateTimer) { platform_set_vbus_lvl_enable(VBUS_LVL_ALL, FALSE, FALSE); // Disable VBUS output platform_set_vbus_discharge(TRUE); // Enabled VBUS discharge path PolicySubIndex++; // Increment the sub-index to move onto the next state } break; case 3: if (VbusVSafe0V()) { RoleSwapToAttachedSink(); platform_set_vbus_discharge(FALSE); // Disable VBUS discharge path PolicyIsSource = FALSE; Registers.Switches.POWERROLE = PolicyIsSource; DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); PolicySubIndex++; } break; case 4: // Allow time for the supply to fall and then send the PS_RDY message Status = PolicySendCommandNoReset(CMTPS_RDY, peSourceSendPRSwap, 5); pr_debug("FUSB %s PS_READY\n", __func__); if (Status == STAT_SUCCESS) // If we successfully sent the PS_RDY command and received the goodCRC PolicyStateTimer = tPSSourceOn; // Start the PSSourceOn timer to allow time for the new supply to come up else if (Status == STAT_ERROR) PolicyState = peErrorRecovery; // If we get an error, go to the error recovery state break; case 5: // Wait to receive a PS_RDY message from the new DFP if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTPS_RDY: // If we get the PS_RDY message... PolicySubIndex++; // Clear the sub index PolicyStateTimer = tGoodCRCDelay; // Make sure GoodCRC has time to send break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the PSSourceOn times out... { PolicyState = peErrorRecovery; // Go to the error recovery state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; default: if (PolicyStateTimer == 0) { PolicyState = peSinkStartup; // Go to the sink startup state PolicySubIndex = 0; // Clear the sub index PolicyStateTimer = 0; // Make sure GoodCRC has time to send } break; } #endif // FSC_HAVE_DRP } void PolicySourceEvaluatePRSwap(void) { #ifdef FSC_HAVE_DRP FSC_U8 Status; switch(PolicySubIndex) { case 0: // Send either the Accept or Reject command pr_info("FUSB %s: Source.DRPower=%d, Sink.DRPower=%d, Source.Ext=%d, Sink.Ext=%d, Sink.ST=%d", __func__, CapsSource[0].FPDOSupply.DualRolePower, PartnerCaps.FPDOSink.DualRolePower, CapsSource[0].FPDOSupply.ExternallyPowered, PartnerCaps.FPDOSink.ExternallyPowered, PartnerCaps.FPDOSink.SupplyType); if ((CapsSource[0].FPDOSupply.DualRolePower == FALSE) || // Determine Accept/Reject based on DualRolePower bit in current PDO ((CapsSource[0].FPDOSupply.ExternallyPowered == TRUE) && // Must also reject if we are externally powered and partner is not (PartnerCaps.FPDOSink.SupplyType == pdoTypeFixed) && (PartnerCaps.FPDOSink.ExternallyPowered == FALSE))) { PolicySendCommand(CMTReject, peSourceReady, 0); // Send the reject if we are not a DRP } else { if (PolicySendCommand(CMTAccept, peSourceEvaluatePRSwap, 1) == STAT_SUCCESS) // Send the Accept message and wait for the good CRC { IsPRSwap = TRUE; pr_debug("FUSB %s(%d): IsPRSwap=%d\n", __func__, __LINE__, IsPRSwap); PolicyHasContract = FALSE; platform_notify_pd_contract(FALSE); RoleSwapToAttachedSink(); PolicyStateTimer = tSrcTransition; } } break; case 1: if(PolicyStateTimer == 0) { platform_set_vbus_lvl_enable(VBUS_LVL_ALL, FALSE, FALSE); // Disable VBUS output platform_set_vbus_discharge(TRUE); // Enabled VBUS discharge path PolicySubIndex++; } break; case 2: if (VbusVSafe0V()) // Allow time for the supply to fall and then send the PS_RDY message { PolicyStateTimer = tSrcTransition; // Allow some extra time for VBUS to discharge PolicyIsSource = FALSE; Registers.Switches.POWERROLE = PolicyIsSource; DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); PolicySubIndex++; } break; case 3: if(PolicyStateTimer == 0) { platform_set_vbus_discharge(FALSE); // Disable VBUS discharge path PolicySubIndex++; } break; case 4: Status = PolicySendCommandNoReset(CMTPS_RDY, peSourceEvaluatePRSwap, 5); // Send the PS_RDY message if (Status == STAT_SUCCESS) // If we successfully sent the PS_RDY command and received the goodCRC PolicyStateTimer = tPSSourceOn; // Start the PSSourceOn timer to allow time for the new supply to come up else if (Status == STAT_ERROR) PolicyState = peErrorRecovery; // If we get an error, go to the error recovery state break; case 5: // Wait to receive a PS_RDY message from the new DFP if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTPS_RDY: // If we get the PS_RDY message... pr_debug("FUSB %s: receive PS_RDY\n", __func__); PolicySubIndex++; // Increment the sub index PolicyStateTimer = tGoodCRCDelay; // Make sure GoodCRC has time to send break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the PSSourceOn times out... { PolicyState = peSourceSendHardReset; // Go to the error recovery state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; default: if (PolicyStateTimer == 0) { PolicyState = peSinkStartup; // Go to the sink startup state PolicySubIndex = 0; // Clear the sub index PolicyStateTimer = 0; } break; } #else PolicySendCommand(CMTReject, peSourceReady, 0); // Send the reject if we are not a DRP #endif // FSC_HAVE_DRP } void PolicySourceWaitNewCapabilities(void) // TODO: DPM integration { #ifdef FSC_INTERRUPT_TRIGGERED if(loopCounter == 0) { g_Idle = TRUE; // Wait for COMP or HARDRST or GCRCSENT Registers.Mask.M_COMP_CHNG = 0; DeviceWrite(regMask, 1, &Registers.Mask.byte); Registers.MaskAdv.M_HARDRST = 0; DeviceWrite(regMaska, 1, &Registers.MaskAdv.byte[0]); Registers.MaskAdv.M_GCRCSENT = 0; DeviceWrite(regMaskb, 1, &Registers.MaskAdv.byte[1]); platform_enable_timer(FALSE); } #endif // FSC_INTERRUPT_TRIGGERED switch(PolicySubIndex) { case 0: // Wait for Policy Manager to change source capabilities break; default: // Transition to peSourceSendCapabilities PolicyState = peSourceSendCaps; PolicySubIndex = 0; break; } } #endif // FSC_HAVE_SRC void PolicySourceEvaluateVCONNSwap(void) { switch(PolicySubIndex) { case 0: pr_info("FUSB %s: accept vconn swap, IsVCONNSource=%d\n", __func__, IsVCONNSource); PolicySendCommand(CMTAccept, peSourceEvaluateVCONNSwap, 1); // Send the Accept message and wait for the good CRC break; case 1: if (IsVCONNSource) // If we are currently sourcing VCONN... { PolicyStateTimer = tVCONNSourceOn; // Enable the VCONNOnTimer and wait for a PS_RDY message PolicySubIndex++; // Increment the subindex to move to waiting for a PS_RDY message } else // Otherwise we need to start sourcing VCONN { platform_set_vconn_enable(TRUE); if (blnCCPinIsCC1) // If the CC pin is CC1... { Registers.Switches.VCONN_CC2 = 1; // Enable VCONN for CC2 Registers.Switches.PDWN2 = 0; // Disable the pull-down on CC2 to avoid sinking unnecessary current } else // Otherwise the CC pin is CC2 { Registers.Switches.VCONN_CC1 = 1; // Enable VCONN for CC1 Registers.Switches.PDWN1 = 0; // Disable the pull-down on CC1 to avoid sinking unnecessary current } DeviceWrite(regSwitches0, 1, &Registers.Switches.byte[0]); // Commit the register setting to the device IsVCONNSource = TRUE; PolicyStateTimer = VbusTransitionTime; // Allow time for the FPF2498 to enable... PolicySubIndex = 3; // Skip the next state and move onto sending the PS_RDY message after the timer expires } } break; case 2: if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTPS_RDY: // If we get the PS_RDY message... Registers.Switches.VCONN_CC1 = 0; // Disable the VCONN source Registers.Switches.VCONN_CC2 = 0; // Disable the VCONN source Registers.Switches.PDWN1 = 1; // Ensure the pull-down on CC1 is enabled Registers.Switches.PDWN2 = 1; // Ensure the pull-down on CC2 is enabled DeviceWrite(regSwitches0, 1, &Registers.Switches.byte[0]); // Commit the register setting to the device platform_set_vconn_enable(FALSE); IsVCONNSource = FALSE; PolicyState = peSourceReady; // Move onto the Sink Ready state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the VCONNOnTimer times out... { PolicyState = peSourceSendHardReset; // Issue a hard reset PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; default: if (!PolicyStateTimer) { PolicySendCommand(CMTPS_RDY, peSourceReady, 0); // Send the Accept message and wait for the good CRC } break; } } // ############################## Sink States ############################## // #ifdef FSC_HAVE_SNK void PolicySinkSendHardReset(void) { IsHardReset = TRUE; PolicySendHardReset(peSinkTransitionDefault, 0); } void PolicySinkSoftReset(void) { if (PolicySendCommand(CMTAccept, peSinkWaitCaps, 0) == STAT_SUCCESS) PolicyStateTimer = tSinkWaitCap; } void PolicySinkSendSoftReset(void) { switch (PolicySubIndex) { case 0: if (PolicySendCommand(CMTSoftReset, peSinkSendSoftReset, 1) == STAT_SUCCESS) // Send the soft reset command to the protocol layer { PolicyStateTimer = tSenderResponse; // Start the sender response timer to wait for an accept message once successfully sent } break; default: if (ProtocolMsgRx) // If we have received a message { ProtocolMsgRx = FALSE; // Reset the message received flag since we've handled it here if ((PolicyRxHeader.NumDataObjects == 0) && (PolicyRxHeader.MessageType == CMTAccept)) // And it was the Accept... { PolicyState = peSinkWaitCaps; // Go to the wait for capabilities state PolicyStateTimer = tSinkWaitCap; // Set the state timer to tSinkWaitCap } else // Otherwise it was a message that we didn't expect, so... PolicyState = peSinkSendHardReset; // Go to the hard reset state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Clear the transmitter status } else if (!PolicyStateTimer) // If we didn't get a response to our request before timing out... { PolicyState = peSinkSendHardReset; // Go to the hard reset state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; } } void PolicySinkTransitionDefault(void) { switch (PolicySubIndex) { case 0: IsHardReset = TRUE; PolicyHasContract = FALSE; platform_notify_pd_contract(FALSE); Registers.Switches.AUTO_CRC = 0; // turn off Auto CRC DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); PolicyStateTimer = tPSHardResetMax + tSafe0V; // Timeout wait for vSafe0V NoResponseTimer = tNoResponse; // Initialize the no response timer if(PolicyIsDFP) // Make sure data role is UFP { PolicyIsDFP = FALSE; // Set the current data role Registers.Switches.DATAROLE = PolicyIsDFP; // Update the data role DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); // Commit the data role in the 302 platform_notify_attached_source(PolicyIsDFP, true); } if(IsVCONNSource) // Disable VCONN if VCONN Source { Registers.Switches.VCONN_CC1 = 0; Registers.Switches.VCONN_CC2 = 0; DeviceWrite(regSwitches0, 1, &Registers.Switches.byte[0]); platform_set_vconn_enable(FALSE); IsVCONNSource = FALSE; } PolicySubIndex++; break; case 1: if(VbusVSafe0V()) { PolicySubIndex++; PolicyStateTimer = tSrcRecoverMax + tSrcTurnOn; // Timeout wait for vSafe5V } else if (PolicyStateTimer == 0) // Break out if we never see 0V { if(PolicyHasContract) { PolicyState = peErrorRecovery; PolicySubIndex = 0; } else { PolicyState = peSinkStartup; PolicySubIndex = 0; PolicyStateTimer = 0; } } break; case 2: if(isVBUSOverVoltage(VBUS_MDAC_4P20)) { PolicySubIndex++; } else if (PolicyStateTimer == 0) // Break out if we never see 0V { if(PolicyHasContract) { PolicyState = peErrorRecovery; PolicySubIndex = 0; } else { PolicyState = peSinkStartup; PolicySubIndex = 0; PolicyStateTimer = 0; } } break; default: PolicyState = peSinkStartup; // Go to the startup state PolicySubIndex = 0; // Clear the sub index PolicyStateTimer = 0; PDTxStatus = txIdle; // Reset the transmitter status break; } } void PolicySinkStartup(void) { #ifdef FSC_HAVE_VDM FSC_S32 i; #endif // FSC_HAVE_VDM // Set masks for PD Registers.Mask.M_COLLISION = 0; DeviceWrite(regMask, 1, &Registers.Mask.byte); Registers.MaskAdv.M_RETRYFAIL = 0; Registers.MaskAdv.M_TXSENT = 0; Registers.MaskAdv.M_HARDRST = 0; DeviceWrite(regMaska, 1, &Registers.MaskAdv.byte[0]); Registers.MaskAdv.M_GCRCSENT = 0; DeviceWrite(regMaskb, 1, &Registers.MaskAdv.byte[1]); if(Registers.DeviceID.VERSION_ID == VERSION_302B) { if(Registers.Control.BIST_TMODE == 1) { Registers.Control.BIST_TMODE = 0; // Disable auto-flush RxFIFO DeviceWrite(regControl3, 1, &Registers.Control.byte[3]); } } else { if(Registers.Control.RX_FLUSH == 1) // Disable Rx flushing if it has been enabled { Registers.Control.RX_FLUSH = 0; DeviceWrite(regControl1, 1, &Registers.Control.byte[1]); } } USBPDContract.object = 0; // Clear the USB PD contract (output power to 5V default) PartnerCaps.object = 0; // Clear partner sink caps IsPRSwap = FALSE; pr_debug("FUSB %s(%d): IsPRSwap=%d\n", __func__, __LINE__, IsPRSwap); PolicyIsSource = FALSE; // Clear the flag to indicate that we are a sink (for PRSwaps) Registers.Switches.POWERROLE = PolicyIsSource; DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); Registers.Switches.AUTO_CRC = 1; // turn on Auto CRC DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); ResetProtocolLayer(TRUE); // Reset the protocol layer CapsCounter = 0; // Clear the caps counter CollisionCounter = 0; // Reset the collision counter PolicyStateTimer = 0; // Reset the policy state timer PolicyState = peSinkDiscovery; // Go to the sink discovery state PolicySubIndex = 0; // Reset the sub index #ifdef FSC_HAVE_VDM AutoVdmState = AUTO_VDM_INIT; auto_mode_disc_tracker = 0; mode_entered = FALSE; core_svid_info.num_svids = 0; for (i = 0; i < MAX_NUM_SVIDS; i++) { core_svid_info.svids[i] = 0; } #endif // FSC_HAVE_VDM #ifdef FSC_HAVE_DP AutoDpModeEntryObjPos = -1; resetDp(); #endif // FSC_HAVE_DP } void PolicySinkDiscovery(void) { IsHardReset = FALSE; PRSwapTimer = 0; // Clear the swap timer PolicyState = peSinkWaitCaps; PolicySubIndex = 0; PolicyStateTimer = tTypeCSinkWaitCap; } void PolicySinkWaitCaps(void) { if (ProtocolMsgRx) // If we have received a message... { ProtocolMsgRx = FALSE; // Reset the message ready flag since we're handling it here if ((PolicyRxHeader.NumDataObjects > 0) && (PolicyRxHeader.MessageType == DMTSourceCapabilities)) // Have we received a valid source cap message? { UpdateCapabilitiesRx(TRUE); // Update the received capabilities PolicyState = peSinkEvaluateCaps; // Set the evaluate source capabilities state } else if ((PolicyRxHeader.NumDataObjects == 0) && (PolicyRxHeader.MessageType == CMTSoftReset)) { PolicyState = peSinkSoftReset; // Go to the soft reset state } PolicySubIndex = 0; // Reset the sub index } else if ((PolicyHasContract == TRUE) && (NoResponseTimer == 0) && (HardResetCounter > nHardResetCount)) { PolicyState = peErrorRecovery; PolicySubIndex = 0; } else if ((PolicyStateTimer == 0) && (HardResetCounter <= nHardResetCount)) { PolicyState = peSinkSendHardReset; PolicySubIndex = 0; } else if ((PolicyHasContract == FALSE) && (NoResponseTimer == 0) && (HardResetCounter > nHardResetCount)) { #ifdef FSC_INTERRUPT_TRIGGERED g_Idle = TRUE; // Wait for VBUSOK or HARDRST or GCRCSENT platform_enable_timer(FALSE); #endif // FSC_INTERRUPT_TRIGGERED } } extern FSC_S32 platform_select_source_capability(u8 obj_cnt, doDataObject_t pd_data[7], int *device_max_ma); void PolicySinkEvaluateCaps(void) { // Due to latency with the PC and evaluating capabilities, we are always going to select the first one by default (5V default) // This will allow the software time to determine if they want to select one of the other capabilities (user selectable) // If we want to automatically show the selection of a different capabilities message, we need to build in the functionality here // The evaluate caps FSC_S32 i, reqPos; FSC_S32 device_max_ma = 0; FSC_U32 objVoltage = 0; FSC_U32 objCurrent, objPower, MaxPower, SelVoltage, ReqCurrent; objCurrent = 0; NoResponseTimer = T_TIMER_DISABLE; // Stop the no response timer HardResetCounter = 0; // Reset the hard reset counter // Indicate Hard Reset is over SelVoltage = 0; MaxPower = 0; reqPos = 0; // Select nothing in case there is an error... for (i=0; i SinkRequestMaxVoltage) // If the voltage is greater than our limit... objPower = 0; // Set the power to zero to ignore the object else // Otherwise... { objCurrent = CapsReceived[i].FPDOSupply.MaxCurrent; objPower = objVoltage * objCurrent; // Calculate the power for comparison } break; case pdoTypeVariable: objVoltage = CapsReceived[i].VPDO.MaxVoltage; // Grab the maximum voltage of the variable supply if (objVoltage > SinkRequestMaxVoltage) // If the max voltage is greater than our limit... objPower = 0; // Set the power to zero to ignore the object else // Otherwise... { objVoltage = CapsReceived[i].VPDO.MinVoltage; // Get the minimum output voltage of the variable supply objCurrent = CapsReceived[i].VPDO.MaxCurrent; // Get the maximum output current of the variable supply objPower = objVoltage * objCurrent; // Calculate the power for comparison (based on min V/max I) } break; case pdoTypeBattery: // We are going to ignore battery powered sources for now default: // We are also ignoring undefined supply types objPower = 0; // Set the object power to zero so we ignore for now break; } if (objPower >= MaxPower) // If the current object has power greater than or equal the previous objects { MaxPower = objPower; // Store the objects power SelVoltage = objVoltage; // Store the objects voltage (used for calculations) reqPos = i + 1; // Store the position of the object } } reqPos = platform_select_source_capability(CapsHeaderReceived.NumDataObjects, CapsReceived, &device_max_ma); if (reqPos >= 0) { if (CapsReceived[reqPos].PDO.SupplyType == pdoTypeFixed) SelVoltage = CapsReceived[reqPos].FPDOSupply.Voltage; else if (CapsReceived[reqPos].PDO.SupplyType == pdoTypeVariable) { SelVoltage = CapsReceived[reqPos].VPDO.MinVoltage; objCurrent = CapsReceived[reqPos].VPDO.MaxCurrent; } reqPos++; } pr_debug("FUSB %s:cnt = %d, reqPos = %d, SelVoltage = %d, device_max_ma = %d after selection\n", __func__, CapsHeaderReceived.NumDataObjects, reqPos, SelVoltage, device_max_ma); if ((reqPos > 0) && (SelVoltage > 0)) { PartnerCaps.object = CapsReceived[0].object; SinkRequest.FVRDO.ObjectPosition = reqPos & 0x07; // Set the object position selected SinkRequest.FVRDO.GiveBack = SinkGotoMinCompatible; // Set whether we will respond to the GotoMin message SinkRequest.FVRDO.NoUSBSuspend = SinkUSBSuspendOperation; // Set whether we want to continue pulling power during USB Suspend SinkRequest.FVRDO.USBCommCapable = SinkUSBCommCapable; // Set whether USB communications is active ReqCurrent = device_max_ma / 10; SinkRequest.FVRDO.OpCurrent = (ReqCurrent & 0x3FF); // Set the current based on the selected voltage (in 10mA units) SinkRequest.FVRDO.MinMaxCurrent = (ReqCurrent & 0x3FF); // Set the min/max current based on the selected voltage (in 10mA units) if (SinkGotoMinCompatible) // If the give back flag is set... SinkRequest.FVRDO.CapabilityMismatch = FALSE; // There can't be a capabilities mismatch else // Otherwise... { if (SelVoltage * ReqCurrent != SinkRequestMaxPower) { SinkRequest.FVRDO.CapabilityMismatch = TRUE; // flag the source that we need more power } else // Otherwise... { SinkRequest.FVRDO.CapabilityMismatch = FALSE; // there is no mismatch in the capabilities } } PolicyState = peSinkSelectCapability; // Go to the select capability state PolicySubIndex = 0; // Reset the sub index PolicyStateTimer = tSenderResponse; // Initialize the sender response timer } else { // For now, we are just going to go back to the wait state instead of sending a reject or reset (may change in future) PolicyState = peSinkWaitCaps; // Go to the wait for capabilities state PolicyStateTimer = tTypeCSinkWaitCap; // Set the state timer to tSinkWaitCap } } void PolicySinkSelectCapability(void) { switch (PolicySubIndex) { case 0: if (PolicySendData(DMTRequest, 1, &SinkRequest, peSinkSelectCapability, 1, SOP_TYPE_SOP) == STAT_SUCCESS) { NoResponseTimer = tSenderResponse; // If there is a good CRC start retry timer PolicyStateTimer = tSenderResponse; // re-Initialize the sender response timer } break; case 1: if (ProtocolMsgRx) { ProtocolMsgRx = FALSE; // Reset the message ready flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a control message... { switch(PolicyRxHeader.MessageType) // Determine the message type { case CMTAccept: PolicyHasContract = TRUE; // Set the flag to indicate that a contract is in place USBPDContract.object = SinkRequest.object; // Set the actual contract that is in place PolicyStateTimer = tPSTransition; // Set the transition timer here PolicyState = peSinkTransitionSink; // Go to the transition state if the source accepted the request break; case CMTWait: case CMTReject: if(PolicyHasContract) { PolicyState = peSinkReady; // Go to the sink ready state if the source rejects or has us wait } else { PolicyState = peSinkWaitCaps; // If we didn't have a contract, go wait for new source caps HardResetCounter = nHardResetCount + 1; // Make sure we don't send hard reset to prevent infinite loop } break; case CMTSoftReset: PolicyState = peSinkSoftReset; // Go to the soft reset state if we received a reset command break; default: PolicyState = peSinkSendSoftReset; // We are going to send a reset message for all other commands received break; } } else // Otherwise we received a data message... { switch (PolicyRxHeader.MessageType) { case DMTSourceCapabilities: // If we received a new source capabilities message UpdateCapabilitiesRx(TRUE); // Update the received capabilities PolicyState = peSinkEvaluateCaps; // Go to the evaluate caps state break; default: PolicyState = peSinkSendSoftReset; // Send a soft reset to get back to a known state break; } } PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } else if (PolicyStateTimer == 0) // If the sender response timer times out... { PolicyState = peSinkSendHardReset; // Go to the hard reset state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } break; } } void PolicySinkTransitionSink(void) { if (ProtocolMsgRx) { ProtocolMsgRx = FALSE; // Reset the message ready flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a control message... { switch(PolicyRxHeader.MessageType) // Determine the message type { case CMTPS_RDY: PolicyState = peSinkReady; // Go to the ready state platform_notify_pd_contract(TRUE); break; case CMTSoftReset: PolicyState = peSinkSoftReset; // Go to the soft reset state if we received a reset command break; default: PolicyState = peSinkSendSoftReset; // We are going to send a reset message for all other commands received break; } } else // Otherwise we received a data message... { switch (PolicyRxHeader.MessageType) // Determine the message type { case DMTSourceCapabilities: // If we received new source capabilities... UpdateCapabilitiesRx(TRUE); // Update the source capabilities PolicyState = peSinkEvaluateCaps; // And go to the evaluate capabilities state break; default: // If we receieved an unexpected data message... PolicyState = peSinkSendSoftReset; // Send a soft reset to get back to a known state break; } } PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } else if (PolicyStateTimer == 0) // If the PSTransitionTimer times out... { PolicyState = peSinkSendHardReset; // Issue a hard reset PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } } void PolicySinkReady(void) { if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTGotoMin: PolicyState = peSinkTransitionSink; // Go to transitioning the sink power PolicyStateTimer = tPSTransition; // Set the transition timer here PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTGetSinkCap: PolicyState = peSinkGiveSinkCap; // Go to sending the sink caps state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTGetSourceCap: PolicyState = peSinkGiveSourceCap; // Go to sending the source caps if we are dual-role PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTDR_Swap: // If we get a DR_Swap message... PolicyState = peSinkEvaluateDRSwap; // Go evaluate whether we are going to accept or reject the swap PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTPR_Swap: PolicyState = peSinkEvaluatePRSwap; // Go evaluate whether we are going to accept or reject the swap PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTVCONN_Swap: // If we get a VCONN_Swap message... PolicyState = peSinkEvaluateVCONNSwap; // Go evaluate whether we are going to accept or reject the swap PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTSoftReset: PolicyState = peSinkSoftReset; // Go to the soft reset state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; default: // For all other commands received, simply ignore them PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status break; } } else { switch (PolicyRxHeader.MessageType) { case DMTSourceCapabilities: UpdateCapabilitiesRx(TRUE); // Update the received capabilities PolicyState = peSinkEvaluateCaps; // Go to the evaluate capabilities state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; #ifdef FSC_HAVE_VDM case DMTVenderDefined: convertAndProcessVdmMessage(ProtocolMsgRxSop); break; #endif // FSC_HAVE_VDM case DMTBIST: processDMTBIST(); break; default: // If we get something we are not expecting... simply ignore them PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status break; } } } else if (USBPDTxFlag) // Has the device policy manager requested us to send a message? { if (PDTransmitHeader.NumDataObjects == 0) { switch (PDTransmitHeader.MessageType) { case CMTGetSourceCap: PolicyState = peSinkGetSourceCap; // Go to retrieve the source caps state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTGetSinkCap: PolicyState = peSinkGetSinkCap; // Go to retrieve the sink caps state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; case CMTDR_Swap: PolicyState = peSinkSendDRSwap; // Issue a DR_Swap message PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; #ifdef FSC_HAVE_DRP case CMTPR_Swap: PolicyState = peSinkSendPRSwap; // Issue a PR_Swap message PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; #endif // FSC_HAVE_DRP case CMTSoftReset: PolicyState = peSinkSendSoftReset; // Go to the send soft reset state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; default: break; } } else { switch (PDTransmitHeader.MessageType) { case DMTRequest: SinkRequest.object = PDTransmitObjects[0].object; // Set the actual object to request PolicyState = peSinkSelectCapability; // Go to the select capability state PolicySubIndex = 0; // Reset the sub index PolicyStateTimer = tSenderResponse; // Initialize the sender response timer break; case DMTVenderDefined: #ifdef FSC_HAVE_VDM doVdmCommand(); #endif // FSC_HAVE_VDM break; default: break; } } USBPDTxFlag = FALSE; } #ifdef FSC_HAVE_VDM else if (PolicyIsDFP && (AutoVdmState != AUTO_VDM_DONE) #ifdef FSC_DEBUG && (GetUSBPDBufferNumBytes() == 0) #endif // FSC_DEBUG ) { autoVdmDiscovery(); } #endif // FSC_HAVE_VDM else { #ifdef FSC_INTERRUPT_TRIGGERED g_Idle = TRUE; // Wait for VBUSOK or HARDRST or GCRCSENT platform_enable_timer(FALSE); #endif // FSC_INTERRUPT_TRIGGERED } } void PolicySinkGiveSinkCap(void) { PolicySendData(DMTSinkCapabilities, CapsHeaderSink.NumDataObjects, &CapsSink[0], peSinkReady, 0, SOP_TYPE_SOP); } void PolicySinkGetSinkCap(void) { PolicySendCommand(CMTGetSinkCap, peSinkReady, 0); } void PolicySinkGiveSourceCap(void) { #ifdef FSC_HAVE_DRP if (PortType == USBTypeC_DRP) PolicySendData(DMTSourceCapabilities, CapsHeaderSource.NumDataObjects, &CapsSource[0], peSinkReady, 0, SOP_TYPE_SOP); else #endif // FSC_HAVE_DRP PolicySendCommand(CMTReject, peSinkReady, 0); // Send the reject message and continue onto the ready state } void PolicySinkGetSourceCap(void) { PolicySendCommand(CMTGetSourceCap, peSinkReady, 0); } void PolicySinkSendDRSwap(void) { FSC_U8 Status; switch (PolicySubIndex) { case 0: Status = PolicySendCommandNoReset(CMTDR_Swap, peSinkSendDRSwap, 1); // Send the DR_Swap command if (Status == STAT_SUCCESS) { // If we received a good CRC message... pr_info("FUSB %s: send data role swap, status(%d)\n", __func__, Status); PolicyStateTimer = tSenderResponse; // Initialize for SenderResponseTimer if we received the GoodCRC } else if (Status == STAT_ERROR) { // If there was an error... pr_err("FUSB %s: send data role swap, status(%d)\n", __func__, Status); PolicyState = peErrorRecovery; // Go directly to the error recovery state } break; default: if (ProtocolMsgRx) { ProtocolMsgRx = FALSE; // Reset the message ready flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a control message... { switch(PolicyRxHeader.MessageType) // Determine the message type { case CMTAccept: PolicyIsDFP = (PolicyIsDFP == TRUE) ? FALSE : TRUE; // Flip the current data role Registers.Switches.DATAROLE = PolicyIsDFP; // Update the data role DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); // Commit the data role in the 302 for the auto CRC PolicyState = peSinkReady; // Sink ready state pr_info("FUSB %s: accept, PolicyIsDFP(%d)\n", __func__, PolicyIsDFP); platform_notify_attached_source(PolicyIsDFP, true); break; case CMTSoftReset: PolicyState = peSinkSoftReset; // Go to the soft reset state if we received a reset command break; default: // If we receive any other command (including Reject & Wait), just go back to the ready state without changing PolicyState = peSinkReady; // Go to the sink ready state break; } } else // Otherwise we received a data message... { PolicyState = peSinkReady; // Go to the sink ready state if we received a unexpected data message (ignoring message) } PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } else if (PolicyStateTimer == 0) // If the sender response timer times out... { PolicyState = peSinkReady; // Go to the sink ready state if the SenderResponseTimer times out PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } break; } } void PolicySinkEvaluateDRSwap(void) { FSC_U8 Status; #ifdef FSC_HAVE_VDM if (mode_entered == TRUE) // If were are in modal operation, send a hard reset { PolicyState = peSinkSendHardReset; PolicySubIndex = 0; return; } #endif // FSC_HAVE_VDM pr_info("FUSB %s: enter\n", __func__); Status = PolicySendCommandNoReset(CMTAccept, peSinkReady, 0); // Send the Accept message and wait for the good CRC if (Status == STAT_SUCCESS) // If we received the good CRC... { PolicyIsDFP = (PolicyIsDFP == TRUE) ? FALSE : TRUE; // We're not really doing anything except flipping the bit Registers.Switches.DATAROLE = PolicyIsDFP; // Update the data role DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); // Commit the data role in the 302 for the auto CRC pr_info("FUSB %s: accept, PolicyIsDFP(%d)\n", __func__, PolicyIsDFP); platform_notify_attached_source(PolicyIsDFP, true); } else if (Status == STAT_ERROR) // If we didn't receive the good CRC... { PolicyState = peErrorRecovery; // Go to the error recovery state PolicySubIndex = 0; // Clear the sub-index PDTxStatus = txIdle; // Clear the transmitter status } } void PolicySinkEvaluateVCONNSwap(void) { switch(PolicySubIndex) { case 0: pr_info("FUSB %s: accept vconn swap, IsVCONNSource=%d\n", __func__, IsVCONNSource); PolicySendCommand(CMTAccept, peSinkEvaluateVCONNSwap, 1); // Send the Accept message and wait for the good CRC break; case 1: if (IsVCONNSource) // If we are currently sourcing VCONN... { PolicyStateTimer = tVCONNSourceOn; // Enable the VCONNOnTimer and wait for a PS_RDY message PolicySubIndex++; // Increment the subindex to move to waiting for a PS_RDY message } else // Otherwise we need to start sourcing VCONN { platform_set_vconn_enable(TRUE); if (blnCCPinIsCC1) // If the CC pin is CC1... { Registers.Switches.VCONN_CC2 = 1; // Enable VCONN for CC2 Registers.Switches.PDWN2 = 0; // Disable the pull-down on CC2 to avoid sinking unnecessary current } else // Otherwise the CC pin is CC2 { Registers.Switches.VCONN_CC1 = 1; // Enable VCONN for CC1 Registers.Switches.PDWN1 = 0; // Disable the pull-down on CC1 to avoid sinking unnecessary current } DeviceWrite(regSwitches0, 1, &Registers.Switches.byte[0]); // Commit the register setting to the device IsVCONNSource = TRUE; PolicyStateTimer = VbusTransitionTime; // Allow time for the FPF2498 to enable... PolicySubIndex = 3; // Skip the next state and move onto sending the PS_RDY message after the timer expires } } break; case 2: if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTPS_RDY: // If we get the PS_RDY message... Registers.Switches.VCONN_CC1 = 0; // Disable the VCONN source Registers.Switches.VCONN_CC2 = 0; // Disable the VCONN source Registers.Switches.PDWN1 = 1; // Ensure the pull-down on CC1 is enabled Registers.Switches.PDWN2 = 1; // Ensure the pull-down on CC2 is enabled DeviceWrite(regSwitches0, 1, &Registers.Switches.byte[0]); // Commit the register setting to the device platform_set_vconn_enable(FALSE); IsVCONNSource = FALSE; PolicyState = peSinkReady; // Move onto the Sink Ready state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the VCONNOnTimer times out... { PolicyState = peSourceSendHardReset; // Issue a hard reset PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; default: if (!PolicyStateTimer) { PolicySendCommand(CMTPS_RDY, peSinkReady, 0); // Send the Accept message and wait for the good CRC } break; } } void PolicySinkSendPRSwap(void) { #ifdef FSC_HAVE_DRP FSC_U8 Status; switch(PolicySubIndex) { case 0: // Send the PRSwap command pr_info("FUSB %s: send PR_Swap command\n", __func__); if (PolicySendCommand(CMTPR_Swap, peSinkSendPRSwap, 1) == STAT_SUCCESS) // Send the PR_Swap message and wait for the good CRC PolicyStateTimer = tSenderResponse; // Once we receive the good CRC, set the sender response timer break; case 1: // Require Accept message to move on or go back to ready state if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTAccept: // If we get the Accept message... IsPRSwap = TRUE; pr_debug("FUSB %s(%d): IsPRSwap=%d\n", __func__, __LINE__, IsPRSwap); PolicyHasContract = FALSE; platform_notify_pd_contract(FALSE); PRSwapTimer = tPRSwapBailout; // Initialize the PRSwapTimer to indicate we are in the middle of a swap PolicyStateTimer = tPSSourceOff; // Start the sink transition timer PolicySubIndex++; // Increment the subindex to move onto the next step break; case CMTWait: // If we get either the reject or wait message... case CMTReject: PolicyState = peSinkReady; // Go back to the sink ready state PolicySubIndex = 0; // Clear the sub index IsPRSwap = FALSE; pr_debug("FUSB %s(%d): IsPRSwap=%d\n", __func__, __LINE__, IsPRSwap); PDTxStatus = txIdle; // Clear the transmitter status break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the SenderResponseTimer times out... { PolicyState = peSinkReady; // Go back to the sink ready state PolicySubIndex = 0; // Clear the sub index IsPRSwap = FALSE; pr_debug("FUSB %s(%d): IsPRSwap=%d\n", __func__, __LINE__, IsPRSwap); PDTxStatus = txIdle; // Clear the transmitter status } break; case 2: // Wait for a PS_RDY message to be received to indicate that the original source is no longer supplying VBUS if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTPS_RDY: // If we get the PS_RDY message... pr_debug("FUSB %s sink receive PS_READY\n", __func__); RoleSwapToAttachedSource(); // Initiate the Type-C state machine for a power role swap PolicyIsSource = TRUE; Registers.Switches.POWERROLE = PolicyIsSource; DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); PolicyStateTimer = tSourceOnDelay; // Allow the output voltage to rise before sending the PS_RDY message PolicySubIndex++; // Increment the sub-index to move onto the next state break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the PSSourceOn times out... { PolicyState = peErrorRecovery; // Go to the error recovery state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; default: // Allow time for the supply to rise and then send the PS_RDY message if (!PolicyStateTimer) { pr_debug("FUSB %s sink send PS_READY\n", __func__); Status = PolicySendCommandNoReset(CMTPS_RDY, peSourceStartup, 0); // When we get the good CRC, we move onto the source startup state to complete the swap if (Status == STAT_ERROR) PolicyState = peErrorRecovery; // If we get an error, go to the error recovery state SwapSourceStartTimer = tSwapSourceStart; } break; } #endif // FSC_HAVE_DRP } void PolicySinkEvaluatePRSwap(void) { #ifdef FSC_HAVE_DRP FSC_U8 Status; switch(PolicySubIndex) { case 0: // Send either the Accept or Reject command if ((PartnerCaps.FPDOSupply.SupplyType == pdoTypeFixed) && (PartnerCaps.FPDOSupply.DualRolePower == FALSE)) // Determine Accept/Reject based on partner's Dual Role Power { PolicySendCommand(CMTReject, peSinkReady, 0); // Send the reject if we are not a DRP } else { if (PolicySendCommand(CMTAccept, peSinkEvaluatePRSwap, 1) == STAT_SUCCESS) // Send the Accept message and wait for the good CRC { IsPRSwap = TRUE; pr_debug("FUSB %s(%d): IsPRSwap=%d\n", __func__, __LINE__, IsPRSwap); PolicyHasContract = FALSE; platform_notify_pd_contract(FALSE); PRSwapTimer = tPRSwapBailout; // Initialize the PRSwapTimer to indicate we are in the middle of a swap PolicyStateTimer = tPSSourceOff; // Start the sink transition timer } } break; case 1: // Wait for the PS_RDY command to come in and indicate the source has turned off VBUS if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects == 0) // If we have received a command { switch (PolicyRxHeader.MessageType) // Determine which command was received { case CMTPS_RDY: // If we get the PS_RDY message... RoleSwapToAttachedSource(); // Initiate the Type-C state machine for a power role swap PolicyIsSource = TRUE; Registers.Switches.POWERROLE = PolicyIsSource; DeviceWrite(regSwitches1, 1, &Registers.Switches.byte[1]); PolicyStateTimer = tSourceOnDelay; // Allow the output voltage to rise before sending the PS_RDY message PolicySubIndex++; // Increment the sub-index to move onto the next state break; default: // For all other commands received, simply ignore them break; } } } else if (!PolicyStateTimer) // If the PSSourceOn times out... { PolicyState = peSinkSendHardReset; // Go to the error recovery state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; default: // Wait for VBUS to rise and then send the PS_RDY message if (!PolicyStateTimer) { Status = PolicySendCommandNoReset(CMTPS_RDY, peSourceStartup, 0); // When we get the good CRC, we move onto the source startup state to complete the swap if (Status == STAT_ERROR) PolicyState = peErrorRecovery; // If we get an error, go to the error recovery state SwapSourceStartTimer = tSwapSourceStart; } break; } #else PolicySendCommand(CMTReject, peSinkReady, 0); // Send the reject if we are not a DRP #endif // FSC_HAVE_DRP } #endif // FSC_HAVE_SNK #ifdef FSC_HAVE_VDM void PolicyGiveVdm(void) { if (ProtocolMsgRx && PolicyRxHeader.MessageType == DMTVenderDefined) // Have we received a VDM message { sendVdmMessageFailed(); // if we receive anything, kick out of here (interruptible) PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } else if (sendingVdmData) { FSC_U8 result = PolicySendData(DMTVenderDefined, vdm_msg_length, vdm_msg_obj, vdm_next_ps, 0, SOP_TYPE_SOP); if (result == STAT_SUCCESS) { if (expectingVdmResponse()) { startVdmTimer(PolicyState); } else { resetPolicyState(); } sendingVdmData = FALSE; } else if (result == STAT_ERROR) { sendVdmMessageFailed(); sendingVdmData = FALSE; } } else { sendVdmMessageFailed(); } if (VdmTimerStarted && (VdmTimer == 0)) { vdmMessageTimeout(); } } void PolicyVdm (void) { FSC_U8 result; if (ProtocolMsgRx) // Have we received a message from the source? { ProtocolMsgRx = FALSE; // Reset the message received flag since we're handling it here if (PolicyRxHeader.NumDataObjects != 0) // If we have received a command { switch (PolicyRxHeader.MessageType) { case DMTVenderDefined: convertAndProcessVdmMessage(ProtocolMsgRxSop); break; default: // If we get something we are not expecting... simply ignore them resetPolicyState(); // if not a VDM message, kick out of VDM state (interruptible) ProtocolMsgRx = TRUE; // reset flag so other state can see the message and process break; } } else { resetPolicyState(); // if not a VDM message, kick out of VDM state (interruptible) ProtocolMsgRx = TRUE; // reset flag so other state can see the message and process } PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status } else { if (sendingVdmData) { result = PolicySendData(DMTVenderDefined, vdm_msg_length, vdm_msg_obj, vdm_next_ps, 0, SOP_TYPE_SOP); if (result == STAT_SUCCESS || result == STAT_ERROR) { sendingVdmData = FALSE; } } } if (VdmTimerStarted && (VdmTimer == 0)) { if(PolicyState == peDfpUfpVdmIdentityRequest) { AutoVdmState = AUTO_VDM_DONE; } vdmMessageTimeout(); } } #endif // FSC_HAVE_VDM void PolicyInvalidState (void) { // reset if we get to an invalid state if (PolicyIsSource) { PolicyState = peSourceSendHardReset; } else { PolicyState = peSinkSendHardReset; } } // ########################## General PD Messaging ########################## // FSC_BOOL PolicySendHardReset(PolicyState_t nextState, FSC_U32 delay) { FSC_BOOL Success = FALSE; switch (PolicySubIndex) { case 0: switch (PDTxStatus) { case txReset: case txWait: // Do nothing until the protocol layer finishes generating the hard reset signaling // The next state should be either txCollision or txSuccess break; case txSuccess: PolicyStateTimer = delay; // Set the amount of time prior to proceeding to the next state PolicySubIndex++; // Move onto the next state Success = TRUE; break; default: // None of the other states should actually occur, so... PDTxStatus = txReset; // Set the transmitter status to resend a hard reset break; } break; default: if (PolicyStateTimer == 0) // Once tPSHardReset has elapsed... { PolicyStateTimer = tPSHardReset - tHardResetOverhead; HardResetCounter++; // Increment the hard reset counter once successfully sent PolicyState = nextState; // Go to the state to transition to the default sink state PolicySubIndex = 0; // Clear the sub index PDTxStatus = txIdle; // Clear the transmitter status } break; } return Success; } FSC_U8 PolicySendCommand(FSC_U8 Command, PolicyState_t nextState, FSC_U8 subIndex) { FSC_U8 Status = STAT_BUSY; switch (PDTxStatus) { case txIdle: PolicyTxHeader.word = 0; // Clear the word to initialize for each transaction PolicyTxHeader.NumDataObjects = 0; // Clear the number of objects since this is a command PolicyTxHeader.MessageType = Command & 0x0F; // Sets the message type to the command passed in PolicyTxHeader.PortDataRole = PolicyIsDFP; // Set whether the port is acting as a DFP or UFP PolicyTxHeader.PortPowerRole = PolicyIsSource; // Set whether the port is serving as a power source or sink PolicyTxHeader.SpecRevision = USBPDSPECREV; // Set the spec revision PDTxStatus = txSend; // Indicate to the Protocol layer that there is something to send break; case txSend: case txBusy: case txWait: // Waiting for GoodCRC or timeout of the protocol // May want to put in a second level timeout in case there's an issue with the protocol getting hung break; case txSuccess: PolicyState = nextState; // Go to the next state requested PolicySubIndex = subIndex; PDTxStatus = txIdle; // Reset the transmitter status Status = STAT_SUCCESS; break; case txError: // Didn't receive a GoodCRC message... if (PolicyState == peSourceSendSoftReset) // If as a source we already failed at sending a soft reset... PolicyState = peSourceSendHardReset; // Move onto sending a hard reset (source) else if (PolicyState == peSinkSendSoftReset) // If as a sink we already failed at sending a soft reset... PolicyState = peSinkSendHardReset; // Move onto sending a hard reset (sink) else if (PolicyIsSource) // Otherwise, if we are a source... PolicyState = peSourceSendSoftReset; // Attempt to send a soft reset (source) else // We are a sink, so... PolicyState = peSinkSendSoftReset; // Attempt to send a soft reset (sink) PolicySubIndex = 0; // Reset the sub index PDTxStatus = txIdle; // Reset the transmitter status Status = STAT_ERROR; break; case txCollision: CollisionCounter++; // Increment the collision counter if (CollisionCounter > nRetryCount) // If we have already retried two times { if (PolicyIsSource) PolicyState = peSourceSendHardReset; // Go to the source hard reset state else PolicyState = peSinkSendHardReset; // Go to the sink hard reset state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txReset; // Set the transmitter status to send a hard reset Status = STAT_ERROR; } else // Otherwise we are going to try resending the soft reset PDTxStatus = txIdle; // Clear the transmitter status for the next operation break; default: // For an undefined case, reset everything (shouldn't get here) if (PolicyIsSource) PolicyState = peSourceSendHardReset; // Go to the source hard reset state else PolicyState = peSinkSendHardReset; // Go to the sink hard reset state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txReset; // Set the transmitter status to send a hard reset Status = STAT_ERROR; break; } return Status; } FSC_U8 PolicySendCommandNoReset(FSC_U8 Command, PolicyState_t nextState, FSC_U8 subIndex) { FSC_U8 Status = STAT_BUSY; switch (PDTxStatus) { case txIdle: PolicyTxHeader.word = 0; // Clear the word to initialize for each transaction PolicyTxHeader.NumDataObjects = 0; // Clear the number of objects since this is a command PolicyTxHeader.MessageType = Command & 0x0F; // Sets the message type to the command passed in PolicyTxHeader.PortDataRole = PolicyIsDFP; // Set whether the port is acting as a DFP or UFP PolicyTxHeader.PortPowerRole = PolicyIsSource; // Set whether the port is serving as a power source or sink PolicyTxHeader.SpecRevision = USBPDSPECREV; // Set the spec revision PDTxStatus = txSend; // Indicate to the Protocol layer that there is something to send break; case txSend: case txBusy: case txWait: // Waiting for GoodCRC or timeout of the protocol // May want to put in a second level timeout in case there's an issue with the protocol getting hung break; case txSuccess: PolicyState = nextState; // Go to the next state requested PolicySubIndex = subIndex; PDTxStatus = txIdle; // Reset the transmitter status Status = STAT_SUCCESS; break; default: // For all error cases (and undefined), PolicyState = peErrorRecovery; // Go to the error recovery state PolicySubIndex = 0; // Reset the sub index PDTxStatus = txReset; // Set the transmitter status to send a hard reset Status = STAT_ERROR; break; } return Status; } FSC_U8 PolicySendData(FSC_U8 MessageType, FSC_U8 NumDataObjects, doDataObject_t* DataObjects, PolicyState_t nextState, FSC_U8 subIndex, SopType sop) { FSC_U8 Status = STAT_BUSY; FSC_U32 i; switch (PDTxStatus) { case txIdle: if (NumDataObjects > 7) NumDataObjects = 7; PolicyTxHeader.word = 0x0000; // Clear the word to initialize for each transaction PolicyTxHeader.NumDataObjects = NumDataObjects; // Set the number of data objects to send PolicyTxHeader.MessageType = MessageType & 0x0F; // Sets the message type to the what was passed in PolicyTxHeader.PortDataRole = PolicyIsDFP; // Set whether the port is acting as a DFP or UFP PolicyTxHeader.PortPowerRole = PolicyIsSource; // Set whether the port is serving as a power source or sink PolicyTxHeader.SpecRevision = USBPDSPECREV; // Set the spec revision for (i=0; i 7) NumDataObjects = 7; PolicyTxHeader.word = 0x0000; // Clear the word to initialize for each transaction PolicyTxHeader.NumDataObjects = NumDataObjects; // Set the number of data objects to send PolicyTxHeader.MessageType = MessageType & 0x0F; // Sets the message type to the what was passed in PolicyTxHeader.PortDataRole = PolicyIsDFP; // Set whether the port is acting as a DFP or UFP PolicyTxHeader.PortPowerRole = PolicyIsSource; // Set whether the port is serving as a power source or sink PolicyTxHeader.SpecRevision = USBPDSPECREV; // Set the spec revision for (i=0; i200us to allow preamble to finish PolicySubIndex++; } case 2: if(PolicyStateTimer == 0) // Transition to SRC_Transition_to_Default, SNK_Transition_to_Default, or CBL_Ready when BISTContModeTimer times out { if (PolicyIsSource) // If we are the source... { #ifdef FSC_HAVE_SRC PolicyState = peSourceSendHardReset; // This will hard reset then transition to default PolicySubIndex = 0; #endif // FSC_HAVE_SRC } else // Otherwise we are the sink... { #ifdef FSC_HAVE_SNK PolicyState = peSinkSendHardReset; // This will hard reset then transition to default PolicySubIndex = 0; #endif // FSC_HAVE_SNK } } break; } } void policyBISTTestData(void) { if(Registers.DeviceID.VERSION_ID == VERSION_302B) { // Do Nothing } else { DeviceWrite(regControl1, 1, &Registers.Control.byte[1]); } } #ifdef FSC_HAVE_VDM void InitializeVdmManager(void) { initializeVdm(); // configure callbacks vdmm.req_id_info = &vdmRequestIdentityInfo; vdmm.req_svid_info = &vdmRequestSvidInfo; vdmm.req_modes_info = &vdmRequestModesInfo; vdmm.enter_mode_result = &vdmEnterModeResult; vdmm.exit_mode_result = &vdmExitModeResult; vdmm.inform_id = &vdmInformIdentity; vdmm.inform_svids = &vdmInformSvids; vdmm.inform_modes = &vdmInformModes; vdmm.inform_attention = &vdmInformAttention; vdmm.req_mode_entry = &vdmModeEntryRequest; vdmm.req_mode_exit = &vdmModeExitRequest; } void convertAndProcessVdmMessage(SopType sop) { FSC_U32 i; // form the word arrays that VDM block expects // note: may need to rethink the interface. but this is quicker to develop right now FSC_U32 vdm_arr[7]; for (i = 0; i < PolicyRxHeader.NumDataObjects; i++) { vdm_arr[i] = 0; vdm_arr[i] = PolicyRxDataObj[i].object; } processVdmMessage(sop, vdm_arr, PolicyRxHeader.NumDataObjects); } void sendVdmMessage(SopType sop, FSC_U32* arr, FSC_U32 length, PolicyState_t next_ps) { FSC_U32 i; // 'cast' to type that PolicySendData expects // didn't think this was necessary, but it fixed some problems. - Gabe vdm_msg_length = length; vdm_next_ps = next_ps; for (i = 0; i < vdm_msg_length; i++) { vdm_msg_obj[i].object = arr[i]; } sendingVdmData = TRUE; ProtocolCheckRxBeforeTx = TRUE; VdmTimerStarted = FALSE; PolicyState = peGiveVdm; } void doVdmCommand(void) { FSC_U32 command; FSC_U32 svid; FSC_U32 mode_index; SopType sop; command = PDTransmitObjects[0].byte[0] & 0x1F; svid = 0; svid |= (PDTransmitObjects[0].byte[3] << 8); svid |= (PDTransmitObjects[0].byte[2] << 0); mode_index = 0; mode_index = PDTransmitObjects[0].byte[1] & 0x7; // only SOP today sop = SOP_TYPE_SOP; #ifdef FSC_HAVE_DP if (svid == DP_SID) { if (command == DP_COMMAND_STATUS) { requestDpStatus(); } else if (command == DP_COMMAND_CONFIG) { DisplayPortConfig_t temp; temp.word = PDTransmitObjects[1].object; requestDpConfig(temp); } } #endif // FSC_HAVE_DP if (command == DISCOVER_IDENTITY) { requestDiscoverIdentity(sop); } else if (command == DISCOVER_SVIDS) { requestDiscoverSvids(sop); } else if (command == DISCOVER_MODES) { requestDiscoverModes(sop, svid); } else if (command == ENTER_MODE) { requestEnterMode(sop, svid, mode_index); } else if (command == EXIT_MODE) { requestExitMode(sop, svid, mode_index); } } // this function assumes we're already in either Source or Sink Ready states! void autoVdmDiscovery (void) { #ifdef FSC_DEBUG // these messages can get pretty fast, don't want to obliterate the USB buffer if (GetUSBPDBufferNumBytes() != 0) return; #endif // FSC_DEBUG if (!PolicyIsDFP) return; // only auto-discover for DFPs - but allow SM to start for DR swaps in the future if (PDTxStatus == txIdle) { // wait for protocol layer to become idle switch (AutoVdmState) { case AUTO_VDM_INIT: case AUTO_VDM_DISCOVER_ID_PP: requestDiscoverIdentity(SOP_TYPE_SOP); AutoVdmState = AUTO_VDM_DISCOVER_SVIDS_PP; break; case AUTO_VDM_DISCOVER_SVIDS_PP: requestDiscoverSvids(SOP_TYPE_SOP); AutoVdmState = AUTO_VDM_DISCOVER_MODES_PP; break; case AUTO_VDM_DISCOVER_MODES_PP: if (auto_mode_disc_tracker == core_svid_info.num_svids) { AutoVdmState = AUTO_VDM_ENTER_MODE_PP; auto_mode_disc_tracker = 0; } else { requestDiscoverModes(SOP_TYPE_SOP, core_svid_info.svids[auto_mode_disc_tracker]); auto_mode_disc_tracker++; } break; case AUTO_VDM_ENTER_MODE_PP: if (AutoDpModeEntryObjPos > 0) { requestEnterMode(SOP_TYPE_SOP, DP_SID, AutoDpModeEntryObjPos); AutoVdmState = AUTO_VDM_DP_GET_STATUS; } else { AutoVdmState = AUTO_VDM_DONE; } break; case AUTO_VDM_DP_GET_STATUS: if (DpModeEntered) { requestDpStatus(); } AutoVdmState = AUTO_VDM_DONE; break; default: AutoVdmState = AUTO_VDM_DONE; break; } } } #endif // FSC_HAVE_VDM // This function is FUSB302 specific SopType TokenToSopType(FSC_U8 data) { SopType ret; // figure out what SOP* the data came in on if ((data & 0b11100000) == 0b11100000) { ret = SOP_TYPE_SOP; } else if ((data & 0b11100000) == 0b11000000) { ret = SOP_TYPE_SOP1; } else if ((data & 0b11100000) == 0b10100000) { ret = SOP_TYPE_SOP2; } else if ((data & 0b11100000) == 0b10000000) { ret = SOP_TYPE_SOP1_DEBUG; } else if ((data & 0b11100000) == 0b01100000) { ret = SOP_TYPE_SOP2_DEBUG; } else { ret = SOP_TYPE_ERROR; } return ret; } void resetLocalHardware(void) { FSC_U8 data = 0x20; DeviceWrite(regReset, 1, &data); // Reset PD DeviceRead(regSwitches1, 1, &Registers.Switches.byte[1]); // Re-read PD Registers DeviceRead(regSlice, 1, &Registers.Slice.byte); DeviceRead(regControl0, 1, &Registers.Control.byte[0]); DeviceRead(regControl1, 1, &Registers.Control.byte[1]); DeviceRead(regControl3, 1, &Registers.Control.byte[3]); DeviceRead(regMask, 1, &Registers.Mask.byte); DeviceRead(regMaska, 1, &Registers.MaskAdv.byte[0]); DeviceRead(regMaskb, 1, &Registers.MaskAdv.byte[1]); DeviceRead(regStatus0a, 2, &Registers.Status.byte[0]); DeviceRead(regStatus0, 2, &Registers.Status.byte[4]); } void processDMTBIST(void) { FSC_U8 bdo = PolicyRxDataObj[0].byte[3]>>4; Registers.Mask.byte = 0xFF; // Mask for VBUS and Hard Reset Registers.Mask.M_VBUSOK = 0; DeviceWrite(regMask, 1, &Registers.Mask.byte); Registers.MaskAdv.byte[0] = 0xFF; Registers.MaskAdv.M_HARDRST = 0; DeviceWrite(regMaska, 1, &Registers.MaskAdv.byte[0]); Registers.MaskAdv.M_GCRCSENT = 1; DeviceWrite(regMaskb, 1, &Registers.MaskAdv.byte[1]); switch (bdo) { case BDO_BIST_Carrier_Mode_2: if(CapsSource[USBPDContract.FVRDO.ObjectPosition-1].FPDOSupply.Voltage == 100) // Only enter BIST for 5V contract { PolicyState = PE_BIST_Carrier_Mode_2; PolicySubIndex = 0; ProtocolState = PRLIdle; } break; default: case BDO_BIST_Test_Data: if(CapsSource[USBPDContract.FVRDO.ObjectPosition-1].FPDOSupply.Voltage == 100) // Only enter BIST for 5V contract { if(Registers.DeviceID.VERSION_ID == VERSION_302B) { Registers.Control.BIST_TMODE = 1; // Auto-flush RxFIFO DeviceWrite(regControl3, 1, &Registers.Control.byte[3]); } else { Registers.Control.RX_FLUSH = 1; // Enable RxFIFO flushing } PolicyState = PE_BIST_Test_Data; ProtocolState = PRLDisabled; // Disable Protocol layer so we don't read FIFO } break; } } #ifdef FSC_DEBUG void SendUSBPDHardReset(void) { if (PolicyIsSource) // If we are the source... PolicyState = peSourceSendHardReset; // set the source state to send a hard reset else // Otherwise we are the sink... PolicyState = peSinkSendHardReset; // so set the sink state to send a hard reset PolicySubIndex = 0; PDTxStatus = txIdle; // Reset the transmitter status } #ifdef FSC_HAVE_SRC void WriteSourceCapabilities(FSC_U8* abytData) { FSC_U32 i, j; sopMainHeader_t Header = {0}; Header.byte[0] = *abytData++; // Set the 1st PD header byte Header.byte[1] = *abytData++; // Set the 2nd PD header byte if ((Header.NumDataObjects > 0) && (Header.MessageType == DMTSourceCapabilities)) // Only do anything if we decoded a source capabilities message { CapsHeaderSource.word = Header.word; // Set the actual caps source header for (i=0; i 0) && (Header.MessageType == DMTSinkCapabilities)) // Only do anything if we decoded a source capabilities message { CapsHeaderSink.word = Header.word; // Set the actual caps sink header for (i=0; i> 8); *abytData++ = (FSC_U8) (SinkRequestOpPower & 0xFF); *abytData++ = (FSC_U8) ((SinkRequestOpPower >> 8) & 0xFF); *abytData++ = (FSC_U8) ((SinkRequestOpPower >> 16) & 0xFF); *abytData++ = (FSC_U8) ((SinkRequestOpPower >> 24) & 0xFF); *abytData++ = (FSC_U8) (SinkRequestMaxPower & 0xFF); *abytData++ = (FSC_U8) ((SinkRequestMaxPower >> 8) & 0xFF); *abytData++ = (FSC_U8) ((SinkRequestMaxPower >> 16) & 0xFF); *abytData++ = (FSC_U8) ((SinkRequestMaxPower >> 24) & 0xFF); } #endif // FSC_HAVE_SNK void ReadSinkCapabilities(FSC_U8* abytData) { FSC_U32 i, j; *abytData++ = CapsHeaderSink.byte[0]; *abytData++ = CapsHeaderSink.byte[1]; for (i=0; i>8); data[i*5+3] = (FSC_U8)time_tms_temp; data[i*5+4] = (time_s_temp)>>8; data[i*5+5] = (FSC_U8)time_s_temp; } data[0] = i; // Send number of log packets return TRUE; } void ProcessReadPDStateLog(FSC_U8* MsgBuffer, FSC_U8* retBuffer) { if (MsgBuffer[1] != 0) { retBuffer[1] = 0x01; // Return that the version is not recognized return; } GetPDStateLog(&retBuffer[3]); // Designed for 64 byte buffer } void ProcessPDBufferRead(FSC_U8* MsgBuffer, FSC_U8* retBuffer) { if (MsgBuffer[1] != 0) retBuffer[1] = 0x01; // Return that the version is not recognized else { retBuffer[4] = GetUSBPDBufferNumBytes(); // Return the total number of bytes in the buffer retBuffer[5] = ReadUSBPDBuffer((FSC_U8*)&retBuffer[6], 58); // Return the number of bytes read and return the data } } #endif // FSC_DEBUG void SetVbusTransitionTime(FSC_U32 time_ms) { VbusTransitionTime = time_ms * TICK_SCALE_TO_MS; }