uint8_t QsfpModule::getSettingsValue(SffField field, uint8_t mask) { int offset; int length; int dataAddress; getQsfpFieldAddress(field, dataAddress, offset, length); const uint8_t* data = getQsfpValuePtr(dataAddress, offset, length); return data[0] & mask; }
FlagLevels QsfpModule::getQsfpSensorFlags(SffField fieldName) { int offset; int length; int dataAddress; /* Determine if QSFP is present */ getQsfpFieldAddress(fieldName, dataAddress, offset, length); const uint8_t *data = getQsfpValuePtr(dataAddress, offset, length); return getQsfpFlags(data, 4); }
void QsfpModule::setCdrIfSupported(cfg::PortSpeed speed, FeatureState currentStateTx, FeatureState currentStateRx) { /* * Note that this function expects to be called with qsfpModuleMutex_ * held. */ LOG(INFO) << "Checking if we need to change CDR on " << folly::to<std::string>(qsfpImpl_->getName()); if (currentStateTx == FeatureState::UNSUPPORTED && currentStateRx == FeatureState::UNSUPPORTED) { LOG(INFO) << "CDR is not supported on " << folly::to<std::string>(qsfpImpl_->getName()); return; } // If only one of Rx or Tx is supported, it doesn't matter what // we set the value to, so in that case, treat is as if // no change is needed auto toChange = [speed](FeatureState state) { return state != FeatureState::UNSUPPORTED && ((speed == cfg::PortSpeed::HUNDREDG && state != FeatureState::ENABLED) || (speed != cfg::PortSpeed::HUNDREDG && state != FeatureState::DISABLED)); }; bool changeRx = toChange(currentStateRx); bool changeTx = toChange(currentStateTx); if (!changeRx && !changeTx) { LOG(INFO) << "Port: " << folly::to<std::string>(qsfpImpl_->getName()) << " Not changing CDR setting, already correctly set"; return; } // If one of rx and tx need a change, set the whole byte - whichever // isn't supported will be ignored anyway FeatureState newState = FeatureState::DISABLED; uint8_t value = 0x0; if (speed == cfg::PortSpeed::HUNDREDG) { value = 0xFF; newState = FeatureState::ENABLED; } int dataLength, dataAddress, dataOffset; getQsfpFieldAddress(SffField::CDR_CONTROL, dataAddress, dataOffset, dataLength); qsfpImpl_->writeTransceiver(TransceiverI2CApi::ADDR_QSFP, dataOffset, sizeof(value), &value); LOG(INFO) << folly::to<std::string>("Port: ", qsfpImpl_->getName(), " Setting CDR to state: ", _FeatureState_VALUES_TO_NAMES.find(newState)->second); }
std::string QsfpModule::getQsfpString(SffField field) const { int offset; int length; int dataAddress; getQsfpFieldAddress(field, dataAddress, offset, length); const uint8_t *data = getQsfpValuePtr(dataAddress, offset, length); while (length > 0 && data[length - 1] == ' ') { --length; } return std::string(reinterpret_cast<const char*>(data), length); }
void QsfpModule::ensureTxEnabled() { // Sometimes transceivers lock up and disable TX. When we customize // the transceiver let's also ensure that tx is enabled. We have // even seen transceivers report to have tx enabled in the DOM, but // no traffic was flowing. When we forcibly set the tx_enable bits // again, traffic began flowing. Because of this, we ALWAYS set the // bits (even if they report enabled). int offset; int length; int dataAddress; getQsfpFieldAddress(SffField::TX_DISABLE, dataAddress, offset, length); std::array<uint8_t, 1> buf = {{0}}; qsfpImpl_->writeTransceiver( TransceiverI2CApi::ADDR_QSFP, offset, 1, buf.data()); }
int QsfpModule::getFieldValue(SffField fieldName, uint8_t* fieldValue) { lock_guard<std::mutex> g(qsfpModuleMutex_); int offset; int length; int dataAddress; /* Determine if QSFP is present */ if (cacheIsValid()) { try { getQsfpFieldAddress(fieldName, dataAddress, offset, length); getQsfpValue(dataAddress, offset, length, fieldValue); } catch (const std::exception& ex) { LOG(ERROR) << "Error reading field value for transceiver:" << folly::to<std::string>(qsfpImpl_->getName()) << " " << ex.what(); } } return -1; }
ThresholdLevels QsfpModule::getThresholdValues(SffField field, double (*conversion)(uint16_t value)) { int offset; int length; int dataAddress; ThresholdLevels thresh; CHECK(!flatMem_); getQsfpFieldAddress(field, dataAddress, offset, length); const uint8_t *data = getQsfpValuePtr(dataAddress, offset, length); CHECK_GE(length, 8); thresh.alarm.high = conversion(data[0] << 8 | data[1]); thresh.alarm.low = conversion(data[2] << 8 | data[3]); thresh.warn.high = conversion(data[4] << 8 | data[5]); thresh.warn.low = conversion(data[6] << 8 | data[7]); return thresh; }
void QsfpModule::setQsfpIdprom() { uint8_t status[2]; int offset; int length; int dataAddress; if (!present_) { throw FbossError("QSFP IDProm set failed as QSFP is not present"); } // Check if the data is ready getQsfpFieldAddress(SffField::STATUS, dataAddress, offset, length); getQsfpValue(dataAddress, offset, length, status); if (status[1] & (1 << 0)) { dirty_ = true; throw FbossError("QSFP IDProm failed as QSFP is not ready"); } flatMem_ = status[1] & (1 << 2); dirty_ = false; }
void QsfpModule::setPowerOverrideIfSupported(PowerControlState currentState) { /* Wedge forces Low Power mode via a pin; we have to reset this * to force High Power mode on all transceivers except SR4-40G. * * Note that this function expects to be called with qsfpModuleMutex_ * held. */ int offset; int length; int dataAddress; getQsfpFieldAddress(SffField::ETHERNET_COMPLIANCE, dataAddress, offset, length); const uint8_t *ether = getQsfpValuePtr(dataAddress, offset, length); getQsfpFieldAddress(SffField::EXTENDED_IDENTIFIER, dataAddress, offset, length); const uint8_t *extId = getQsfpValuePtr(dataAddress, offset, length); auto desiredSetting = PowerControlState::POWER_OVERRIDE; // SR4-40G is represented by a value of 2 - SFF-8636 // This is the only transceiver that should use LP mode if (*ether == EthernetCompliance::SR4_40GBASE) { desiredSetting = PowerControlState::POWER_LPMODE; } else { uint8_t highPowerLevel = (*extId & EXT_ID_HI_POWER_MASK); if (highPowerLevel > 0) { desiredSetting = PowerControlState::HIGH_POWER_OVERRIDE; } } auto portStr = folly::to<std::string>(qsfpImpl_->getName()); VLOG(1) << "Port " << portStr << ": Power control " << _PowerControlState_VALUES_TO_NAMES.find(currentState)->second << " Ext ID " << std::hex << (int) *extId << " Ethernet compliance " << std::hex << (int) *ether << " Desired power control " << _PowerControlState_VALUES_TO_NAMES.find(desiredSetting)->second; if (currentState == desiredSetting) { LOG(INFO) << "Port: " << folly::to<std::string>(qsfpImpl_->getName()) << " Power override already correctly set, doing nothing"; return; } uint8_t power = uint8_t(PowerControl::POWER_OVERRIDE); if (desiredSetting == PowerControlState::HIGH_POWER_OVERRIDE) { power = uint8_t(PowerControl::HIGH_POWER_OVERRIDE); } else if (desiredSetting == PowerControlState::POWER_LPMODE) { power = uint8_t(PowerControl::POWER_LPMODE); } getQsfpFieldAddress(SffField::POWER_CONTROL, dataAddress, offset, length); qsfpImpl_->writeTransceiver(TransceiverI2CApi::ADDR_QSFP, offset, sizeof(power), &power); LOG(INFO) << "Port " << portStr << ": QSFP set to power setting " << _PowerControlState_VALUES_TO_NAMES.find(desiredSetting)->second << " (" << int(power) << ")"; }
void QsfpModule::setRateSelectIfSupported(cfg::PortSpeed speed, RateSelectState currentState, RateSelectSetting currentSetting) { if (currentState == RateSelectState::UNSUPPORTED) { return; } else if (currentState == RateSelectState::APPLICATION_RATE_SELECT) { // Currently only support extended rate select, so treat application // rate select as an invalid option throw FbossError(folly::to<std::string>("Port: ", qsfpImpl_->getName(), " Rate select in unknown state, treating as unsupported: ", _RateSelectState_VALUES_TO_NAMES.find(currentState)->second)); } uint8_t value; RateSelectSetting newSetting; bool alreadySet = false; auto translateEnum = [currentSetting, &value, &newSetting] ( RateSelectSetting desired, uint8_t newValue) { if (currentSetting == desired) { return true; } newSetting = desired; value = newValue; return false; }; if (currentState == RateSelectState::EXTENDED_RATE_SELECT_V1) { // Use the highest possible speed in this version alreadySet = translateEnum(RateSelectSetting::FROM_6_6GB_AND_ABOVE, 0b10101010); } else if (speed == cfg::PortSpeed::FORTYG) { // Optimised for 10G channels alreadySet = translateEnum(RateSelectSetting::LESS_THAN_12GB, 0b00000000); } else if (speed == cfg::PortSpeed::HUNDREDG) { // Optimised for 25GB channels alreadySet = translateEnum(RateSelectSetting::FROM_24GB_to_26GB, 0b10101010); } else { throw FbossError(folly::to<std::string>("Port: ", qsfpImpl_->getName(), " Unable to set rate select for port speed: ", cfg::_PortSpeed_VALUES_TO_NAMES.find(speed)->second)); } if (alreadySet) { return; } int dataLength, dataAddress, dataOffset; getQsfpFieldAddress(SffField::RATE_SELECT_RX, dataAddress, dataOffset, dataLength); qsfpImpl_->writeTransceiver(TransceiverI2CApi::ADDR_QSFP, dataOffset, sizeof(value), &value); getQsfpFieldAddress(SffField::RATE_SELECT_RX, dataAddress, dataOffset, dataLength); qsfpImpl_->writeTransceiver(TransceiverI2CApi::ADDR_QSFP, dataOffset, sizeof(value), &value); LOG(INFO) << "Port: " << folly::to<std::string>(qsfpImpl_->getName()) << " set rate select to " << _RateSelectSetting_VALUES_TO_NAMES.find(newSetting)->second; }
bool QsfpModule::getSensorsPerChanInfo(std::vector<Channel>& channels) { int offset; int length; int dataAddress; /* * Interestingly enough, the QSFP stores the four alarm flags * (alarm high, alarm low, warning high, warning low) in two bytes by * channel in order 2, 1, 4, 3; by using this set of offsets, we * should be able to read them in order, by reading the appriopriate * bit offsets combined with a byte offset into the data. * * That is, read bits 4 through 7 of the first byte, then 0 through 3, * then 4 through 7 of the second byte, and so on. Ugh. */ int bitOffset[] = {4, 0, 4, 0}; int byteOffset[] = {0, 0, 1, 1}; getQsfpFieldAddress(SffField::CHANNEL_RX_PWR_ALARMS, dataAddress, offset, length); const uint8_t *data = getQsfpValuePtr(dataAddress, offset, length); for (int channel = 0; channel < CHANNEL_COUNT; channel++) { channels[channel].sensors.rxPwr.flags = getQsfpFlags(data + byteOffset[channel], bitOffset[channel]); channels[channel].sensors.rxPwr.__isset.flags = true; } getQsfpFieldAddress(SffField::CHANNEL_TX_BIAS_ALARMS, dataAddress, offset, length); data = getQsfpValuePtr(dataAddress, offset, length); for (int channel = 0; channel < CHANNEL_COUNT; channel++) { channels[channel].sensors.txBias.flags = getQsfpFlags(data + byteOffset[channel], bitOffset[channel]); channels[channel].sensors.txBias.__isset.flags = true; } getQsfpFieldAddress(SffField::CHANNEL_TX_PWR_ALARMS, dataAddress, offset, length); data = getQsfpValuePtr(dataAddress, offset, length); for (int channel = 0; channel < CHANNEL_COUNT; channel++) { channels[channel].sensors.txPwr.flags = getQsfpFlags(data + byteOffset[channel], bitOffset[channel]); channels[channel].sensors.txPwr.__isset.flags = true; } getQsfpFieldAddress(SffField::CHANNEL_RX_PWR, dataAddress, offset, length); data = getQsfpValuePtr(dataAddress, offset, length); for (auto& channel : channels) { uint16_t value = data[0] << 8 | data[1]; channel.sensors.rxPwr.value = SffFieldInfo::getPwr(value); data += 2; length--; } CHECK_GE(length, 0); getQsfpFieldAddress(SffField::CHANNEL_TX_BIAS, dataAddress, offset, length); data = getQsfpValuePtr(dataAddress, offset, length); for (auto& channel : channels) { uint16_t value = data[0] << 8 | data[1]; channel.sensors.txBias.value = SffFieldInfo::getTxBias(value); data += 2; length--; } CHECK_GE(length, 0); getQsfpFieldAddress(SffField::CHANNEL_TX_PWR, dataAddress, offset, length); data = getQsfpValuePtr(dataAddress, offset, length); for (auto& channel : channels) { uint16_t value = data[0] << 8 | data[1]; channel.sensors.txPwr.value = SffFieldInfo::getPwr(value); data += 2; length--; } CHECK_GE(length, 0); return true; }
void QsfpModule::customizeTransceiver() { /* * Determine whether we need to customize any of the QSFP registers. * Wedge forces Low Power mode via a pin; we have to reset this * to force High Power mode on LR4s. * * Note that this function expects to be called with qsfpModuleMutex_ * held. */ if (dirty_ == true) { return; } int offset; int length; int dataAddress; getQsfpFieldAddress(SffField::EXTENDED_IDENTIFIER, dataAddress, offset, length); const uint8_t *extId = getQsfpValuePtr(dataAddress, offset, length); getQsfpFieldAddress(SffField::ETHERNET_COMPLIANCE, dataAddress, offset, length); const uint8_t *ethCompliance = getQsfpValuePtr(dataAddress, offset, length); int pwrCtrlAddress; int pwrCtrlOffset; int pwrCtrlLength; getQsfpFieldAddress(SffField::POWER_CONTROL, pwrCtrlAddress, pwrCtrlOffset, pwrCtrlLength); const uint8_t *pwrCtrl = getQsfpValuePtr(pwrCtrlAddress, pwrCtrlOffset, pwrCtrlLength); /* * It is not clear whether we'll have to use some of these values * in future to determine whether or not to set the high power override. * Leave the logging in until this is fully debugged -- this should * only trigger on QSFP insertion. */ VLOG(1) << "Port: " << folly::to<std::string>(qsfpImpl_->getName()) << " QSFP Ext ID " << std::hex << (int) *extId << " Ether Compliance " << std::hex << (int) *ethCompliance << " Power Control " << std::hex << (int) *pwrCtrl; int highPowerLevel = (*extId & EXT_ID_HI_POWER_MASK); int powerLevel = (*extId & EXT_ID_MASK) >> EXT_ID_SHIFT; if (highPowerLevel > 0 || powerLevel > 0) { uint8_t power = POWER_OVERRIDE; if (highPowerLevel > 0) { power |= HIGH_POWER_OVERRIDE; } // Note that we don't have to set the page here, but there should // probably be a setQsfpValue() function to handle pages, etc. if (pwrCtrlAddress != QsfpPages::LOWER) { throw FbossError("QSFP failed to set POWER_CONTROL for LR4 " "due to incorrect page number"); } if (pwrCtrlLength != sizeof(power)) { throw FbossError("QSFP failed to set POWER_CONTROL for LR4 " "due to incorrect length"); } qsfpImpl_->writeTransceiver(0x50, pwrCtrlOffset, sizeof(power), &power); LOG(INFO) << "Port: " << folly::to<std::string>(qsfpImpl_->getName()) << " QSFP set to override low power"; } }