double QsfpModule::getQsfpSensor(SffField field, double (*conversion)(uint16_t value)) { auto info = SffFieldInfo::getSffFieldAddress(qsfpFields, field); const uint8_t *data = getQsfpValuePtr(info.dataAddress, info.offset, info.length); return conversion(data[0] << 8 | data[1]); }
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); }
int QsfpModule::getQsfpCableLength(SffField field) { int length; auto info = SffFieldInfo::getSffFieldAddress(qsfpFields, field); const uint8_t *data = getQsfpValuePtr(info.dataAddress, info.offset, info.length); auto multiplier = qsfpMultiplier.at(field); length = *data * multiplier; if (*data == MAX_CABLE_LEN) { length = -(MAX_CABLE_LEN - 1) * multiplier; } return length; }
/* * Cable length is report as a single byte; each field has a * specific multiplier to use to get the true length. For instance, * single mode fiber length is specified in km, so the multiplier * is 1000. In addition, the raw value of 255 indicates that the * cable is longer than can be represented. We use a negative * value of the appropriate magnitude to communicate that to thrift * clients. */ double QsfpModule::getQsfpCableLength(SffField field) const { double length; auto info = SffFieldInfo::getSffFieldAddress(qsfpFields, field); const uint8_t *data = getQsfpValuePtr(info.dataAddress, info.offset, info.length); auto multiplier = qsfpMultiplier.at(field); length = *data * multiplier; if (*data == EEPROM_DEFAULT) { // TODO: does this really mean the cable is too long? length = -(EEPROM_DEFAULT - 1) * multiplier; } return length; }
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); }
int QsfpModule::getQsfpDACGauge() const { auto info = SffFieldInfo::getSffFieldAddress(qsfpFields, SffField::DAC_GAUGE); const uint8_t *val = getQsfpValuePtr(info.dataAddress, info.offset, info.length); //Guard against FF default value auto gauge = *val; if (gauge == EEPROM_DEFAULT) { return 0; } else if (gauge > MAX_GAUGE) { // HACK: We never use cables with more than 30 (in decimal) gauge // However, some vendors put in hex values. For example, some put // 0x28 to represent 28 gauge cable (why?!?), which we would // incorrectly interpret as 40 if using decimal. This converts // values > 30 to the hex value. return (gauge / HEX_BASE) * DECIMAL_BASE + gauge % HEX_BASE; } else{ return gauge; } }
TransmitterTechnology QsfpModule::getQsfpTransmitterTechnology() { auto info = SffFieldInfo::getSffFieldAddress(qsfpFields, SffField::DEVICE_TECHNOLOGY); try { const uint8_t* data = getQsfpValuePtr(info.dataAddress, info.offset, info.length); uint8_t transTech = *data >> DeviceTechnology::TRANSMITTER_TECH_SHIFT; if (transTech == DeviceTechnology::UNKNOWN_VALUE) { return TransmitterTechnology::UNKNOWN; } else if (transTech <= DeviceTechnology::OPTICAL_MAX_VALUE) { return TransmitterTechnology::OPTICAL; } else { return TransmitterTechnology::COPPER; } } catch (const std::exception& e) { LOG(INFO) << " Unable to get transmitter technology for QSFP: " << qsfpImpl_->getName(); } return TransmitterTechnology::UNKNOWN; }
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::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::getQsfpValue(int dataAddress, int offset, int length, uint8_t* data) const { const uint8_t *ptr = getQsfpValuePtr(dataAddress, offset, length); memcpy(data, ptr, length); }
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"; } }