diff --git a/device/accton/x86_64-accton_as9716_32d-r0/installer.conf b/device/accton/x86_64-accton_as9716_32d-r0/installer.conf index d5f9419d77ff..950679a3aa91 100644 --- a/device/accton/x86_64-accton_as9716_32d-r0/installer.conf +++ b/device/accton/x86_64-accton_as9716_32d-r0/installer.conf @@ -1,4 +1,4 @@ CONSOLE_PORT=0x3f8 CONSOLE_DEV=0 CONSOLE_SPEED=115200 -ONIE_PLATFORM_EXTRA_CMDLINE_LINUX="pcie_aspm=off" +ONIE_PLATFORM_EXTRA_CMDLINE_LINUX="pcie_aspm=off tg3.short_preamble=1 tg3.bcm5718s_reset=1" diff --git a/device/accton/x86_64-accton_as9716_32d-r0/pcie.yaml b/device/accton/x86_64-accton_as9716_32d-r0/pcie.yaml new file mode 100644 index 000000000000..56a47d1c365f --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/pcie.yaml @@ -0,0 +1,471 @@ +- bus: '00' + dev: '00' + fn: '0' + id: 6f00 + name: 'Host bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2 + (rev 03)' +- bus: '00' + dev: '01' + fn: '0' + id: 6f02 + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 1 (rev 03)' +- bus: '00' + dev: '01' + fn: '1' + id: 6f03 + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 1 (rev 03)' +- bus: '00' + dev: '02' + fn: '0' + id: 6f04 + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 2 (rev 03)' +- bus: '00' + dev: '02' + fn: '1' + id: 6f05 + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 2 (rev 03)' +- bus: '00' + dev: '02' + fn: '2' + id: 6f06 + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 2 (rev 03)' +- bus: '00' + dev: '02' + fn: '3' + id: 6f07 + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 2 (rev 03)' +- bus: '00' + dev: '03' + fn: '0' + id: 6f08 + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 3 (rev 03)' +- bus: '00' + dev: '03' + fn: '1' + id: 6f09 + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 3 (rev 03)' +- bus: '00' + dev: '03' + fn: '2' + id: 6f0a + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 3 (rev 03)' +- bus: '00' + dev: '03' + fn: '3' + id: 6f0b + name: 'PCI bridge: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI + Express Root Port 3 (rev 03)' +- bus: '00' + dev: '05' + fn: '0' + id: 6f28 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Map/VTd_Misc/System Management (rev 03)' +- bus: '00' + dev: '05' + fn: '1' + id: 6f29 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D IIO Hot Plug (rev 03)' +- bus: '00' + dev: '05' + fn: '2' + id: 6f2a + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D IIO RAS/Control Status/Global Errors (rev 03)' +- bus: '00' + dev: '05' + fn: '4' + id: 6f2c + name: 'PIC: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D I/O APIC (rev + 03)' +- bus: '00' + dev: '14' + fn: '0' + id: 8c31 + name: 'USB controller: Intel Corporation 8 Series/C220 Series Chipset Family USB + xHCI (rev 05)' +- bus: '00' + dev: '16' + fn: '0' + id: 8c3a + name: 'Communication controller: Intel Corporation 8 Series/C220 Series Chipset + Family MEI Controller #1 (rev 04)' +- bus: '00' + dev: '16' + fn: '1' + id: 8c3b + name: 'Communication controller: Intel Corporation 8 Series/C220 Series Chipset + Family MEI Controller #2 (rev 04)' +- bus: '00' + dev: 1c + fn: '0' + id: 8c10 + name: 'PCI bridge: Intel Corporation 8 Series/C220 Series Chipset Family PCI Express + Root Port #1 (rev d5)' +- bus: '00' + dev: 1c + fn: '1' + id: 8c12 + name: 'PCI bridge: Intel Corporation 8 Series/C220 Series Chipset Family PCI Express + Root Port #2 (rev d5)' +- bus: '00' + dev: 1c + fn: '4' + id: 8c18 + name: 'PCI bridge: Intel Corporation 8 Series/C220 Series Chipset Family PCI Express + Root Port #5 (rev d5)' +- bus: '00' + dev: 1d + fn: '0' + id: 8c26 + name: 'USB controller: Intel Corporation 8 Series/C220 Series Chipset Family USB + EHCI #1 (rev 05)' +- bus: '00' + dev: 1f + fn: '0' + id: 8c54 + name: 'ISA bridge: Intel Corporation C224 Series Chipset Family Server Standard + SKU LPC Controller (rev 05)' +- bus: '00' + dev: 1f + fn: '2' + id: 8c02 + name: 'SATA controller: Intel Corporation 8 Series/C220 Series Chipset Family 6-port + SATA Controller 1 [AHCI mode] (rev 05)' +- bus: '00' + dev: 1f + fn: '3' + id: 8c22 + name: 'SMBus: Intel Corporation 8 Series/C220 Series Chipset Family SMBus Controller + (rev 05)' +- bus: '03' + dev: '00' + fn: '0' + id: 6f50 + name: 'System peripheral: Intel Corporation Xeon Processor D Family QuickData Technology + Register DMA Channel 0' +- bus: '03' + dev: '00' + fn: '1' + id: 6f51 + name: 'System peripheral: Intel Corporation Xeon Processor D Family QuickData Technology + Register DMA Channel 1' +- bus: '03' + dev: '00' + fn: '2' + id: 6f52 + name: 'System peripheral: Intel Corporation Xeon Processor D Family QuickData Technology + Register DMA Channel 2' +- bus: '03' + dev: '00' + fn: '3' + id: 6f53 + name: 'System peripheral: Intel Corporation Xeon Processor D Family QuickData Technology + Register DMA Channel 3' +- bus: '05' + dev: '00' + fn: '0' + id: 15ab + name: 'Ethernet controller: Intel Corporation Ethernet Connection X552 10 GbE Backplane' +- bus: '05' + dev: '00' + fn: '1' + id: 15ab + name: 'Ethernet controller: Intel Corporation Ethernet Connection X552 10 GbE Backplane' +- bus: '07' + dev: '00' + fn: '0' + id: b980 + name: 'Ethernet controller: Broadcom Inc. and subsidiaries Device b980 (rev 11)' +- bus: 0b + dev: '00' + fn: '0' + id: 165f + name: 'Ethernet controller: Broadcom Inc. and subsidiaries NetXtreme BCM5720 2-port + Gigabit Ethernet PCIe' +- bus: ff + dev: 0b + fn: '0' + id: 6f81 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D R3 QPI Link 0/1 (rev 03)' +- bus: ff + dev: 0b + fn: '1' + id: 6f36 + name: 'Performance counters: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D R3 QPI Link 0/1 (rev 03)' +- bus: ff + dev: 0b + fn: '2' + id: 6f37 + name: 'Performance counters: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D R3 QPI Link 0/1 (rev 03)' +- bus: ff + dev: 0b + fn: '3' + id: 6f76 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D R3 QPI Link Debug (rev 03)' +- bus: ff + dev: 0c + fn: '0' + id: 6fe0 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Caching Agent (rev 03)' +- bus: ff + dev: 0c + fn: '1' + id: 6fe1 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Caching Agent (rev 03)' +- bus: ff + dev: 0c + fn: '2' + id: 6fe2 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Caching Agent (rev 03)' +- bus: ff + dev: 0c + fn: '3' + id: 6fe3 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Caching Agent (rev 03)' +- bus: ff + dev: 0f + fn: '0' + id: 6ff8 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Caching Agent (rev 03)' +- bus: ff + dev: 0f + fn: '4' + id: 6ffc + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Caching Agent (rev 03)' +- bus: ff + dev: 0f + fn: '5' + id: 6ffd + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Caching Agent (rev 03)' +- bus: ff + dev: 0f + fn: '6' + id: 6ffe + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Caching Agent (rev 03)' +- bus: ff + dev: '10' + fn: '0' + id: 6f1d + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D R2PCIe Agent (rev 03)' +- bus: ff + dev: '10' + fn: '1' + id: 6f34 + name: 'Performance counters: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D R2PCIe Agent (rev 03)' +- bus: ff + dev: '10' + fn: '5' + id: 6f1e + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Ubox (rev 03)' +- bus: ff + dev: '10' + fn: '6' + id: 6f7d + name: 'Performance counters: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Ubox (rev 03)' +- bus: ff + dev: '10' + fn: '7' + id: 6f1f + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Ubox (rev 03)' +- bus: ff + dev: '12' + fn: '0' + id: 6fa0 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Home Agent 0 (rev 03)' +- bus: ff + dev: '12' + fn: '1' + id: 6f30 + name: 'Performance counters: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Home Agent 0 (rev 03)' +- bus: ff + dev: '13' + fn: '0' + id: 6fa8 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Target Address/Thermal/RAS (rev 03)' +- bus: ff + dev: '13' + fn: '1' + id: 6f71 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Target Address/Thermal/RAS (rev 03)' +- bus: ff + dev: '13' + fn: '2' + id: 6faa + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel Target Address Decoder (rev 03)' +- bus: ff + dev: '13' + fn: '3' + id: 6fab + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel Target Address Decoder (rev 03)' +- bus: ff + dev: '13' + fn: '4' + id: 6fac + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel Target Address Decoder (rev 03)' +- bus: ff + dev: '13' + fn: '5' + id: 6fad + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel Target Address Decoder (rev 03)' +- bus: ff + dev: '13' + fn: '6' + id: 6fae + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D DDRIO Channel 0/1 Broadcast (rev 03)' +- bus: ff + dev: '13' + fn: '7' + id: 6faf + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D DDRIO Global Broadcast (rev 03)' +- bus: ff + dev: '14' + fn: '0' + id: 6fb0 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel 0 Thermal Control (rev 03)' +- bus: ff + dev: '14' + fn: '1' + id: 6fb1 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel 1 Thermal Control (rev 03)' +- bus: ff + dev: '14' + fn: '2' + id: 6fb2 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel 0 Error (rev 03)' +- bus: ff + dev: '14' + fn: '3' + id: 6fb3 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel 1 Error (rev 03)' +- bus: ff + dev: '14' + fn: '4' + id: 6fbc + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D DDRIO Channel 0/1 Interface (rev 03)' +- bus: ff + dev: '14' + fn: '5' + id: 6fbd + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D DDRIO Channel 0/1 Interface (rev 03)' +- bus: ff + dev: '14' + fn: '6' + id: 6fbe + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D DDRIO Channel 0/1 Interface (rev 03)' +- bus: ff + dev: '14' + fn: '7' + id: 6fbf + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D DDRIO Channel 0/1 Interface (rev 03)' +- bus: ff + dev: '15' + fn: '0' + id: 6fb4 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel 2 Thermal Control (rev 03)' +- bus: ff + dev: '15' + fn: '1' + id: 6fb5 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel 3 Thermal Control (rev 03)' +- bus: ff + dev: '15' + fn: '2' + id: 6fb6 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel 2 Error (rev 03)' +- bus: ff + dev: '15' + fn: '3' + id: 6fb7 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Memory Controller 0 - Channel 3 Error (rev 03)' +- bus: ff + dev: 1e + fn: '0' + id: 6f98 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Power Control Unit (rev 03)' +- bus: ff + dev: 1e + fn: '1' + id: 6f99 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Power Control Unit (rev 03)' +- bus: ff + dev: 1e + fn: '2' + id: 6f9a + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Power Control Unit (rev 03)' +- bus: ff + dev: 1e + fn: '3' + id: 6fc0 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Power Control Unit (rev 03)' +- bus: ff + dev: 1e + fn: '4' + id: 6f9c + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Power Control Unit (rev 03)' +- bus: ff + dev: 1f + fn: '0' + id: 6f88 + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Power Control Unit (rev 03)' +- bus: ff + dev: 1f + fn: '2' + id: 6f8a + name: 'System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon + D Power Control Unit (rev 03)' diff --git a/device/accton/x86_64-accton_as9716_32d-r0/pmon_daemon_control.json b/device/accton/x86_64-accton_as9716_32d-r0/pmon_daemon_control.json index 584a14b9d942..44bad6494229 100644 --- a/device/accton/x86_64-accton_as9716_32d-r0/pmon_daemon_control.json +++ b/device/accton/x86_64-accton_as9716_32d-r0/pmon_daemon_control.json @@ -1,5 +1,4 @@ { - "skip_ledd": true, - "skip_thermalctld": true + "skip_ledd": true } diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/__init__.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/__init__.py new file mode 100644 index 000000000000..73a7720e8979 --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/__init__.py @@ -0,0 +1,2 @@ +__all__ = [ "platform", "chassis", "sfp", "eeprom", "component", "psu", "thermal", "fan", "fan_drawer" ] +from . import platform diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/chassis.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/chassis.py new file mode 100644 index 000000000000..9befd9bb51d4 --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/chassis.py @@ -0,0 +1,264 @@ +############################################################################# +# Edgecore +# +# Module contains an implementation of SONiC Platform Base API and +# provides the Chassis information which are available in the platform +# +############################################################################# + +import os +import sys + +try: + from sonic_platform_base.chassis_base import ChassisBase + from .helper import APIHelper + from .event import SfpEvent +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +NUM_FAN_TRAY = 6 +NUM_FAN = 2 +NUM_PSU = 2 +NUM_THERMAL = 7 +NUM_PORT = 34 +PORT_START = 1 +PORT_END = 34 +QSFP_PORT_START = 1 +QSFP_PORT_END = 32 +NUM_COMPONENT = 4 +HOST_REBOOT_CAUSE_PATH = "/host/reboot-cause/" +PMON_REBOOT_CAUSE_PATH = "/usr/share/sonic/platform/api_files/reboot-cause/" +REBOOT_CAUSE_FILE = "reboot-cause.txt" +PREV_REBOOT_CAUSE_FILE = "previous-reboot-cause.txt" +HOST_CHK_CMD = "which systemctl > /dev/null 2>&1" +SYSLED_FNODE = "/sys/class/leds/accton_as9716_32d_led::diag/brightness" +SYSLED_MODES = { + "0" : "STATUS_LED_COLOR_OFF", + "1" : "STATUS_LED_COLOR_GREEN", + "2" : "STATUS_LED_COLOR_AMBER", + "5" : "STATUS_LED_COLOR_GREEN_BLINK" +} + + +class Chassis(ChassisBase): + """Platform-specific Chassis class""" + + def __init__(self): + ChassisBase.__init__(self) + self._api_helper = APIHelper() + self.is_host = self._api_helper.is_host() + + self.config_data = {} + + self.__initialize_fan() + self.__initialize_psu() + self.__initialize_thermals() + self.__initialize_components() + self.__initialize_sfp() + self.__initialize_eeprom() + + def __initialize_sfp(self): + from sonic_platform.sfp import Sfp + + self.QSFP_PORT_START = QSFP_PORT_START + self.QSFP_PORT_END = QSFP_PORT_END + for index in range(0, PORT_END): + if index in range(self.QSFP_PORT_START-1, self.QSFP_PORT_END): + sfp_module = Sfp(index, 'QSFP') + else: + sfp_module = Sfp(index, 'SFP') + + self._sfp_list.append(sfp_module) + + self._sfpevent = SfpEvent(self._sfp_list) + self.sfp_module_initialized = True + + def __initialize_fan(self): + from sonic_platform.fan_drawer import FanDrawer + for fant_index in range(NUM_FAN_TRAY): + fandrawer = FanDrawer(fant_index) + self._fan_drawer_list.append(fandrawer) + self._fan_list.extend(fandrawer._fan_list) + + def __initialize_psu(self): + from sonic_platform.psu import Psu + for index in range(0, NUM_PSU): + psu = Psu(index) + self._psu_list.append(psu) + + def __initialize_thermals(self): + from sonic_platform.thermal import Thermal + for index in range(0, NUM_THERMAL): + thermal = Thermal(index) + self._thermal_list.append(thermal) + + def __initialize_eeprom(self): + from sonic_platform.eeprom import Tlv + self._eeprom = Tlv() + + def __initialize_components(self): + from sonic_platform.component import Component + for index in range(0, NUM_COMPONENT): + component = Component(index) + self._component_list.append(component) + + def __initialize_watchdog(self): + from sonic_platform.watchdog import Watchdog + self._watchdog = Watchdog() + + + def __is_host(self): + return os.system(HOST_CHK_CMD) == 0 + + def __read_txt_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.read() + return data.strip() + except IOError: + pass + return None + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return self._eeprom.get_product_name() + + def get_presence(self): + """ + Retrieves the presence of the Chassis + Returns: + bool: True if Chassis is present, False if not + """ + return True + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return True + + def get_base_mac(self): + """ + Retrieves the base MAC address for the chassis + Returns: + A string containing the MAC address in the format + 'XX:XX:XX:XX:XX:XX' + """ + return self._eeprom.get_mac() + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return self._eeprom.get_pn() + + def get_serial(self): + """ + Retrieves the hardware serial number for the chassis + Returns: + A string containing the hardware serial number for this chassis. + """ + return self._eeprom.get_serial() + + def get_system_eeprom_info(self): + """ + Retrieves the full content of system EEPROM information for the chassis + Returns: + A dictionary where keys are the type code defined in + OCP ONIE TlvInfo EEPROM format and values are their corresponding + values. + """ + return self._eeprom.get_eeprom() + + def get_reboot_cause(self): + """ + Retrieves the cause of the previous reboot + + Returns: + A tuple (string, string) where the first element is a string + containing the cause of the previous reboot. This string must be + one of the predefined strings in this class. If the first string + is "REBOOT_CAUSE_HARDWARE_OTHER", the second string can be used + to pass a description of the reboot cause. + """ + + reboot_cause_path = (HOST_REBOOT_CAUSE_PATH + REBOOT_CAUSE_FILE) + sw_reboot_cause = self._api_helper.read_txt_file( + reboot_cause_path) or "Unknown" + + + return ('REBOOT_CAUSE_NON_HARDWARE', sw_reboot_cause) + + def get_change_event(self, timeout=0): + # SFP event + if not self.sfp_module_initialized: + self.__initialize_sfp() + return self._sfpevent.get_sfp_event(timeout) + + def get_sfp(self, index): + """ + Retrieves sfp represented by (1-based) index + Args: + index: An integer, the index (1-based) of the sfp to retrieve. + The index should be the sequence of a physical port in a chassis, + starting from 1. + For example, 1 for Ethernet0, 2 for Ethernet4 and so on. + Returns: + An object dervied from SfpBase representing the specified sfp + """ + sfp = None + if not self.sfp_module_initialized: + self.__initialize_sfp() + + try: + # The index will start from 1 + sfp = self._sfp_list[index-1] + except IndexError: + sys.stderr.write("SFP index {} out of range (1-{})\n".format( + index, len(self._sfp_list))) + return sfp + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. If the agent cannot determine the parent-relative position + for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + + + def initizalize_system_led(self): + return True + + def get_status_led(self): + val = self._api_helper.read_txt_file(SYSLED_FNODE) + return SYSLED_MODES[val] if val in SYSLED_MODES else "UNKNOWN" + + def set_status_led(self, color): + mode = None + for key, val in SYSLED_MODES.items(): + if val == color: + mode = key + break + if mode is None: + return False + else: + return self._api_helper.write_txt_file(SYSLED_FNODE, mode) + diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/component.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/component.py new file mode 100644 index 000000000000..e71157fbe8df --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/component.py @@ -0,0 +1,175 @@ +############################################################################# +# Edgecore +# +# Component contains an implementation of SONiC Platform Base API and +# provides the components firmware management function +# +############################################################################# + +import shlex +import subprocess + +try: + from sonic_platform_base.component_base import ComponentBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +CPLD_ADDR_MAPPING = { + "FPGA": "19-0060", + "CPLD1": "20-0061", + "CPLD2": "21-0062" +} +SYSFS_PATH = "/sys/bus/i2c/devices/" +BIOS_VERSION_PATH = "/sys/class/dmi/id/bios_version" +COMPONENT_LIST= [ + ("FPGA", "FPGA "), + ("CPLD1", "CPLD 1"), + ("CPLD2", "CPLD 2"), + ("BIOS", "Basic Input/Output System") + +] + +class Component(ComponentBase): + """Platform-specific Component class""" + + DEVICE_TYPE = "component" + + def __init__(self, component_index=0): + self._api_helper=APIHelper() + ComponentBase.__init__(self) + self.index = component_index + self.name = self.get_name() + + def __run_command(self, command): + # Run bash command and print output to stdout + try: + process = subprocess.Popen( + shlex.split(command), stdout=subprocess.PIPE) + while True: + output = process.stdout.readline() + if output == '' and process.poll() is not None: + break + rc = process.poll() + if rc != 0: + return False + except Exception: + return False + return True + + def __get_bios_version(self): + # Retrieves the BIOS firmware version + try: + with open(BIOS_VERSION_PATH, 'r') as fd: + bios_version = fd.read() + return bios_version.strip() + except Exception as e: + return None + + def __get_cpld_version(self): + # Retrieves the CPLD firmware version + cpld_version = dict() + for cpld_name in CPLD_ADDR_MAPPING: + try: + cpld_path = "{}{}{}".format(SYSFS_PATH, CPLD_ADDR_MAPPING[cpld_name], '/version') + cpld_version_raw= self._api_helper.read_txt_file(cpld_path) + cpld_version[cpld_name] = "{}".format(int(cpld_version_raw,10)) + except Exception as e: + print('Get exception when read cpld') + cpld_version[cpld_name] = 'None' + + return cpld_version + + def get_name(self): + """ + Retrieves the name of the component + Returns: + A string containing the name of the component + """ + return COMPONENT_LIST[self.index][0] + + def get_description(self): + """ + Retrieves the description of the component + Returns: + A string containing the description of the component + """ + return COMPONENT_LIST[self.index][1] + + def get_firmware_version(self): + """ + Retrieves the firmware version of module + Returns: + string: The firmware versions of the module + """ + fw_version = None + + if self.name == "BIOS": + fw_version = self.__get_bios_version() + elif "CPLD" or "FPGA" in self.name: + cpld_version = self.__get_cpld_version() + fw_version = cpld_version.get(self.name) + + return fw_version + + def install_firmware(self, image_path): + """ + Install firmware to module + Args: + image_path: A string, path to firmware image + Returns: + A boolean, True if install successfully, False if not + """ + raise NotImplementedError + + def get_presence(self): + """ + Retrieves the presence of the device + Returns: + bool: True if device is present, False if not + """ + return True + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return 'N/A' + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return True + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + If the agent cannot determine the parent-relative position + for some reason, or if the associated value of + entPhysicalContainedIn is'0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device + or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/eeprom.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/eeprom.py new file mode 100644 index 000000000000..d6f081d8b8c8 --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/eeprom.py @@ -0,0 +1,139 @@ +try: + import os + import sys + import re + if sys.version_info[0] >= 3: + from io import StringIO + else: + from cStringIO import StringIO + + from sonic_platform_base.sonic_eeprom import eeprom_tlvinfo +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +CACHE_ROOT = '/var/cache/sonic/decode-syseeprom' +CACHE_FILE = 'syseeprom_cache' +NULL = 'N/A' + +class Tlv(eeprom_tlvinfo.TlvInfoDecoder): + + EEPROM_DECODE_HEADLINES = 6 + + def __init__(self): + exists = os.path.isfile("/sys/bus/i2c/devices/0-0056/eeprom") + if (exists is True): + self._eeprom_path = "/sys/bus/i2c/devices/0-0056/eeprom" + else: + self._eeprom_path = "/sys/bus/i2c/devices/0-0057/eeprom" + + super(Tlv, self).__init__(self._eeprom_path, 0, '', True) + self._eeprom = self._load_eeprom() + + def __parse_output(self, decode_output): + decode_output.replace('\0', '') + lines = decode_output.split('\n') + lines = lines[self.EEPROM_DECODE_HEADLINES:] + _eeprom_info_dict = dict() + + for line in lines: + try: + match = re.search( + '(0x[0-9a-fA-F]{2})([\s]+[\S]+[\s]+)(.+)', line) + if match is not None: + idx = match.group(1) + value = match.group(3).rstrip('\0') + + _eeprom_info_dict[idx] = value + except Exception: + pass + + return _eeprom_info_dict + + def _load_eeprom(self): + original_stdout = sys.stdout + sys.stdout = StringIO() + try: + self.read_eeprom_db() + except Exception: + decode_output = sys.stdout.getvalue() + sys.stdout = original_stdout + return self.__parse_output(decode_output) + + status = self.check_status() + if 'ok' not in status: + return False + + if not os.path.exists(CACHE_ROOT): + try: + os.makedirs(CACHE_ROOT) + except Exception: + pass + + # + # only the eeprom classes that inherit from eeprom_base + # support caching. Others will work normally + # + try: + self.set_cache_name(os.path.join(CACHE_ROOT, CACHE_FILE)) + except Exception: + pass + + e = self.read_eeprom() + if e is None: + return 0 + + try: + self.update_cache(e) + except Exception: + pass + + self.decode_eeprom(e) + decode_output = sys.stdout.getvalue() + sys.stdout = original_stdout + + (is_valid, valid_crc) = self.is_checksum_valid(e) + if not is_valid: + return False + + return self.__parse_output(decode_output) + + def _valid_tlv(self, eeprom_data): + tlvinfo_type_codes_list = [ + self._TLV_CODE_PRODUCT_NAME, + self._TLV_CODE_PART_NUMBER, + self._TLV_CODE_SERIAL_NUMBER, + self._TLV_CODE_MAC_BASE, + self._TLV_CODE_MANUF_DATE, + self._TLV_CODE_DEVICE_VERSION, + self._TLV_CODE_LABEL_REVISION, + self._TLV_CODE_PLATFORM_NAME, + self._TLV_CODE_ONIE_VERSION, + self._TLV_CODE_MAC_SIZE, + self._TLV_CODE_MANUF_NAME, + self._TLV_CODE_MANUF_COUNTRY, + self._TLV_CODE_VENDOR_NAME, + self._TLV_CODE_DIAG_VERSION, + self._TLV_CODE_SERVICE_TAG, + self._TLV_CODE_VENDOR_EXT, + self._TLV_CODE_CRC_32 + ] + + for code in tlvinfo_type_codes_list: + code_str = "0x{:X}".format(code) + eeprom_data[code_str] = eeprom_data.get(code_str, NULL) + return eeprom_data + + def get_eeprom(self): + return self._valid_tlv(self._eeprom) + + def get_pn(self): + return self._eeprom.get('0x22', NULL) + + def get_serial(self): + return self._eeprom.get('0x23', NULL) + + def get_mac(self): + return self._eeprom.get('0x24', NULL) + + def get_product_name(self): + return self._eeprom.get('0x21', NULL) diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/event.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/event.py new file mode 100644 index 000000000000..2d296e5137b2 --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/event.py @@ -0,0 +1,62 @@ +try: + import time + from .helper import APIHelper + from sonic_py_common.logger import Logger +except ImportError as e: + raise ImportError(repr(e) + " - required module not found") + +POLL_INTERVAL_IN_SEC = 1 + +class SfpEvent: + ''' Listen to insert/remove sfp events ''' + + def __init__(self, sfp_list): + self._api_helper = APIHelper() + self._sfp_list = sfp_list + self._logger = Logger() + self._sfp_change_event_data = {'present': 0} + + def get_presence_bitmap(self): + bitmap = 0 + for sfp in self._sfp_list: + modpres = sfp.get_presence() + i=sfp.port_num-1 + if modpres: + bitmap = bitmap | (1 << i) + return bitmap + + def get_sfp_event(self, timeout=2000): + port_dict = {} + change_dict = {} + change_dict['sfp'] = port_dict + + if timeout < 1000: + cd_ms = 1000 + else: + cd_ms = timeout + + while cd_ms > 0: + bitmap = self.get_presence_bitmap() + changed_ports = self._sfp_change_event_data['present'] ^ bitmap + if changed_ports != 0: + break + time.sleep(POLL_INTERVAL_IN_SEC) + # timeout=0 means wait for event forever + if timeout != 0: + cd_ms = cd_ms - POLL_INTERVAL_IN_SEC * 1000 + + if changed_ports != 0: + for sfp in self._sfp_list: + i=sfp.port_num-1 + if (changed_ports & (1 << i)): + if (bitmap & (1 << i)) == 0: + port_dict[i+1] = '0' + else: + port_dict[i+1] = '1' + + + # Update the cache dict + self._sfp_change_event_data['present'] = bitmap + return True, change_dict + else: + return True, change_dict diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/fan.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/fan.py new file mode 100644 index 000000000000..f3497877dcab --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/fan.py @@ -0,0 +1,297 @@ +############################################################################# +# Edgecore +# +# Module contains an implementation of SONiC Platform Base API and +# provides the fan status which are available in the platform +# +############################################################################# + +import logging + +try: + from sonic_platform_base.fan_base import FanBase + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +SPEED_TOLERANCE = 15 +CPLD_FAN_I2C_PATH = "/sys/bus/i2c/devices/17-0066/fan" +I2C_PATH ="/sys/bus/i2c/devices/{}-00{}/" +PSU_HWMON_I2C_MAPPING = { + 0: { + "num": 9, + "addr": "58" + }, + 1: { + "num": 10, + "addr": "59" + }, +} + +PSU_CPLD_I2C_MAPPING = { + 0: { + "num": 9, + "addr": "50" + }, + 1: { + "num": 10, + "addr": "51" + }, +} + +FAN_NAME_LIST = ["FAN-1F", "FAN-1R", "FAN-2F", "FAN-2R", + "FAN-3F", "FAN-3R", "FAN-4F", "FAN-4R", + "FAN-5F", "FAN-5R", "FAN-6F", "FAN-6R"] + +class Fan(FanBase): + """Platform-specific Fan class""" + + def __init__(self, fan_tray_index, fan_index=0, is_psu_fan=False, psu_index=0): + self._api_helper=APIHelper() + self.fan_index = fan_index + self.fan_tray_index = fan_tray_index + self.is_psu_fan = is_psu_fan + + + if self.is_psu_fan: + self.psu_index = psu_index + self.psu_i2c_num = PSU_HWMON_I2C_MAPPING[self.psu_index]['num'] + self.psu_i2c_addr = PSU_HWMON_I2C_MAPPING[self.psu_index]['addr'] + self.psu_hwmon_path = I2C_PATH.format( + self.psu_i2c_num, self.psu_i2c_addr) + + self.psu_i2c_num = PSU_CPLD_I2C_MAPPING[self.psu_index]['num'] + self.psu_i2c_addr = PSU_CPLD_I2C_MAPPING[self.psu_index]['addr'] + self.psu_cpld_path = I2C_PATH.format( + self.psu_i2c_num, self.psu_i2c_addr) + + FanBase.__init__(self) + + + def get_direction(self): + """ + Retrieves the direction of fan + Returns: + A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST + depending on fan direction + """ + if not self.is_psu_fan: + dir_str = "{}{}{}".format(CPLD_FAN_I2C_PATH, self.fan_tray_index+1, '_direction') + val=self._api_helper.read_txt_file(dir_str) + if val is not None: #F2B is FAN_DIRECTION_EXHAUST + direction = self.FAN_DIRECTION_EXHAUST if ( + val == "0") else self.FAN_DIRECTION_INTAKE + else: + direction=self.FAN_DIRECTION_EXHAUST + + else: #For PSU + psu_path = "{}{}".format(self.psu_cpld_path, 'psu_power_good') + val = self._api_helper.read_txt_file(psu_path) + if val is None or int(val, 10)==0: + return None + + dir_str = "{}{}".format(self.psu_cpld_path, 'psu_fan_dir') + val = self._api_helper.read_txt_file(dir_str) + if val is not None: + if val == 'F2B': + direction=self.FAN_DIRECTION_EXHAUST + else: + direction=self.FAN_DIRECTION_INTAKE + else: + return None + + return direction + + def get_speed(self): + """ + Retrieves the speed of fan as a percentage of full speed + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + + """ + speed = 0 + if self.is_psu_fan: + psu_fan_path= "{}{}".format(self.psu_hwmon_path, 'psu_fan1_speed_rpm') + fan_speed_rpm = self._api_helper.read_txt_file(psu_fan_path) + if fan_speed_rpm is not None: + speed = (int(fan_speed_rpm,10))*100/26688 + if speed > 100: + speed=100 + else: + return 0 + elif self.get_presence(): + speed_path = "{}{}".format(CPLD_FAN_I2C_PATH, '_duty_cycle_percentage') + speed=self._api_helper.read_txt_file(speed_path) + if speed is None: + return 0 + return int(speed) + + def get_target_speed(self): + """ + Retrieves the target (expected) speed of the fan + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + + Note: + speed_pc = pwm_target/255*100 + + 0 : when PWM mode is use + pwm : when pwm mode is not use + """ + return self.get_speed() + + def get_speed_tolerance(self): + """ + Retrieves the speed tolerance of the fan + Returns: + An integer, the percentage of variance from target speed which is + considered tolerable + """ + return SPEED_TOLERANCE + + def set_speed(self, speed): + """ + Sets the fan speed + Args: + speed: An integer, the percentage of full fan speed to set fan to, + in the range 0 (off) to 100 (full speed) + Returns: + A boolean, True if speed is set successfully, False if not + + """ + + if not self.is_psu_fan and self.get_presence(): + speed_path = "{}{}".format(CPLD_FAN_I2C_PATH, '_duty_cycle_percentage') + return self._api_helper.write_txt_file(speed_path, int(speed)) + + return False + + def set_status_led(self, color): + """ + Sets the state of the fan module status LED + Args: + color: A string representing the color with which to set the + fan module status LED + Returns: + bool: True if status LED state is set successfully, False if not + """ + return False #Not supported + + def get_status_led(self): + """ + Gets the state of the fan status LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + status=self.get_status() + if status is None: + return self.STATUS_LED_COLOR_OFF + + return { + True: self.STATUS_LED_COLOR_GREEN, + False: self.STATUS_LED_COLOR_RED + }.get(status, self.STATUS_LED_COLOR_OFF) + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + fan_name = FAN_NAME_LIST[self.fan_tray_index*2 + self.fan_index] \ + if not self.is_psu_fan \ + else "PSU-{} FAN-{}".format(self.psu_index+1, self.fan_index+1) + + return fan_name + + def get_presence(self): + """ + Retrieves the presence of the FAN + Returns: + bool: True if FAN is present, False if not + """ + if not self.is_psu_fan: + present_path = "{}{}{}".format(CPLD_FAN_I2C_PATH, self.fan_tray_index+1, '_present') + val=self._api_helper.read_txt_file(present_path) + if val is not None: + return int(val, 10)==1 + else: + return False + else: + present_path= "{}{}".format(self.psu_cpld_path, 'psu_present') + val=self._api_helper.read_txt_file(present_path) + if val is not None: + return int(val, 10)==1 + else: + return False + + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + if self.is_psu_fan: + psu_path = "{}{}".format(self.psu_cpld_path, 'psu_power_good') + val = self._api_helper.read_txt_file(psu_path) + if val is None or int(val, 10)==0: + return False + + psu_path = "{}{}".format(self.psu_hwmon_path, 'psu_fan1_fault') + val = self._api_helper.read_txt_file(psu_path) + if val is not None: + return int(val, 10)==0 + else: + return False + + else: + sys_index = self.fan_index * 10 + self.fan_tray_index+1 + path = "{}{}{}".format(CPLD_FAN_I2C_PATH, sys_index, '_fault') + val=self._api_helper.read_txt_file(path) + if val is not None: + return int(val, 10)==0 + else: + return False + + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + + return "N/A" + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return "N/A" + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + If the agent cannot determine the parent-relative position + for some reason, or if the associated value of + entPhysicalContainedIn is'0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device + or -1 if cannot determine the position + """ + return (self.fan_index+1) \ + if not self.is_psu_fan else (self.psu_index+1) + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True if not self.is_psu_fan else False + diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/fan_drawer.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/fan_drawer.py new file mode 100644 index 000000000000..260a052dbc9d --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/fan_drawer.py @@ -0,0 +1,101 @@ +######################################################################## +# +# Module contains an implementation of SONiC Platform Base API and +# provides the Fan-Drawers' information available in the platform. +# +######################################################################## + +try: + from sonic_platform_base.fan_drawer_base import FanDrawerBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +FANS_PER_FANTRAY = 2 + + +class FanDrawer(FanDrawerBase): + """Platform-specific Fan class""" + + def __init__(self, fantray_index): + + FanDrawerBase.__init__(self) + # FanTray is 0-based in platforms + self.fantrayindex = fantray_index + self.__initialize_fan_drawer() + + + def __initialize_fan_drawer(self): + from sonic_platform.fan import Fan + for i in range(FANS_PER_FANTRAY): + self._fan_list.append(Fan(self.fantrayindex, i)) + + def get_name(self): + """ + Retrieves the fan drawer name + Returns: + string: The name of the device + """ + return "FanTray{}".format(self.fantrayindex+1) + + def get_presence(self): + """ + Retrieves the presence of the device + Returns: + bool: True if device is present, False if not + """ + return self._fan_list[0].get_presence() + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return self._fan_list[0].get_model() + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return self._fan_list[0].get_serial() + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self._fan_list[0].get_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + If the agent cannot determine the parent-relative position + for some reason, or if the associated value of + entPhysicalContainedIn is'0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device + or -1 if cannot determine the position + """ + return (self.fantrayindex+1) + + def set_status_led(self, color): + """ + Sets the state of the fan module status LED + Args: + color: A string representing the color with which to set the + fan module status LED + Returns: + bool: True if status LED state is set successfully, False if not + """ + return False #Not supported + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/helper.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/helper.py new file mode 100644 index 000000000000..b124ca29f0df --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/helper.py @@ -0,0 +1,117 @@ +import os +import struct +import subprocess +from mmap import * +from sonic_py_common import device_info + +HOST_CHK_CMD = "docker > /dev/null 2>&1" +EMPTY_STRING = "" + + +class APIHelper(): + + def __init__(self): + (self.platform, self.hwsku) = device_info.get_platform_and_hwsku() + + def is_host(self): + return os.system(HOST_CHK_CMD) == 0 + + def pci_get_value(self, resource, offset): + status = True + result = "" + try: + fd = os.open(resource, os.O_RDWR) + mm = mmap(fd, 0) + mm.seek(int(offset)) + read_data_stream = mm.read(4) + result = struct.unpack('I', read_data_stream) + except Exception: + status = False + return status, result + + def run_command(self, cmd): + status = True + result = "" + try: + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + except Exception: + status = False + return status, result + + def run_interactive_command(self, cmd): + try: + os.system(cmd) + except Exception: + return False + return True + + def read_txt_file(self, file_path): + try: + with open(file_path, 'r', errors='replace') as fd: + data = fd.read() + return data.strip() + except IOError: + pass + return None + + def write_txt_file(self, file_path, value): + try: + with open(file_path, 'w') as fd: + fd.write(str(value)) + except IOError: + return False + return True + + def ipmi_raw(self, netfn, cmd): + status = True + result = "" + try: + cmd = "ipmitool raw {} {}".format(str(netfn), str(cmd)) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_fru_id(self, id, key=None): + status = True + result = "" + try: + cmd = "ipmitool fru print {}".format(str( + id)) if not key else "ipmitool fru print {0} | grep '{1}' ".format(str(id), str(key)) + + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_set_ss_thres(self, id, threshold_key, value): + status = True + result = "" + try: + cmd = "ipmitool sensor thresh '{}' {} {}".format(str(id), str(threshold_key), str(value)) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/pcie.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/pcie.py new file mode 100644 index 000000000000..e4da32adf9f8 --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/pcie.py @@ -0,0 +1,19 @@ +############################################################################# +# Edgecore +# +# Module contains an implementation of SONiC Platform Base API and +# provides the fan status which are available in the platform +# Base PCIe class +############################################################################# + +try: + from sonic_platform_base.sonic_pcie.pcie_common import PcieUtil +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Pcie(PcieUtil): + """Edgecore Platform-specific PCIe class""" + + def __init__(self, platform_path): + PcieUtil.__init__(self, platform_path) diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/platform.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/platform.py new file mode 100644 index 000000000000..2f2c2a447fcf --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/platform.py @@ -0,0 +1,21 @@ +############################################################################# +# Edgecore +# +# Module contains an implementation of SONiC Platform Base API and +# provides the platform information +# +############################################################################# + +try: + from sonic_platform_base.platform_base import PlatformBase + from sonic_platform.chassis import Chassis +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Platform(PlatformBase): + """Platform-specific Platform class""" + + def __init__(self): + PlatformBase.__init__(self) + self._chassis = Chassis() diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/psu.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/psu.py new file mode 100644 index 000000000000..0b59e30acd15 --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/psu.py @@ -0,0 +1,263 @@ +############################################################################# +# Edgecore +# +# Module contains an implementation of SONiC Platform Base API and +# provides the PSUs status which are available in the platform +# +############################################################################# + +try: + from sonic_platform_base.psu_base import PsuBase + from sonic_platform.thermal import Thermal + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +I2C_PATH ="/sys/bus/i2c/devices/{0}-00{1}/" + +PSU_NAME_LIST = ["PSU-1", "PSU-2"] +PSU_NUM_FAN = [1, 1] + +PSU_CPLD_I2C_MAPPING = { + 0: { + "num": 9, + "addr": "50" + }, + 1: { + "num": 10, + "addr": "51" + }, +} + +PSU_HWMON_I2C_MAPPING = { + 0: { + "num": 9, + "addr": "58" + }, + 1: { + "num": 10, + "addr": "59" + }, +} + + +class Psu(PsuBase): + """Platform-specific Psu class""" + def __init__(self, psu_index=0): + PsuBase.__init__(self) + self.index = psu_index + self._api_helper = APIHelper() + + self.i2c_num = PSU_HWMON_I2C_MAPPING[self.index]["num"] + self.i2c_addr = PSU_HWMON_I2C_MAPPING[self.index]["addr"] + self.hwmon_path = I2C_PATH.format(self.i2c_num, self.i2c_addr) + + self.i2c_num = PSU_CPLD_I2C_MAPPING[self.index]["num"] + self.i2c_addr = PSU_CPLD_I2C_MAPPING[self.index]["addr"] + self.cpld_path = I2C_PATH.format(self.i2c_num, self.i2c_addr) + + self.__initialize_fan() + + def __initialize_fan(self): + from sonic_platform.fan import Fan + for fan_index in range(0, PSU_NUM_FAN[self.index]): + fan = Fan(fan_index, 0, is_psu_fan=True, psu_index=self.index) + self._fan_list.append(fan) + + self._thermal_list.append(Thermal(is_psu=True, psu_index=self.index)) + + def get_voltage(self): + """ + Retrieves current PSU voltage output + Returns: + A float number, the output voltage in volts, + e.g. 12.1 + """ + vout_path = "{}{}".format(self.hwmon_path, 'psu_v_out') + vout_val=self._api_helper.read_txt_file(vout_path) + + if vout_val is not None: + return float(vout_val)/ 1000 + else: + return 0 + + def get_current(self): + """ + Retrieves present electric current supplied by PSU + Returns: + A float number, the electric current in amperes, e.g 15.4 + """ + iout_path = "{}{}".format(self.hwmon_path, 'psu_i_out') + val=self._api_helper.read_txt_file(iout_path) + if val is not None: + return float(val)/1000 + else: + return 0 + + def get_power(self): + """ + Retrieves current energy supplied by PSU + Returns: + A float number, the power in watts, e.g. 302.6 + """ + pout_path = "{}{}".format(self.hwmon_path, 'psu_p_out') + val=self._api_helper.read_txt_file(pout_path) + if val is not None: + return float(val)/1000 + else: + return 0 + + def get_powergood_status(self): + """ + Retrieves the powergood status of PSU + Returns: + A boolean, True if PSU has stablized its output voltages and passed all + its internal self-tests, False if not. + """ + return self.get_status() + + def set_status_led(self, color): + """ + Sets the state of the PSU status LED + Args: + color: A string representing the color with which to set the PSU status LED + Note: Only support green and off + Returns: + bool: True if status LED state is set successfully, False if not + """ + + return False #Controlled by HW + + def get_status_led(self): + """ + Gets the state of the PSU status LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + status=self.get_status() + if status is None: + return self.STATUS_LED_COLOR_OFF + + return { + 1: self.STATUS_LED_COLOR_GREEN, + 0: self.STATUS_LED_COLOR_RED + }.get(status, self.STATUS_LED_COLOR_OFF) + + def get_temperature(self): + """ + Retrieves current temperature reading from PSU + Returns: + A float number of current temperature in Celsius up to nearest thousandth + of one degree Celsius, e.g. 30.125 + """ + temp_path = "{}{}".format(self.hwmon_path, 'psu_temp1_input') + val=self._api_helper.read_txt_file(temp_path) + if val is not None: + return float(val)/1000 + else: + return 0 + + def get_temperature_high_threshold(self): + """ + Retrieves the high threshold temperature of PSU + Returns: + A float number, the high threshold temperature of PSU in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + return False #Not supported + + def get_voltage_high_threshold(self): + """ + Retrieves the high threshold PSU voltage output + Returns: + A float number, the high threshold output voltage in volts, + e.g. 12.1 + """ + return 14.72 + + def get_voltage_low_threshold(self): + """ + Retrieves the low threshold PSU voltage output + Returns: + A float number, the low threshold output voltage in volts, + e.g. 12.1 + """ + return 7.68 + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return PSU_NAME_LIST[self.index] + + def get_presence(self): + """ + Retrieves the presence of the PSU + Returns: + bool: True if PSU is present, False if not + """ + presence_path="{}{}".format(self.cpld_path, 'psu_present') + val=self._api_helper.read_txt_file(presence_path) + if val is not None: + return int(val, 10) == 1 + else: + return False + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + power_path="{}{}".format(self.cpld_path, 'psu_power_good') + val=self._api_helper.read_txt_file(power_path) + if val is not None: + return int(val, 10) == 1 + else: + return False + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + model_path="{}{}".format(self.cpld_path, 'psu_model_name') + model=self._api_helper.read_txt_file(model_path) + + if model is None: + return "N/A" + return model + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + serial_path="{}{}".format(self.cpld_path, 'psu_serial_number') + serial=self._api_helper.read_txt_file(serial_path) + + if serial is None: + return "N/A" + return serial + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. If the agent cannot determine the parent-relative position + for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device or -1 if cannot determine the position + """ + return self.index+1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/sfp.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/sfp.py new file mode 100644 index 000000000000..c1d9b0e50687 --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/sfp.py @@ -0,0 +1,2258 @@ +############################################################################# +# Edgecore +# +# Sfp contains an implementation of SONiC Platform Base API and +# provides the sfp device status which are available in the platform +# +############################################################################# + +import os +import sys +import time +import struct + +from ctypes import create_string_buffer + +try: + from sonic_platform_base.sfp_base import SfpBase + from sonic_platform_base.sonic_sfp.sff8472 import sff8472InterfaceId + from sonic_platform_base.sonic_sfp.sff8472 import sff8472Dom + from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId + from sonic_platform_base.sonic_sfp.sff8436 import sff8436Dom + from sonic_platform_base.sonic_sfp.inf8628 import inf8628InterfaceId + from sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_InterfaceId + from sonic_platform_base.sonic_sfp.qsfp_dd import qsfp_dd_Dom + from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper + from .helper import APIHelper +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +#Edge-core definitions +CPLD_ADDR_MAPPING = { + 0: { + "bus": 20, + "addr": "61" + }, # port 1-16 + 1: { + "bus": 21, + "addr": "62" + }, # port 17-34 +} +CPLD_I2C_PATH = "/sys/bus/i2c/devices/{}-00{}/" + +# definitions of the offset and width for values in XCVR info eeprom +XCVR_INTFACE_BULK_OFFSET = 0 +XCVR_INTFACE_BULK_WIDTH_QSFP = 20 +XCVR_INTFACE_BULK_WIDTH_SFP = 21 +XCVR_TYPE_OFFSET = 0 +XCVR_TYPE_WIDTH = 1 +XCVR_EXT_TYPE_OFFSET = 1 +XCVR_EXT_TYPE_WIDTH = 1 +XCVR_CONNECTOR_OFFSET = 2 +XCVR_CONNECTOR_WIDTH = 1 +XCVR_COMPLIANCE_CODE_OFFSET = 3 +XCVR_COMPLIANCE_CODE_WIDTH = 8 +XCVR_ENCODING_OFFSET = 11 +XCVR_ENCODING_WIDTH = 1 +XCVR_NBR_OFFSET = 12 +XCVR_NBR_WIDTH = 1 +XCVR_EXT_RATE_SEL_OFFSET = 13 +XCVR_EXT_RATE_SEL_WIDTH = 1 +XCVR_CABLE_LENGTH_OFFSET = 14 +XCVR_CABLE_LENGTH_WIDTH_QSFP = 5 +XCVR_CABLE_LENGTH_WIDTH_SFP = 6 +XCVR_VENDOR_NAME_OFFSET = 20 +XCVR_VENDOR_NAME_WIDTH = 16 +XCVR_VENDOR_OUI_OFFSET = 37 +XCVR_VENDOR_OUI_WIDTH = 3 +XCVR_VENDOR_PN_OFFSET = 40 +XCVR_VENDOR_PN_WIDTH = 16 +XCVR_HW_REV_OFFSET = 56 +XCVR_HW_REV_WIDTH_OSFP = 2 +XCVR_HW_REV_WIDTH_QSFP = 2 +XCVR_HW_REV_WIDTH_SFP = 4 +XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET = 64 +XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH = 1 +XCVR_VENDOR_SN_OFFSET = 68 +XCVR_VENDOR_SN_WIDTH = 16 +XCVR_VENDOR_DATE_OFFSET = 84 +XCVR_VENDOR_DATE_WIDTH = 8 +XCVR_DOM_CAPABILITY_OFFSET = 92 +XCVR_DOM_CAPABILITY_WIDTH = 2 + +XCVR_INTERFACE_DATA_START = 0 +XCVR_INTERFACE_DATA_SIZE = 92 + +QSFP_DOM_BULK_DATA_START = 22 +QSFP_DOM_BULK_DATA_SIZE = 36 +SFP_DOM_BULK_DATA_START = 96 +SFP_DOM_BULK_DATA_SIZE = 10 +QSFP_DD_DOM_BULK_DATA_START = 14 +QSFP_DD_DOM_BULK_DATA_SIZE = 4 + +# definitions of the offset for values in OSFP info eeprom +OSFP_TYPE_OFFSET = 0 +OSFP_VENDOR_NAME_OFFSET = 129 +OSFP_VENDOR_PN_OFFSET = 148 +OSFP_HW_REV_OFFSET = 164 +OSFP_VENDOR_SN_OFFSET = 166 + +# definitions of the offset for values in QSFP_DD info eeprom +QSFP_DD_TYPE_OFFSET = 0 +QSFP_DD_VENDOR_NAME_OFFSET = 1 +QSFP_DD_VENDOR_PN_OFFSET = 20 +QSFP_DD_VENDOR_SN_OFFSET = 38 +QSFP_DD_VENDOR_OUI_OFFSET = 17 + +# definitions of the offset and width for values in XCVR_QSFP_DD info eeprom +XCVR_EXT_TYPE_OFFSET_QSFP_DD = 72 +XCVR_EXT_TYPE_WIDTH_QSFP_DD = 2 +XCVR_CONNECTOR_OFFSET_QSFP_DD = 75 +XCVR_CONNECTOR_WIDTH_QSFP_DD = 1 +XCVR_CABLE_LENGTH_OFFSET_QSFP_DD = 74 +XCVR_CABLE_LENGTH_WIDTH_QSFP_DD = 1 +XCVR_HW_REV_OFFSET_QSFP_DD = 36 +XCVR_HW_REV_WIDTH_QSFP_DD = 2 +XCVR_VENDOR_DATE_OFFSET_QSFP_DD = 54 +XCVR_VENDOR_DATE_WIDTH_QSFP_DD = 8 +XCVR_DOM_CAPABILITY_OFFSET_QSFP_DD = 2 +XCVR_DOM_CAPABILITY_WIDTH_QSFP_DD = 1 +XCVR_MEDIA_TYPE_OFFSET_QSFP_DD = 85 +XCVR_MEDIA_TYPE_WIDTH_QSFP_DD = 1 +XCVR_FIRST_APPLICATION_LIST_OFFSET_QSFP_DD = 86 +XCVR_FIRST_APPLICATION_LIST_WIDTH_QSFP_DD = 32 +XCVR_SECOND_APPLICATION_LIST_OFFSET_QSFP_DD = 351 +XCVR_SECOND_APPLICATION_LIST_WIDTH_QSFP_DD = 28 + +# Offset for values in QSFP eeprom +QSFP_DOM_REV_OFFSET = 1 +QSFP_DOM_REV_WIDTH = 1 +QSFP_TEMPE_OFFSET = 22 +QSFP_TEMPE_WIDTH = 2 +QSFP_VOLT_OFFSET = 26 +QSFP_VOLT_WIDTH = 2 +QSFP_VERSION_COMPLIANCE_OFFSET = 1 +QSFP_VERSION_COMPLIANCE_WIDTH = 2 +QSFP_CHANNL_MON_OFFSET = 34 +QSFP_CHANNL_MON_WIDTH = 16 +QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH = 24 +QSFP_CHANNL_DISABLE_STATUS_OFFSET = 86 +QSFP_CHANNL_DISABLE_STATUS_WIDTH = 1 +QSFP_CHANNL_RX_LOS_STATUS_OFFSET = 3 +QSFP_CHANNL_RX_LOS_STATUS_WIDTH = 1 +QSFP_CHANNL_TX_FAULT_STATUS_OFFSET = 4 +QSFP_CHANNL_TX_FAULT_STATUS_WIDTH = 1 +QSFP_CONTROL_OFFSET = 86 +QSFP_CONTROL_WIDTH = 8 +QSFP_MODULE_MONITOR_OFFSET = 0 +QSFP_MODULE_MONITOR_WIDTH = 9 +QSFP_POWEROVERRIDE_OFFSET = 93 +QSFP_POWEROVERRIDE_WIDTH = 1 +QSFP_POWEROVERRIDE_BIT = 0 +QSFP_POWERSET_BIT = 1 +QSFP_OPTION_VALUE_OFFSET = 192 +QSFP_OPTION_VALUE_WIDTH = 4 +QSFP_MODULE_UPPER_PAGE3_START = 384 +QSFP_MODULE_THRESHOLD_OFFSET = 128 +QSFP_MODULE_THRESHOLD_WIDTH = 24 +QSFP_CHANNL_THRESHOLD_OFFSET = 176 +QSFP_CHANNL_THRESHOLD_WIDTH = 24 + +SFP_MODULE_ADDRA2_OFFSET = 256 +SFP_MODULE_THRESHOLD_OFFSET = 0 +SFP_MODULE_THRESHOLD_WIDTH = 56 +SFP_CHANNL_THRESHOLD_OFFSET = 112 +SFP_CHANNL_THRESHOLD_WIDTH = 2 + +SFP_TEMPE_OFFSET = 96 +SFP_TEMPE_WIDTH = 2 +SFP_VOLT_OFFSET = 98 +SFP_VOLT_WIDTH = 2 +SFP_CHANNL_MON_OFFSET = 100 +SFP_CHANNL_MON_WIDTH = 6 +SFP_CHANNL_STATUS_OFFSET = 110 +SFP_CHANNL_STATUS_WIDTH = 1 + +QSFP_DD_TEMPE_OFFSET = 14 +QSFP_DD_TEMPE_WIDTH = 2 +QSFP_DD_VOLT_OFFSET = 16 +QSFP_DD_VOLT_WIDTH = 2 +QSFP_DD_TX_BIAS_OFFSET = 42 +QSFP_DD_TX_BIAS_WIDTH = 16 +QSFP_DD_RX_POWER_OFFSET = 58 +QSFP_DD_RX_POWER_WIDTH = 16 +QSFP_DD_TX_POWER_OFFSET = 26 +QSFP_DD_TX_POWER_WIDTH = 16 +QSFP_DD_CHANNL_MON_OFFSET = 154 +QSFP_DD_CHANNL_MON_WIDTH = 48 +QSFP_DD_CHANNL_DISABLE_STATUS_OFFSET = 86 +QSFP_DD_CHANNL_DISABLE_STATUS_WIDTH = 1 +QSFP_DD_CHANNL_RX_LOS_STATUS_OFFSET = 19 +QSFP_DD_CHANNL_RX_LOS_STATUS_WIDTH = 1 +QSFP_DD_CHANNL_TX_FAULT_STATUS_OFFSET = 7 +QSFP_DD_CHANNL_TX_FAULT_STATUS_WIDTH = 1 +QSFP_DD_MODULE_THRESHOLD_OFFSET = 0 +QSFP_DD_MODULE_THRESHOLD_WIDTH = 72 +QSFP_DD_CHANNL_STATUS_OFFSET = 26 +QSFP_DD_CHANNL_STATUS_WIDTH = 1 + + +sfp_cable_length_tup = ( + 'LengthSMFkm-UnitsOfKm', 'LengthSMF(UnitsOf100m)', + 'Length50um(UnitsOf10m)', 'Length62.5um(UnitsOfm)', + 'LengthCable(UnitsOfm)', 'LengthOM3(UnitsOf10m)' +) + +sfp_compliance_code_tup = ( + '10GEthernetComplianceCode', 'InfinibandComplianceCode', + 'ESCONComplianceCodes', 'SONETComplianceCodes', + 'EthernetComplianceCodes', 'FibreChannelLinkLength', + 'FibreChannelTechnology', 'SFP+CableTechnology', + 'FibreChannelTransmissionMedia', 'FibreChannelSpeed' +) + +qsfp_compliance_code_tup = ( + '10/40G Ethernet Compliance Code', 'SONET Compliance codes', + 'SAS/SATA compliance codes', 'Gigabit Ethernet Compliant codes', + 'Fibre Channel link length/Transmitter Technology', + 'Fibre Channel transmission media', 'Fibre Channel Speed' +) + +info_dict_keys = [ + 'type', 'hardware_rev', 'serial', 'manufacturer', + 'model', 'connector', 'encoding', 'ext_identifier', + 'ext_rateselect_compliance', 'cable_type', 'cable_length', + 'nominal_bit_rate', 'specification_compliance', 'vendor_date', + 'vendor_oui', 'application_advertisement', 'type_abbrv_name' +] + +qsfp_cable_length_tup = ('Length(km)', 'Length OM3(2m)', + 'Length OM2(m)', 'Length OM1(m)', + 'Length Cable Assembly(m)') + +dom_info_dict_keys = [ + 'rx_los', 'tx_fault', 'reset_status', 'lp_mode', + 'tx_disable', 'tx_disable_channel', 'temperature', 'voltage', + 'rx1power', 'rx2power', 'rx3power', 'rx4power', + 'rx5power', 'rx6power', 'rx7power', 'rx8power', + 'tx1bias', 'tx2bias', 'tx3bias', 'tx4bias', + 'tx5bias', 'tx6bias', 'tx7bias', 'tx8bias', + 'tx1power', 'tx2power', 'tx3power', 'tx4power', + 'tx5power', 'tx6power', 'tx7power', 'tx8power'] + +threshold_dict_keys = [ + 'temphighalarm', 'temphighwarning', + 'templowalarm', 'templowwarning', + 'vcchighalarm', 'vcchighwarning', + 'vcclowalarm', 'vcclowwarning', + 'rxpowerhighalarm', 'rxpowerhighwarning', + 'rxpowerlowalarm', 'rxpowerlowwarning', + 'txpowerhighalarm', 'txpowerhighwarning', + 'txpowerlowalarm', 'txpowerlowwarning', + 'txbiashighalarm', 'txbiashighwarning', + 'txbiaslowalarm', 'txbiaslowwarning'] + +SFP_TYPE_CODE_LIST = [ + '03' # SFP/SFP+/SFP28 +] +QSFP_TYPE_CODE_LIST = [ + '0d', # QSFP+ or later + '11' # QSFP28 or later +] +QSFP_DD_TYPE_CODE_LIST = [ + '18' # QSFP-DD Double Density 8X Pluggable Transceiver +] + +SFP_TYPE = "SFP" +QSFP_TYPE = "QSFP" +OSFP_TYPE = "OSFP" +QSFP_DD_TYPE = "QSFP_DD" + +NULL_VAL = 'N/A' + +I2C_EEPROM_PATH = '/sys/bus/i2c/devices/{0}-0050/eeprom' + + +class Sfp(SfpBase): + """Platform-specific Sfp class""" + # Path to sysfs + PLATFORM_ROOT_PATH = "/usr/share/sonic/device" + PMON_HWSKU_PATH = "/usr/share/sonic/hwsku" + HOST_CHK_CMD = "which systemctl > /dev/null 2>&1" + PLATFORM = "x86_64-accton_as9716_32d-r0" + HWSKU = "Accton-AS9716-32D" + + _port_to_i2c_mapping = { + 1: 25, + 2: 26, + 3: 27, + 4: 28, + 5: 29, + 6: 30, + 7: 31, + 8: 32, + 9: 33, + 10: 34, + 11: 35, + 12: 36, + 13: 37, + 14: 38, + 15: 39, + 16: 40, + 17: 41, + 18: 42, + 19: 43, + 20: 44, + 21: 45, + 22: 46, + 23: 47, + 24: 48, + 25: 49, + 26: 50, + 27: 51, + 28: 52, + 29: 53, + 30: 54, + 31: 55, + 32: 56, + 33: 57, + 34: 58, + } + + def __init__(self, sfp_index=0, sfp_name=None): + self._api_helper=APIHelper() + self._index = sfp_index + self.port_num = self._index + 1 + self._name = sfp_name + self._eeprom_path = self._get_eeprom_path() + + cpld_idx = 1 if self.port_num > 16 else 0 + + self.bus = CPLD_ADDR_MAPPING[cpld_idx]["bus"] + self.addr = CPLD_ADDR_MAPPING[cpld_idx]["addr"] + self.i2c_cpld_path = CPLD_I2C_PATH.format(self.bus,self.addr) + + self._dom_capability_detect() + + SfpBase.__init__(self) + + def __is_host(self): + return os.system(self.HOST_CHK_CMD) == 0 + + def __get_path_to_port_config_file(self): + platform_path = "/".join([self.PLATFORM_ROOT_PATH, self.PLATFORM]) + hwsku_path = "/".join([platform_path, self.HWSKU] + ) if self.__is_host() else self.PMON_HWSKU_PATH + return "/".join([hwsku_path, "port_config.ini"]) + + def _convert_string_to_num(self, value_str): + if "-inf" in value_str: + return 'N/A' + elif "Unknown" in value_str: + return 'N/A' + elif 'dBm' in value_str: + t_str = value_str.rstrip('dBm') + return float(t_str) + elif 'mA' in value_str: + t_str = value_str.rstrip('mA') + return float(t_str) + elif 'C' in value_str: + t_str = value_str.rstrip('C') + return float(t_str) + elif 'Volts' in value_str: + t_str = value_str.rstrip('Volts') + return float(t_str) + else: + return 'N/A' + + def _read_eeprom_specific_bytes(self, offset, num_bytes): + sysfs_sfp_i2c_client_eeprom_path = self._get_eeprom_path() + eeprom_raw = [] + try: + eeprom = open( + sysfs_sfp_i2c_client_eeprom_path, + mode="rb", buffering=0) + except IOError: + return None + + for i in range(0, num_bytes): + eeprom_raw.append("0x00") + + try: + eeprom.seek(offset) + raw = eeprom.read(num_bytes) + except IOError: + eeprom.close() + return None + + try: + if isinstance(raw, str): + for n in range(0, num_bytes): + eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2) + else: + for n in range(0, num_bytes): + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) + + except BaseException: + eeprom.close() + return None + + eeprom.close() + return eeprom_raw + + def _detect_sfp_type(self): + sfp_type = QSFP_TYPE + eeprom_raw = [] + eeprom_raw = self._read_eeprom_specific_bytes( + XCVR_TYPE_OFFSET, XCVR_TYPE_WIDTH) + if eeprom_raw: + if eeprom_raw[0] in SFP_TYPE_CODE_LIST: + self.sfp_type = SFP_TYPE + elif eeprom_raw[0] in QSFP_TYPE_CODE_LIST: + self.sfp_type = QSFP_TYPE + elif eeprom_raw[0] in QSFP_DD_TYPE_CODE_LIST: + self.sfp_type = QSFP_DD_TYPE + else: + self.sfp_type = sfp_type + else: + self.sfp_type = sfp_type + + def _get_eeprom_path(self): + port_eeprom_path = I2C_EEPROM_PATH.format(self._port_to_i2c_mapping[self.port_num]) + return port_eeprom_path + + def _dom_capability_detect(self): + self._detect_sfp_type() + + if not self.get_presence(): + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + return + + if self.sfp_type == QSFP_TYPE: + self.calibration = 1 + sfpi_obj = sff8436InterfaceId() + if sfpi_obj is None: + self.dom_supported = False + offset = 128 + + # QSFP capability byte parse, + # through this byte can know whether it support tx_power or not. + # TODO: in the future when decided to migrate to support SFF-8636 instead of SFF-8436, + # need to add more code for determining the capability and version compliance + # in SFF-8636 dom capability definitions evolving with the versions. + + qsfp_dom_capability_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_DOM_CAPABILITY_OFFSET), + XCVR_DOM_CAPABILITY_WIDTH) + if qsfp_dom_capability_raw is not None: + qsfp_version_compliance_raw = self._read_eeprom_specific_bytes( + QSFP_VERSION_COMPLIANCE_OFFSET, + QSFP_VERSION_COMPLIANCE_WIDTH) + qsfp_version_compliance = int( + qsfp_version_compliance_raw[0], 16) + dom_capability = sfpi_obj.parse_dom_capability( + qsfp_dom_capability_raw, 0) + if qsfp_version_compliance >= 0x08: + self.dom_temp_supported = dom_capability['data']['Temp_support']['value'] == 'On' + self.dom_volt_supported = dom_capability['data']['Voltage_support']['value'] == 'On' + self.dom_rx_power_supported = dom_capability['data']['Rx_power_support']['value'] == 'On' + self.dom_tx_power_supported = dom_capability['data']['Tx_power_support']['value'] == 'On' + else: + self.dom_temp_supported = True + self.dom_volt_supported = True + self.dom_rx_power_supported = dom_capability['data']['Rx_power_support']['value'] == 'On' + self.dom_tx_power_supported = True + + self.dom_supported = True + self.calibration = 1 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return None + qsfp_option_value_raw = self._read_eeprom_specific_bytes( + QSFP_OPTION_VALUE_OFFSET, QSFP_OPTION_VALUE_WIDTH) + if qsfp_option_value_raw is not None: + optional_capability = sfpd_obj.parse_option_params( + qsfp_option_value_raw, 0) + self.dom_tx_disable_supported = optional_capability[ + 'data']['TxDisable']['value'] == 'On' + dom_status_indicator = sfpd_obj.parse_dom_status_indicator( + qsfp_version_compliance_raw, 1) + self.qsfp_page3_available = dom_status_indicator['data']['FlatMem']['value'] == 'Off' + else: + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + self.qsfp_page3_available = False + + elif self.sfp_type == QSFP_DD_TYPE: + sfpi_obj = qsfp_dd_InterfaceId() + if sfpi_obj is None: + self.dom_supported = False + + offset = 0 + # two types of QSFP-DD cable types supported: Copper and Optical. + qsfp_dom_capability_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_DOM_CAPABILITY_OFFSET_QSFP_DD), XCVR_DOM_CAPABILITY_WIDTH_QSFP_DD) + if qsfp_dom_capability_raw is not None: + self.dom_temp_supported = True + self.dom_volt_supported = True + dom_capability = sfpi_obj.parse_dom_capability( + qsfp_dom_capability_raw, 0) + if dom_capability['data']['Flat_MEM']['value'] == 'Off': + self.dom_supported = True + self.second_application_list = True + self.dom_rx_power_supported = True + self.dom_tx_power_supported = True + self.dom_tx_bias_power_supported = True + self.dom_thresholds_supported = True + # currently set to False becasue Page 11h is not supported by FW + self.dom_rx_tx_power_bias_supported = False + else: + self.dom_supported = False + self.second_application_list = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.dom_tx_bias_power_supported = False + self.dom_thresholds_supported = False + self.dom_rx_tx_power_bias_supported = False + else: + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.dom_tx_bias_power_supported = False + self.dom_thresholds_supported = False + self.dom_rx_tx_power_bias_supported = False + + elif self.sfp_type == SFP_TYPE: + sfpi_obj = sff8472InterfaceId() + if sfpi_obj is None: + return None + sfp_dom_capability_raw = self._read_eeprom_specific_bytes( + XCVR_DOM_CAPABILITY_OFFSET, XCVR_DOM_CAPABILITY_WIDTH) + if sfp_dom_capability_raw is not None: + sfp_dom_capability = int(sfp_dom_capability_raw[0], 16) + self.dom_supported = (sfp_dom_capability & 0x40 != 0) + if self.dom_supported: + self.dom_temp_supported = True + self.dom_volt_supported = True + self.dom_rx_power_supported = True + self.dom_tx_power_supported = True + if sfp_dom_capability & 0x20 != 0: + self.calibration = 1 + elif sfp_dom_capability & 0x10 != 0: + self.calibration = 2 + else: + self.calibration = 0 + else: + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + self.calibration = 0 + self.dom_tx_disable_supported = ( + int(sfp_dom_capability_raw[1], 16) & 0x40 != 0) + else: + self.dom_supported = False + self.dom_temp_supported = False + self.dom_volt_supported = False + self.dom_rx_power_supported = False + self.dom_tx_power_supported = False + + def get_transceiver_info(self): + """ + Retrieves transceiver info of this SFP + Returns: + A dict which contains following keys/values : + ================================================================================ + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + type |1*255VCHAR |type of SFP + hardware_rev |1*255VCHAR |hardware version of SFP + serial |1*255VCHAR |serial number of the SFP + manufacturer |1*255VCHAR |SFP vendor name + model |1*255VCHAR |SFP model name + connector |1*255VCHAR |connector information + encoding |1*255VCHAR |encoding information + ext_identifier |1*255VCHAR |extend identifier + ext_rateselect_compliance |1*255VCHAR |extended rateSelect compliance + cable_length |INT |cable length in m + nominal_bit_rate |INT |nominal bit rate by 100Mbs + specification_compliance |1*255VCHAR |specification compliance + vendor_date |1*255VCHAR |vendor date + vendor_oui |1*255VCHAR |vendor OUI + application_advertisement |1*255VCHAR |supported applications advertisement + ================================================================================ + """ + + transceiver_info_dict = {} + compliance_code_dict = {} + transceiver_info_dict = dict.fromkeys( + info_dict_keys, NULL_VAL) + transceiver_info_dict["specification_compliance"] = '{}' + + # ToDo: OSFP tranceiver info parsing not fully supported. + # in inf8628.py lack of some memory map definition + # will be implemented when the inf8628 memory map ready + if self.sfp_type == OSFP_TYPE: + offset = 0 + vendor_rev_width = XCVR_HW_REV_WIDTH_OSFP + + sfpi_obj = inf8628InterfaceId() + if sfpi_obj is None: + return transceiver_info_dict + + sfp_type_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_TYPE_OFFSET), XCVR_TYPE_WIDTH) + if sfp_type_raw is not None: + sfp_type_data = sfpi_obj.parse_sfp_type(sfp_type_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_name_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) + if sfp_vendor_name_raw is not None: + sfp_vendor_name_data = sfpi_obj.parse_vendor_name( + sfp_vendor_name_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_pn_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) + if sfp_vendor_pn_raw is not None: + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( + sfp_vendor_pn_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_rev_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_HW_REV_OFFSET), vendor_rev_width) + if sfp_vendor_rev_raw is not None: + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( + sfp_vendor_rev_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_sn_raw = self._read_eeprom_specific_bytes( + (offset + OSFP_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) + if sfp_vendor_sn_raw is not None: + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( + sfp_vendor_sn_raw, 0) + else: + return transceiver_info_dict + + transceiver_info_dict['type'] = sfp_type_data['data']['type']['value'] + transceiver_info_dict['manufacturer'] = sfp_vendor_name_data['data']['Vendor Name']['value'] + transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] + transceiver_info_dict['hardware_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] + transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] + + elif self.sfp_type == QSFP_TYPE: + offset = 128 + vendor_rev_width = XCVR_HW_REV_WIDTH_QSFP + interface_info_bulk_width = XCVR_INTFACE_BULK_WIDTH_QSFP + + sfpi_obj = sff8436InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return transceiver_info_dict + + elif self.sfp_type == QSFP_DD_TYPE: + offset = 128 + + sfpi_obj = qsfp_dd_InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return transceiver_info_dict + + sfp_type_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TYPE_OFFSET), XCVR_TYPE_WIDTH) + if sfp_type_raw is not None: + sfp_type_data = sfpi_obj.parse_sfp_type(sfp_type_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_name_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) + if sfp_vendor_name_raw is not None: + sfp_vendor_name_data = sfpi_obj.parse_vendor_name( + sfp_vendor_name_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_pn_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) + if sfp_vendor_pn_raw is not None: + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( + sfp_vendor_pn_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_rev_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_HW_REV_OFFSET_QSFP_DD), XCVR_HW_REV_WIDTH_QSFP_DD) + if sfp_vendor_rev_raw is not None: + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( + sfp_vendor_rev_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_sn_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) + if sfp_vendor_sn_raw is not None: + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( + sfp_vendor_sn_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_oui_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VENDOR_OUI_OFFSET), XCVR_VENDOR_OUI_WIDTH) + if sfp_vendor_oui_raw is not None: + sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui( + sfp_vendor_oui_raw, 0) + else: + return transceiver_info_dict + + sfp_vendor_date_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_VENDOR_DATE_OFFSET_QSFP_DD), XCVR_VENDOR_DATE_WIDTH_QSFP_DD) + if sfp_vendor_date_raw is not None: + sfp_vendor_date_data = sfpi_obj.parse_vendor_date( + sfp_vendor_date_raw, 0) + else: + return transceiver_info_dict + + sfp_connector_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_CONNECTOR_OFFSET_QSFP_DD), XCVR_CONNECTOR_WIDTH_QSFP_DD) + if sfp_connector_raw is not None: + sfp_connector_data = sfpi_obj.parse_connector( + sfp_connector_raw, 0) + else: + return transceiver_info_dict + + sfp_ext_identifier_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_EXT_TYPE_OFFSET_QSFP_DD), XCVR_EXT_TYPE_WIDTH_QSFP_DD) + if sfp_ext_identifier_raw is not None: + sfp_ext_identifier_data = sfpi_obj.parse_ext_iden( + sfp_ext_identifier_raw, 0) + else: + return transceiver_info_dict + + sfp_cable_len_raw = self._read_eeprom_specific_bytes( + (offset + XCVR_CABLE_LENGTH_OFFSET_QSFP_DD), XCVR_CABLE_LENGTH_WIDTH_QSFP_DD) + if sfp_cable_len_raw is not None: + sfp_cable_len_data = sfpi_obj.parse_cable_len( + sfp_cable_len_raw, 0) + else: + return transceiver_info_dict + + sfp_media_type_raw = self._read_eeprom_specific_bytes( + XCVR_MEDIA_TYPE_OFFSET_QSFP_DD, XCVR_MEDIA_TYPE_WIDTH_QSFP_DD) + if sfp_media_type_raw is not None: + sfp_media_type_dict = sfpi_obj.parse_media_type( + sfp_media_type_raw, 0) + if sfp_media_type_dict is None: + return transceiver_info_dict + + host_media_list = "" + sfp_application_type_first_list = self._read_eeprom_specific_bytes( + (XCVR_FIRST_APPLICATION_LIST_OFFSET_QSFP_DD), XCVR_FIRST_APPLICATION_LIST_WIDTH_QSFP_DD) + if self.second_application_list: + possible_application_count = 15 + sfp_application_type_second_list = self._read_eeprom_specific_bytes( + (XCVR_SECOND_APPLICATION_LIST_OFFSET_QSFP_DD), XCVR_SECOND_APPLICATION_LIST_WIDTH_QSFP_DD) + if sfp_application_type_first_list is not None and sfp_application_type_second_list is not None: + sfp_application_type_list = sfp_application_type_first_list + \ + sfp_application_type_second_list + else: + return transceiver_info_dict + else: + possible_application_count = 8 + if sfp_application_type_first_list is not None: + sfp_application_type_list = sfp_application_type_first_list + else: + return transceiver_info_dict + + for i in range(0, possible_application_count): + if sfp_application_type_list[i * 4] == 'ff': + break + host_electrical, media_interface = sfpi_obj.parse_application( + sfp_media_type_dict, sfp_application_type_list[i * 4], sfp_application_type_list[i * 4 + 1]) + host_media_list = host_media_list + host_electrical + \ + ' - ' + media_interface + '\n\t\t\t\t ' + else: + return transceiver_info_dict + + transceiver_info_dict['type'] = str( + sfp_type_data['data']['type']['value']) + transceiver_info_dict['manufacturer'] = str( + sfp_vendor_name_data['data']['Vendor Name']['value']) + transceiver_info_dict['model'] = str( + sfp_vendor_pn_data['data']['Vendor PN']['value']) + transceiver_info_dict['hardware_rev'] = str( + sfp_vendor_rev_data['data']['Vendor Rev']['value']) + transceiver_info_dict['serial'] = str( + sfp_vendor_sn_data['data']['Vendor SN']['value']) + transceiver_info_dict['vendor_oui'] = str( + sfp_vendor_oui_data['data']['Vendor OUI']['value']) + transceiver_info_dict['vendor_date'] = str( + sfp_vendor_date_data['data']['VendorDataCode(YYYY-MM-DD Lot)']['value']) + transceiver_info_dict['connector'] = str( + sfp_connector_data['data']['Connector']['value']) + transceiver_info_dict['encoding'] = "Not supported for CMIS cables" + transceiver_info_dict['ext_identifier'] = str( + sfp_ext_identifier_data['data']['Extended Identifier']['value']) + transceiver_info_dict['ext_rateselect_compliance'] = "Not supported for CMIS cables" + transceiver_info_dict['specification_compliance'] = "Not supported for CMIS cables" + transceiver_info_dict['cable_type'] = "Length Cable Assembly(m)" + transceiver_info_dict['cable_length'] = str( + sfp_cable_len_data['data']['Length Cable Assembly(m)']['value']) + transceiver_info_dict['nominal_bit_rate'] = "Not supported for CMIS cables" + transceiver_info_dict['application_advertisement'] = host_media_list + + else: + offset = 0 + vendor_rev_width = XCVR_HW_REV_WIDTH_SFP + interface_info_bulk_width = XCVR_INTFACE_BULK_WIDTH_SFP + + sfpi_obj = sff8472InterfaceId() + if sfpi_obj is None: + print("Error: sfp_object open failed") + return transceiver_info_dict + + if self.sfp_type != QSFP_DD_TYPE: + sfp_interface_bulk_raw = self._read_eeprom_specific_bytes( + offset + XCVR_INTERFACE_DATA_START, XCVR_INTERFACE_DATA_SIZE) + if sfp_interface_bulk_raw is None: + return transceiver_info_dict + + start = XCVR_INTFACE_BULK_OFFSET - XCVR_INTERFACE_DATA_START + end = start + interface_info_bulk_width + sfp_interface_bulk_data = sfpi_obj.parse_sfp_info_bulk( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_NAME_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_NAME_WIDTH + sfp_vendor_name_data = sfpi_obj.parse_vendor_name( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_PN_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_PN_WIDTH + sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_HW_REV_OFFSET - XCVR_INTERFACE_DATA_START + end = start + vendor_rev_width + sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_SN_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_SN_WIDTH + sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_OUI_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_OUI_WIDTH + sfp_vendor_oui_data = sfpi_obj.parse_vendor_oui( + sfp_interface_bulk_raw[start: end], 0) + + start = XCVR_VENDOR_DATE_OFFSET - XCVR_INTERFACE_DATA_START + end = start + XCVR_VENDOR_DATE_WIDTH + sfp_vendor_date_data = sfpi_obj.parse_vendor_date( + sfp_interface_bulk_raw[start: end], 0) + + transceiver_info_dict['type'] = sfp_interface_bulk_data['data']['type']['value'] + transceiver_info_dict['manufacturer'] = sfp_vendor_name_data['data']['Vendor Name']['value'] + transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] + transceiver_info_dict['hardware_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] + transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] + transceiver_info_dict['vendor_oui'] = sfp_vendor_oui_data['data']['Vendor OUI']['value'] + transceiver_info_dict['vendor_date'] = sfp_vendor_date_data[ + 'data']['VendorDataCode(YYYY-MM-DD Lot)']['value'] + transceiver_info_dict['connector'] = sfp_interface_bulk_data['data']['Connector']['value'] + transceiver_info_dict['encoding'] = sfp_interface_bulk_data['data']['EncodingCodes']['value'] + transceiver_info_dict['ext_identifier'] = sfp_interface_bulk_data['data']['Extended Identifier']['value'] + transceiver_info_dict['ext_rateselect_compliance'] = sfp_interface_bulk_data['data']['RateIdentifier']['value'] + + if self.sfp_type == QSFP_TYPE: + for key in qsfp_cable_length_tup: + if key in sfp_interface_bulk_data['data']: + transceiver_info_dict['cable_type'] = key + transceiver_info_dict['cable_length'] = str( + sfp_interface_bulk_data['data'][key]['value']) + + for key in qsfp_compliance_code_tup: + if key in sfp_interface_bulk_data['data']['Specification compliance']['value']: + compliance_code_dict[key] = sfp_interface_bulk_data['data']['Specification compliance']['value'][key]['value'] + sfp_ext_specification_compliance_raw = self._read_eeprom_specific_bytes( + offset + XCVR_EXT_SPECIFICATION_COMPLIANCE_OFFSET, XCVR_EXT_SPECIFICATION_COMPLIANCE_WIDTH) + if sfp_ext_specification_compliance_raw is not None: + sfp_ext_specification_compliance_data = sfpi_obj.parse_ext_specification_compliance( + sfp_ext_specification_compliance_raw[0: 1], 0) + if sfp_ext_specification_compliance_data['data']['Extended Specification compliance']['value'] != "Unspecified": + compliance_code_dict['Extended Specification compliance'] = sfp_ext_specification_compliance_data[ + 'data']['Extended Specification compliance']['value'] + transceiver_info_dict['specification_compliance'] = str( + compliance_code_dict) + transceiver_info_dict['nominal_bit_rate'] = str( + sfp_interface_bulk_data['data']['Nominal Bit Rate(100Mbs)']['value']) + else: + for key in sfp_cable_length_tup: + if key in sfp_interface_bulk_data['data']: + transceiver_info_dict['cable_type'] = key + transceiver_info_dict['cable_length'] = str( + sfp_interface_bulk_data['data'][key]['value']) + + for key in sfp_compliance_code_tup: + if key in sfp_interface_bulk_data['data']['Specification compliance']['value']: + compliance_code_dict[key] = sfp_interface_bulk_data['data']['Specification compliance']['value'][key]['value'] + transceiver_info_dict['specification_compliance'] = str( + compliance_code_dict) + + transceiver_info_dict['nominal_bit_rate'] = str( + sfp_interface_bulk_data['data']['NominalSignallingRate(UnitsOf100Mbd)']['value']) + + return transceiver_info_dict + + def get_transceiver_bulk_status(self): + """ + Retrieves transceiver bulk status of this SFP + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + rx_los |BOOLEAN |RX loss-of-signal status, True if has RX los, False if not. + tx_fault |BOOLEAN |TX fault status, True if has TX fault, False if not. + reset_status |BOOLEAN |reset status, True if SFP in reset, False if not. + lp_mode |BOOLEAN |low power mode status, True in lp mode, False if not. + tx_disable |BOOLEAN |TX disable status, True TX disabled, False if not. + tx_disabled_channel |HEX |disabled TX channels in hex, bits 0 to 3 represent channel 0 + | |to channel 3. + temperature |INT |module temperature in Celsius + voltage |INT |supply voltage in mV + txbias |INT |TX Bias Current in mA, n is the channel number, + | |for example, tx2bias stands for tx bias of channel 2. + rxpower |INT |received optical power in mW, n is the channel number, + | |for example, rx2power stands for rx power of channel 2. + txpower |INT |TX output power in mW, n is the channel number, + | |for example, tx2power stands for tx power of channel 2. + ======================================================================== + """ + transceiver_dom_info_dict = dict.fromkeys( + dom_info_dict_keys, NULL_VAL) + + if self.sfp_type == OSFP_TYPE: + pass + + elif self.sfp_type == QSFP_TYPE: + if not self.dom_supported: + return transceiver_dom_info_dict + + offset = 0 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + + dom_data_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DOM_BULK_DATA_START), QSFP_DOM_BULK_DATA_SIZE) + if dom_data_raw is None: + return transceiver_dom_info_dict + + if self.dom_temp_supported: + start = QSFP_TEMPE_OFFSET - QSFP_DOM_BULK_DATA_START + end = start + QSFP_TEMPE_WIDTH + dom_temperature_data = sfpd_obj.parse_temperature( + dom_data_raw[start: end], 0) + temp = dom_temperature_data['data']['Temperature']['value'] + if temp is not None: + transceiver_dom_info_dict['temperature'] = temp + + if self.dom_volt_supported: + start = QSFP_VOLT_OFFSET - QSFP_DOM_BULK_DATA_START + end = start + QSFP_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage( + dom_data_raw[start: end], 0) + volt = dom_voltage_data['data']['Vcc']['value'] + if volt is not None: + transceiver_dom_info_dict['voltage'] = volt + + start = QSFP_CHANNL_MON_OFFSET - QSFP_DOM_BULK_DATA_START + end = start + QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_data_raw[start: end], 0) + + if self.dom_tx_power_supported: + transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TX1Power']['value'] + transceiver_dom_info_dict['tx2power'] = dom_channel_monitor_data['data']['TX2Power']['value'] + transceiver_dom_info_dict['tx3power'] = dom_channel_monitor_data['data']['TX3Power']['value'] + transceiver_dom_info_dict['tx4power'] = dom_channel_monitor_data['data']['TX4Power']['value'] + + if self.dom_rx_power_supported: + transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RX1Power']['value'] + transceiver_dom_info_dict['rx2power'] = dom_channel_monitor_data['data']['RX2Power']['value'] + transceiver_dom_info_dict['rx3power'] = dom_channel_monitor_data['data']['RX3Power']['value'] + transceiver_dom_info_dict['rx4power'] = dom_channel_monitor_data['data']['RX4Power']['value'] + + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TX1Bias']['value'] + transceiver_dom_info_dict['tx2bias'] = dom_channel_monitor_data['data']['TX2Bias']['value'] + transceiver_dom_info_dict['tx3bias'] = dom_channel_monitor_data['data']['TX3Bias']['value'] + transceiver_dom_info_dict['tx4bias'] = dom_channel_monitor_data['data']['TX4Bias']['value'] + + elif self.sfp_type == QSFP_DD_TYPE: + + offset = 0 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + + dom_data_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_DOM_BULK_DATA_START), QSFP_DD_DOM_BULK_DATA_SIZE) + if dom_data_raw is None: + return transceiver_dom_info_dict + + if self.dom_temp_supported: + start = QSFP_DD_TEMPE_OFFSET - QSFP_DD_DOM_BULK_DATA_START + end = start + QSFP_DD_TEMPE_WIDTH + dom_temperature_data = sfpd_obj.parse_temperature( + dom_data_raw[start: end], 0) + temp = dom_temperature_data['data']['Temperature']['value'] + if temp is not None: + transceiver_dom_info_dict['temperature'] = temp + + if self.dom_volt_supported: + start = QSFP_DD_VOLT_OFFSET - QSFP_DD_DOM_BULK_DATA_START + end = start + QSFP_DD_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage( + dom_data_raw[start: end], 0) + volt = dom_voltage_data['data']['Vcc']['value'] + if volt is not None: + transceiver_dom_info_dict['voltage'] = volt + + if self.dom_rx_tx_power_bias_supported: + # page 11h + dom_data_raw = self._read_eeprom_specific_bytes( + (QSFP_DD_CHANNL_MON_OFFSET), QSFP_DD_CHANNL_MON_WIDTH) + if dom_data_raw is None: + return transceiver_dom_info_dict + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_data_raw, 0) + + if self.dom_tx_power_supported: + transceiver_dom_info_dict['tx1power'] = str( + dom_channel_monitor_data['data']['TX1Power']['value']) + transceiver_dom_info_dict['tx2power'] = str( + dom_channel_monitor_data['data']['TX2Power']['value']) + transceiver_dom_info_dict['tx3power'] = str( + dom_channel_monitor_data['data']['TX3Power']['value']) + transceiver_dom_info_dict['tx4power'] = str( + dom_channel_monitor_data['data']['TX4Power']['value']) + transceiver_dom_info_dict['tx5power'] = str( + dom_channel_monitor_data['data']['TX5Power']['value']) + transceiver_dom_info_dict['tx6power'] = str( + dom_channel_monitor_data['data']['TX6Power']['value']) + transceiver_dom_info_dict['tx7power'] = str( + dom_channel_monitor_data['data']['TX7Power']['value']) + transceiver_dom_info_dict['tx8power'] = str( + dom_channel_monitor_data['data']['TX8Power']['value']) + + if self.dom_rx_power_supported: + transceiver_dom_info_dict['rx1power'] = str( + dom_channel_monitor_data['data']['RX1Power']['value']) + transceiver_dom_info_dict['rx2power'] = str( + dom_channel_monitor_data['data']['RX2Power']['value']) + transceiver_dom_info_dict['rx3power'] = str( + dom_channel_monitor_data['data']['RX3Power']['value']) + transceiver_dom_info_dict['rx4power'] = str( + dom_channel_monitor_data['data']['RX4Power']['value']) + transceiver_dom_info_dict['rx5power'] = str( + dom_channel_monitor_data['data']['RX5Power']['value']) + transceiver_dom_info_dict['rx6power'] = str( + dom_channel_monitor_data['data']['RX6Power']['value']) + transceiver_dom_info_dict['rx7power'] = str( + dom_channel_monitor_data['data']['RX7Power']['value']) + transceiver_dom_info_dict['rx8power'] = str( + dom_channel_monitor_data['data']['RX8Power']['value']) + + if self.dom_tx_bias_power_supported: + transceiver_dom_info_dict['tx1bias'] = str( + dom_channel_monitor_data['data']['TX1Bias']['value']) + transceiver_dom_info_dict['tx2bias'] = str( + dom_channel_monitor_data['data']['TX2Bias']['value']) + transceiver_dom_info_dict['tx3bias'] = str( + dom_channel_monitor_data['data']['TX3Bias']['value']) + transceiver_dom_info_dict['tx4bias'] = str( + dom_channel_monitor_data['data']['TX4Bias']['value']) + transceiver_dom_info_dict['tx5bias'] = str( + dom_channel_monitor_data['data']['TX5Bias']['value']) + transceiver_dom_info_dict['tx6bias'] = str( + dom_channel_monitor_data['data']['TX6Bias']['value']) + transceiver_dom_info_dict['tx7bias'] = str( + dom_channel_monitor_data['data']['TX7Bias']['value']) + transceiver_dom_info_dict['tx8bias'] = str( + dom_channel_monitor_data['data']['TX8Bias']['value']) + + return transceiver_dom_info_dict + + else: + if not self.dom_supported: + return transceiver_dom_info_dict + + offset = 256 + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + sfpd_obj._calibration_type = self.calibration + + dom_data_raw = self._read_eeprom_specific_bytes( + (offset + SFP_DOM_BULK_DATA_START), SFP_DOM_BULK_DATA_SIZE) + if dom_data_raw is None: + return transceiver_dom_info_dict + + start = SFP_TEMPE_OFFSET - SFP_DOM_BULK_DATA_START + end = start + SFP_TEMPE_WIDTH + dom_temperature_data = sfpd_obj.parse_temperature( + dom_data_raw[start: end], 0) + + start = SFP_VOLT_OFFSET - SFP_DOM_BULK_DATA_START + end = start + SFP_VOLT_WIDTH + dom_voltage_data = sfpd_obj.parse_voltage( + dom_data_raw[start: end], 0) + + start = SFP_CHANNL_MON_OFFSET - SFP_DOM_BULK_DATA_START + end = start + SFP_CHANNL_MON_WIDTH + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_data_raw[start: end], 0) + + transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] + transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] + transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RXPower']['value'] + transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TXBias']['value'] + transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TXPower']['value'] + + transceiver_dom_info_dict['lp_mode'] = self.get_lpmode() + transceiver_dom_info_dict['reset_status'] = self.get_reset_status() + transceiver_dom_info_dict['tx_disable'] = self.get_tx_disable() + transceiver_dom_info_dict['tx_disabled_channel'] = self.get_tx_disable_channel( + ) + + for key in transceiver_dom_info_dict: + val = transceiver_dom_info_dict[key] + transceiver_dom_info_dict[key] = self._convert_string_to_num( + val) if type(val) is str else val + + return transceiver_dom_info_dict + + def get_transceiver_threshold_info(self): + """ + Retrieves transceiver threshold info of this SFP + + Returns: + A dict which contains following keys/values : + ======================================================================== + keys |Value Format |Information + ---------------------------|---------------|---------------------------- + temphighalarm |FLOAT |High Alarm Threshold value of temperature in Celsius. + templowalarm |FLOAT |Low Alarm Threshold value of temperature in Celsius. + temphighwarning |FLOAT |High Warning Threshold value of temperature in Celsius. + templowwarning |FLOAT |Low Warning Threshold value of temperature in Celsius. + vcchighalarm |FLOAT |High Alarm Threshold value of supply voltage in mV. + vcclowalarm |FLOAT |Low Alarm Threshold value of supply voltage in mV. + vcchighwarning |FLOAT |High Warning Threshold value of supply voltage in mV. + vcclowwarning |FLOAT |Low Warning Threshold value of supply voltage in mV. + rxpowerhighalarm |FLOAT |High Alarm Threshold value of received power in dBm. + rxpowerlowalarm |FLOAT |Low Alarm Threshold value of received power in dBm. + rxpowerhighwarning |FLOAT |High Warning Threshold value of received power in dBm. + rxpowerlowwarning |FLOAT |Low Warning Threshold value of received power in dBm. + txpowerhighalarm |FLOAT |High Alarm Threshold value of transmit power in dBm. + txpowerlowalarm |FLOAT |Low Alarm Threshold value of transmit power in dBm. + txpowerhighwarning |FLOAT |High Warning Threshold value of transmit power in dBm. + txpowerlowwarning |FLOAT |Low Warning Threshold value of transmit power in dBm. + txbiashighalarm |FLOAT |High Alarm Threshold value of tx Bias Current in mA. + txbiaslowalarm |FLOAT |Low Alarm Threshold value of tx Bias Current in mA. + txbiashighwarning |FLOAT |High Warning Threshold value of tx Bias Current in mA. + txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. + ======================================================================== + """ + transceiver_dom_threshold_info_dict = dict.fromkeys( + threshold_dict_keys, NULL_VAL) + + if self.sfp_type == OSFP_TYPE: + pass + + elif self.sfp_type == QSFP_TYPE: + if not self.dom_supported or not self.qsfp_page3_available: + return transceiver_dom_threshold_info_dict + + # Dom Threshold data starts from offset 384 + # Revert offset back to 0 once data is retrieved + offset = QSFP_MODULE_UPPER_PAGE3_START + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_MODULE_THRESHOLD_OFFSET), QSFP_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_data = sfpd_obj.parse_module_threshold_values( + dom_module_threshold_raw, 0) + + dom_channel_threshold_raw = self._read_eeprom_specific_bytes((offset + QSFP_CHANNL_THRESHOLD_OFFSET), + QSFP_CHANNL_THRESHOLD_WIDTH) + if dom_channel_threshold_raw is None: + return transceiver_dom_threshold_info_dict + dom_channel_threshold_data = sfpd_obj.parse_channel_threshold_values( + dom_channel_threshold_raw, 0) + + # Threshold Data + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VccHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data['data']['VccHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VccLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VccLowWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_channel_threshold_data['data']['RxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_channel_threshold_data['data']['RxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_channel_threshold_data['data']['RxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_channel_threshold_data['data']['RxPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_channel_threshold_data['data']['TxBiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_channel_threshold_data['data']['TxBiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_channel_threshold_data['data']['TxBiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_channel_threshold_data['data']['TxBiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_channel_threshold_data['data']['TxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_channel_threshold_data['data']['TxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_channel_threshold_data['data']['TxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_channel_threshold_data['data']['TxPowerLowWarning']['value'] + + elif self.sfp_type == QSFP_DD_TYPE: + if not self.dom_supported: + return transceiver_dom_threshold_info_dict + + if not self.dom_thresholds_supported: + return transceiver_dom_threshold_info_dict + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + # page 02 + offset = 384 + dom_module_threshold_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_MODULE_THRESHOLD_OFFSET), QSFP_DD_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_data = sfpd_obj.parse_module_threshold_values( + dom_module_threshold_raw, 0) + + # Threshold Data + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VccHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data['data']['VccHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VccLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VccLowWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_module_threshold_data['data']['RxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_module_threshold_data['data']['RxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_module_threshold_data['data']['RxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_module_threshold_data['data']['RxPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_module_threshold_data['data']['TxBiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_module_threshold_data['data']['TxBiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_module_threshold_data['data']['TxBiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_module_threshold_data['data']['TxBiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_module_threshold_data['data']['TxPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_module_threshold_data['data']['TxPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_module_threshold_data['data']['TxPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_module_threshold_data['data']['TxPowerLowWarning']['value'] + + else: + offset = SFP_MODULE_ADDRA2_OFFSET + + if not self.dom_supported: + return transceiver_dom_threshold_info_dict + + sfpd_obj = sff8472Dom(None, self.calibration) + if sfpd_obj is None: + return transceiver_dom_threshold_info_dict + + dom_module_threshold_raw = self._read_eeprom_specific_bytes((offset + SFP_MODULE_THRESHOLD_OFFSET), + SFP_MODULE_THRESHOLD_WIDTH) + if dom_module_threshold_raw is not None: + dom_module_threshold_data = sfpd_obj.parse_alarm_warning_threshold( + dom_module_threshold_raw, 0) + else: + return transceiver_dom_threshold_info_dict + + # Threshold Data + transceiver_dom_threshold_info_dict['temphighalarm'] = dom_module_threshold_data['data']['TempHighAlarm']['value'] + transceiver_dom_threshold_info_dict['templowalarm'] = dom_module_threshold_data['data']['TempLowAlarm']['value'] + transceiver_dom_threshold_info_dict['temphighwarning'] = dom_module_threshold_data['data']['TempHighWarning']['value'] + transceiver_dom_threshold_info_dict['templowwarning'] = dom_module_threshold_data['data']['TempLowWarning']['value'] + transceiver_dom_threshold_info_dict['vcchighalarm'] = dom_module_threshold_data['data']['VoltageHighAlarm']['value'] + transceiver_dom_threshold_info_dict['vcclowalarm'] = dom_module_threshold_data['data']['VoltageLowAlarm']['value'] + transceiver_dom_threshold_info_dict['vcchighwarning'] = dom_module_threshold_data[ + 'data']['VoltageHighWarning']['value'] + transceiver_dom_threshold_info_dict['vcclowwarning'] = dom_module_threshold_data['data']['VoltageLowWarning']['value'] + transceiver_dom_threshold_info_dict['txbiashighalarm'] = dom_module_threshold_data['data']['BiasHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiaslowalarm'] = dom_module_threshold_data['data']['BiasLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txbiashighwarning'] = dom_module_threshold_data['data']['BiasHighWarning']['value'] + transceiver_dom_threshold_info_dict['txbiaslowwarning'] = dom_module_threshold_data['data']['BiasLowWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerhighalarm'] = dom_module_threshold_data['data']['TXPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerlowalarm'] = dom_module_threshold_data['data']['TXPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['txpowerhighwarning'] = dom_module_threshold_data['data']['TXPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['txpowerlowwarning'] = dom_module_threshold_data['data']['TXPowerLowWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighalarm'] = dom_module_threshold_data['data']['RXPowerHighAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowalarm'] = dom_module_threshold_data['data']['RXPowerLowAlarm']['value'] + transceiver_dom_threshold_info_dict['rxpowerhighwarning'] = dom_module_threshold_data['data']['RXPowerHighWarning']['value'] + transceiver_dom_threshold_info_dict['rxpowerlowwarning'] = dom_module_threshold_data['data']['RXPowerLowWarning']['value'] + + for key in transceiver_dom_threshold_info_dict: + transceiver_dom_threshold_info_dict[key] = self._convert_string_to_num( + transceiver_dom_threshold_info_dict[key]) + + return transceiver_dom_threshold_info_dict + + def get_reset_status(self): + """ + Retrieves the reset status of SFP + Returns: + A Boolean, True if reset enabled, False if disabled + """ + if not self.get_presence(): + return False + + if 1 <= self.port_num <= 32: + reset_path = "{}{}{}".format(self.i2c_cpld_path, '/module_reset_', self.port_num) + else: + return False + + val = self._api_helper.read_txt_file(reset_path) + if val is not None: + return int(val, 10)==1 + else: + return False + + def get_rx_los(self): + """ + Retrieves the RX LOS (lost-of-signal) status of SFP + Returns: + A Boolean, True if SFP has RX LOS, False if not. + Note : RX LOS status is latched until a call to get_rx_los or a reset. + """ + rx_los_list = [] + + if self.sfp_type == OSFP_TYPE: + return None + elif self.sfp_type == QSFP_TYPE: + offset = 0 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_RX_LOS_STATUS_OFFSET), + QSFP_CHANNL_RX_LOS_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 16) + rx_los_list.append(rx_los_data & 0x01 != 0) + rx_los_list.append(rx_los_data & 0x02 != 0) + rx_los_list.append(rx_los_data & 0x04 != 0) + rx_los_list.append(rx_los_data & 0x08 != 0) + else: + return [False] * 4 + + elif self.sfp_type == QSFP_DD_TYPE: + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = 128 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_CHANNL_RX_LOS_STATUS_OFFSET), + QSFP_DD_CHANNL_RX_LOS_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 8) + rx_los_list.append(rx_los_data & 0x01 != 0) + rx_los_list.append(rx_los_data & 0x02 != 0) + rx_los_list.append(rx_los_data & 0x04 != 0) + rx_los_list.append(rx_los_data & 0x08 != 0) + rx_los_list.append(rx_los_data & 0x10 != 0) + rx_los_list.append(rx_los_data & 0x20 != 0) + rx_los_list.append(rx_los_data & 0x40 != 0) + rx_los_list.append(rx_los_data & 0x80 != 0) + else: + return [False] * 8 + else: # SFP_TYPE + offset = 256 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_STATUS_OFFSET), SFP_CHANNL_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + rx_los_data = int(dom_channel_monitor_raw[0], 16) + rx_los_list.append(rx_los_data & 0x02 != 0) + else: + return [False] + return rx_los_list + + def get_tx_fault(self): + """ + Retrieves the TX fault status of SFP + Returns: + A Boolean, True if SFP has TX fault, False if not + Note : TX fault status is lached until a call to get_tx_fault or a reset. + """ + tx_fault_list = [] + + if self.sfp_type == OSFP_TYPE: + return None + elif self.sfp_type == QSFP_TYPE: + offset = 0 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_TX_FAULT_STATUS_OFFSET), + QSFP_CHANNL_TX_FAULT_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 16) + tx_fault_list.append(tx_fault_data & 0x01 != 0) + tx_fault_list.append(tx_fault_data & 0x02 != 0) + tx_fault_list.append(tx_fault_data & 0x04 != 0) + tx_fault_list.append(tx_fault_data & 0x08 != 0) + else: + return [False] * 4 + elif self.sfp_type == QSFP_DD_TYPE: + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = 128 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_CHANNL_TX_FAULT_STATUS_OFFSET), + QSFP_DD_CHANNL_TX_FAULT_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 8) + tx_fault_list.append(tx_fault_data & 0x01 != 0) + tx_fault_list.append(tx_fault_data & 0x02 != 0) + tx_fault_list.append(tx_fault_data & 0x04 != 0) + tx_fault_list.append(tx_fault_data & 0x08 != 0) + tx_fault_list.append(tx_fault_data & 0x10 != 0) + tx_fault_list.append(tx_fault_data & 0x20 != 0) + tx_fault_list.append(tx_fault_data & 0x40 != 0) + tx_fault_list.append(tx_fault_data & 0x80 != 0) + else: + return [False] * 8 + else: # SFP_TYPE + offset = 256 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_STATUS_OFFSET), SFP_CHANNL_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_fault_data = int(dom_channel_monitor_raw[0], 16) + tx_fault_list.append(tx_fault_data & 0x04 != 0) + else: + return None + return tx_fault_list + + def get_tx_disable(self): + """ + Retrieves the tx_disable status of this SFP + Returns: + A list of boolean values, representing the TX disable status + of each available channel, value is True if SFP channel + is TX disabled, False if not. + E.g., for a tranceiver with four channels: [False, False, True, False] + """ + tx_disable_list = [] + if self.sfp_type == OSFP_TYPE: + return None + elif self.sfp_type == QSFP_TYPE: + offset = 0 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_DISABLE_STATUS_OFFSET), + QSFP_CHANNL_DISABLE_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_disable_data = int(dom_channel_monitor_raw[0], 16) + tx_disable_list.append(tx_disable_data & 0x01 != 0) + tx_disable_list.append(tx_disable_data & 0x02 != 0) + tx_disable_list.append(tx_disable_data & 0x04 != 0) + tx_disable_list.append(tx_disable_data & 0x08 != 0) + else: + return [False] * 4 + + elif self.sfp_type == QSFP_DD_TYPE: + if self.dom_rx_tx_power_bias_supported: + offset = 128 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_CHANNL_DISABLE_STATUS_OFFSET), + QSFP_DD_CHANNL_DISABLE_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_disable_data = int(dom_channel_monitor_raw[0], 16) + tx_disable_list.append(tx_disable_data & 0x01 != 0) + tx_disable_list.append(tx_disable_data & 0x02 != 0) + tx_disable_list.append(tx_disable_data & 0x04 != 0) + tx_disable_list.append(tx_disable_data & 0x08 != 0) + tx_disable_list.append(tx_disable_data & 0x10 != 0) + tx_disable_list.append(tx_disable_data & 0x20 != 0) + tx_disable_list.append(tx_disable_data & 0x40 != 0) + tx_disable_list.append(tx_disable_data & 0x80 != 0) + else: + return [False] * 8 + else: # SFP_TYPE + offset = 256 + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_STATUS_OFFSET), SFP_CHANNL_STATUS_WIDTH) + if dom_channel_monitor_raw is not None: + tx_disable_data = int(dom_channel_monitor_raw[0], 16) + tx_disable_list.append(tx_disable_data & 0xC0 != 0) + else: + return [False] + return tx_disable_list + + def get_tx_disable_channel(self): + """ + Retrieves the TX disabled channels in this SFP + Returns: + A hex of 4 bits (bit 0 to bit 3 as channel 0 to channel 3) to represent + TX channels which have been disabled in this SFP. + As an example, a returned value of 0x5 indicates that channel 0 + and channel 2 have been disabled. + """ + tx_disable_list = self.get_tx_disable() + if tx_disable_list is None: + return 0 + tx_disabled = 0 + for i in range(len(tx_disable_list)): + if tx_disable_list[i]: + tx_disabled |= 1 << i + return tx_disabled + + def get_lpmode(self): + """ + Retrieves the lpmode (low power mode) status of this SFP + Returns: + A Boolean, True if lpmode is enabled, False if disabled + """ + if self.sfp_type == SFP_TYPE: + # SFP doesn't support this feature + return False + else: + try: + eeprom = None + + if not self.get_presence(): + return False + # Write to eeprom + port_eeprom_path = I2C_EEPROM_PATH.format(self._port_to_i2c_mapping[self.port_num]) + + eeprom = open(port_eeprom_path, "rb") + eeprom.seek(QSFP_POWEROVERRIDE_OFFSET) + lpmode = ord(eeprom.read(1)) + + if ((lpmode & 0x3) == 0x3): + return True # Low Power Mode if "Power override" bit is 1 and "Power set" bit is 1 + else: + # High Power Mode if one of the following conditions is matched: + # 1. "Power override" bit is 0 + # 2. "Power override" bit is 1 and "Power set" bit is 0 + return False + except IOError as e: + print("Error: unable to open file: %s" % str(e)) + return False + finally: + if eeprom is not None: + eeprom.close() + time.sleep(0.01) + + def get_power_set(self): + + if self.sfp_type == SFP_TYPE: + # SFP doesn't support this feature + return False + else: + power_set = False + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return False + + dom_control_raw = self._read_eeprom_specific_bytes( + QSFP_CONTROL_OFFSET, QSFP_CONTROL_WIDTH) if self.get_presence() else None + if dom_control_raw is not None: + dom_control_data = sfpd_obj.parse_control_bytes(dom_control_raw, 0) + power_set = ( + 'On' == dom_control_data['data']['PowerSet']['value']) + + return power_set + + def get_power_override(self): + """ + Retrieves the power-override status of this SFP + Returns: + A Boolean, True if power-override is enabled, False if disabled + """ + if self.sfp_type == QSFP_TYPE: + offset = 0 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return False + + dom_control_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CONTROL_OFFSET), QSFP_CONTROL_WIDTH) + if dom_control_raw is not None: + dom_control_data = sfpd_obj.parse_control_bytes( + dom_control_raw, 0) + return ('On' == dom_control_data['data']['PowerOverride']['value']) + else: + return False + else: + return NotImplementedError + + def get_temperature(self): + """ + Retrieves the temperature of this SFP + Returns: + An integer number of current temperature in Celsius + """ + default = 0.0 + if not self.dom_supported: + return default + + if self.sfp_type == QSFP_TYPE: + offset = 0 + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return default + + if self.dom_temp_supported: + dom_temperature_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature( + dom_temperature_raw, 0) + temp = self._convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + return temp + + elif self.sfp_type == QSFP_DD_TYPE: + offset = 0 + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if self.dom_temp_supported: + dom_temperature_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TEMPE_OFFSET), QSFP_DD_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature( + dom_temperature_raw, 0) + temp = self._convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + return temp + + else: + offset = 256 + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + sfpd_obj._calibration_type = 1 + + dom_temperature_raw = self._read_eeprom_specific_bytes( + (offset + SFP_TEMPE_OFFSET), SFP_TEMPE_WIDTH) + if dom_temperature_raw is not None: + dom_temperature_data = sfpd_obj.parse_temperature( + dom_temperature_raw, 0) + temp = self._convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + return temp + + return default + + def get_voltage(self): + """ + Retrieves the supply voltage of this SFP + Returns: + An integer number of supply voltage in mV + """ + default = 0.0 + + if not self.dom_supported: + return default + + if self.sfp_type == QSFP_TYPE: + offset = 0 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return default + + if self.dom_volt_supported: + dom_voltage_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_VOLT_OFFSET), QSFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage( + dom_voltage_raw, 0) + voltage = self._convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + return voltage + + if self.sfp_type == QSFP_DD_TYPE: + offset = 128 + + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if self.dom_volt_supported: + dom_voltage_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_VOLT_OFFSET), QSFP_DD_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage( + dom_voltage_raw, 0) + voltage = self._convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + return voltage + + else: + offset = 256 + + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + + sfpd_obj._calibration_type = self.calibration + + dom_voltage_raw = self._read_eeprom_specific_bytes( + (offset + SFP_VOLT_OFFSET), SFP_VOLT_WIDTH) + if dom_voltage_raw is not None: + dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0) + voltage = self._convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + return voltage + + return default + + def get_tx_bias(self): + """ + Retrieves the TX bias current of this SFP + Returns: + A list of four integer numbers, representing TX bias in mA + for channel 0 to channel 4. + Ex. ['110.09', '111.12', '108.21', '112.09'] + """ + tx_bias_list = [] + if self.sfp_type == QSFP_TYPE: + offset = 0 + default = [0.0] * 4 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return default + + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_channel_monitor_raw, 0) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX1Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX2Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX3Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX4Bias']['value'])) + else: + return default + + elif self.sfp_type == QSFP_DD_TYPE: + default = [0.0] * 8 + # page 11h + if self.dom_rx_tx_power_bias_supported: + offset = 128 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if dom_tx_bias_power_supported: + dom_tx_bias_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TX_BIAS_OFFSET), QSFP_DD_TX_BIAS_WIDTH) + if dom_tx_bias_raw is not None: + dom_tx_bias_data = sfpd_obj.parse_dom_tx_bias( + dom_tx_bias_raw, 0) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX1Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX2Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX3Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX4Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX5Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX6Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX7Bias']['value'])) + tx_bias_list.append(self._convert_string_to_num( + dom_tx_bias_data['data']['TX8Bias']['value'])) + else: + return default + else: + return default + + else: + offset = 256 + default = [0.0] + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + sfpd_obj._calibration_type = self.calibration + + if self.dom_supported: + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_channel_monitor_raw, 0) + tx_bias_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TXBias']['value'])) + else: + return default + else: + return default + + return tx_bias_list + + def get_rx_power(self): + """ + Retrieves the received optical power for this SFP + Returns: + A list of four integer numbers, representing received optical + power in mW for channel 0 to channel 4. + Ex. ['1.77', '1.71', '1.68', '1.70'] + """ + rx_power_list = [] + if self.sfp_type == OSFP_TYPE: + # OSFP not supported on our platform yet. + return None + + elif self.sfp_type == QSFP_TYPE: + offset = 0 + default = [0.0] * 4 + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return default + + if self.dom_rx_power_supported: + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_channel_monitor_raw, 0) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX1Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX2Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX3Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RX4Power']['value'])) + else: + return default + else: + return default + + elif self.sfp_type == QSFP_DD_TYPE: + default = [0.0] * 8 + # page 11 + if self.dom_rx_tx_power_bias_supported: + offset = 128 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if self.dom_rx_power_supported: + dom_rx_power_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_RX_POWER_OFFSET), QSFP_DD_RX_POWER_WIDTH) + if dom_rx_power_raw is not None: + dom_rx_power_data = sfpd_obj.parse_dom_rx_power( + dom_rx_power_raw, 0) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX1Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX2Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX3Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX4Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX5Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX6Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX7Power']['value'])) + rx_power_list.append(self._convert_string_to_num( + dom_rx_power_data['data']['RX8Power']['value'])) + else: + return default + else: + return default + + else: + offset = 256 + default = [0.0] + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + + if self.dom_supported: + sfpd_obj._calibration_type = self.calibration + + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_channel_monitor_raw, 0) + rx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['RXPower']['value'])) + else: + return default + else: + return default + return rx_power_list + + def get_tx_power(self): + """ + Retrieves the TX power of this SFP + Returns: + A list of four integer numbers, representing TX power in mW + for channel 0 to channel 4. + Ex. ['1.86', '1.86', '1.86', '1.86'] + """ + tx_power_list = [] + if self.sfp_type == OSFP_TYPE: + # OSFP not supported on our platform yet. + return tx_power_list + + elif self.sfp_type == QSFP_TYPE: + offset = 0 + default = [0.0] * 4 + + sfpd_obj = sff8436Dom() + if sfpd_obj is None: + return tx_power_list + + if self.dom_tx_power_supported: + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_CHANNL_MON_OFFSET), + QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power( + dom_channel_monitor_raw, 0) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX1Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX2Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX3Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TX4Power']['value'])) + else: + return default + else: + return default + + elif self.sfp_type == QSFP_DD_TYPE: + default = [0.0] * 8 + + # page 11 + if self.dom_rx_tx_power_bias_supported: + offset = 128 + sfpd_obj = qsfp_dd_Dom() + if sfpd_obj is None: + return default + + if self.dom_tx_power_supported: + dom_tx_power_raw = self._read_eeprom_specific_bytes( + (offset + QSFP_DD_TX_POWER_OFFSET), + QSFP_DD_TX_POWER_WIDTH) + if dom_tx_power_raw is not None: + dom_tx_power_data = sfpd_obj.parse_dom_tx_power( + dom_tx_power_raw, 0) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX1Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX2Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX3Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX4Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX5Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX6Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX7Power']['value'])) + tx_power_list.append(self._convert_string_to_num( + dom_tx_power_data['data']['TX8Power']['value'])) + else: + return default + else: + return default + + else: + offset = 256 + default = [0.0] + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return default + + if self.dom_supported: + sfpd_obj._calibration_type = self.calibration + + dom_channel_monitor_raw = self._read_eeprom_specific_bytes( + (offset + SFP_CHANNL_MON_OFFSET), SFP_CHANNL_MON_WIDTH) + if dom_channel_monitor_raw is not None: + dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params( + dom_channel_monitor_raw, 0) + tx_power_list.append(self._convert_string_to_num( + dom_channel_monitor_data['data']['TXPower']['value'])) + else: + return default + else: + return default + return tx_power_list + + def reset(self): + """ + Reset SFP and return all user module settings to their default srate. + Returns: + A boolean, True if successful, False if not + """ + # Check for invalid port_num + + if not self.get_presence(): + return False + + if 1 <= self.port_num <= 32: + reset_path = "{}{}{}".format(self.i2c_cpld_path, 'module_reset_', self.port_num) + else: + return False + + ret = self._api_helper.write_txt_file(reset_path, 1) #sysfs 1: enable reset + if ret is not True: + return ret + + time.sleep(0.2) + ret = self._api_helper.write_txt_file(reset_path, 0) #sysfs 1: disable reset + time.sleep(0.2) + + return ret + + def tx_disable(self, tx_disable): + """ + Disable SFP TX for all channels + Args: + tx_disable : A Boolean, True to enable tx_disable mode, False to disable + tx_disable mode. + Returns: + A boolean, True if tx_disable is set successfully, False if not + """ + if self.sfp_type == QSFP_TYPE: + sysfsfile_eeprom = None + try: + tx_disable_value = 0xf if tx_disable else 0x0 + # Write to eeprom + sysfsfile_eeprom = open(self._eeprom_path, "r+b") + sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) + sysfsfile_eeprom.write(struct.pack('B', tx_disable_value)) + except IOError: + return False + finally: + if sysfsfile_eeprom is not None: + sysfsfile_eeprom.close() + time.sleep(0.01) + return True + + elif self.sfp_type == SFP_TYPE: + disable_path = "{}{}{}".format(self.i2c_cpld_path, 'module_tx_disable_', self.port_num) + tx_disable_value = 1 if tx_disable else 0 + ret = self._api_helper.write_txt_file(disable_path, tx_disable_value) #sysfs 1: disable tx + if ret is not True: + return ret + + return False + + def tx_disable_channel(self, channel, disable): + """ + Sets the tx_disable for specified SFP channels + Args: + channel : A hex of 4 bits (bit 0 to bit 3) which represent channel 0 to 3, + e.g. 0x5 for channel 0 and channel 2. + disable : A boolean, True to disable TX channels specified in channel, + False to enable + Returns: + A boolean, True if successful, False if not + """ + if self.sfp_type == QSFP_TYPE: + sysfsfile_eeprom = None + try: + current_state = self.get_tx_disable_channel() + tx_disable_value = (current_state | channel) if \ + disable else (current_state & (~channel)) + + # Write to eeprom + sysfsfile_eeprom = open(self._eeprom_path, "r+b") + sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) + sysfsfile_eeprom.write(struct.pack('B', tx_disable_value)) + except IOError: + return False + finally: + if sysfsfile_eeprom is not None: + sysfsfile_eeprom.close() + time.sleep(0.01) + return True + + return False + + def set_lpmode(self, lpmode): + """ + Sets the lpmode (low power mode) of SFP + Args: + lpmode: A Boolean, True to enable lpmode, False to disable it + Note : lpmode can be overridden by set_power_override + Returns: + A boolean, True if lpmode is set successfully, False if not + """ + if self.sfp_type == SFP_TYPE: + return False # SFP doesn't support this feature + else: + if not self.get_presence(): + return False + + if lpmode: + self.set_power_override(True, True) + else: + self.set_power_override(True, False) + + return True + + def set_power_override(self, power_override, power_set): + """ + Sets SFP power level using power_override and power_set + Args: + power_override : + A Boolean, True to override set_lpmode and use power_set + to control SFP power, False to disable SFP power control + through power_override/power_set and use set_lpmode + to control SFP power. + power_set : + Only valid when power_override is True. + A Boolean, True to set SFP to low power mode, False to set + SFP to high power mode. + Returns: + A boolean, True if power-override and power_set are set successfully, + False if not + """ + if self.sfp_type == SFP_TYPE: + return False # SFP doesn't support this feature + else: + if not self.get_presence(): + return False + try: + power_override_bit = (1 << 0) if power_override else 0 + power_set_bit = (1 << 1) if power_set else (1 << 3) + + buffer = create_string_buffer(1) + if sys.version_info[0] >= 3: + buffer[0] = (power_override_bit | power_set_bit) + else: + buffer[0] = chr(power_override_bit | power_set_bit) + # Write to eeprom + port_eeprom_path = I2C_EEPROM_PATH.format(self._port_to_i2c_mapping[self.port_num]) + with open(port_eeprom_path, "r+b") as fd: + fd.seek(QSFP_POWEROVERRIDE_OFFSET) + fd.write(buffer[0]) + time.sleep(0.01) + except Exception as e: + print ('Error: unable to open file: ', str(e)) + return False + return True + + ############################################################## + ###################### Device methods ######################## + ############################################################## + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + sfputil_helper = SfpUtilHelper() + sfputil_helper.read_porttab_mappings( + self.__get_path_to_port_config_file()) + name = sfputil_helper.logical[self._index] or "Unknown" + return name + + def get_presence(self): + """ + Retrieves the presence of the device + Returns: + bool: True if device is present, False if not + """ + val = self._api_helper.read_txt_file( + self.i2c_cpld_path + "module_present_" + str(self.port_num)) + + if val is not None: + return int(val, 10)==1 + else: + return False + + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + transceiver_dom_info_dict = self.get_transceiver_info() + return transceiver_dom_info_dict.get("model", "N/A") + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + transceiver_dom_info_dict = self.get_transceiver_info() + return transceiver_dom_info_dict.get("serial", "N/A") + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return self.get_presence() and not self.get_reset_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + If the agent cannot determine the parent-relative position + for some reason, or if the associated value of + entPhysicalContainedIn is'0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device + or -1 if cannot determine the position + """ + return self.port_num + + def is_replaceable(self): + """ + Retrieves if replaceable + Returns: + A boolean value, True if replaceable + """ + return True diff --git a/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/thermal.py b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/thermal.py new file mode 100644 index 000000000000..93898faba664 --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/sonic_platform/thermal.py @@ -0,0 +1,238 @@ +############################################################################# +# Edgecore +# +# Thermal contains an implementation of SONiC Platform Base API and +# provides the thermal device status which are available in the platform +# +############################################################################# + +import os +import os.path +import glob + +try: + from sonic_platform_base.thermal_base import ThermalBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +PSU_I2C_PATH = "/sys/bus/i2c/devices/{}-00{}/" + +PSU_HWMON_I2C_MAPPING = { + 0: { + "num": 9, + "addr": "58" + }, + 1: { + "num": 10, + "addr": "59" + } +} + +PSU_CPLD_I2C_MAPPING = { + 0: { + "num": 9, + "addr": "50" + }, + 1: { + "num": 10, + "addr": "51" + } +} + +THERMAL_NAME_LIST = ["Temp sensor 1", "Temp sensor 2", "Temp sensor 3", + "Temp sensor 4", "Temp sensor 5", "Temp sensor 6" , + "Temp sensor 7"] + +PSU_THERMAL_NAME_LIST = ["PSU-1 temp sensor 1", "PSU-2 temp sensor 1"] + +SYSFS_PATH = "/sys/bus/i2c/devices" + +class Thermal(ThermalBase): + """Platform-specific Thermal class""" + + def __init__(self, thermal_index=0, is_psu=False, psu_index=0): + self.index = thermal_index + self.is_psu = is_psu + self.psu_index = psu_index + + if self.is_psu: + psu_i2c_bus = PSU_HWMON_I2C_MAPPING[psu_index]["num"] + psu_i2c_addr = PSU_HWMON_I2C_MAPPING[psu_index]["addr"] + self.psu_hwmon_path = PSU_I2C_PATH.format(psu_i2c_bus, + psu_i2c_addr) + psu_i2c_bus = PSU_CPLD_I2C_MAPPING[psu_index]["num"] + psu_i2c_addr = PSU_CPLD_I2C_MAPPING[psu_index]["addr"] + self.cpld_path = PSU_I2C_PATH.format(psu_i2c_bus, psu_i2c_addr) + + # Set hwmon path + i2c_path = { + 0: "18-0048/hwmon/hwmon*/", + 1: "18-0049/hwmon/hwmon*/", + 2: "18-004a/hwmon/hwmon*/", + 3: "18-004b/hwmon/hwmon*/", + 4: "18-004c/hwmon/hwmon*/", + 5: "18-004e/hwmon/hwmon*/", + 6: "18-004f/hwmon/hwmon*/" + }.get(self.index, None) + + self.hwmon_path = "{}/{}".format(SYSFS_PATH, i2c_path) + self.ss_key = THERMAL_NAME_LIST[self.index] + self.ss_index = 1 + + def __read_txt_file(self, file_path): + for filename in glob.glob(file_path): + try: + with open(filename, 'r') as fd: + data =fd.readline().rstrip() + return data + except IOError as e: + pass + + return None + + def __get_temp(self, temp_file): + if not self.is_psu: + temp_file_path = os.path.join(self.hwmon_path, temp_file) + else: + temp_file_path = temp_file + raw_temp = self.__read_txt_file(temp_file_path) + if raw_temp is not None: + return float(raw_temp)/1000 + else: + return 0 + + def __set_threshold(self, file_name, temperature): + if self.is_psu: + return True + temp_file_path = os.path.join(self.hwmon_path, file_name) + for filename in glob.glob(temp_file_path): + try: + with open(filename, 'w') as fd: + fd.write(str(temperature)) + return True + except IOError as e: + print("IOError") + return False + + + def get_temperature(self): + """ + Retrieves current temperature reading from thermal + Returns: + A float number of current temperature in Celsius up to nearest thousandth + of one degree Celsius, e.g. 30.125 + """ + if not self.is_psu: + temp_file = "temp{}_input".format(self.ss_index) + else: + temp_file = self.psu_hwmon_path + "psu_temp1_input" + return self.__get_temp(temp_file) + + def get_high_threshold(self): + """ + Retrieves the high threshold temperature of thermal + Returns: + A float number, the high threshold temperature of thermal in Celsius + up to nearest thousandth of one degree Celsius, e.g. 30.125 + """ + if self.is_psu: + return 80 + + temp_file = "temp{}_max".format(self.ss_index) + return self.__get_temp(temp_file) + + def set_high_threshold(self, temperature): + """ + Sets the high threshold temperature of thermal + Args : + temperature: A float number up to nearest thousandth of one degree Celsius, + e.g. 30.125 + Returns: + A boolean, True if threshold is set successfully, False if not + """ + temp_file = "temp{}_max".format(self.ss_index) + temperature = temperature *1000 + self.__set_threshold(temp_file, temperature) + + return True + + def get_name(self): + """ + Retrieves the name of the thermal device + Returns: + string: The name of the thermal device + """ + if self.is_psu: + return PSU_THERMAL_NAME_LIST[self.psu_index] + else: + return THERMAL_NAME_LIST[self.index] + + def get_presence(self): + """ + Retrieves the presence of the Thermal + Returns: + bool: True if Thermal is present, False if not + """ + if self.is_psu: + val = self.__read_txt_file(self.cpld_path + "psu_present") + return int(val, 10) == 1 + temp_file = "temp{}_input".format(self.ss_index) + temp_file_path = os.path.join(self.hwmon_path, temp_file) + raw_txt = self.__read_txt_file(temp_file_path) + if raw_txt is not None: + return True + else: + return False + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + if self.is_psu: + temp_file = self.psu_hwmon_path + "psu_temp1_input" + return self.get_presence() and (int( + self.__read_txt_file(temp_file))) + + file_str = "temp{}_input".format(self.ss_index) + file_path = os.path.join(self.hwmon_path, file_str) + raw_txt = self.__read_txt_file(file_path) + if raw_txt is None: + return False + else: + return int(raw_txt) != 0 + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + + return "N/A" + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return "N/A" + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. If the agent cannot determine the parent-relative position + for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device or -1 if cannot determine the position + """ + return self.index+1 + + def is_replaceable(self): + """ + Retrieves whether thermal module is replaceable + Returns: + A boolean value, True if replaceable, False if not + """ + return False diff --git a/device/accton/x86_64-accton_as9716_32d-r0/system_health_monitoring_config.json b/device/accton/x86_64-accton_as9716_32d-r0/system_health_monitoring_config.json new file mode 100644 index 000000000000..5ce3c6844b4a --- /dev/null +++ b/device/accton/x86_64-accton_as9716_32d-r0/system_health_monitoring_config.json @@ -0,0 +1,15 @@ +{ + "services_to_ignore": [], + "devices_to_ignore": [ + "asic", + "psu.temperature" + + ], + "user_defined_checkers": [], + "polling_interval": 60, + "led_color": { + "fault": "STATUS_LED_COLOR_AMBER", + "normal": "STATUS_LED_COLOR_GREEN", + "booting":"STATUS_LED_COLOR_GREEN_BLINK" + } +} diff --git a/platform/broadcom/sonic-platform-modules-accton/as9716-32d/modules/accton_as9716_32d_psu.c b/platform/broadcom/sonic-platform-modules-accton/as9716-32d/modules/accton_as9716_32d_psu.c index 06e8e23c3c53..3584e2aeaa72 100755 --- a/platform/broadcom/sonic-platform-modules-accton/as9716-32d/modules/accton_as9716_32d_psu.c +++ b/platform/broadcom/sonic-platform-modules-accton/as9716-32d/modules/accton_as9716_32d_psu.c @@ -34,8 +34,13 @@ #include #include -#define MAX_MODEL_NAME 16 +#define MAX_MODEL_NAME 19 #define MAX_SERIAL_NUMBER 19 +#define FAN_DIR_LEN 3 + +const char FAN_DIR_F2B[] = "F2B"; +const char FAN_DIR_B2F[] = "B2F"; +const char FAN_DIR_UNKNOWN[] = ""; static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); static ssize_t show_string(struct device *dev, struct device_attribute *da, char *buf); @@ -55,8 +60,9 @@ struct as9716_32d_psu_data { unsigned long last_updated; /* In jiffies */ u8 index; /* PSU index */ u8 status; /* Status(present/power_good) register read from CPLD */ - char model_name[MAX_MODEL_NAME]; /* Model name, read from eeprom */ - char serial_number[MAX_SERIAL_NUMBER]; + char model_name[MAX_MODEL_NAME+1]; /* Model name, read from eeprom */ + char serial_number[MAX_SERIAL_NUMBER+1]; + const char* fan_dir; }; static struct as9716_32d_psu_data *as9716_32d_psu_update_device(struct device *dev); @@ -65,7 +71,8 @@ enum as9716_32d_psu_sysfs_attributes { PSU_PRESENT, PSU_MODEL_NAME, PSU_POWER_GOOD, - PSU_SERIAL_NUMBER + PSU_SERIAL_NUMBER, + PSU_FAN_DIR }; /* sysfs attributes for hwmon @@ -74,13 +81,14 @@ static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRE static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_string, NULL, PSU_MODEL_NAME); static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); static SENSOR_DEVICE_ATTR(psu_serial_number, S_IRUGO, show_string, NULL, PSU_SERIAL_NUMBER); - +static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_string, NULL, PSU_FAN_DIR); static struct attribute *as9716_32d_psu_attributes[] = { &sensor_dev_attr_psu_present.dev_attr.attr, &sensor_dev_attr_psu_model_name.dev_attr.attr, &sensor_dev_attr_psu_power_good.dev_attr.attr, &sensor_dev_attr_psu_serial_number.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, NULL }; @@ -106,7 +114,7 @@ static ssize_t show_string(struct device *dev, struct device_attribute *da, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct as9716_32d_psu_data *data = as9716_32d_psu_update_device(dev); - char *ptr = NULL; + const char *ptr = NULL; if (!data->valid) { return -EIO; @@ -119,6 +127,9 @@ static ssize_t show_string(struct device *dev, struct device_attribute *da, case PSU_SERIAL_NUMBER: ptr = data->serial_number; break; + case PSU_FAN_DIR: + ptr = data->fan_dir; + break; default: return -EINVAL; } @@ -244,6 +255,44 @@ static int as9716_32d_psu_read_block(struct i2c_client *client, u8 command, u8 * return result; } +enum psu_type { + PSU_TYPE_AC_ACBEL_FSF019_F2B, // FSF019 + PSU_TYPE_AC_ACBEL_FSH082_F2B, // FSH082-610G + PSU_TYPE_AC_ACBEL_FSH095_B2F, // FSH095-610G + PSU_TYPE_3Y_YESM1300AM // YESM1300AM-2A01P10(F2B) or YESM1300AM-2R01P10(B2F) +}; + +struct model_name_info { + enum psu_type type; + u8 offset; + u8 length; + u8 chk_length; + char* model_name; + const char* fan_dir; +}; + +struct serial_number_info { + enum psu_type type; + u8 offset; + u8 length; + u8 chk_length; + char* serial_number; +}; + +struct model_name_info models[] = { + { PSU_TYPE_AC_ACBEL_FSF019_F2B, 0x20, 13, 6, "FSF019", FAN_DIR_F2B }, + { PSU_TYPE_AC_ACBEL_FSH082_F2B, 0x20, 13, 6, "FSH082", FAN_DIR_F2B }, + { PSU_TYPE_AC_ACBEL_FSH095_B2F, 0x20, 13, 6, "FSH095", FAN_DIR_B2F }, + { PSU_TYPE_3Y_YESM1300AM, 0x20, 19, 8, "YESM1300", NULL } +}; + +struct serial_number_info serials[] = { + { PSU_TYPE_AC_ACBEL_FSF019_F2B, 0x2e, 16, 16, "FSF019" }, + { PSU_TYPE_AC_ACBEL_FSH082_F2B, 0x35, 18, 18, "FSH082" }, + { PSU_TYPE_AC_ACBEL_FSH095_B2F, 0x35, 18, 18, "FSH095" }, + { PSU_TYPE_3Y_YESM1300AM, 0x35, 19, 19, "YESM1300" } +}; + static struct as9716_32d_psu_data *as9716_32d_psu_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -253,53 +302,102 @@ static struct as9716_32d_psu_data *as9716_32d_psu_update_device(struct device *d if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || !data->valid) { - int status; + int i, status; int power_good = 0; dev_dbg(&client->dev, "Starting as9716_32d update\n"); + data->valid = 0; /* Read psu status */ status = as9716_32d_cpld_read(0x60, 0x3); if (status < 0) { dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); + goto exit; } else { data->status = status; } /* Read model name */ - memset(data->model_name, 0, sizeof(data->model_name)); - memset(data->serial_number, 0, sizeof(data->serial_number)); + data->model_name[0] = '\0'; + data->serial_number[0] = '\0'; + data->fan_dir = FAN_DIR_UNKNOWN; power_good = (data->status >> (3-data->index) & 0x1); - + if (power_good) { - status = as9716_32d_psu_read_block(client, 0x20, data->model_name, - ARRAY_SIZE(data->model_name)-1); - if (status < 0) { - data->model_name[0] = '\0'; - dev_dbg(&client->dev, "unable to read model name from (0x%x)\n", client->addr); - } - else { - data->model_name[ARRAY_SIZE(data->model_name)-1] = '\0'; - + for (i = 0; i < ARRAY_SIZE(models); i++) { + memset(data->model_name, 0, sizeof(data->model_name)); + memset(data->serial_number, 0, sizeof(data->serial_number)); + + status = as9716_32d_psu_read_block(client, models[i].offset, + data->model_name, models[i].length); + if (status < 0) { + data->model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x) offset(0x%x)\n", + client->addr, models[i].offset); + goto exit; + } + else { + data->model_name[models[i].length] = '\0'; + } + + /* Determine if the model name is known, if not, read next index + */ + if (strncmp(data->model_name, models[i].model_name, models[i].chk_length) == 0) { + status = as9716_32d_psu_read_block(client, serials[i].offset, + data->serial_number, serials[i].length); + if (models[i].type == PSU_TYPE_3Y_YESM1300AM) { + // Adjust model name for PSU_TYPE_3Y_YESM1300AM + char buf[10] = {0}; + memcpy(buf, &data->model_name[9], 10); + memcpy(&data->model_name[8], buf, 10); + data->model_name[models[i].length-1] = '\0'; + + if (data->model_name[12] == 'A') + data->fan_dir = FAN_DIR_F2B; // YESM1300AM-2A01P10 + else + data->fan_dir = FAN_DIR_B2F; // YESM1300AM-2R01P10 + } + else if ((models[i].type == PSU_TYPE_AC_ACBEL_FSH082_F2B) || + (models[i].type == PSU_TYPE_AC_ACBEL_FSH095_B2F) || + (models[i].type == PSU_TYPE_AC_ACBEL_FSF019_F2B)) { + // Adjust model name for FSH082 / FSH095 / FSF019 + char buf[4] = {0}; + memcpy(buf, &data->model_name[9], 4); + memcpy(&data->model_name[7], buf, 4); + data->model_name[6] = '-'; + data->model_name[11] = '\0'; + data->fan_dir = models[i].fan_dir; + } + else { + data->fan_dir = models[i].fan_dir; + } + + // Read serial number + if (status < 0) { + data->serial_number[0] = '\0'; + dev_dbg(&client->dev, "unable to read serial num from (0x%x) offset(0x%x)\n", + client->addr, serials[i].offset); + goto exit; + } + else { + data->serial_number[serials[i].length] = '\0'; + break; + } + } + else { + data->serial_number[0] = '\0'; // model does not match, read next model + } } - /* Read from offset 0x2e ~ 0x3d (16 bytes) */ - status = as9716_32d_psu_read_block(client, 0x2e,data->serial_number, MAX_SERIAL_NUMBER); - if (status < 0) - { - data->serial_number[0] = '\0'; - dev_dbg(&client->dev, "unable to read model name from (0x%x) offset(0x2e)\n", client->addr); - } - data->serial_number[MAX_SERIAL_NUMBER-1]='\0'; } data->last_updated = jiffies; data->valid = 1; } +exit: mutex_unlock(&data->update_lock); - return data; } @@ -308,4 +406,3 @@ module_i2c_driver(as9716_32d_psu_driver); MODULE_AUTHOR("Jostar Yang "); MODULE_DESCRIPTION("as9716_32d_psu driver"); MODULE_LICENSE("GPL"); - diff --git a/platform/broadcom/sonic-platform-modules-accton/as9716-32d/sonic_platform_setup.py b/platform/broadcom/sonic-platform-modules-accton/as9716-32d/sonic_platform_setup.py new file mode 100644 index 000000000000..3356a429989e --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as9716-32d/sonic_platform_setup.py @@ -0,0 +1,34 @@ +from setuptools import setup + +DEVICE_NAME = 'accton' +HW_SKU = 'x86_64-accton_as9716_32d-r0' + +setup( + name='sonic-platform', + version='1.0', + description='SONiC platform API implementation on Accton Platforms', + license='Apache 2.0', + author='SONiC Team', + author_email='linuxnetdev@microsoft.com', + url='https://github.com/Azure/sonic-buildimage', + maintainer='Jostar Yang', + maintainer_email='jostar_yang@edge-core.com', + packages=[ + 'sonic_platform', + ], + package_dir={ + 'sonic_platform': '../../../../device/{}/{}/sonic_platform'.format(DEVICE_NAME, HW_SKU)}, + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Plugins', + 'Intended Audience :: Developers', + 'Intended Audience :: Information Technology', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Natural Language :: English', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 3.7', + 'Topic :: Utilities', + ], + keywords='sonic SONiC platform PLATFORM', +) diff --git a/platform/broadcom/sonic-platform-modules-accton/as9716-32d/utils/accton_as9716_32d_util.py b/platform/broadcom/sonic-platform-modules-accton/as9716-32d/utils/accton_as9716_32d_util.py index 898fcaa0f0d3..62c4df8144fe 100755 --- a/platform/broadcom/sonic-platform-modules-accton/as9716-32d/utils/accton_as9716_32d_util.py +++ b/platform/broadcom/sonic-platform-modules-accton/as9716-32d/utils/accton_as9716_32d_util.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2016 Accton Networks, Inc. # @@ -25,17 +25,14 @@ command: install : install drivers and generate related sysfs nodes clean : uninstall drivers and remove related sysfs nodes - show : show all systen status - sff : dump SFP eeprom - set : change board setting with fan|led|sfp """ - -import commands +import subprocess import getopt import sys import logging import re import time +import os PROJECT_NAME = 'as9716_32d' version = '0.0.1' @@ -43,35 +40,9 @@ DEBUG = False args = [] ALL_DEVICE = {} -DEVICE_NO = {'led':5, 'fan1':1, 'fan2':1,'fan3':1,'fan4':1,'fan5':1,'thermal':3, 'psu':2, 'sfp':34} - - -led_prefix ='/sys/devices/platform/as9716_32d_led/leds/accton_'+PROJECT_NAME+'_led::' -fan_prefix ='/sys/devices/platform/as9716_32d_' -hwmon_types = {'led': ['diag','fan','loc','psu1','psu2'], - 'fan1': ['fan'], - 'fan2': ['fan'], - 'fan3': ['fan'], - 'fan4': ['fan'], - 'fan5': ['fan'], - } -hwmon_nodes = {'led': ['brightness'] , - 'fan1': ['fan_duty_cycle_percentage', 'fan1_fault', 'fan1_speed_rpm', 'fan1_direction', 'fanr1_fault', 'fanr1_speed_rpm'], - 'fan2': ['fan_duty_cycle_percentage','fan2_fault', 'fan2_speed_rpm', 'fan2_direction', 'fanr2_fault', 'fanr2_speed_rpm'], - 'fan3': ['fan_duty_cycle_percentage','fan3_fault', 'fan3_speed_rpm', 'fan3_direction', 'fanr3_fault', 'fanr3_speed_rpm'], - 'fan4': ['fan_duty_cycle_percentage','fan4_fault', 'fan4_speed_rpm', 'fan4_direction', 'fanr4_fault', 'fanr4_speed_rpm'], - 'fan5': ['fan_duty_cycle_percentage','fan5_fault', 'fan5_speed_rpm', 'fan5_direction', 'fanr5_fault', 'fanr5_speed_rpm'], - 'fan6': ['fan_duty_cycle_percentage','fan6_fault', 'fan6_speed_rpm', 'fan6_direction', 'fanr5_fault', 'fanr5_speed_rpm'], - } -hwmon_prefix ={'led': led_prefix, - 'fan1': fan_prefix, - 'fan2': fan_prefix, - 'fan3': fan_prefix, - 'fan4': fan_prefix, - 'fan5': fan_prefix, - } i2c_prefix = '/sys/bus/i2c/devices/' +''' i2c_bus = {'fan': ['54-0066'], 'thermal': ['18-0048', '18-0049','18-004a', '18-004b', '18-004c', '18-004e', '18-004f'] , 'psu': ['9-0058', '10-0059'], @@ -80,6 +51,7 @@ 'thermal': ['hwmon/hwmon*/temp1_input'] , 'psu': ['psu_present ', 'psu_power_good'] , 'sfp': ['module_present_ ', 'module_tx_disable_']} +''' sfp_map = [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, @@ -135,8 +107,8 @@ if DEBUG == True: - print sys.argv[0] - print 'ARGV :', sys.argv[1:] + print(sys.argv[0]) + print('ARGV :', sys.argv[1:]) def main(): @@ -152,9 +124,9 @@ def main(): 'force', ]) if DEBUG == True: - print options - print args - print len(sys.argv) + print(options) + print(args) + print(len(sys.argv)) for opt, arg in options: if opt in ('-h', '--help'): @@ -171,22 +143,10 @@ def main(): do_install() elif arg == 'clean': do_uninstall() - elif arg == 'show': - device_traversal() - elif arg == 'sff': - if len(args)!=2: - show_eeprom_help() - elif int(args[1]) > DEVICE_NO['sfp'] -1: - show_eeprom_help() - else: - show_eeprom(args[1]) - return - elif arg == 'set': - if len(args)<3: - show_set_help() - else: - set_device(args[1:]) - return + elif arg == 'api': + do_sonic_platform_install() + elif arg == 'api_clean': + do_sonic_platform_clean() else: show_help() @@ -194,28 +154,20 @@ def main(): return 0 def show_help(): - print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]} - sys.exit(0) - -def show_set_help(): - cmd = sys.argv[0].split("/")[-1]+ " " + args[0] - print cmd +" [led|sfp|fan]" - print " use \""+ cmd + " led 0-4 \" to set led color" - print " use \""+ cmd + " fan 0-100\" to set fan duty percetage" - print " use \""+ cmd + " sfp 1-32 {0|1}\" to set sfp# tx_disable" + print( __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]}) sys.exit(0) def dis_i2c_ir3570a(addr): cmd = "i2cset -y 0 0x%x 0xE5 0x01" % addr - status, output = commands.getstatusoutput(cmd) + status, output = subprocess.getstatusoutput(cmd) cmd = "i2cset -y 0 0x%x 0x12 0x02" % addr - status, output = commands.getstatusoutput(cmd) + status, output = subprocess.getstatusoutput(cmd) return status def ir3570_check(): cmd = "i2cdump -y 0 0x42 s 0x9a" try: - status, output = commands.getstatusoutput(cmd) + status, output = subprocess.getstatusoutput(cmd) lines = output.split('\n') hn = re.findall(r'\w+', lines[-1]) version = int(hn[1], 16) @@ -224,29 +176,24 @@ def ir3570_check(): else: ret = 0 except Exception as e: - print "Error on ir3570_check() e:" + str(e) + print( "Error on ir3570_check() e:" + str(e)) return -1 return ret -def show_eeprom_help(): - cmd = sys.argv[0].split("/")[-1]+ " " + args[0] - print " use \""+ cmd + " 1-32 \" to dump sfp# eeprom" - sys.exit(0) - def my_log(txt): if DEBUG == True: - print "[ACCTON DBG]: "+txt + print("[ACCTON DBG]: "+txt) return def log_os_system(cmd, show): logging.info('Run :'+cmd) status = 1 output = "" - status, output = commands.getstatusoutput(cmd) + status, output = subprocess.getstatusoutput(cmd) my_log (cmd +"with result:" + str(status)) - my_log ("cmd:" + cmd) - my_log (" output:"+output) + #my_log ("cmd:" + cmd) + #my_log (" output:"+output) if status: logging.info('Failed :'+cmd) if show: @@ -285,6 +232,7 @@ def driver_install(): if FORCE == 0: return status log_os_system(cpld_reset_stop, 1) + print("Done driver_install") return 0 def driver_uninstall(): @@ -292,7 +240,7 @@ def driver_uninstall(): for i in range(0,len(kos)): rm = kos[-(i+1)].replace("modprobe", "modprobe -rq") lst = rm.split(" ") - print "lst=%s"%lst + if len(lst) > 3: del(lst[3]) rm = " ".join(lst) @@ -304,7 +252,7 @@ def driver_uninstall(): def eeprom_check(): cmd = "i2cget -y -f 0 0x57" - status, output = commands.getstatusoutput(cmd) + status, output = subprocess.getstatusoutput(cmd) return status def device_install(): @@ -315,10 +263,10 @@ def device_install(): #for pca954x need times to built new i2c buses if mknod[i].find('pca954') != -1: time.sleep(2) - print "init i2c device instance" + #print("init i2c device instance") status, output = log_os_system(mknod[i], 1) if status: - print output + print(output) if FORCE == 0: return status @@ -331,14 +279,17 @@ def device_install(): for i in range(0,len(sfp_map)): status, output =log_os_system("echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/new_device", 1) if status: - print output + print(output) if FORCE == 0: return status status, output =log_os_system("echo port"+str(i)+" > /sys/bus/i2c/devices/"+str(sfp_map[i])+"-0050/port_name", 1) if status: - print output + print(output) if FORCE == 0: return status + + print("Done device_install") + return def device_uninstall(): @@ -354,7 +305,7 @@ def device_uninstall(): target = "/sys/bus/i2c/devices/i2c-"+str(sfp_map[i])+"/delete_device" status, output =log_os_system("echo 0x50 > "+ target, 1) if status: - print output + print(output) if FORCE == 0: return status @@ -367,7 +318,7 @@ def device_uninstall(): temp[-1] = temp[-1].replace('new_device', 'delete_device') status, output = log_os_system(" ".join(temp), 1) if status: - print output + print(output) if FORCE == 0: return status @@ -388,10 +339,47 @@ def system_ready(): if driver_inserted() == False: return False if not device_exist(): - print "not device_exist()" + print("not device_exist()") return False return True +PLATFORM_ROOT_PATH = '/usr/share/sonic/device' +PLATFORM_API2_WHL_FILE_PY3 ='sonic_platform-1.0-py3-none-any.whl' +def do_sonic_platform_install(): + device_path = "{}{}{}{}".format(PLATFORM_ROOT_PATH, '/x86_64-accton_', PROJECT_NAME, '-r0') + SONIC_PLATFORM_BSP_WHL_PKG_PY3 = "/".join([device_path, PLATFORM_API2_WHL_FILE_PY3]) + + #Check API2.0 on py whl file + status, output = log_os_system("pip3 show sonic-platform > /dev/null 2>&1", 0) + if status: + if os.path.exists(SONIC_PLATFORM_BSP_WHL_PKG_PY3): + status, output = log_os_system("pip3 install "+ SONIC_PLATFORM_BSP_WHL_PKG_PY3, 1) + if status: + print("Error: Failed to install {}".format(PLATFORM_API2_WHL_FILE_PY3)) + return status + else: + print("Successfully installed {} package".format(PLATFORM_API2_WHL_FILE_PY3)) + else: + print('{} is not found'.format(PLATFORM_API2_WHL_FILE_PY3)) + else: + print('{} has installed'.format(PLATFORM_API2_WHL_FILE_PY3)) + + return + +def do_sonic_platform_clean(): + status, output = log_os_system("pip3 show sonic-platform > /dev/null 2>&1", 0) + if status: + print('{} does not install, not need to uninstall'.format(PLATFORM_API2_WHL_FILE_PY3)) + + else: + status, output = log_os_system("pip3 uninstall sonic-platform -y", 0) + if status: + print('Error: Failed to uninstall {}'.format(PLATFORM_API2_WHL_FILE_PY3)) + return status + else: + print('{} is uninstalled'.format(PLATFORM_API2_WHL_FILE_PY3)) + + return def do_install(): if driver_inserted() == False: status = driver_install() @@ -399,7 +387,7 @@ def do_install(): if FORCE == 0: return status else: - print PROJECT_NAME.upper()+" drivers detected...." + print(PROJECT_NAME.upper()+" drivers detected....") ir3570_check() @@ -409,208 +397,33 @@ def do_install(): if FORCE == 0: return status else: - print PROJECT_NAME.upper()+" devices detected...." + print(PROJECT_NAME.upper()+" devices detected....") + + do_sonic_platform_install() + return def do_uninstall(): if not device_exist(): - print PROJECT_NAME.upper() +" has no device installed...." + print(PROJECT_NAME.upper()+" has no device installed....") else: - print "Removing device...." + print("Removing device....") status = device_uninstall() if status: if FORCE == 0: return status if driver_inserted()== False : - print PROJECT_NAME.upper() +" has no driver installed...." + print(PROJECT_NAME.upper()+" has no driver installed....") else: - print "Removing installed driver...." + print("Removing installed driver....") status = driver_uninstall() if status: if FORCE == 0: return status - return - -def devices_info(): - global DEVICE_NO - global ALL_DEVICE - global i2c_bus, hwmon_types - for key in DEVICE_NO: - ALL_DEVICE[key]= {} - for i in range(0,DEVICE_NO[key]): - ALL_DEVICE[key][key+str(i+1)] = [] - - for key in i2c_bus: - buses = i2c_bus[key] - nodes = i2c_nodes[key] - for i in range(0,len(buses)): - for j in range(0,len(nodes)): - if 'fan' == key: - for k in range(0,DEVICE_NO[key]): - node = key+str(k+1) - path = i2c_prefix+ buses[i]+"/fan"+str(k+1)+"_"+ nodes[j] - my_log(node+": "+ path) - ALL_DEVICE[key][node].append(path) - elif 'sfp' == key: - for k in range(0,DEVICE_NO[key]): - node = key+str(k+1) - path = i2c_prefix+ str(sfp_map[k])+ buses[i]+"/"+ nodes[j] - my_log(node+": "+ path) - ALL_DEVICE[key][node].append(path) - else: - node = key+str(i+1) - path = i2c_prefix+ buses[i]+"/"+ nodes[j] - my_log(node+": "+ path) - ALL_DEVICE[key][node].append(path) - - for key in hwmon_types: - itypes = hwmon_types[key] - nodes = hwmon_nodes[key] - for i in range(0,len(itypes)): - for j in range(0,len(nodes)): - node = key+"_"+itypes[i] - path = hwmon_prefix[key]+ itypes[i]+"/"+ nodes[j] - my_log(node+": "+ path) - ALL_DEVICE[key][ key+str(i+1)].append(path) - - #show dict all in the order - if DEBUG == True: - for i in sorted(ALL_DEVICE.keys()): - print(i+": ") - for j in sorted(ALL_DEVICE[i].keys()): - print(" "+j) - for k in (ALL_DEVICE[i][j]): - print(" "+" "+k) - return - -def show_eeprom(index): - if system_ready()==False: - print("System's not ready.") - print("Please install first!") - return - - #if len(ALL_DEVICE)==0: - # devices_info() - #node = ALL_DEVICE['sfp'] ['sfp'+str(index)][0] - #node = node.replace(node.split("/")[-1], 'eeprom') - node= "/sys/bus/i2c/devices/"+str(sfp_map[int(index)])+"-0050" + "/eeprom" - # check if got hexdump command in current environment - ret, log = log_os_system("which hexdump", 0) - ret, log2 = log_os_system("which busybox hexdump", 0) - if len(log): - hex_cmd = 'hexdump' - elif len(log2): - hex_cmd = ' busybox hexdump' - else: - log = 'Failed : no hexdump cmd!!' - logging.info(log) - print log - return 1 - print "node=%s"%node - #print node + ":" - #ret, log = log_os_system("cat "+node+"| "+hex_cmd+" -C", 1) - ret, log = log_os_system(hex_cmd+" -C " + node, 1) - if ret==0: - print log - else: - print "**********device no found**********" - return - -def set_device(args): - global DEVICE_NO - global ALL_DEVICE - if system_ready()==False: - print("System's not ready.") - print("Please install first!") - return - - if len(ALL_DEVICE)==0: - devices_info() - - if args[0]=='led': - if int(args[1])>4: - show_set_help() - return - #print ALL_DEVICE['led'] - for i in range(0,len(ALL_DEVICE['led'])): - for k in (ALL_DEVICE['led']['led'+str(i+1)]): - ret, log = log_os_system("echo "+args[1]+" >"+k, 1) - if ret: - return ret - elif args[0]=='fan': - if int(args[1])>100: - show_set_help() - return - #print ALL_DEVICE['fan'] - #fan1~6 is all fine, all fan share same setting - node = ALL_DEVICE['fan1'] ['fan11'][0] - node = node.replace(node.split("/")[-1], 'fan1_duty_cycle_percentage') - ret, log = log_os_system("cat "+ node, 1) - if ret==0: - print ("Previous fan duty: " + log.strip() +"%") - ret, log = log_os_system("echo "+args[1]+" >"+node, 1) - if ret==0: - print ("Current fan duty: " + args[1] +"%") - return ret - elif args[0]=='sfp': - if int(args[1])> DEVICE_NO[args[0]] or int(args[1])==0: - show_set_help() - return - if len(args)<2: - show_set_help() - return - - if int(args[2])>1: - show_set_help() - return - - #print ALL_DEVICE[args[0]] - for i in range(0,len(ALL_DEVICE[args[0]])): - for j in ALL_DEVICE[args[0]][args[0]+str(args[1])]: - if j.find('tx_disable')!= -1: - ret, log = log_os_system("echo "+args[2]+" >"+ j, 1) - if ret: - return ret - - return + do_sonic_platform_clean() -#get digits inside a string. -#Ex: 31 for "sfp31" -def get_value(input): - digit = re.findall('\d+', input) - return int(digit[0]) - -def device_traversal(): - if system_ready()==False: - print("System's not ready.") - print("Please install first!") - return - - if len(ALL_DEVICE)==0: - devices_info() - for i in sorted(ALL_DEVICE.keys()): - print("============================================") - print(i.upper()+": ") - print("============================================") - - for j in sorted(ALL_DEVICE[i].keys(), key=get_value): - print " "+j+":", - for k in (ALL_DEVICE[i][j]): - ret, log = log_os_system("cat "+k, 0) - func = k.split("/")[-1].strip() - func = re.sub(j+'_','',func,1) - func = re.sub(i.lower()+'_','',func,1) - if ret==0: - print func+"="+log+" ", - else: - print func+"="+"X"+" ", - print - print("----------------------------------------------------------------") - - - print return def device_exist(): diff --git a/platform/broadcom/sonic-platform-modules-accton/debian/sonic-platform-accton-as9716-32d.install b/platform/broadcom/sonic-platform-modules-accton/debian/sonic-platform-accton-as9716-32d.install new file mode 100644 index 000000000000..14361a9a036f --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/debian/sonic-platform-accton-as9716-32d.install @@ -0,0 +1,2 @@ +as9716-32d/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-accton_as9716_32d-r0 +