text: // Copyright 2004-present Facebook. All Rights Reserved.
  
  #pragma once
  
  #include "fboss/qsfp_service/module/QsfpModule.h"
  
  #include "fboss/agent/gen-cpp2/switch_config_types.h"
  #include "fboss/qsfp_service/if/gen-cpp2/transceiver_types.h"
  
  namespace facebook {
  namespace fboss {
  
  enum class SffField;
  
  class SffModule : public QsfpModule {
   public:
    explicit SffModule(
        TransceiverManager* transceiverManager,
        std::unique_ptr<TransceiverImpl> qsfpImpl,
        unsigned int portsPerTransceiver);
    virtual ~SffModule() override;
  
    /*
     * Return a valid type.
     */
    TransceiverType type() const override {
      return TransceiverType::QSFP;
    }
  
    /*
     * Return the spec this transceiver follows.
     */
    TransceiverManagementInterface managementInterface() const override {
      return TransceiverManagementInterface::SFF;
    }
  
    /*
     * Get the QSFP EEPROM Field
     */
    void getFieldValue(SffField fieldName, uint8_t* fieldValue);
  
    RawDOMData getRawDOMData() override;
  
    DOMDataUnion getDOMDataUnion() override;
  
    /*
     * The size of the pages used by QSFP.  See below for an explanation of
     * how they are laid out.  This needs to be publicly accessible for
     * testing.
     */
    enum : unsigned int {
      // Size of page read from QSFP via I2C
      MAX_QSFP_PAGE_SIZE = 128,
      // Number of channels per module
      CHANNEL_COUNT = 4,
    };
  
    using LengthAndGauge = std::pair<double, uint8_t>;
  
    /*
     * Returns the number of lanes on the host side
     */
    unsigned int numHostLanes() const override;
    /*
     * Returns the number of lanes on the media side
     */
    unsigned int numMediaLanes() const override;
  
   protected:
    // no copy or assignment
    SffModule(SffModule const&) = delete;
    SffModule& operator=(SffModule const&) = delete;
  
    enum : unsigned int {
      EEPROM_DEFAULT = 255,
      MAX_GAUGE = 30,
      DECIMAL_BASE = 10,
      HEX_BASE = 16,
    };
    // QSFP+ requires a bottom 128 byte page describing important monitoring
    // information, and then an upper 128 byte page with less frequently
    // referenced information, including vendor identifiers.  There are
    // three other optional pages;  the third provides a bunch of
    // alarm and warning thresholds which we are interested in.
    // This needs to be initialized to 0 because in software simulated
    // environment this should not contain any arbitrary values
    uint8_t lowerPage_[MAX_QSFP_PAGE_SIZE] = {0};
    uint8_t page0_[MAX_QSFP_PAGE_SIZE] = {0};
    uint8_t page3_[MAX_QSFP_PAGE_SIZE] = {0};
  
    void customizeTransceiverLocked(cfg::PortSpeed speed) override;
  
    /*
     * This function returns a pointer to the value in the static cached
     * data after checking the length fits. The thread needs to have the lock
     * before calling this function.
     */
    const uint8_t* getQsfpValuePtr(int dataAddress, int offset, int length)
        const override;
    /*
     * Based on identifier, sets whether the upper memory of the module is flat or
     * paged.
     */
    void setQsfpFlatMem() override;
    /*
     * Set power mode
     * Wedge forces Low Power mode via a pin;  we have to reset this
     * to force High Power mode on LR4s.
     */
    void setPowerOverrideIfSupported(PowerControlState currentState) override;
    /*
     * Enable or disable CDR
     * Which action to take is determined by the port speed
     */
    void setCdrIfSupported(
        cfg::PortSpeed speed,
        FeatureState currentStateTx,
        FeatureState currentStateRx) override;
    /*
     * Set appropriate rate select value for PortSpeed, if supported
     */
    void setRateSelectIfSupported(
        cfg::PortSpeed speed,
        RateSelectState currentState,
        RateSelectSetting currentSetting) override;
    /*
     * returns individual sensor values after scaling
     */
    double getQsfpSensor(SffField field, double (*conversion)(uint16_t value));
    /*
     * returns cable length (negative for "longer than we can represent")
     */
    double getQsfpCableLength(SffField field) const;
  
    /*
     * returns the freeside transceiver technology type
     */
    TransmitterTechnology getQsfpTransmitterTechnology() const override;
    /*
     * Extract sensor flag levels
     */
    FlagLevels getQsfpSensorFlags(SffField fieldName);
    /*
     * This function returns various strings from the QSFP EEPROM
     * caller needs to check if DOM is supported or not
     */
    std::string getQsfpString(SffField flag) const;
  
    /*
     * Fills in values for alarm and warning thresholds based on field name
     */
    ThresholdLevels getThresholdValues(
        SffField field,
        double (*conversion)(uint16_t value));
    /*
     * Retreives all alarm and warning thresholds
     */
    std::optional<AlarmThreshold> getThresholdInfo() override;
    /*
     * Gather the sensor info for thrift queries
     */
    GlobalSensors getSensorInfo() override;
    /*
     * Gather per-channel information for thrift queries
     */
    bool getSensorsPerChanInfo(std::vector<Channel>& channels) override;
    /*
     * Gather per-media-lane signal information for thrift queries
     */
    bool getSignalsPerMediaLane(std::vector<MediaLaneSignals>& signals) override;
    /*
     * Gather per-host-lane signal information for thrift queries
     */
    bool getSignalsPerHostLane(std::vector<HostLaneSignals>& signals) override;
    /*
     * Gather the vendor info for thrift queries
     */
    Vendor getVendorInfo() override;
    /*
     * Gather the cable info for thrift queries
     */
    Cable getCableInfo() override;
    /*
     * Retrieves the values of settings based on field name and bit placement
     * Default mask is a noop
     */
    virtual uint8_t getSettingsValue(SffField field, uint8_t mask = 0xff) const;
    /*
     * Gather info on what features are enabled and supported
     */
    TransceiverSettings getTransceiverSettingsInfo() override;
    /*
     * Fetches the media interface ids per media lane and returns false if it
     * fails
     */
    bool getMediaInterfaceId(std::vector<MediaInterfaceId>& mediaInterface);
    /*
     * Return which rate select capability is being used, if any
     */
    virtual RateSelectState getRateSelectValue();
    /*
     * Return the rate select optimised bit rates for each channel
     */
    virtual RateSelectSetting getRateSelectSettingValue(RateSelectState state);
    /*
     * Return what power control capability is currently enabled
     */
    PowerControlState getPowerControlValue() override;
    /*
     * Return TransceiverStats
     */
    std::optional<TransceiverStats> getTransceiverStats();
    /*
     * Return SignalFlag which contains Tx/Rx LOS/LOL
     */
    virtual SignalFlags getSignalFlagInfo() override;
    /*
     * Return the extended specification compliance code of the module.
     * This is the field of Byte 192 on page00 and following table 4-4
     * of SFF-8024.
     */
    ExtendedSpecComplianceCode getExtendedSpecificationComplianceCode()
        const override;
    /*
     * Return information in the identifier byte 0
     */
    TransceiverModuleIdentifier getIdentifier() override;
    /*
     * Returns the status in bytes 1 and 2 in the lower page
     */
    ModuleStatus getModuleStatus() override;
    /*
     * Gather host side per lane configuration settings and return false when it
     * fails
     */
    bool getHostLaneSettings(std::vector<HostLaneSettings>& laneSettings);
    /*
     * Gather media side per lane configuration settings and return false when it
     * fails
     */
    bool getMediaLaneSettings(std::vector<MediaLaneSettings>& laneSettings);
    /*
     * Update the cached data with the information from the physical QSFP.
     *
     * The 'allPages' parameter determines which pages we refresh. Data
     * on the first page holds most of the fields that actually change,
     * so unless we have reason to believe the transceiver was unplugged
     * there is not much point in refreshing static data on other pages.
     */
    void updateQsfpData(bool allPages = true) override;
  
   private:
    /*
     * Helpers to parse DOM data for DAC cables. These incorporate some
     * extra fields that FB has vendors put in the 'Vendor specific'
     * byte range of the SFF spec.
     */
    double getQsfpDACLength() const override;
    int getQsfpDACGauge() const;
    /*
     * Provides the option to override the length/gauge values read from
     * the DOM for certain transceivers. This is useful when vendors
     * input incorrect data and the accuracy of these fields is
     * important for proper tuning.
     */
    const std::optional<LengthAndGauge> getDACCableOverride() const;
  
    /*
     * Put logic here that should only be run on ports that have been
     * down for a long time. These are actions that are potentially more
     * disruptive, but have worked in the past to recover a transceiver.
     */
    void remediateFlakyTransceiver() override;
  
    // make sure that tx_disable bits are clear
    void ensureTxEnabled() override;
  
    // set to low power and then back to original setting. This has
    // effect of restarting the transceiver state machine
    void resetLowPowerMode() override;
  
    // Some of the transceiver has set their channel control settings
    // different from the others which causes low signal quality when working
    // with some xphy. overwrite the value to default as a remediation.
    void overwriteChannelControlSettings();
  
    /*
     * Determine if it is safe to customize the ports based on the
     * status of our member ports.
     */
    bool safeToCustomize() const;
  
    /*
     * Similar to safeToCustomize, but also factors in whether we think
     * we need customization (needsCustomization_) and also makes sure
     * we haven't customized too recently via the cooldown param.
     */
    bool customizationWanted(time_t cooldown) const;
  
    /*
     * Whether enough time has passed that we should refresh our data.
     * Cooldown parameter indicates how much time must have elapsed
     * since last time we refreshed the DOM data.
     */
    bool shouldRefresh(time_t cooldown) const;
  
    /*
     * In the case of Minipack using Facebook FPGA, we need to clear the reset
     * register of QSFP whenever it is newly inserted.
     */
    void ensureOutOfReset() const;
  
    /*
     * Determine set speed of enabled member ports.
     */
    cfg::PortSpeed getPortSpeed() const;
  
    std::map<uint32_t, PortStatus> ports_;
    unsigned int portsPerTransceiver_{0};
  };
  
  } // namespace fboss
  } // namespace facebook
  