From 9b4a96cb1852f2252ebcd9c0f4c9acd4fe79d0d4 Mon Sep 17 00:00:00 2001 From: Wirut Getbamrung <wgetbum@icloud.com> Date: Fri, 22 May 2020 17:50:43 +0700 Subject: [PATCH 1/3] [device/celestica]: DX010 platform API update (#4608) - Fix fancontrol.service path - Fix return temp format in thermal API - Improve init time in chassis API - Upgrade sfp API --- .../sonic_platform/chassis.py | 177 ++- .../sonic_platform/component.py | 23 +- .../sonic_platform/fan.py | 73 +- .../sonic_platform/helper.py | 121 ++ .../sonic_platform/psu.py | 25 +- .../sonic_platform/sfp.py | 1178 +++++++++++------ .../sonic_platform/thermal.py | 14 +- .../debian/platform-modules-dx010.install | 2 +- .../platform-modules-haliburton.install | 2 +- .../dx010/scripts/fancontrol.service | 12 + .../script}/fancontrol.service | 0 11 files changed, 1130 insertions(+), 497 deletions(-) create mode 100644 device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py create mode 100755 platform/broadcom/sonic-platform-modules-cel/dx010/scripts/fancontrol.service rename platform/broadcom/sonic-platform-modules-cel/{services/fancontrol => haliburton/script}/fancontrol.service (100%) diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py index 91e3dc0bf464..421d03c82bb4 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py @@ -8,20 +8,10 @@ # ############################################################################# -import sys -import re -import os -import subprocess -import json - try: + import sys from sonic_platform_base.chassis_base import ChassisBase - from sonic_platform.fan import Fan - from sonic_platform.psu import Psu - from sonic_platform.component import Component - from sonic_platform.thermal import Thermal - from sonic_platform.sfp import Sfp - from sonic_platform.eeprom import Tlv + from helper import APIHelper except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -44,38 +34,53 @@ class Chassis(ChassisBase): def __init__(self): ChassisBase.__init__(self) - self.config_data = {} + self._api_helper = APIHelper() + self.sfp_module_initialized = False + self.__initialize_eeprom() + self.is_host = self._api_helper.is_host() + + if not self.is_host: + self.__initialize_fan() + self.__initialize_psu() + self.__initialize_thermals() + else: + self.__initialize_components() + + def __initialize_sfp(self): + from sonic_platform.sfp import Sfp + for index in range(0, NUM_SFP): + sfp = Sfp(index) + self._sfp_list.append(sfp) + self.sfp_module_initialized = True + + 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_fan(self): + from sonic_platform.fan import Fan for fant_index in range(0, NUM_FAN_TRAY): for fan_index in range(0, NUM_FAN): fan = Fan(fant_index, fan_index) self._fan_list.append(fan) - 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) - # sfp index start from 1 - for index in range(0, NUM_SFP): - sfp = Sfp(index) - self._sfp_list.append(sfp) - for index in range(0, NUM_COMPONENT): - component = Component(index) - self._component_list.append(component) + def __initialize_eeprom(self): + from sonic_platform.eeprom import Tlv self._eeprom = Tlv() - 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 __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 get_base_mac(self): """ @@ -107,7 +112,6 @@ def get_system_eeprom_info(self): 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 @@ -118,16 +122,14 @@ def get_reboot_cause(self): description = 'None' reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER - reboot_cause_path = (HOST_REBOOT_CAUSE_PATH + REBOOT_CAUSE_FILE) if self.__is_host( - ) else PMON_REBOOT_CAUSE_PATH + REBOOT_CAUSE_FILE - prev_reboot_cause_path = (HOST_REBOOT_CAUSE_PATH + PREV_REBOOT_CAUSE_FILE) if self.__is_host( - ) else PMON_REBOOT_CAUSE_PATH + PREV_REBOOT_CAUSE_FILE + reboot_cause_path = (HOST_REBOOT_CAUSE_PATH + REBOOT_CAUSE_FILE) if self.is_host else PMON_REBOOT_CAUSE_PATH + REBOOT_CAUSE_FILE + prev_reboot_cause_path = (HOST_REBOOT_CAUSE_PATH + PREV_REBOOT_CAUSE_FILE) if self.is_host else PMON_REBOOT_CAUSE_PATH + PREV_REBOOT_CAUSE_FILE hw_reboot_cause = self._component_list[0].get_register_value(RESET_REGISTER) - sw_reboot_cause = self.__read_txt_file( + sw_reboot_cause = self._api_helper.read_txt_file( reboot_cause_path) or "Unknown" - prev_sw_reboot_cause = self.__read_txt_file( + prev_sw_reboot_cause = self._api_helper.read_txt_file( prev_reboot_cause_path) or "Unknown" if sw_reboot_cause == "Unknown" and (prev_sw_reboot_cause == "Unknown" or prev_sw_reboot_cause == self.REBOOT_CAUSE_POWER_LOSS) and hw_reboot_cause == "0x11": @@ -146,18 +148,32 @@ def get_reboot_cause(self): return (reboot_cause, description) - def get_watchdog(self): + ############################################################## + ######################## SFP methods ######################### + ############################################################## + + def get_num_sfps(self): """ - Retreives hardware watchdog device on this chassis + Retrieves the number of sfps available on this chassis Returns: - An object derived from WatchdogBase representing the hardware - watchdog device + An integer, the number of sfps available on this chassis """ - if self._watchdog is None: - from sonic_platform.watchdog import Watchdog - self._watchdog = Watchdog() + if not self.sfp_module_initialized: + self.__initialize_sfp() - return self._watchdog + return len(self._sfp_list) + + def get_all_sfps(self): + """ + Retrieves all sfps available on this chassis + Returns: + A list of objects derived from SfpBase representing all sfps + available on this chassis + """ + if not self.sfp_module_initialized: + self.__initialize_sfp() + + return self._sfp_list def get_sfp(self, index): """ @@ -171,6 +187,8 @@ def get_sfp(self, index): 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 @@ -179,3 +197,64 @@ def get_sfp(self, index): sys.stderr.write("SFP index {} out of range (1-{})\n".format( index, len(self._sfp_list))) return sfp + + ############################################################## + ####################### Other methods ######################## + ############################################################## + + def get_watchdog(self): + """ + Retreives hardware watchdog device on this chassis + Returns: + An object derived from WatchdogBase representing the hardware + watchdog device + """ + if self._watchdog is None: + from sonic_platform.watchdog import Watchdog + self._watchdog = Watchdog() + + return self._watchdog + + ############################################################## + ###################### Device methods ######################## + ############################################################## + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return self._api_helper.hwsku + + def get_presence(self): + """ + Retrieves the presence of the PSU + Returns: + bool: True if PSU 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 self._eeprom.get_pn() + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return self.get_serial_number() + + 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 diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py index d94a93474452..e51cd639a2ef 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py @@ -8,14 +8,13 @@ # ############################################################################# -import json import os.path import shutil -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") @@ -29,7 +28,8 @@ GETREG_PATH = "/sys/devices/platform/dx010_cpld/getreg" BIOS_VERSION_PATH = "/sys/class/dmi/id/bios_version" COMPONENT_NAME_LIST = ["CPLD1", "CPLD2", "CPLD3", "CPLD4", "BIOS"] -COMPONENT_DES_LIST = ["CPLD1", "CPLD2", "CPLD3", "CPLD4", "Basic Input/Output System"] +COMPONENT_DES_LIST = ["Used for managing the CPU", + "Used for managing QSFP+ ports (1-10)", "Used for managing QSFP+ ports (11-20)", "Used for managing QSFP+ ports (22-32)", "Basic Input/Output System"] class Component(ComponentBase): @@ -40,24 +40,9 @@ class Component(ComponentBase): def __init__(self, component_index): ComponentBase.__init__(self) self.index = component_index + self._api_helper = APIHelper() 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: - return False - return True - def __get_bios_version(self): # Retrieves the BIOS firmware version try: diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan.py index c0724e9bb688..c38b57f83cd9 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan.py @@ -14,6 +14,7 @@ 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") @@ -38,13 +39,16 @@ "addr": "5b" }, } +NULL_VAL = "N/A" class Fan(FanBase): """Platform-specific Fan class""" def __init__(self, fan_tray_index, fan_index=0, is_psu_fan=False, psu_index=0): + FanBase.__init__(self) self.fan_index = fan_index + self._api_helper = APIHelper() self.fan_tray_index = fan_tray_index self.is_psu_fan = is_psu_fan if self.is_psu_fan: @@ -75,16 +79,6 @@ def __init__(self, fan_tray_index, fan_index=0, is_psu_fan=False, psu_index=0): {'prs': 14, 'dir': 19, 'color': {'red': 37, 'green': 38}}, # 4 {'prs': 12, 'dir': 17, 'color': {'red': 33, 'green': 34}}, # 5 ] - FanBase.__init__(self) - - 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 "" def __write_txt_file(self, file_path, value): try: @@ -105,7 +99,7 @@ def __search_file_by_name(self, directory, file_name): def __get_gpio_base(self): for r in os.listdir(GPIO_DIR): label_path = os.path.join(GPIO_DIR, r, "label") - if "gpiochip" in r and GPIO_LABEL in self.__read_txt_file(label_path): + if "gpiochip" in r and GPIO_LABEL in self._api_helper.read_txt_file(label_path): return int(r[8:], 10) return 216 # Reserve @@ -113,7 +107,7 @@ def __get_gpio_value(self, pinnum): gpio_base = self.dx010_fan_gpio[0]['base'] gpio_dir = GPIO_DIR + '/gpio' + str(gpio_base+pinnum) gpio_file = gpio_dir + "/value" - retval = self.__read_txt_file(gpio_file) + retval = self._api_helper.read_txt_file(gpio_file) return retval.rstrip('\r\n') def __set_gpio_value(self, pinnum, value=0): @@ -154,7 +148,8 @@ def get_speed(self): fan_speed_sysfs_name = "fan{}_input".format(self.fan_index+1) fan_speed_sysfs_path = self.__search_file_by_name( self.psu_hwmon_path, fan_speed_sysfs_name) - fan_speed_rpm = self.__read_txt_file(fan_speed_sysfs_path) or 0 + fan_speed_rpm = self._api_helper.read_txt_file( + fan_speed_sysfs_path) or 0 fan_speed_raw = float(fan_speed_rpm)/PSU_FAN_MAX_RPM * 100 speed = math.ceil(float(fan_speed_rpm) * 100 / PSU_FAN_MAX_RPM) elif self.get_presence(): @@ -164,7 +159,7 @@ def get_speed(self): sysfs_path = "%s%s/%s" % ( EMC2305_PATH, device, EMC2305_FAN_INPUT) sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index]) - raw = self.__read_txt_file(sysfs_path).strip('\r\n') + raw = self._api_helper.read_txt_file(sysfs_path).strip('\r\n') pwm = int(raw, 10) if raw else 0 speed = math.ceil(float(pwm * 100 / EMC2305_MAX_PWM)) @@ -183,19 +178,7 @@ def get_target_speed(self): 0 : when PWM mode is use pwm : when pwm mode is not use """ - target = 0 - if not self.is_psu_fan: - chip = self.emc2305_chip_mapping[self.fan_index] - device = chip['device'] - fan_index = chip['index_map'] - sysfs_path = "%s%s/%s" % ( - EMC2305_PATH, device, EMC2305_FAN_TARGET) - sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index]) - raw = self.__read_txt_file(sysfs_path).strip('\r\n') - pwm = int(raw, 10) if raw else 0 - target = math.ceil(float(pwm) * 100 / EMC2305_MAX_PWM) - - return target + return 'N/A' def get_speed_tolerance(self): """ @@ -284,11 +267,43 @@ def get_name(self): def get_presence(self): """ - Retrieves the presence of the PSU + Retrieves the presence of the FAN Returns: - bool: True if PSU is present, False if not + bool: True if FAN is present, False if not """ present_str = self.__get_gpio_value( self.dx010_fan_gpio[self.fan_tray_index+1]['prs']) return int(present_str, 10) == 0 if not self.is_psu_fan else True + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + if self.is_psu_fan: + return NULL_VAL + + model = NULL_VAL + return model + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + if self.is_psu_fan: + return NULL_VAL + + serial = NULL_VAL + return 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.get_presence() and self.get_speed() > 0 diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py new file mode 100644 index 000000000000..86fc5ea726ef --- /dev/null +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python + +import os +import struct +import subprocess +from sonic_daemon_base.daemon_base import DaemonBase +from mmap import * + +HOST_CHK_CMD = "docker > /dev/null 2>&1" +EMPTY_STRING = "" + + +class APIHelper(): + + def __init__(self): + (self.platform, self.hwsku) = DaemonBase().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: + 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: + status = False + return status, result + + def run_interactive_command(self, cmd): + try: + os.system(cmd) + except: + return False + return True + + 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 read_one_line_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.readline() + return data.strip() + except IOError: + pass + return None + + 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: + 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: + 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: + status = False + return status, result diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/psu.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/psu.py index 8558b0b0cadd..d34c130dd4be 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/psu.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/psu.py @@ -14,6 +14,7 @@ try: from sonic_platform_base.psu_base import PsuBase from sonic_platform.fan import Fan + from helper import APIHelper except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -41,6 +42,7 @@ class Psu(PsuBase): def __init__(self, psu_index): PsuBase.__init__(self) self.index = psu_index + self._api_helper = APIHelper() self.green_led_path = GREEN_LED_PATH.format(self.index+1) self.dx010_psu_gpio = [ {'base': self.__get_gpio_base()}, @@ -54,27 +56,18 @@ def __init__(self, psu_index): fan = Fan(fan_index, 0, is_psu_fan=True, psu_index=self.index) self._fan_list.append(fan) - 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 "" - def __search_file_by_contain(self, directory, search_str, file_start): for dirpath, dirnames, files in os.walk(directory): for name in files: file_path = os.path.join(dirpath, name) - if name.startswith(file_start) and search_str in self.__read_txt_file(file_path): + if name.startswith(file_start) and search_str in self._api_helper.read_txt_file(file_path): return file_path return None def __get_gpio_base(self): for r in os.listdir(GPIO_DIR): label_path = os.path.join(GPIO_DIR, r, "label") - if "gpiochip" in r and GPIO_LABEL in self.__read_txt_file(label_path): + if "gpiochip" in r and GPIO_LABEL in self._api_helper.read_txt_file(label_path): return int(r[8:], 10) return 216 # Reserve @@ -82,7 +75,7 @@ def __get_gpio_value(self, pinnum): gpio_base = self.dx010_psu_gpio[0]['base'] gpio_dir = GPIO_DIR + '/gpio' + str(gpio_base+pinnum) gpio_file = gpio_dir + "/value" - retval = self.__read_txt_file(gpio_file) + retval = self._api_helper.read_txt_file(gpio_file) return retval.rstrip('\r\n') def get_voltage(self): @@ -104,7 +97,7 @@ def get_voltage(self): in_num = filter(str.isdigit, basename) vout_path = os.path.join( dir_name, voltage_name.format(in_num)) - vout_val = self.__read_txt_file(vout_path) + vout_val = self._api_helper.read_txt_file(vout_path) psu_voltage = float(vout_val) / 1000 return psu_voltage @@ -127,7 +120,7 @@ def get_current(self): cur_num = filter(str.isdigit, basename) cur_path = os.path.join( dir_name, current_name.format(cur_num)) - cur_val = self.__read_txt_file(cur_path) + cur_val = self._api_helper.read_txt_file(cur_path) psu_current = float(cur_val) / 1000 return psu_current @@ -150,7 +143,7 @@ def get_power(self): pw_num = filter(str.isdigit, basename) pw_path = os.path.join( dir_name, current_name.format(pw_num)) - pw_val = self.__read_txt_file(pw_path) + pw_val = self._api_helper.read_txt_file(pw_path) psu_power = float(pw_val) / 1000000 return psu_power @@ -196,7 +189,7 @@ def get_status_led(self): Returns: A string, one of the predefined STATUS_LED_COLOR_* strings above """ - status = self.__read_txt_file(self.green_led_path) + status = self._api_helper.read_txt_file(self.green_led_path) status_str = { '255': self.STATUS_LED_COLOR_GREEN, '0': self.STATUS_LED_COLOR_OFF diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py index 09c965a9b925..9b447798dddd 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py @@ -8,26 +8,46 @@ # ############################################################################# -import os import time import subprocess from ctypes import create_string_buffer try: from sonic_platform_base.sfp_base import SfpBase - from sonic_platform_base.sonic_sfp.sff8436 import sff8436Dom + 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.sfputilhelper import SfpUtilHelper + from helper import APIHelper except ImportError as e: raise ImportError(str(e) + "- required module not found") INFO_OFFSET = 128 DOM_OFFSET = 0 +# definitions of the offset and width for values in XCVR info eeprom XCVR_INTFACE_BULK_OFFSET = 0 XCVR_INTFACE_BULK_WIDTH_QSFP = 20 -XCVR_HW_REV_WIDTH_QSFP = 2 +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 @@ -36,13 +56,29 @@ 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_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 = 1 +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 + +# 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 # Offset for values in QSFP eeprom QSFP_DOM_REV_OFFSET = 1 @@ -51,62 +87,110 @@ 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_CONTROL_OFFSET = 86 -QSFP_CONTROL_WIDTH = 8 +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_CHANNEL_THRESHOLD_OFFSET = 176 -QSFP_CHANNEL_THRESHOLD_WIDTH = 16 +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_cable_length_tup = ('Length(km)', 'Length OM3(2m)', 'Length OM2(m)', 'Length OM1(m)', 'Length Cable Assembly(m)') +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') +SFP_TYPE = "SFP" +QSFP_TYPE = "QSFP" +OSFP_TYPE = "OSFP" + +PORT_START = 1 +PORT_END = 56 +QSFP_PORT_START = 1 +QSFP_PORT_END = 32 + +SFP_I2C_START = 26 class Sfp(SfpBase): """Platform-specific Sfp class""" - # Port number - PORT_START = 1 - PORT_END = 32 - # Path to QSFP sysfs RESET_PATH = "/sys/devices/platform/dx010_cpld/qsfp_reset" LP_PATH = "/sys/devices/platform/dx010_cpld/qsfp_lpmode" PRS_PATH = "/sys/devices/platform/dx010_cpld/qsfp_modprs" PLATFORM_ROOT_PATH = "/usr/share/sonic/device" PMON_HWSKU_PATH = "/usr/share/sonic/hwsku" - HOST_CHK_CMD = "docker > /dev/null 2>&1" - - PLATFORM = "x86_64-cel_seastone-r0" - HWSKU = "Seastone-DX010" def __init__(self, sfp_index): + SfpBase.__init__(self) # Init index self.index = sfp_index self.port_num = self.index + 1 + self.dom_supported = False + self.sfp_type, self.port_name = self.__get_sfp_info() + self._api_helper = APIHelper() + self.platform = self._api_helper.platform + self.hwsku = self._api_helper.hwsku # Init eeprom path eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom' self.port_to_eeprom_mapping = {} - for x in range(self.PORT_START, self.PORT_END + 1): - p_num = x - 1 if self.PORT_START == 1 else x - self.port_to_eeprom_mapping[x] = eeprom_path.format(p_num + 26) + self.port_to_i2c_mapping = {} + + for x in range(PORT_START, PORT_END + 1): + self.port_to_i2c_mapping[x] = (SFP_I2C_START + x) - 1 + port_eeprom_path = eeprom_path.format(self.port_to_i2c_mapping[x]) + self.port_to_eeprom_mapping[x] = port_eeprom_path self.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'] @@ -117,9 +201,21 @@ def __init__(self, sfp_index): self.threshold_dict_keys = ['temphighalarm', 'temphighwarning', 'templowalarm', 'templowwarning', 'vcchighalarm', 'vcchighwarning', 'vcclowalarm', 'vcclowwarning', 'rxpowerhighalarm', 'rxpowerhighwarning', 'rxpowerlowalarm', 'rxpowerlowwarning', 'txpowerhighalarm', 'txpowerhighwarning', 'txpowerlowalarm', 'txpowerlowwarning', 'txbiashighalarm', 'txbiashighwarning', 'txbiaslowalarm', 'txbiaslowwarning'] - SfpBase.__init__(self) + self._dom_capability_detect() + + def __get_sfp_info(self): + port_name = "Unknown" + sfp_type = "Unknown" - def _convert_string_to_num(self, value_str): + if self.port_num >= QSFP_PORT_START and self.port_num <= QSFP_TYPE: + sfp_type = QSFP_TYPE + port_name = "QSFP" + str(self.port_num - QSFP_PORT_START + 1) + elif self.port_num >= SFP_PORT_START and self.port_num <= SFP_PORT_END: + sfp_type = SFP_TYPE + port_name = "SFP" + str(self.port_num - SFP_PORT_START + 1) + return sfp_type, port_name + + def __convert_string_to_num(self, value_str): if "-inf" in value_str: return 'N/A' elif "Unknown" in value_str: @@ -139,22 +235,10 @@ def _convert_string_to_num(self, value_str): else: return 'N/A' - 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 "" - - 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 + platform_path = "/".join([self.PLATFORM_ROOT_PATH, self.platform]) + hwsku_path = "/".join([platform_path, self.hwsku] + ) if self._api_helper.is_host() else self.PMON_HWSKU_PATH return "/".join([hwsku_path, "port_config.ini"]) def __read_eeprom_specific_bytes(self, offset, num_bytes): @@ -179,6 +263,106 @@ def __read_eeprom_specific_bytes(self, offset, num_bytes): return eeprom_raw + def _dom_capability_detect(self): + 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": + 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_qsfp_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 == "SFP": + 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 @@ -202,85 +386,183 @@ def get_transceiver_info(self): vendor_date |1*255VCHAR |vendor date vendor_oui |1*255VCHAR |vendor OUI ======================================================================== - """ - # check present status - sfpi_obj = sff8436InterfaceId() - if not self.get_presence() or not sfpi_obj: - return {} - - offset = INFO_OFFSET - - sfp_interface_bulk_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_INTFACE_BULK_OFFSET), XCVR_INTFACE_BULK_WIDTH_QSFP) - sfp_interface_bulk_data = sfpi_obj.parse_sfp_info_bulk( - sfp_interface_bulk_raw, 0) - - sfp_vendor_name_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_VENDOR_NAME_OFFSET), XCVR_VENDOR_NAME_WIDTH) - sfp_vendor_name_data = sfpi_obj.parse_vendor_name( - sfp_vendor_name_raw, 0) - - sfp_vendor_pn_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_VENDOR_PN_OFFSET), XCVR_VENDOR_PN_WIDTH) - sfp_vendor_pn_data = sfpi_obj.parse_vendor_pn( - sfp_vendor_pn_raw, 0) - - sfp_vendor_rev_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_HW_REV_OFFSET), XCVR_HW_REV_WIDTH_QSFP) - sfp_vendor_rev_data = sfpi_obj.parse_vendor_rev( - sfp_vendor_rev_raw, 0) - - sfp_vendor_sn_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_VENDOR_SN_OFFSET), XCVR_VENDOR_SN_WIDTH) - sfp_vendor_sn_data = sfpi_obj.parse_vendor_sn( - sfp_vendor_sn_raw, 0) - - sfp_vendor_oui_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_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) - - sfp_vendor_date_raw = self.__read_eeprom_specific_bytes( - (offset + XCVR_VENDOR_DATE_OFFSET), XCVR_VENDOR_DATE_WIDTH) - sfp_vendor_date_data = sfpi_obj.parse_vendor_date( - sfp_vendor_date_raw, 0) - + """ + compliance_code_dict = {} transceiver_info_dict = dict.fromkeys(self.info_dict_keys, 'N/A') - compliance_code_dict = dict() + if not self.get_presence(): + return transceiver_info_dict + + # 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 None + + 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 None + + 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 None + + 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 None + + 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 None + + 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 None + + transceiver_info_dict['type'] = sfp_type_data['data']['type']['value'] + transceiver_info_dict['manufacturename'] = sfp_vendor_name_data['data']['Vendor Name']['value'] + transceiver_info_dict['modelname'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] + transceiver_info_dict['hardwarerev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] + transceiver_info_dict['serialnum'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] + transceiver_info_dict['vendor_oui'] = 'N/A' + transceiver_info_dict['vendor_date'] = 'N/A' + transceiver_info_dict['Connector'] = 'N/A' + transceiver_info_dict['encoding'] = 'N/A' + transceiver_info_dict['ext_identifier'] = 'N/A' + transceiver_info_dict['ext_rateselect_compliance'] = 'N/A' + transceiver_info_dict['cable_type'] = 'N/A' + transceiver_info_dict['cable_length'] = 'N/A' + transceiver_info_dict['specification_compliance'] = '{}' + transceiver_info_dict['nominal_bit_rate'] = 'N/A' - if sfp_interface_bulk_data: + else: + if self.sfp_type == QSFP_TYPE: + offset = 128 + vendor_rev_width = XCVR_HW_REV_WIDTH_QSFP + # cable_length_width = XCVR_CABLE_LENGTH_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 None + + else: + offset = 0 + vendor_rev_width = XCVR_HW_REV_WIDTH_SFP + # cable_length_width = XCVR_CABLE_LENGTH_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 None + 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 None + + 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['connector'] = sfp_interface_bulk_data['data']['Connector']['value'] + transceiver_info_dict['manufacturename'] = sfp_vendor_name_data['data']['Vendor Name']['value'] + transceiver_info_dict['modelname'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] + transceiver_info_dict['hardwarerev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] + transceiver_info_dict['serialnum'] = 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'] - transceiver_info_dict['type_abbrv_name'] = sfp_interface_bulk_data['data']['type_abbrv_name']['value'] - - transceiver_info_dict['manufacturer'] = sfp_vendor_name_data[ - 'data']['Vendor Name']['value'] if sfp_vendor_name_data else 'N/A' - transceiver_info_dict['model'] = sfp_vendor_pn_data['data']['Vendor PN']['value'] if sfp_vendor_pn_data else 'N/A' - transceiver_info_dict['hardware_rev'] = sfp_vendor_rev_data['data']['Vendor Rev']['value'] if sfp_vendor_rev_data else 'N/A' - transceiver_info_dict['serial'] = sfp_vendor_sn_data['data']['Vendor SN']['value'] if sfp_vendor_sn_data else 'N/A' - transceiver_info_dict['vendor_oui'] = sfp_vendor_oui_data['data']['Vendor OUI']['value'] if sfp_vendor_oui_data else 'N/A' - transceiver_info_dict['vendor_date'] = sfp_vendor_date_data[ - 'data']['VendorDataCode(YYYY-MM-DD Lot)']['value'] if sfp_vendor_date_data else 'N/A' - transceiver_info_dict['cable_type'] = "Unknown" - transceiver_info_dict['cable_length'] = "Unknown" - - 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'] - 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']) + + + 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'] + 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 @@ -309,84 +591,117 @@ def get_transceiver_bulk_status(self): | |for example, tx2power stands for tx power of channel 2. ======================================================================== """ - # check present status - sfpd_obj = sff8436Dom() - sfpi_obj = sff8436InterfaceId() + transceiver_dom_info_dict = dict.fromkeys(self.dom_dict_keys, 'N/A') - if not self.get_presence() or not sfpi_obj or not sfpd_obj: - return {} + 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 = self.__convert_string_to_num( + 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 = self.__convert_string_to_num( + 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'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX1Power']['value']) + transceiver_dom_info_dict['tx2power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX2Power']['value']) + transceiver_dom_info_dict['tx3power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX3Power']['value']) + transceiver_dom_info_dict['tx4power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX4Power']['value']) + + if self.dom_rx_power_supported: + transceiver_dom_info_dict['rx1power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RX1Power']['value']) + transceiver_dom_info_dict['rx2power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RX2Power']['value']) + transceiver_dom_info_dict['rx3power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RX3Power']['value']) + transceiver_dom_info_dict['rx4power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RX4Power']['value']) + + transceiver_dom_info_dict['tx1bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX1Bias']['value']) + transceiver_dom_info_dict['tx2bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX2Bias']['value']) + transceiver_dom_info_dict['tx3bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX3Bias']['value']) + transceiver_dom_info_dict['tx4bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TX4Bias']['value']) - transceiver_dom_info_dict = dict.fromkeys(self.dom_dict_keys, 'N/A') - offset = DOM_OFFSET - offset_xcvr = INFO_OFFSET - - # 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 + XCVR_DOM_CAPABILITY_OFFSET), XCVR_DOM_CAPABILITY_WIDTH) - if qsfp_dom_capability_raw is not None: - qspf_dom_capability_data = sfpi_obj.parse_qsfp_dom_capability( - qsfp_dom_capability_raw, 0) else: - return None + if not self.dom_supported: + return transceiver_dom_info_dict - 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) - transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value'] - - 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) - transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value'] - - qsfp_dom_rev_raw = self.__read_eeprom_specific_bytes( - (offset + QSFP_DOM_REV_OFFSET), QSFP_DOM_REV_WIDTH) - if qsfp_dom_rev_raw is not None: - qsfp_dom_rev_data = sfpd_obj.parse_sfp_dom_rev(qsfp_dom_rev_raw, 0) - qsfp_dom_rev = qsfp_dom_rev_data['data']['dom_rev']['value'] - - # The tx_power monitoring is only available on QSFP which compliant with SFF-8636 - # and claimed that it support tx_power with one indicator bit. - dom_channel_monitor_data = {} - dom_channel_monitor_raw = None - qsfp_tx_power_support = qspf_dom_capability_data['data']['Tx_power_support']['value'] - if (qsfp_dom_rev[0:8] != 'SFF-8636' or (qsfp_dom_rev[0:8] == 'SFF-8636' and qsfp_tx_power_support != 'on')): - dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( - (offset + QSFP_CHANNL_MON_OFFSET), QSFP_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) + offset = 256 + sfpd_obj = sff8472Dom() + if sfpd_obj is None: + return transceiver_dom_info_dict + sfpd_obj._calibration_type = self.calibration - else: - 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) - 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 dom_channel_monitor_raw: - 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'] - - for key in transceiver_dom_info_dict: - transceiver_dom_info_dict[key] = self._convert_string_to_num( - transceiver_dom_info_dict[key]) + dom_data_raw = self.__read_eeprom_specific_bytes( + (offset + SFP_DOM_BULK_DATA_START), SFP_DOM_BULK_DATA_SIZE) + + 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'] = self.__convert_string_to_num( + dom_temperature_data['data']['Temperature']['value']) + transceiver_dom_info_dict['voltage'] = self.__convert_string_to_num( + dom_voltage_data['data']['Vcc']['value']) + transceiver_dom_info_dict['rx1power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['RXPower']['value']) + transceiver_dom_info_dict['tx1bias'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TXBias']['value']) + transceiver_dom_info_dict['tx1power'] = self.__convert_string_to_num( + dom_channel_monitor_data['data']['TXPower']['value']) transceiver_dom_info_dict['rx_los'] = self.get_rx_los() transceiver_dom_info_dict['tx_fault'] = self.get_tx_fault() @@ -425,55 +740,106 @@ def get_transceiver_threshold_info(self): txbiaslowwarning |FLOAT |Low Warning Threshold value of tx Bias Current in mA. ======================================================================== """ - # check present status - sfpd_obj = sff8436Dom() + transceiver_dom_threshold_info_dict = dict.fromkeys( + self.threshold_dict_keys, 'N/A') - if not self.get_presence() or not sfpd_obj: - return {} + if self.sfp_type == OSFP_TYPE: + pass - transceiver_dom_threshold_dict = dict.fromkeys( - self.threshold_dict_keys, 'N/A') - dom_thres_raw = self.__read_eeprom_specific_bytes( - QSFP_MODULE_THRESHOLD_OFFSET, QSFP_MODULE_THRESHOLD_WIDTH) if self.get_presence() and sfpd_obj else None - - if dom_thres_raw: - module_threshold_values = sfpd_obj.parse_module_threshold_values( - dom_thres_raw, 0) - module_threshold_data = module_threshold_values.get('data') - if module_threshold_data: - transceiver_dom_threshold_dict['temphighalarm'] = module_threshold_data['TempHighAlarm']['value'] - transceiver_dom_threshold_dict['templowalarm'] = module_threshold_data['TempLowAlarm']['value'] - transceiver_dom_threshold_dict['temphighwarning'] = module_threshold_data['TempHighWarning']['value'] - transceiver_dom_threshold_dict['templowwarning'] = module_threshold_data['TempLowWarning']['value'] - transceiver_dom_threshold_dict['vcchighalarm'] = module_threshold_data['VccHighAlarm']['value'] - transceiver_dom_threshold_dict['vcclowalarm'] = module_threshold_data['VccLowAlarm']['value'] - transceiver_dom_threshold_dict['vcchighwarning'] = module_threshold_data['VccHighWarning']['value'] - transceiver_dom_threshold_dict['vcclowwarning'] = module_threshold_data['VccLowWarning']['value'] - - dom_thres_raw = self.__read_eeprom_specific_bytes( - QSFP_CHANNEL_THRESHOLD_OFFSET, QSFP_CHANNEL_THRESHOLD_WIDTH) if self.get_presence() and sfpd_obj else None - channel_threshold_values = sfpd_obj.parse_channel_threshold_values( - dom_thres_raw, 0) - channel_threshold_data = channel_threshold_values.get('data') - if channel_threshold_data: - transceiver_dom_threshold_dict['rxpowerhighalarm'] = channel_threshold_data['RxPowerHighAlarm']['value'] - transceiver_dom_threshold_dict['rxpowerlowalarm'] = channel_threshold_data['RxPowerLowAlarm']['value'] - transceiver_dom_threshold_dict['rxpowerhighwarning'] = channel_threshold_data['RxPowerHighWarning']['value'] - transceiver_dom_threshold_dict['rxpowerlowwarning'] = channel_threshold_data['RxPowerLowWarning']['value'] - transceiver_dom_threshold_dict['txpowerhighalarm'] = "0.0dBm" - transceiver_dom_threshold_dict['txpowerlowalarm'] = "0.0dBm" - transceiver_dom_threshold_dict['txpowerhighwarning'] = "0.0dBm" - transceiver_dom_threshold_dict['txpowerlowwarning'] = "0.0dBm" - transceiver_dom_threshold_dict['txbiashighalarm'] = channel_threshold_data['TxBiasHighAlarm']['value'] - transceiver_dom_threshold_dict['txbiaslowalarm'] = channel_threshold_data['TxBiasLowAlarm']['value'] - transceiver_dom_threshold_dict['txbiashighwarning'] = channel_threshold_data['TxBiasHighWarning']['value'] - transceiver_dom_threshold_dict['txbiaslowwarning'] = channel_threshold_data['TxBiasLowWarning']['value'] - - for key in transceiver_dom_threshold_dict: - transceiver_dom_threshold_dict[key] = self._convert_string_to_num( - transceiver_dom_threshold_dict[key]) - - return transceiver_dom_threshold_dict + 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'] + + 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): """ @@ -481,7 +847,7 @@ def get_reset_status(self): Returns: A Boolean, True if reset enabled, False if disabled """ - reset_status_raw = self.__read_txt_file(self.RESET_PATH).rstrip() + reset_status_raw = self._api_helper.read_txt_file(self.RESET_PATH).rstrip() if not reset_status_raw: return False @@ -497,16 +863,28 @@ def get_rx_los(self): Note : RX LOS status is latched until a call to get_rx_los or a reset. """ rx_los = False - rx_los_list = [] - dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( - QSFP_CHANNL_RX_LOS_STATUS_OFFSET, QSFP_CHANNL_RX_LOS_STATUS_WIDTH) if self.get_presence() else None - 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) - rx_los = rx_los_list[0] and rx_los_list[1] and rx_los_list[2] and rx_los_list[3] + if self.sfp_type == OSFP_TYPE: + return False + + 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) + rx1_los = (rx_los_data & 0x01 != 0) + rx2_los = (rx_los_data & 0x02 != 0) + rx3_los = (rx_los_data & 0x04 != 0) + rx4_los = (rx_los_data & 0x08 != 0) + rx_los = (rx1_los and rx2_los and rx3_los and rx4_los) + else: + 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 = (rx_los_data & 0x02 != 0) + return rx_los def get_tx_fault(self): @@ -516,19 +894,32 @@ def get_tx_fault(self): 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 = False - tx_fault_list = [] - dom_channel_monitor_raw = self.__read_eeprom_specific_bytes( - QSFP_CHANNL_TX_FAULT_STATUS_OFFSET, QSFP_CHANNL_TX_FAULT_STATUS_WIDTH) if self.get_presence() else None - 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) - tx_fault = tx_fault_list[0] and tx_fault_list[1] and tx_fault_list[2] and tx_fault_list[3] + tx4_fault = False + + if self.sfp_type == OSFP_TYPE or not self.dom_supported: + return False + + 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) + tx1_fault = (tx_fault_data & 0x01 != 0) + tx2_fault = (tx_fault_data & 0x02 != 0) + tx3_fault = (tx_fault_data & 0x04 != 0) + tx4_fault = (tx_fault_data & 0x08 != 0) + tx4_fault = ( + tx1_fault and tx2_fault and tx3_fault and tx4_fault) + else: + 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) + tx4_fault = (tx_fault_data & 0x04 != 0) - return tx_fault + return tx4_fault def get_tx_disable(self): """ @@ -536,26 +927,33 @@ def get_tx_disable(self): Returns: A Boolean, True if tx_disable is enabled, False if disabled """ - tx_disable_list = [] - sfpd_obj = sff8436Dom() - if sfpd_obj is None: + tx_disable = False + + if self.sfp_type == OSFP_TYPE and not self.dom_supported: 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) - tx_disable_list.append( - 'On' == dom_control_data['data']['TX1Disable']['value']) - tx_disable_list.append( - 'On' == dom_control_data['data']['TX2Disable']['value']) - tx_disable_list.append( - 'On' == dom_control_data['data']['TX3Disable']['value']) - tx_disable_list.append( - 'On' == dom_control_data['data']['TX4Disable']['value']) - - return tx_disable_list + 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) + tx1_disable = (tx_disable_data & 0x01 != 0) + tx2_disable = (tx_disable_data & 0x02 != 0) + tx3_disable = (tx_disable_data & 0x04 != 0) + tx4_disable = (tx_disable_data & 0x08 != 0) + tx_disable = ( + tx1_disable and tx2_disable and tx3_disable and tx4_disable) + else: + 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 = (tx_disable_data & 0xC0 != 0) + + return tx_disable def get_tx_disable_channel(self): """ @@ -566,9 +964,29 @@ def get_tx_disable_channel(self): 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_disable_list = [False, False, False, False] + + if self.sfp_type == OSFP_TYPE: + pass + + 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[0] = (tx_disable_data & 0x01 != 0) + tx_disable_list[1] = (tx_disable_data & 0x02 != 0) + tx_disable_list[2] = (tx_disable_data & 0x04 != 0) + tx_disable_list[3] = (tx_disable_data & 0x08 != 0) + else: + 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[0] = (tx_disable_data & 0xC0 != 0) + tx_disabled = 0 for i in range(len(tx_disable_list)): if tx_disable_list[i]: @@ -609,30 +1027,31 @@ def get_power_override(self): Returns: A Boolean, True if power-override is enabled, False if disabled """ - power_override = False - - offset = 0 - sfpd_obj = sff8436Dom() - if sfpd_obj is None: + 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( + 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_override = ( + 'On' == dom_control_data['data']['PowerOverride']['value']) + return power_override + else: 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_override = ( - 'On' == dom_control_data['data']['PowerOverride']['value']) - - return power_override - def get_temperature(self): """ Retrieves the temperature of this SFP Returns: An integer number of current temperature in Celsius """ - transceiver_dom_info_dict = self.get_transceiver_bulk_status() - return transceiver_dom_info_dict.get("temperature", "N/A") + transceiver_bulk_status = self.get_transceiver_bulk_status() + return transceiver_bulk_status.get("temperature", "N/A") def get_voltage(self): """ @@ -640,8 +1059,8 @@ def get_voltage(self): Returns: An integer number of supply voltage in mV """ - transceiver_dom_info_dict = self.get_transceiver_bulk_status() - return transceiver_dom_info_dict.get("voltage", "N/A") + transceiver_bulk_status = self.get_transceiver_bulk_status() + return transceiver_bulk_status.get("voltage", "N/A") def get_tx_bias(self): """ @@ -651,12 +1070,13 @@ def get_tx_bias(self): for channel 0 to channel 4. Ex. ['110.09', '111.12', '108.21', '112.09'] """ - transceiver_dom_info_dict = self.get_transceiver_bulk_status() - tx1_bs = transceiver_dom_info_dict.get("tx1bias", "N/A") - tx2_bs = transceiver_dom_info_dict.get("tx2bias", "N/A") - tx3_bs = transceiver_dom_info_dict.get("tx3bias", "N/A") - tx4_bs = transceiver_dom_info_dict.get("tx4bias", "N/A") - return [tx1_bs, tx2_bs, tx3_bs, tx4_bs] if transceiver_dom_info_dict else [] + transceiver_bulk_status = self.get_transceiver_bulk_status() + tx1_bs = transceiver_bulk_status.get("tx1bias", "N/A") + tx2_bs = transceiver_bulk_status.get("tx2bias", "N/A") + tx3_bs = transceiver_bulk_status.get("tx3bias", "N/A") + tx4_bs = transceiver_bulk_status.get("tx4bias", "N/A") + tx_bias_list = [tx1_bs, tx2_bs, tx3_bs, tx4_bs] + return tx_bias_list def get_rx_power(self): """ @@ -666,12 +1086,14 @@ def get_rx_power(self): power in mW for channel 0 to channel 4. Ex. ['1.77', '1.71', '1.68', '1.70'] """ - transceiver_dom_info_dict = self.get_transceiver_bulk_status() - rx1_pw = transceiver_dom_info_dict.get("rx1power", "N/A") - rx2_pw = transceiver_dom_info_dict.get("rx2power", "N/A") - rx3_pw = transceiver_dom_info_dict.get("rx3power", "N/A") - rx4_pw = transceiver_dom_info_dict.get("rx4power", "N/A") - return [rx1_pw, rx2_pw, rx3_pw, rx4_pw] if transceiver_dom_info_dict else [] + rx_power_list = [] + transceiver_bulk_status = self.get_transceiver_bulk_status() + rx1_p = transceiver_bulk_status.get("rx1power", "N/A") + rx2_p = transceiver_bulk_status.get("rx2power", "N/A") + rx3_p = transceiver_bulk_status.get("rx3power", "N/A") + rx4_p = transceiver_bulk_status.get("rx4power", "N/A") + rx_power_list = [rx1_p, rx2_p, rx3_p, rx4_p] + return rx_power_list def get_tx_power(self): """ @@ -681,12 +1103,14 @@ def get_tx_power(self): for channel 0 to channel 4. Ex. ['1.86', '1.86', '1.86', '1.86'] """ - transceiver_dom_info_dict = self.get_transceiver_bulk_status() - tx1_pw = transceiver_dom_info_dict.get("tx1power", "N/A") - tx2_pw = transceiver_dom_info_dict.get("tx2power", "N/A") - tx3_pw = transceiver_dom_info_dict.get("tx3power", "N/A") - tx4_pw = transceiver_dom_info_dict.get("tx4power", "N/A") - return [tx1_pw, tx2_pw, tx3_pw, tx4_pw] + tx_power_list = [] + transceiver_bulk_status = self.get_transceiver_bulk_status() + tx1_p = transceiver_bulk_status.get("tx1power", "N/A") + tx2_p = transceiver_bulk_status.get("tx2power", "N/A") + tx3_p = transceiver_bulk_status.get("tx3power", "N/A") + tx4_p = transceiver_bulk_status.get("tx4power", "N/A") + tx_power_list = [tx1_p, tx2_p, tx3_p, tx4_p] + return tx_power_list def reset(self): """ @@ -748,24 +1172,26 @@ def tx_disable(self, tx_disable): Returns: A boolean, True if tx_disable is set successfully, False if not """ - sysfsfile_eeprom = None - try: - tx_disable_ctl = 0xf if tx_disable else 0x0 - buffer = create_string_buffer(1) - buffer[0] = chr(tx_disable_ctl) - # Write to eeprom - sysfsfile_eeprom = open( - self.port_to_eeprom_mapping[self.port_num], "r+b") - sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) - sysfsfile_eeprom.write(buffer[0]) - except IOError as e: - print "Error: unable to open file: %s" % str(e) - return False - finally: - if sysfsfile_eeprom is not None: - sysfsfile_eeprom.close() - time.sleep(0.01) - return True + if self.sfp_type == QSFP_TYPE: + sysfsfile_eeprom = None + try: + tx_disable_ctl = 0xf if tx_disable else 0x0 + buffer = create_string_buffer(1) + buffer[0] = chr(tx_disable_ctl) + # Write to eeprom + sysfsfile_eeprom = open( + self.port_to_eeprom_mapping[self.port_num], "r+b") + sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) + sysfsfile_eeprom.write(buffer[0]) + except IOError as e: + #print("Error: unable to open file: %s" % str(e)) + return False + finally: + if sysfsfile_eeprom is not None: + sysfsfile_eeprom.close() + time.sleep(0.01) + return True + return False def tx_disable_channel(self, channel, disable): """ @@ -778,28 +1204,30 @@ def tx_disable_channel(self, channel, disable): Returns: A boolean, True if successful, False if not """ - sysfsfile_eeprom = None - try: - channel_state = self.get_tx_disable_channel() - tx_enable_mask = [0xe, 0xd, 0xb, 0x7] - tx_disable_mask = [0x1, 0x3, 0x7, 0xf] - tx_disable_ctl = channel_state | tx_disable_mask[ - channel] if disable else channel_state & tx_enable_mask[channel] - buffer = create_string_buffer(1) - buffer[0] = chr(tx_disable_ctl) - # Write to eeprom - sysfsfile_eeprom = open( - self.port_to_eeprom_mapping[self.port_num], "r+b") - sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) - sysfsfile_eeprom.write(buffer[0]) - except IOError as e: - print "Error: unable to open file: %s" % str(e) - return False - finally: - if sysfsfile_eeprom is not None: - sysfsfile_eeprom.close() - time.sleep(0.01) - return True + if self.sfp_type == QSFP_TYPE: + sysfsfile_eeprom = None + try: + channel_state = self.get_tx_disable_channel() + tx_enable_mask = [0xe, 0xd, 0xb, 0x7] + tx_disable_mask = [0x1, 0x3, 0x7, 0xf] + tx_disable_ctl = channel_state | tx_disable_mask[ + channel] if disable else channel_state & tx_enable_mask[channel] + buffer = create_string_buffer(1) + buffer[0] = chr(tx_disable_ctl) + # Write to eeprom + sysfsfile_eeprom = open( + self.port_to_eeprom_mapping[self.port_num], "r+b") + sysfsfile_eeprom.seek(QSFP_CONTROL_OFFSET) + sysfsfile_eeprom.write(buffer[0]) + except IOError as e: + #print("Error: unable to open file: %s" % str(e)) + 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): """ @@ -855,30 +1283,36 @@ def set_power_override(self, power_override, power_set): A boolean, True if power-override and power_set are set successfully, False if not """ - try: - power_override_bit = 0 - if power_override: - power_override_bit |= 1 << 0 - - power_set_bit = 0 - if power_set: - power_set_bit |= 1 << 1 + if self.sfp_type == QSFP_TYPE: + try: + power_override_bit = 0 + if power_override: + power_override_bit |= 1 << 0 + + power_set_bit = 0 + if power_set: + power_set_bit |= 1 << 1 + + buffer = create_string_buffer(1) + buffer[0] = chr(power_override_bit | power_set_bit) + # Write to eeprom + sysfsfile_eeprom = open( + self.port_to_eeprom_mapping[self.port_num], "r+b") + sysfsfile_eeprom.seek(QSFP_POWEROVERRIDE_OFFSET) + sysfsfile_eeprom.write(buffer[0]) + except IOError as e: + #print("Error: unable to open file: %s" % str(e)) + return False + finally: + if sysfsfile_eeprom is not None: + sysfsfile_eeprom.close() + time.sleep(0.01) + return True + return False - buffer = create_string_buffer(1) - buffer[0] = chr(power_override_bit | power_set_bit) - # Write to eeprom - sysfsfile_eeprom = open( - self.port_to_eeprom_mapping[self.port_num], "r+b") - sysfsfile_eeprom.seek(QSFP_POWEROVERRIDE_OFFSET) - sysfsfile_eeprom.write(buffer[0]) - except IOError as e: - print "Error: unable to open file: %s" % str(e) - return False - finally: - if sysfsfile_eeprom is not None: - sysfsfile_eeprom.close() - time.sleep(0.01) - return True + ############################################################## + ###################### Device methods ######################## + ############################################################## def get_name(self): """ @@ -898,7 +1332,7 @@ def get_presence(self): Returns: bool: True if PSU is present, False if not """ - presence_status_raw = self.__read_txt_file(self.PRS_PATH).rstrip() + presence_status_raw = self._api_helper.read_txt_file(self.PRS_PATH).rstrip() if not presence_status_raw: return False diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/thermal.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/thermal.py index 1e0a2c4b5645..18c2bd30c03e 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/thermal.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/thermal.py @@ -14,6 +14,7 @@ try: from sonic_platform_base.thermal_base import ThermalBase + from helper import APIHelper except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -27,6 +28,7 @@ class Thermal(ThermalBase): def __init__(self, thermal_index): self.index = thermal_index + self._api_helper = APIHelper() # Add thermal name self.THERMAL_NAME_LIST.append("Front-panel temp sensor 1") @@ -48,19 +50,11 @@ def __init__(self, thermal_index): self.ss_key = self.THERMAL_NAME_LIST[self.index] self.ss_index = 1 - def __read_txt_file(self, file_path): - try: - with open(file_path, 'r') as fd: - data = fd.read() - return data.strip() - except IOError: - pass - def __get_temp(self, temp_file): temp_file_path = os.path.join(self.hwmon_path, temp_file) - raw_temp = self.__read_txt_file(temp_file_path) + raw_temp = self._api_helper.read_txt_file(temp_file_path) temp = float(raw_temp)/1000 - return "{:.3f}".format(temp) + return float("{:.3f}".format(temp)) def __set_threshold(self, file_name, temperature): temp_file_path = os.path.join(self.hwmon_path, file_name) diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install index 9b456a7c9112..a36e2cd1377c 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install @@ -2,7 +2,7 @@ dx010/scripts/dx010_check_qsfp.sh usr/local/bin dx010/cfg/dx010-modules.conf etc/modules-load.d dx010/systemd/platform-modules-dx010.service lib/systemd/system dx010/scripts/fancontrol.sh etc/init.d -services/fancontrol/fancontrol.service lib/systemd/system +dx010/scripts/fancontrol.service lib/systemd/system services/fancontrol/fancontrol usr/local/bin dx010/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-cel_seastone-r0 services/platform_api/platform_api_mgnt.sh usr/local/bin diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-haliburton.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-haliburton.install index df78b7a34ea4..4fdfd33f1ec3 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-haliburton.install +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-haliburton.install @@ -1,7 +1,7 @@ haliburton/cfg/haliburton-modules.conf etc/modules-load.d haliburton/systemd/platform-modules-haliburton.service lib/systemd/system haliburton/script/fancontrol.sh etc/init.d -services/fancontrol/fancontrol.service lib/systemd/system +haliburton/script/fancontrol.service lib/systemd/system services/fancontrol/fancontrol usr/local/bin haliburton/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-cel_e1031-r0 services/platform_api/platform_api_mgnt.sh usr/local/bin diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/scripts/fancontrol.service b/platform/broadcom/sonic-platform-modules-cel/dx010/scripts/fancontrol.service new file mode 100755 index 000000000000..25b9f34df2d0 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/scripts/fancontrol.service @@ -0,0 +1,12 @@ +[Unit] +Description=fan speed regulator +After=platform-modules-dx010.service +Before=pmon.service + +[Service] +ExecStart=-/etc/init.d/fancontrol.sh start +ExecStop=-/etc/init.d/fancontrol.sh stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/platform/broadcom/sonic-platform-modules-cel/services/fancontrol/fancontrol.service b/platform/broadcom/sonic-platform-modules-cel/haliburton/script/fancontrol.service similarity index 100% rename from platform/broadcom/sonic-platform-modules-cel/services/fancontrol/fancontrol.service rename to platform/broadcom/sonic-platform-modules-cel/haliburton/script/fancontrol.service From 4389b9d34c27611c7c63100ea02be57bc29d3330 Mon Sep 17 00:00:00 2001 From: Wirut Getbamrung <wgetbum@icloud.com> Date: Thu, 17 Sep 2020 22:56:52 +0700 Subject: [PATCH 2/3] [device/celestica]: Update DX010 reboot cause API (#4678) - Add more cases support in DX010 reboot cause API - Add Thermal Overload reboot cause support - Add new Watchdog reboot cause support --- .../x86_64-cel_seastone-r0/fancontrol-B2F | 4 +- .../x86_64-cel_seastone-r0/fancontrol-F2B | 3 +- .../sonic_platform/__init__.py | 4 +- .../sonic_platform/chassis.py | 57 +- .../sonic_platform/helper.py | 13 + .../sonic_platform/watchdog.py | 172 ++-- .../debian/platform-modules-dx010.install | 1 + .../dx010/modules/dx010_cpld.c | 379 ++++--- .../dx010/scripts/thermal_overload_control.sh | 75 ++ .../services/fancontrol/fancontrol | 937 +++++++++--------- 10 files changed, 896 insertions(+), 749 deletions(-) create mode 100755 platform/broadcom/sonic-platform-modules-cel/dx010/scripts/thermal_overload_control.sh diff --git a/device/celestica/x86_64-cel_seastone-r0/fancontrol-B2F b/device/celestica/x86_64-cel_seastone-r0/fancontrol-B2F index c2132d7f2806..61b1c386f3d8 100644 --- a/device/celestica/x86_64-cel_seastone-r0/fancontrol-B2F +++ b/device/celestica/x86_64-cel_seastone-r0/fancontrol-B2F @@ -8,4 +8,6 @@ MINSTART=13-002e/pwm1=89 13-002e/pwm2=89 13-002e/pwm3=89 13-002e/pwm4=89 13-002e MINSTOP=13-002e/pwm1=89 13-002e/pwm2=89 13-002e/pwm3=89 13-002e/pwm4=89 13-002e/pwm5=89 13-004d/pwm1=89 13-004d/pwm2=89 13-004d/pwm3=89 13-004d/pwm4=89 13-004d/pwm5=89 MINPWM=13-002e/pwm1=89 13-002e/pwm2=89 13-002e/pwm3=89 13-002e/pwm4=89 13-002e/pwm5=89 13-004d/pwm1=89 13-004d/pwm2=89 13-004d/pwm3=89 13-004d/pwm4=89 13-004d/pwm5=89 MAXPWM=13-002e/pwm1=255 13-002e/pwm2=255 13-002e/pwm3=255 13-002e/pwm4=255 13-002e/pwm5=255 13-004d/pwm1=255 13-004d/pwm2=255 13-004d/pwm3=255 13-004d/pwm4=255 13-004d/pwm5=255 -THYST=13-002e/pwm1=3 13-002e/pwm2=3 13-002e/pwm3=3 13-002e/pwm4=3 13-002e/pwm5=3 13-004d/pwm1=3 13-004d/pwm2=3 13-004d/pwm3=3 13-004d/pwm4=3 13-004d/pwm5=3 \ No newline at end of file +THYST=13-002e/pwm1=3 13-002e/pwm2=3 13-002e/pwm3=3 13-002e/pwm4=3 13-002e/pwm5=3 13-004d/pwm1=3 13-004d/pwm2=3 13-004d/pwm3=3 13-004d/pwm4=3 13-004d/pwm5=3 +MAXTEMPCRIT=/sys/bus/i2c/devices/7-004a/hwmon/hwmon*/temp1_input=65 /sys/bus/i2c/devices/14-0048/hwmon/hwmon*/temp1_input=75 +MAXTEMPTYPE=/sys/bus/i2c/devices/7-004a/hwmon/hwmon*/temp1_input=ASIC /sys/bus/i2c/devices/14-0048/hwmon/hwmon*/temp1_input=CPU diff --git a/device/celestica/x86_64-cel_seastone-r0/fancontrol-F2B b/device/celestica/x86_64-cel_seastone-r0/fancontrol-F2B index dc67e2623cc2..b851d0a6d6ca 100644 --- a/device/celestica/x86_64-cel_seastone-r0/fancontrol-F2B +++ b/device/celestica/x86_64-cel_seastone-r0/fancontrol-F2B @@ -9,4 +9,5 @@ MINSTOP=13-002e/pwm1=89 13-002e/pwm2=89 13-002e/pwm3=89 13-002e/pwm4=89 13-002e/ MINPWM=13-002e/pwm1=89 13-002e/pwm2=89 13-002e/pwm3=89 13-002e/pwm4=89 13-002e/pwm5=89 13-004d/pwm1=89 13-004d/pwm2=89 13-004d/pwm3=89 13-004d/pwm4=89 13-004d/pwm5=89 MAXPWM=13-002e/pwm1=255 13-002e/pwm2=255 13-002e/pwm3=255 13-002e/pwm4=255 13-002e/pwm5=255 13-004d/pwm1=255 13-004d/pwm2=255 13-004d/pwm3=255 13-004d/pwm4=255 13-004d/pwm5=255 THYST=13-002e/pwm1=3 13-002e/pwm2=3 13-002e/pwm3=3 13-002e/pwm4=3 13-002e/pwm5=3 13-004d/pwm1=3 13-004d/pwm2=3 13-004d/pwm3=3 13-004d/pwm4=3 13-004d/pwm5=3 - +MAXTEMPCRIT=/sys/bus/i2c/devices/7-004a/hwmon/hwmon*/temp1_input=75 /sys/bus/i2c/devices/14-0048/hwmon/hwmon*/temp1_input=75 +MAXTEMPTYPE=/sys/bus/i2c/devices/7-004a/hwmon/hwmon*/temp1_input=ASIC /sys/bus/i2c/devices/14-0048/hwmon/hwmon*/temp1_input=CPU diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/__init__.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/__init__.py index d82f3749319c..7b86fa12b515 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/__init__.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/__init__.py @@ -1,2 +1,2 @@ -__all__ = ["platform", "chassis"] -from sonic_platform import * +import chassis +import platform diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py index 421d03c82bb4..18d57096b6d3 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py @@ -23,9 +23,9 @@ NUM_COMPONENT = 5 RESET_REGISTER = "0x103" 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" +GETREG_PATH = "/sys/devices/platform/dx010_cpld/getreg" HOST_CHK_CMD = "docker > /dev/null 2>&1" @@ -118,35 +118,38 @@ def get_reboot_cause(self): 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. - """ - description = 'None' - reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER - - reboot_cause_path = (HOST_REBOOT_CAUSE_PATH + REBOOT_CAUSE_FILE) if self.is_host else PMON_REBOOT_CAUSE_PATH + REBOOT_CAUSE_FILE - prev_reboot_cause_path = (HOST_REBOOT_CAUSE_PATH + PREV_REBOOT_CAUSE_FILE) if self.is_host else PMON_REBOOT_CAUSE_PATH + PREV_REBOOT_CAUSE_FILE - hw_reboot_cause = self._component_list[0].get_register_value(RESET_REGISTER) + REBOOT_CAUSE_POWER_LOSS = "Power Loss" + REBOOT_CAUSE_THERMAL_OVERLOAD_CPU = "Thermal Overload: CPU" + REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC = "Thermal Overload: ASIC" + REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER = "Thermal Overload: Other" + REBOOT_CAUSE_INSUFFICIENT_FAN_SPEED = "Insufficient Fan Speed" + REBOOT_CAUSE_WATCHDOG = "Watchdog" + REBOOT_CAUSE_HARDWARE_OTHER = "Hardware - Other" + REBOOT_CAUSE_NON_HARDWARE = "Non-Hardware" + """ + reboot_cause_path = (HOST_REBOOT_CAUSE_PATH + REBOOT_CAUSE_FILE) sw_reboot_cause = self._api_helper.read_txt_file( reboot_cause_path) or "Unknown" - prev_sw_reboot_cause = self._api_helper.read_txt_file( - prev_reboot_cause_path) or "Unknown" - - if sw_reboot_cause == "Unknown" and (prev_sw_reboot_cause == "Unknown" or prev_sw_reboot_cause == self.REBOOT_CAUSE_POWER_LOSS) and hw_reboot_cause == "0x11": - reboot_cause = self.REBOOT_CAUSE_POWER_LOSS - elif sw_reboot_cause != "Unknown" and hw_reboot_cause == "0x11": - reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE - description = sw_reboot_cause - elif prev_reboot_cause_path != "Unknown" and hw_reboot_cause == "0x11": - reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE - description = prev_sw_reboot_cause - elif hw_reboot_cause == "0x22": - reboot_cause = self.REBOOT_CAUSE_WATCHDOG, - else: - reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER - description = 'Unknown reason' + hw_reboot_cause = self._api_helper.get_cpld_reg_value( + GETREG_PATH, RESET_REGISTER) + + prev_reboot_cause = { + '0x11': (self.REBOOT_CAUSE_POWER_LOSS, 'Power on reset'), + '0x22': (self.REBOOT_CAUSE_HARDWARE_OTHER, 'CPLD_WD_RESET'), + '0x33': (self.REBOOT_CAUSE_HARDWARE_OTHER, 'Power cycle reset triggered by CPU'), + '0x44': (self.REBOOT_CAUSE_HARDWARE_OTHER, 'Power cycle reset triggered by reset button'), + '0x55': (self.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU, ''), + '0x66': (self.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC, ''), + '0x77': (self.REBOOT_CAUSE_WATCHDOG, '') + }.get(hw_reboot_cause, (self.REBOOT_CAUSE_HARDWARE_OTHER, 'Unknown reason')) + + if sw_reboot_cause != 'Unknown' and hw_reboot_cause == '0x11': + prev_reboot_cause = ( + self.REBOOT_CAUSE_NON_HARDWARE, sw_reboot_cause) - return (reboot_cause, description) + return prev_reboot_cause ############################################################## ######################## SFP methods ######################### @@ -229,9 +232,9 @@ def get_name(self): def get_presence(self): """ - Retrieves the presence of the PSU + Retrieves the presence of the Chassis Returns: - bool: True if PSU is present, False if not + bool: True if Chassis is present, False if not """ return True diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py index 86fc5ea726ef..6566d4698e8d 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py @@ -69,6 +69,19 @@ def read_one_line_file(self, file_path): pass return None + def write_txt_file(self, file_path, value): + try: + with open(file_path, 'w') as fd: + fd.write(str(value)) + except Exception: + return False + return True + + def get_cpld_reg_value(self, getreg_path, register): + cmd = "echo {1} > {0}; cat {0}".format(getreg_path, register) + status, result = self.run_command(cmd) + return result if status else None + def ipmi_raw(self, netfn, cmd): status = True result = "" diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py index b539bc06f618..e410a940ee7e 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py @@ -7,115 +7,79 @@ # ############################################################################# import ctypes -import fcntl import os import subprocess import time -import array try: from sonic_platform_base.watchdog_base import WatchdogBase + from helper import APIHelper except ImportError as e: raise ImportError(str(e) + "- required module not found") -""" ioctl constants """ -IO_WRITE = 0x40000000 -IO_READ = 0x80000000 -IO_READ_WRITE = 0xC0000000 -IO_SIZE_INT = 0x00040000 -IO_SIZE_40 = 0x00280000 -IO_TYPE_WATCHDOG = ord('W') << 8 - -WDR_INT = IO_READ | IO_SIZE_INT | IO_TYPE_WATCHDOG -WDR_40 = IO_READ | IO_SIZE_40 | IO_TYPE_WATCHDOG -WDWR_INT = IO_READ_WRITE | IO_SIZE_INT | IO_TYPE_WATCHDOG - -""" Watchdog ioctl commands """ -WDIOC_GETSUPPORT = 0 | WDR_40 -WDIOC_GETSTATUS = 1 | WDR_INT -WDIOC_GETBOOTSTATUS = 2 | WDR_INT -WDIOC_GETTEMP = 3 | WDR_INT -WDIOC_SETOPTIONS = 4 | WDR_INT -WDIOC_KEEPALIVE = 5 | WDR_INT -WDIOC_SETTIMEOUT = 6 | WDWR_INT -WDIOC_GETTIMEOUT = 7 | WDR_INT -WDIOC_SETPRETIMEOUT = 8 | WDWR_INT -WDIOC_GETPRETIMEOUT = 9 | WDR_INT -WDIOC_GETTIMELEFT = 10 | WDR_INT - -""" Watchdog status constants """ -WDIOS_DISABLECARD = 0x0001 -WDIOS_ENABLECARD = 0x0002 - +PLATFORM_CPLD_PATH = '/sys/devices/platform/dx010_cpld' +GETREG_FILE = 'getreg' +SETREG_FILE = 'setreg' +WDT_ENABLE_REG = '0x141' +WDT_TIMER_L_BIT_REG = '0x142' +WDT_TIMER_M_BIT_REG = '0x143' +WDT_TIMER_H_BIT_REG = '0x144' +WDT_KEEP_ALVIVE_REG = '0x145' +ENABLE_CMD = '0x1' +DISABLE_CMD = '0x0' WDT_COMMON_ERROR = -1 -WD_MAIN_IDENTITY = "iTCO_wdt" -WDT_SYSFS_PATH = "/sys/class/watchdog/" class Watchdog(WatchdogBase): def __init__(self): + # Init helper + self._api_helper = APIHelper() + + # Init cpld reg path + self.setreg_path = os.path.join(PLATFORM_CPLD_PATH, SETREG_FILE) + self.getreg_path = os.path.join(PLATFORM_CPLD_PATH, GETREG_FILE) - self.watchdog, self.wdt_main_dev_name = self._get_wdt() - self.status_path = "/sys/class/watchdog/%s/status" % self.wdt_main_dev_name - self.state_path = "/sys/class/watchdog/%s/state" % self.wdt_main_dev_name - self.timeout_path = "/sys/class/watchdog/%s/timeout" % self.wdt_main_dev_name # Set default value self._disable() self.armed = False - self.timeout = self._gettimeout(self.timeout_path) - - def _is_wd_main(self, dev): - """ - Checks watchdog identity - """ - identity = self._read_file( - "{}/{}/identity".format(WDT_SYSFS_PATH, dev)) - return identity == WD_MAIN_IDENTITY - - def _get_wdt(self): - """ - Retrieves watchdog device - """ - wdt_main_dev_list = [dev for dev in os.listdir( - "/dev/") if dev.startswith("watchdog") and self._is_wd_main(dev)] - if not wdt_main_dev_list: - return None - wdt_main_dev_name = wdt_main_dev_list[0] - watchdog_device_path = "/dev/{}".format(wdt_main_dev_name) - watchdog = os.open(watchdog_device_path, os.O_RDWR) - return watchdog, wdt_main_dev_name - - def _read_file(self, file_path): - """ - Read text file - """ - try: - with open(file_path, "r") as fd: - txt = fd.read() - except IOError: - return WDT_COMMON_ERROR - return txt.strip() + self.timeout = self._gettimeout() def _enable(self): """ Turn on the watchdog timer """ - req = array.array('h', [WDIOS_ENABLECARD]) - fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + # echo 0x141 0x1 > /sys/devices/platform/dx010_cpld/setreg + enable_val = '{} {}'.format(WDT_ENABLE_REG, ENABLE_CMD) + return self._api_helper.write_txt_file(self.setreg_path, enable_val) def _disable(self): """ Turn off the watchdog timer """ - req = array.array('h', [WDIOS_DISABLECARD]) - fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + # echo 0x141 0x0 > /sys/devices/platform/dx010_cpld/setreg + disable_val = '{} {}'.format(WDT_ENABLE_REG, DISABLE_CMD) + return self._api_helper.write_txt_file(self.setreg_path, disable_val) def _keepalive(self): """ Keep alive watchdog timer """ - fcntl.ioctl(self.watchdog, WDIOC_KEEPALIVE) + # echo 0x145 0x1 > /sys/devices/platform/dx010_cpld/setreg + enable_val = '{} {}'.format(WDT_KEEP_ALVIVE_REG, ENABLE_CMD) + return self._api_helper.write_txt_file(self.setreg_path, enable_val) + + def _get_level_hex(self, sub_hex): + sub_hex_str = sub_hex.replace("x", "0") + return hex(int(sub_hex_str, 16)) + + def _seconds_to_lmh_hex(self, seconds): + ms = seconds*1000 # calculate timeout in ms format + hex_str = hex(ms) + l = self._get_level_hex(hex_str[-2:]) + m = self._get_level_hex(hex_str[-4:-2]) + h = self._get_level_hex(hex_str[-6:-4]) + return (l, m, h) def _settimeout(self, seconds): """ @@ -123,29 +87,35 @@ def _settimeout(self, seconds): @param seconds - timeout in seconds @return is the actual set timeout """ - req = array.array('I', [seconds]) - fcntl.ioctl(self.watchdog, WDIOC_SETTIMEOUT, req, True) - return int(req[0]) + # max = 0xffffff = 16777.215 seconds + + (l, m, h) = self._seconds_to_lmh_hex(seconds) + set_h_val = '{} {}'.format(WDT_TIMER_H_BIT_REG, h) + set_m_val = '{} {}'.format(WDT_TIMER_M_BIT_REG, m) + set_l_val = '{} {}'.format(WDT_TIMER_L_BIT_REG, l) + + self._api_helper.write_txt_file(self.setreg_path, set_h_val) + self._api_helper.write_txt_file(self.setreg_path, set_m_val) + self._api_helper.write_txt_file(self.setreg_path, set_l_val) - def _gettimeout(self, timeout_path): + return seconds + + def _gettimeout(self): """ Get watchdog timeout @return watchdog timeout """ - req = array.array('I', [0]) - fcntl.ioctl(self.watchdog, WDIOC_GETTIMEOUT, req, True) - - return int(req[0]) - def _gettimeleft(self): - """ - Get time left before watchdog timer expires - @return time left in seconds - """ - req = array.array('I', [0]) - fcntl.ioctl(self.watchdog, WDIOC_GETTIMELEFT, req, True) + h_bit = self._api_helper.get_cpld_reg_value( + self.getreg_path, WDT_TIMER_H_BIT_REG) + m_bit = self._api_helper.get_cpld_reg_value( + self.getreg_path, WDT_TIMER_M_BIT_REG) + l_bit = self._api_helper.get_cpld_reg_value( + self.getreg_path, WDT_TIMER_L_BIT_REG) - return int(req[0]) + hex_time = '0x{}{}{}'.format(h_bit[2:], m_bit[2:], l_bit[2:]) + ms = int(hex_time, 16) + return int(float(ms)/1000) ################################################################# @@ -169,12 +139,15 @@ def arm(self, seconds): try: if self.timeout != seconds: self.timeout = self._settimeout(seconds) + if self.armed: self._keepalive() else: self._enable() self.armed = True + ret = self.timeout + self.arm_timestamp = time.time() except IOError as e: pass @@ -215,19 +188,4 @@ def get_remaining_time(self): watchdog timer. If the watchdog is not armed, returns -1. """ - timeleft = WDT_COMMON_ERROR - - if self.armed: - try: - timeleft = self._gettimeleft() - except IOError: - pass - - return timeleft - - def __del__(self): - """ - Close watchdog - """ - - os.close(self.watchdog) + return int(self.timeout - (time.time() - self.arm_timestamp)) if self.armed else WDT_COMMON_ERROR diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install index a36e2cd1377c..d7720cea90f4 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.install @@ -3,6 +3,7 @@ dx010/cfg/dx010-modules.conf etc/modules-load.d dx010/systemd/platform-modules-dx010.service lib/systemd/system dx010/scripts/fancontrol.sh etc/init.d dx010/scripts/fancontrol.service lib/systemd/system +dx010/scripts/thermal_overload_control.sh usr/local/bin services/fancontrol/fancontrol usr/local/bin dx010/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-cel_seastone-r0 services/platform_api/platform_api_mgnt.sh usr/local/bin diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c index 7893220ff6d5..ead28807832d 100644 --- a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c @@ -76,42 +76,52 @@ #define PORT_SFPP1 33 #define PORT_SFPP2 34 -#define PORT_ID_BANK1 0x210 -#define PORT_ID_BANK2 0x290 -#define PORT_ID_BANK3 0x390 -#define OPCODE_ID_BANK1 0x211 -#define OPCODE_ID_BANK2 0x291 -#define OPCODE_ID_BANK3 0x391 +#define CPLD_I2C_CLK_100Khz_BIT BIT(6) +#define CPLD_I2C_DATA_SZ_MASK GENMASK(7,4) +#define CPLD_I2C_CMD_SZ_MASK GENMASK(1,0) +#define CPLD_I2C_ERR BIT(7) +#define CPLD_I2C_BUSY BIT(6) +#define CPLD_I2C_RST_BIT BIT(0) +#define CPLD_I2C_RESET 0 +#define CPLD_I2C_UNRESET 1 +#define CPLD_I2C_DATA_SZ_MAX 8 +#define CPLD_I2C_CMD_SZ_MAX 3 + + +#define CPLD_I2C_BANK1_BASE 0x210 +#define CPLD_I2C_BANK2_BASE 0x290 +#define CPLD_I2C_BANK3_BASE 0x390 + +#define I2C_PORT_ID 0x0 +#define I2C_OPCODE 0x1 +#define I2C_DEV_ADDR 0x2 +#define I2C_CMD_BYT0 0x3 +#define I2C_SSR 0x6 +#define I2C_WRITE_DATA 0x10 +#define I2C_READ_DATA 0x20 -#define DEVADDR_ID_BANK1 0x212 -#define DEVADDR_ID_BANK2 0x292 -#define DEVADDR_ID_BANK3 0x392 - -#define CMDBYT_ID_BANK1 0x213 -#define CMDBYT_ID_BANK2 0x293 -#define CMDBYT_ID_BANK3 0x393 - -#define WRITE_ID_BANK1 0x220 -#define WRITE_ID_BANK2 0x2A0 -#define WRITE_ID_BANK3 0x3A0 - -#define READ_ID_BANK1 0x230 -#define READ_ID_BANK2 0x2B0 -#define READ_ID_BANK3 0x3B0 - -#define SSRR_ID_BANK1 0x216 -#define SSRR_ID_BANK2 0x296 -#define SSRR_ID_BANK3 0x396 - -#define HST_CNTL2_QUICK 0x00 -#define HST_CNTL2_BYTE 0x01 -#define HST_CNTL2_BYTE_DATA 0x02 -#define HST_CNTL2_WORD_DATA 0x03 -#define HST_CNTL2_BLOCK 0x05 +/* + * private data to send to I2C core + */ +struct current_xfer { + u8 addr; + u8 cmd[CPLD_I2C_CMD_SZ_MAX]; + u8 cmd_len; + u8 data_len; + union i2c_smbus_data *data; +}; +/* + * private data of I2C adapter + * base_addr: Base address of this I2C adapter core. + * port_id: The port ID, use to mux an i2c core to a font panel port. + * current_xfer: The struct carry current data setup of current smbus transfer. + */ struct dx010_i2c_data { + int base_addr; int portid; + struct current_xfer curr_xfer; }; struct dx010_cpld_data { @@ -364,179 +374,210 @@ static struct platform_device cel_dx010_lpc_dev = { } }; +// TODO: Refactoring this function with helper functions. +static s32 cpld_smbus_transfer(struct dx010_i2c_data *priv) { -/** - * Read eeprom of QSFP device. - * @param a i2c adapter. - * @param addr address to read. - * @param new_data QSFP port number struct. - * @param cmd i2c command. - * @return 0 if not error, else the error code. - */ -static int i2c_read_eeprom(struct i2c_adapter *a, u16 addr, - struct dx010_i2c_data *new_data, u8 cmd, union i2c_smbus_data *data){ + u8 val; + s32 error; + unsigned long ioBase; + short portid, opcode, devaddr, cmdbyte0, ssr, writedata, readdata; + union i2c_smbus_data *data; - u32 reg; - int ioBase=0; - char byte; - short temp; - short portid, opcode, devaddr, cmdbyte0, ssrr, writedata, readdata; - __u16 word_data; - int error = -EIO; + error = -EIO; mutex_lock(&cpld_data->cpld_lock); - if (((new_data->portid >= PORT_BANK1_START) - && (new_data->portid <= PORT_BANK1_END)) - || (new_data->portid == PORT_SFPP1) - || (new_data->portid == PORT_SFPP2)) - { - portid = PORT_ID_BANK1; - opcode = OPCODE_ID_BANK1; - devaddr = DEVADDR_ID_BANK1; - cmdbyte0 = CMDBYT_ID_BANK1; - ssrr = SSRR_ID_BANK1; - writedata = WRITE_ID_BANK1; - readdata = READ_ID_BANK1; - }else if ((new_data->portid >= PORT_BANK2_START) && (new_data->portid <= PORT_BANK2_END)){ - portid = PORT_ID_BANK2; - opcode = OPCODE_ID_BANK2; - devaddr = DEVADDR_ID_BANK2; - cmdbyte0 = CMDBYT_ID_BANK2; - ssrr = SSRR_ID_BANK2; - writedata = WRITE_ID_BANK2; - readdata = READ_ID_BANK2; - }else if ((new_data->portid >= PORT_BANK3_START) && (new_data->portid <= PORT_BANK3_END)){ - portid = PORT_ID_BANK3; - opcode = OPCODE_ID_BANK3; - devaddr = DEVADDR_ID_BANK3; - cmdbyte0 = CMDBYT_ID_BANK3; - ssrr = SSRR_ID_BANK3; - writedata = WRITE_ID_BANK3; - readdata = READ_ID_BANK3; - }else{ - /* Invalid parameter! */ - error = -EINVAL; - goto exit; + ioBase = priv->base_addr; + data = priv->curr_xfer.data; + + portid = ioBase + I2C_PORT_ID; + opcode = ioBase + I2C_OPCODE; + devaddr = ioBase + I2C_DEV_ADDR; + cmdbyte0 = ioBase + I2C_CMD_BYT0; + ssr = ioBase + I2C_SSR; + writedata = ioBase + I2C_WRITE_DATA; + readdata = ioBase + I2C_READ_DATA; + + /* Wait for the core to be free */ + pr_debug("CPLD_I2C Wait busy bit(6) to be cleared\n"); + do { + val = inb(ssr); + if ((val & CPLD_I2C_BUSY) == 0) + break; + udelay(100); + } while (true); // Risky - add timeout + + /* + * If any error happen here, we do soft-reset + * and check the BUSY/ERROR again. + */ + pr_debug("CPLD_I2C Check error bit(7)\n"); + if (val & CPLD_I2C_ERR) { + pr_debug("CPLD_I2C Error, try soft-reset\n"); + outb(CPLD_I2C_RESET, ssr); + udelay(3000); + outb(CPLD_I2C_UNRESET, ssr); + + val = inb(ssr); + if (val & (CPLD_I2C_BUSY | CPLD_I2C_ERR)) { + pr_debug("CPLD_I2C Error, core busy after reset\n"); + error = -EIO; + goto exit_unlock; + } } - while ((inb(ioBase + ssrr) & 0x40)); - if ((inb(ioBase + ssrr) & 0x80) == 0x80) { - error = -EIO; - /* Read error reset the port */ - outb(0x00, ioBase + ssrr); - udelay(3000); - outb(0x01, ioBase + ssrr); - goto exit; + /* Configure PortID */ + val = priv->portid | CPLD_I2C_CLK_100Khz_BIT; + outb(val, portid); + pr_debug("CPLD_I2C Write PortID 0x%x\n", val); + + /* Configure OP_Code */ + val = (priv->curr_xfer.data_len << 4) & CPLD_I2C_DATA_SZ_MASK; + val |= (priv->curr_xfer.cmd_len & CPLD_I2C_CMD_SZ_MASK); + outb(val, opcode); + pr_debug("CPLD_I2C Write OP_Code 0x%x\n", val); + + /* Configure CMD_Byte */ + outb(priv->curr_xfer.cmd[0], cmdbyte0); + pr_debug("CPLD_I2C Write CMD_Byte 0x%x\n", priv->curr_xfer.cmd[0]); + + /* Configure write data buffer */ + if ((priv->curr_xfer.addr & BIT(0)) == I2C_SMBUS_WRITE){ + pr_debug("CPLD_I2C Write WR_DATA buffer\n"); + switch(priv->curr_xfer.data_len){ + case 1: + outb(data->byte, writedata); + break; + case 2: + outb(data->block[0], writedata); + outb(data->block[1], ++writedata); + break; + } } - byte = 0x40 +new_data->portid; - reg = cmd; - outb(byte, ioBase + portid); - outb(reg,ioBase + cmdbyte0); - byte = 33; - outb(byte, ioBase + opcode); - addr = addr << 1; - addr |= 0x01; - outb(addr, ioBase + devaddr); - while ((inb(ioBase + ssrr) & 0x40)) - { + /* Start transfer, write the device address register */ + pr_debug("CPLD_I2C Write DEV_ADDR 0x%x\n", priv->curr_xfer.addr); + outb(priv->curr_xfer.addr, devaddr); + + /* Wait for transfer finish */ + pr_debug("CPLD_I2C Wait busy bit(6) to be cleared\n"); + do { + val = inb(ssr); + if ((val & CPLD_I2C_BUSY) == 0) + break; udelay(100); - } + } while (true); // Risky - add timeout - if ((inb(ioBase + ssrr) & 0x80) == 0x80) { - /* Read error reset the port */ + pr_debug("CPLD_I2C Check error bit(7)\n"); + if (val & CPLD_I2C_ERR) { error = -EIO; - outb(0x00, ioBase + ssrr); - udelay(3000); - outb(0x01, ioBase + ssrr); - goto exit; + goto exit_unlock; } - temp = ioBase + readdata; - word_data = inb(temp); - word_data |= (inb(++temp) << 8); + /* Get the data from buffer */ + if ((priv->curr_xfer.addr & BIT(0)) == I2C_SMBUS_READ){ + pr_debug("CPLD_I2C Read RD_DATA buffer\n"); + switch (priv->curr_xfer.data_len) { + case 1: + data->byte = inb(readdata); + break; + case 2: + data->block[0] = inb(readdata); + data->block[1] = inb(++readdata); + break; + } + } - mutex_unlock(&cpld_data->cpld_lock); - data->word = word_data; - return 0; + error = 0; -exit: +exit_unlock: + pr_debug("CPLD_I2C Exit with %d\n", error); mutex_unlock(&cpld_data->cpld_lock); return error; + } -static int dx010_i2c_access(struct i2c_adapter *a, u16 addr, - unsigned short flags, char rw, u8 cmd, - int size, union i2c_smbus_data *data) -{ +/* + * dx010_smbus_xfer - execute LPC-SMBus transfer + * Returns a negative errno code else zero on success. + */ +static s32 dx010_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) { int error = 0; + struct dx010_i2c_data *priv; + + priv = i2c_get_adapdata(adap); - struct dx010_i2c_data *new_data; + pr_debug("smbus_xfer called RW:%x CMD:%x SIZE:0x%x", + read_write, command, size); - /* Write the command register */ - new_data = i2c_get_adapdata(a); + priv->curr_xfer.addr = (addr << 1) | read_write; + priv->curr_xfer.data = data; /* Map the size to what the chip understands */ switch (size) { - case I2C_SMBUS_QUICK: - size = HST_CNTL2_QUICK; - break; case I2C_SMBUS_BYTE: - size = HST_CNTL2_BYTE; - break; + priv->curr_xfer.cmd_len = 0; + priv->curr_xfer.data_len = 1; + break; case I2C_SMBUS_BYTE_DATA: - size = HST_CNTL2_BYTE_DATA; - break; + priv->curr_xfer.cmd_len = 1; + priv->curr_xfer.data_len = 1; + priv->curr_xfer.cmd[0] = command; + break; case I2C_SMBUS_WORD_DATA: - size = HST_CNTL2_WORD_DATA; - break; - case I2C_SMBUS_BLOCK_DATA: - size = HST_CNTL2_BLOCK; - break; + priv->curr_xfer.cmd_len = 1; + priv->curr_xfer.data_len = 2; + priv->curr_xfer.cmd[0] = command; + break; default: - dev_warn(&a->dev, "Unsupported transaction %d\n", size); + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); error = -EOPNOTSUPP; goto Done; } - switch (size) { - case HST_CNTL2_BYTE: /* Result put in SMBHSTDAT0 */ - break; - case HST_CNTL2_BYTE_DATA: - break; - case HST_CNTL2_WORD_DATA: - if( 0 == i2c_read_eeprom(a,addr,new_data,cmd,data)){ - error = 0; - }else{ - error = -EIO; - } - break; - } + error = cpld_smbus_transfer(priv); Done: return error; } -static u32 dx010_i2c_func(struct i2c_adapter *a) -{ - return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA; +// TODO: Add support for I2C_FUNC_SMBUS_PROC_CALL and I2C_FUNC_SMBUS_I2C_BLOCK +static u32 dx010_i2c_func(struct i2c_adapter *a) { + return I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA; } static const struct i2c_algorithm dx010_i2c_algorithm = { - .smbus_xfer = dx010_i2c_access, + .smbus_xfer = dx010_smbus_xfer, .functionality = dx010_i2c_func, }; -static struct i2c_adapter * cel_dx010_i2c_init(struct platform_device *pdev, int portid) +static struct i2c_adapter *cel_dx010_i2c_init(struct platform_device *pdev, int portid) { int error; - + int base_addr; struct i2c_adapter *new_adapter; - struct dx010_i2c_data *new_data; + struct dx010_i2c_data *priv; + + switch (portid) { + case PORT_SFPP1 ... PORT_SFPP2: + case PORT_BANK1_START ... PORT_BANK1_END: + base_addr = CPLD_I2C_BANK1_BASE; + break; + case PORT_BANK2_START ... PORT_BANK2_END: + base_addr = CPLD_I2C_BANK2_BASE; + break; + case PORT_BANK3_START ... PORT_BANK3_END: + base_addr = CPLD_I2C_BANK3_BASE; + break; + default: + dev_err(&pdev->dev, "Invalid port adapter ID: %d\n", portid); + goto error_exit; + } new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL); if (!new_adapter) @@ -544,31 +585,39 @@ static struct i2c_adapter * cel_dx010_i2c_init(struct platform_device *pdev, int new_adapter->dev.parent = &pdev->dev; new_adapter->owner = THIS_MODULE; - new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + new_adapter->class = I2C_CLASS_DEPRECATED; new_adapter->algo = &dx010_i2c_algorithm; snprintf(new_adapter->name, sizeof(new_adapter->name), - "SMBus dx010 i2c Adapter portid@%04x", portid); - - new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); - if (!new_data) - return NULL; + "SMBus dx010 i2c Adapter port %d", portid); - new_data->portid = portid; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + goto free_adap; + } - i2c_set_adapdata(new_adapter,new_data); + priv->portid = portid; + priv->base_addr = base_addr; + i2c_set_adapdata(new_adapter, priv); error = i2c_add_adapter(new_adapter); if(error) - return NULL; + goto free_data; return new_adapter; + +free_adap: + kzfree(new_adapter); +free_data: + kzfree(priv); +error_exit: + return NULL; }; static int cel_dx010_lpc_drv_probe(struct platform_device *pdev) { struct resource *res; - int ret =0; + int ret = 0; int portid_count; cpld_data = devm_kzalloc(&pdev->dev, sizeof(struct dx010_cpld_data), diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/scripts/thermal_overload_control.sh b/platform/broadcom/sonic-platform-modules-cel/dx010/scripts/thermal_overload_control.sh new file mode 100755 index 000000000000..57fd851f9cfb --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/scripts/thermal_overload_control.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# +# Copyright 2020-present Celestica. All Rights Reserved. +# +# This program file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin +SETREG_FILE=/sys/devices/platform/dx010_cpld/setreg +TOVERREG=0x140 +CPUOVER=0xa1 +ASICOVER=0xa2 + +prog="$0" +command="$1" + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root" + exit 1 +fi + +usage() { + echo "Usage: thermal_overload_control.sh [option] <command>" + echo + echo "Options:" + echo " -h, --help : to print this message." + echo + echo "Commands:" + echo + echo " cpu: To enabling CPU thermal overload handler" + echo + echo " asic : To enabling ASIC thermal overload handler" + echo +} + +cpu_overload() { + logger "Enable CPU thermal overload control" + set_reg=`echo ${TOVERREG} ${CPUOVER} > ${SETREG_FILE}` +} + +asic_overload() { + logger "Enable ASIC thermal overload control" + set_reg=`echo ${TOVERREG} ${ASICOVER} > ${SETREG_FILE}` +} + +if [ $# -lt 1 ]; then + usage + exit -1 +fi + +case "$command" in +-h | --help) + usage + ;; +cpu) + cpu_overload + ;; +asic) + asic_overload + ;; +*) + usage + exit -1 + ;; +esac + +exit $? diff --git a/platform/broadcom/sonic-platform-modules-cel/services/fancontrol/fancontrol b/platform/broadcom/sonic-platform-modules-cel/services/fancontrol/fancontrol index eb15598b0efa..cdd5005e3688 100755 --- a/platform/broadcom/sonic-platform-modules-cel/services/fancontrol/fancontrol +++ b/platform/broadcom/sonic-platform-modules-cel/services/fancontrol/fancontrol @@ -39,285 +39,327 @@ # PIDFILE="/var/run/fancontrol.pid" +THERMAL_OVERLOAD_CONTROL_FILE="/usr/local/bin/thermal_overload_control.sh" #DEBUG=1 MAX=255 function LoadConfig { - local fcvcount fcv - - echo "Loading configuration from $1 ..." - if [ ! -r "$1" ] - then - echo "Error: Can't read configuration file" >&2 - exit 1 - fi - - # grep configuration from file - INTERVAL=`egrep '^INTERVAL=.*$' $1 | sed -e 's/INTERVAL=//g'` - DEVPATH=`egrep '^DEVPATH=.*$' $1 | sed -e 's/DEVPATH= *//g'` - DEVNAME=`egrep '^DEVNAME=.*$' $1 | sed -e 's/DEVNAME= *//g'` - FCTEMPS=`egrep '^FCTEMPS=.*$' $1 | sed -e 's/FCTEMPS=//g'` - MINTEMP=`egrep '^MINTEMP=.*$' $1 | sed -e 's/MINTEMP=//g'` - MAXTEMP=`egrep '^MAXTEMP=.*$' $1 | sed -e 's/MAXTEMP=//g'` - MINSTART=`egrep '^MINSTART=.*$' $1 | sed -e 's/MINSTART=//g'` - MINSTOP=`egrep '^MINSTOP=.*$' $1 | sed -e 's/MINSTOP=//g'` - # optional settings: - FCFANS=`egrep '^FCFANS=.*$' $1 | sed -e 's/FCFANS=//g'` - MINPWM=`egrep '^MINPWM=.*$' $1 | sed -e 's/MINPWM=//g'` - MAXPWM=`egrep '^MAXPWM=.*$' $1 | sed -e 's/MAXPWM=//g'` - THYST=`egrep '^THYST=.*$' $1 | sed -e 's/THYST=//g'` - echo - # Check whether all mandatory settings are set - if [[ -z ${INTERVAL} || -z ${FCTEMPS} || -z ${MINTEMP} || -z ${MAXTEMP} || -z ${MINSTART} || -z ${MINSTOP} ]] - then - echo "Some mandatory settings missing, please check your config file!" >&2 - exit 1 - fi - if [ "$INTERVAL" -le 0 ] - then - echo "Error in configuration file:" >&2 - echo "INTERVAL must be at least 1" >&2 - exit 1 - fi - - # write settings to arrays for easier use and print them - echo - echo "Common settings:" - echo " INTERVAL=$INTERVAL" - - let fcvcount=0 - for fcv in $FCTEMPS - do - if ! echo $fcv | egrep -q '=' - then - echo "Error in configuration file:" >&2 - echo "FCTEMPS value is improperly formatted" >&2 - exit 1 - fi - - AFCPWM[$fcvcount]=`echo $fcv |cut -d'=' -f1` - AFCTEMP[$fcvcount]=`echo $fcv |cut -d'=' -f2` - AFCFAN[$fcvcount]=`echo $FCFANS |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` - AFCFANFAULT[$fcvcount]=`echo "${AFCFAN[$fcvcount]}" |sed -e 's/input/fault/g'` - AFCMINTEMP[$fcvcount]=`echo $MINTEMP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` - AFCMAXTEMP[$fcvcount]=`echo $MAXTEMP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` - AFCMINSTART[$fcvcount]=`echo $MINSTART |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` - AFCMINSTOP[$fcvcount]=`echo $MINSTOP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` - AFCMINPWM[$fcvcount]=`echo $MINPWM |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` - [ -z "${AFCMINPWM[$fcvcount]}" ] && AFCMINPWM[$fcvcount]=0 - AFCMAXPWM[$fcvcount]=`echo $MAXPWM |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` - [ -z "${AFCMAXPWM[$fcvcount]}" ] && AFCMAXPWM[$fcvcount]=255 - AFCTHYST[$fcvcount]=`echo $THYST |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` - [ -z "${AFCTHYST[$fcvcount]}" ] && AFCTHYST[$fcvcount]=0 - - # verify the validity of the settings - if [ "${AFCMINTEMP[$fcvcount]}" -ge "${AFCMAXTEMP[$fcvcount]}" ] - then - echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 - echo "MINTEMP must be less than MAXTEMP" >&2 - exit 1 - fi - if [ "${AFCMAXPWM[$fcvcount]}" -gt 255 ] - then - echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 - echo "MAXPWM must be at most 255" >&2 - exit 1 - fi - if [ "${AFCMINSTOP[$fcvcount]}" -ge "${AFCMAXPWM[$fcvcount]}" ] - then - echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 - echo "MINSTOP must be less than MAXPWM" >&2 - exit 1 - fi - if [ "${AFCMINSTOP[$fcvcount]}" -lt "${AFCMINPWM[$fcvcount]}" ] - then - echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 - echo "MINSTOP must be greater than or equal to MINPWM" >&2 - exit 1 - fi - if [ "${AFCMINPWM[$fcvcount]}" -lt 0 ] - then - echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 - echo "MINPWM must be at least 0" >&2 - exit 1 - fi - - echo - echo "Settings for ${AFCPWM[$fcvcount]}:" - echo " Depends on ${AFCTEMP[$fcvcount]}" - echo " Controls ${AFCFAN[$fcvcount]}" - echo " MINTEMP=${AFCMINTEMP[$fcvcount]}" - echo " MAXTEMP=${AFCMAXTEMP[$fcvcount]}" - echo " MINSTART=${AFCMINSTART[$fcvcount]}" - echo " MINSTOP=${AFCMINSTOP[$fcvcount]}" - echo " MINPWM=${AFCMINPWM[$fcvcount]}" - echo " MAXPWM=${AFCMAXPWM[$fcvcount]}" - echo " THYST=${AFCTHYST[$fcvcount]}" - let fcvcount=fcvcount+1 - done - echo + local fcvcount fcv + + echo "Loading configuration from $1 ..." + if [ ! -r "$1" ] + then + echo "Error: Can't read configuration file" >&2 + exit 1 + fi + + # grep configuration from file + INTERVAL=`egrep '^INTERVAL=.*$' $1 | sed -e 's/INTERVAL=//g'` + DEVPATH=`egrep '^DEVPATH=.*$' $1 | sed -e 's/DEVPATH= *//g'` + DEVNAME=`egrep '^DEVNAME=.*$' $1 | sed -e 's/DEVNAME= *//g'` + FCTEMPS=`egrep '^FCTEMPS=.*$' $1 | sed -e 's/FCTEMPS=//g'` + MINTEMP=`egrep '^MINTEMP=.*$' $1 | sed -e 's/MINTEMP=//g'` + MAXTEMP=`egrep '^MAXTEMP=.*$' $1 | sed -e 's/MAXTEMP=//g'` + MINSTART=`egrep '^MINSTART=.*$' $1 | sed -e 's/MINSTART=//g'` + MINSTOP=`egrep '^MINSTOP=.*$' $1 | sed -e 's/MINSTOP=//g'` + # optional settings: + FCFANS=`egrep '^FCFANS=.*$' $1 | sed -e 's/FCFANS=//g'` + MINPWM=`egrep '^MINPWM=.*$' $1 | sed -e 's/MINPWM=//g'` + MAXPWM=`egrep '^MAXPWM=.*$' $1 | sed -e 's/MAXPWM=//g'` + THYST=`egrep '^THYST=.*$' $1 | sed -e 's/THYST=//g'` + MAXTEMPCRIT=`egrep '^MAXTEMPCRIT=.*$' $1 | sed -e 's/MAXTEMPCRIT=//g'` + MAXTEMPTYPE=`egrep '^MAXTEMPTYPE=.*$' $1 | sed -e 's/MAXTEMPTYPE=//g'` + echo + # Check whether all mandatory settings are set + if [[ -z ${INTERVAL} || -z ${FCTEMPS} || -z ${MINTEMP} || -z ${MAXTEMP} || -z ${MINSTART} || -z ${MINSTOP} ]] + then + echo "Some mandatory settings missing, please check your config file!" >&2 + exit 1 + fi + if [ "$INTERVAL" -le 0 ] + then + echo "Error in configuration file:" >&2 + echo "INTERVAL must be at least 1" >&2 + exit 1 + fi + + # write settings to arrays for easier use and print them + echo + echo "Common settings:" + echo " INTERVAL=$INTERVAL" + + let fcvcount=0 + for fcv in $FCTEMPS + do + if ! echo $fcv | egrep -q '=' + then + echo "Error in configuration file:" >&2 + echo "FCTEMPS value is improperly formatted" >&2 + exit 1 + fi + + AFCPWM[$fcvcount]=`echo $fcv |cut -d'=' -f1` + AFCTEMP[$fcvcount]=`echo $fcv |cut -d'=' -f2` + AFCFAN[$fcvcount]=`echo $FCFANS |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` + AFCFANFAULT[$fcvcount]=`echo "${AFCFAN[$fcvcount]}" |sed -e 's/input/fault/g'` + AFCMINTEMP[$fcvcount]=`echo $MINTEMP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` + AFCMAXTEMP[$fcvcount]=`echo $MAXTEMP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` + AFCMINSTART[$fcvcount]=`echo $MINSTART |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` + AFCMINSTOP[$fcvcount]=`echo $MINSTOP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` + AFCMINPWM[$fcvcount]=`echo $MINPWM |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` + [ -z "${AFCMINPWM[$fcvcount]}" ] && AFCMINPWM[$fcvcount]=0 + AFCMAXPWM[$fcvcount]=`echo $MAXPWM |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` + [ -z "${AFCMAXPWM[$fcvcount]}" ] && AFCMAXPWM[$fcvcount]=255 + AFCTHYST[$fcvcount]=`echo $THYST |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2` + [ -z "${AFCTHYST[$fcvcount]}" ] && AFCTHYST[$fcvcount]=0 + + # verify the validity of the settings + if [ "${AFCMINTEMP[$fcvcount]}" -ge "${AFCMAXTEMP[$fcvcount]}" ] + then + echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 + echo "MINTEMP must be less than MAXTEMP" >&2 + exit 1 + fi + if [ "${AFCMAXPWM[$fcvcount]}" -gt 255 ] + then + echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 + echo "MAXPWM must be at most 255" >&2 + exit 1 + fi + if [ "${AFCMINSTOP[$fcvcount]}" -ge "${AFCMAXPWM[$fcvcount]}" ] + then + echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 + echo "MINSTOP must be less than MAXPWM" >&2 + exit 1 + fi + if [ "${AFCMINSTOP[$fcvcount]}" -lt "${AFCMINPWM[$fcvcount]}" ] + then + echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 + echo "MINSTOP must be greater than or equal to MINPWM" >&2 + exit 1 + fi + if [ "${AFCMINPWM[$fcvcount]}" -lt 0 ] + then + echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2 + echo "MINPWM must be at least 0" >&2 + exit 1 + fi + + echo + echo "Settings for ${AFCPWM[$fcvcount]}:" + echo " Depends on ${AFCTEMP[$fcvcount]}" + echo " Controls ${AFCFAN[$fcvcount]}" + echo " MINTEMP=${AFCMINTEMP[$fcvcount]}" + echo " MAXTEMP=${AFCMAXTEMP[$fcvcount]}" + echo " MINSTART=${AFCMINSTART[$fcvcount]}" + echo " MINSTOP=${AFCMINSTOP[$fcvcount]}" + echo " MINPWM=${AFCMINPWM[$fcvcount]}" + echo " MAXPWM=${AFCMAXPWM[$fcvcount]}" + echo " THYST=${AFCTHYST[$fcvcount]}" + let fcvcount=fcvcount+1 + done + echo + + + let tscount=0 + for ts in $MAXTEMPCRIT + do + CSTEMP[$tscount]=`echo $ts | cut -d '=' -f1` + CSMAXTEMPCRIT[$tscount]=`echo $ts | cut -d '=' -f2` + CSMAXTEMPTYPE=($(echo $MAXTEMPTYPE |sed -e 's/ /\n/g'| cut -d'=' -f2)) + + echo + echo "Settings for ${CSMAXTEMPTYPE[$tscount]} temperature sensor:" + echo " Depends on ${CSTEMP[$tscount]}" + echo " MAXTEMPCRIT=${CSMAXTEMPCRIT[$tscount]}" + let tscount=tscount+1 + done + echo + } function CheckFanFault() { - let fancount=0 - while (( $fancount < ${#AFCFANFAULT[@]} )) # go through all fan fault. - do - fault=`cat ${AFCFANFAULT[$fancount]}` - if [[ "$fault" == "1" ]] - then - return 1 # fan fault detected - fi - let fancount=$fancount+1 - done - return 0 + let fancount=0 + while (( $fancount < ${#AFCFANFAULT[@]} )) # go through all fan fault. + do + fault=`cat ${AFCFANFAULT[$fancount]}` + if [[ "$fault" == "1" ]] + then + return 1 # fan fault detected + fi + let fancount=$fancount+1 + done + return 0 +} + +function CheckTempOver() +{ + let tempcount=0 + while (( $tempcount < ${#CSTEMP[@]} )) # go through all temp. + do + ctemp=`cat ${CSTEMP[$tempcount]}` + let maxcrit="${CSMAXTEMPCRIT[$tempcount]}*1000" + if [ $ctemp -ge $maxcrit ] + then + logger "Thermal overload : ${CSMAXTEMPTYPE[$tempcount]} temperature ${ctemp} > ${maxcrit}" + if [ -f "$THERMAL_OVERLOAD_CONTROL_FILE" ] + then + toc_cmd="${THERMAL_OVERLOAD_CONTROL_FILE} ${CSMAXTEMPTYPE[$tempcount],,}" + bash $toc_cmd + exit 1 + fi + fi + let tempcount=$tempcount+1 + done + return 0 } function DevicePath() { - if [ -h "$1/device" ] - then - readlink -f "$1/device" | sed -e 's/^\/sys\///' - fi + if [ -h "$1/device" ] + then + readlink -f "$1/device" | sed -e 's/^\/sys\///' + fi } function DeviceName() { - if [ -r "$1/name" ] - then - cat "$1/name" | sed -e 's/[[:space:]=]/_/g' - elif [ -r "$1/device/name" ] - then - cat "$1/device/name" | sed -e 's/[[:space:]=]/_/g' - fi + if [ -r "$1/name" ] + then + cat "$1/name" | sed -e 's/[[:space:]=]/_/g' + elif [ -r "$1/device/name" ] + then + cat "$1/device/name" | sed -e 's/[[:space:]=]/_/g' + fi } function ValidateDevices() { - local OLD_DEVPATH="$1" OLD_DEVNAME="$2" outdated=0 - local entry device name path - - for entry in $OLD_DEVPATH - do - device=`echo "$entry" | sed -e 's/=[^=]*$//'` - path=`echo "$entry" | sed -e 's/^[^=]*=//'` - - if [ "`DevicePath "$device"`" != "$path" ] - then - echo "Device path of $device has changed" >&2 - outdated=1 - fi - done - - for entry in $OLD_DEVNAME - do - device=`echo "$entry" | sed -e 's/=[^=]*$//'` - name=`echo "$entry" | sed -e 's/^[^=]*=//'` - - if [ "`DeviceName "$device"`" != "$name" ] - then - echo "Device name of $device has changed" >&2 - outdated=1 - fi - done - - return $outdated + local OLD_DEVPATH="$1" OLD_DEVNAME="$2" outdated=0 + local entry device name path + + for entry in $OLD_DEVPATH + do + device=`echo "$entry" | sed -e 's/=[^=]*$//'` + path=`echo "$entry" | sed -e 's/^[^=]*=//'` + + if [ "`DevicePath "$device"`" != "$path" ] + then + echo "Device path of $device has changed" >&2 + outdated=1 + fi + done + + for entry in $OLD_DEVNAME + do + device=`echo "$entry" | sed -e 's/=[^=]*$//'` + name=`echo "$entry" | sed -e 's/^[^=]*=//'` + + if [ "`DeviceName "$device"`" != "$name" ] + then + echo "Device name of $device has changed" >&2 + outdated=1 + fi + done + + return $outdated } # Check that all referenced sysfs files exist function CheckFiles { - local outdated=0 fcvcount pwmo tsen fan - - let fcvcount=0 - while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs - do - pwmo=${AFCPWM[$fcvcount]} - if [ ! -w $pwmo ] - then - echo "Error: file $pwmo doesn't exist" >&2 - outdated=1 - fi - let fcvcount=$fcvcount+1 - done - - let fcvcount=0 - while (( $fcvcount < ${#AFCTEMP[@]} )) # go through all temp inputs - do - tsen=${AFCTEMP[$fcvcount]} - if [ ! -r $tsen ] - then - echo "Error: file $tsen doesn't exist" >&2 - outdated=1 - fi - let fcvcount=$fcvcount+1 - done - - let fcvcount=0 - while (( $fcvcount < ${#AFCFAN[@]} )) # go through all fan inputs - do - # A given PWM output can control several fans - for fan in $(echo ${AFCFAN[$fcvcount]} | sed -e 's/+/ /') - do - if [ ! -r $fan ] - then - echo "Error: file $fan doesn't exist" >&2 - outdated=1 - fi - done - let fcvcount=$fcvcount+1 - done - - if [ $outdated -eq 1 ] - then - echo >&2 - echo "At least one referenced file is missing. Either some required kernel" >&2 - echo "modules haven't been loaded, or your configuration file is outdated." >&2 - echo "In the latter case, you should run pwmconfig again." >&2 - fi - - return $outdated + local outdated=0 fcvcount pwmo tsen fan + + let fcvcount=0 + while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs + do + pwmo=${AFCPWM[$fcvcount]} + if [ ! -w $pwmo ] + then + echo "Error: file $pwmo doesn't exist" >&2 + outdated=1 + fi + let fcvcount=$fcvcount+1 + done + + let fcvcount=0 + while (( $fcvcount < ${#AFCTEMP[@]} )) # go through all temp inputs + do + tsen=${AFCTEMP[$fcvcount]} + if [ ! -r $tsen ] + then + echo "Error: file $tsen doesn't exist" >&2 + outdated=1 + fi + let fcvcount=$fcvcount+1 + done + + let fcvcount=0 + while (( $fcvcount < ${#AFCFAN[@]} )) # go through all fan inputs + do + # A given PWM output can control several fans + for fan in $(echo ${AFCFAN[$fcvcount]} | sed -e 's/+/ /') + do + if [ ! -r $fan ] + then + echo "Error: file $fan doesn't exist" >&2 + outdated=1 + fi + done + let fcvcount=$fcvcount+1 + done + + if [ $outdated -eq 1 ] + then + echo >&2 + echo "At least one referenced file is missing. Either some required kernel" >&2 + echo "modules haven't been loaded, or your configuration file is outdated." >&2 + echo "In the latter case, you should run pwmconfig again." >&2 + fi + + return $outdated } if [ "$1" == "--check" ] then - if [ -f "$2" ] - then - LoadConfig $2 - else - LoadConfig /etc/fancontrol - fi - exit 0 + if [ -f "$2" ] + then + LoadConfig $2 + else + LoadConfig /etc/fancontrol + fi + exit 0 fi if [ -f "$1" ] then - LoadConfig $1 + LoadConfig $1 else - LoadConfig /etc/fancontrol + LoadConfig /etc/fancontrol fi # Detect path to sensors if echo "${AFCPWM[0]}" | egrep -q '^/' then - DIR=/ + DIR=/ elif echo "${AFCPWM[0]}" | egrep -q '^hwmon[0-9]' then - DIR=/sys/class/hwmon + DIR=/sys/class/hwmon elif echo "${AFCPWM[0]}" | egrep -q '^[1-9]*[0-9]-[0-9abcdef]{4}' then - DIR=/sys/bus/i2c/devices + DIR=/sys/bus/i2c/devices else - echo "$0: Invalid path to sensors" >&2 - exit 1 + echo "$0: Invalid path to sensors" >&2 + exit 1 fi if [ ! -d $DIR ] then - echo $0: 'No sensors found! (did you load the necessary modules?)' >&2 - exit 1 + echo $0: 'No sensors found! (did you load the necessary modules?)' >&2 + exit 1 fi cd $DIR @@ -325,94 +367,94 @@ cd $DIR # if [ "$DIR" != "/" ] && [ -z "$DEVPATH" -o -z "$DEVNAME" ] # then # echo "Configuration is too old, please run pwmconfig again" >&2 -# exit 1 +# exit 1 # fi if [ "$DIR" = "/" -a -n "$DEVPATH" ] then - echo "Unneeded DEVPATH with absolute device paths" >&2 - exit 1 + echo "Unneeded DEVPATH with absolute device paths" >&2 + exit 1 fi if ! ValidateDevices "$DEVPATH" "$DEVNAME" then - echo "Configuration appears to be outdated, please run pwmconfig again" >&2 - exit 1 + echo "Configuration appears to be outdated, please run pwmconfig again" >&2 + exit 1 fi CheckFiles || exit 1 if [ -f "$PIDFILE" ] then - echo "File $PIDFILE exists, is fancontrol already running?" >&2 - exit 1 + echo "File $PIDFILE exists, is fancontrol already running?" >&2 + exit 1 fi echo $$ > "$PIDFILE" # $1 = pwm file name function pwmdisable() { - local ENABLE=${1}_enable - - # No enable file? Just set to max - if [ ! -f $ENABLE ] - then - echo $MAX > $1 - return 0 - fi - - # Try pwmN_enable=0 - echo 0 > $ENABLE 2> /dev/null - if [ `cat $ENABLE` -eq 0 ] - then - # Success - echo $MAX > $1 - return 0 - fi - - # It didn't work, try pwmN_enable=1 pwmN=255 - echo 1 > $ENABLE 2> /dev/null - echo $MAX > $1 - if [ `cat $ENABLE` -eq 1 -a `cat $1` -ge 190 ] - then - # Success - return 0 - fi - - # Nothing worked - echo "$ENABLE stuck to" `cat $ENABLE` >&2 - return 1 + local ENABLE=${1}_enable + + # No enable file? Just set to max + if [ ! -f $ENABLE ] + then + echo $MAX > $1 + return 0 + fi + + # Try pwmN_enable=0 + echo 0 > $ENABLE 2> /dev/null + if [ `cat $ENABLE` -eq 0 ] + then + # Success + echo $MAX > $1 + return 0 + fi + + # It didn't work, try pwmN_enable=1 pwmN=255 + echo 1 > $ENABLE 2> /dev/null + echo $MAX > $1 + if [ `cat $ENABLE` -eq 1 -a `cat $1` -ge 190 ] + then + # Success + return 0 + fi + + # Nothing worked + echo "$ENABLE stuck to" `cat $ENABLE` >&2 + return 1 } # $1 = pwm file name function pwmenable() { - local ENABLE=${1}_enable - - if [ -f $ENABLE ] - then - echo 1 > $ENABLE 2> /dev/null - if [ $? -ne 0 ] - then - return 1 - fi - fi - echo $MAX > $1 + local ENABLE=${1}_enable + + if [ -f $ENABLE ] + then + echo 1 > $ENABLE 2> /dev/null + if [ $? -ne 0 ] + then + return 1 + fi + fi + echo $MAX > $1 } function restorefans() { - local status=$1 fcvcount pwmo - - echo 'Aborting, restoring fans...' - let fcvcount=0 - while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs - do - pwmo=${AFCPWM[$fcvcount]} - pwmdisable $pwmo - let fcvcount=$fcvcount+1 - done - echo 'Verify fans have returned to full speed' - rm -f "$PIDFILE" - exit $status + local status=$1 fcvcount pwmo + + echo 'Aborting, restoring fans...' + let fcvcount=0 + while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs + do + pwmo=${AFCPWM[$fcvcount]} + pwmdisable $pwmo + let fcvcount=$fcvcount+1 + done + echo 'Verify fans have returned to full speed' + rm -f "$PIDFILE" + exit $status } trap 'restorefans 0' SIGQUIT SIGTERM @@ -446,163 +488,166 @@ function lowerBound # main function function UpdateFanSpeeds { - local fcvcount - local pwmo tsens fan mint maxt minsa minso minpwm maxpwm tHyst - local tval pwmpval fanval min_fanval one_fan one_fanval - local -i pwmval - - let fcvcount=0 - while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs - do - #hopefully shorter vars will improve readability: - pwmo=${AFCPWM[$fcvcount]} - tsens=${AFCTEMP[$fcvcount]} - fan=${AFCFAN[$fcvcount]} - let mint="${AFCMINTEMP[$fcvcount]}*1000" - let maxt="${AFCMAXTEMP[$fcvcount]}*1000" - minsa=${AFCMINSTART[$fcvcount]} - minso=${AFCMINSTOP[$fcvcount]} - minpwm=${AFCMINPWM[$fcvcount]} - maxpwm=${AFCMAXPWM[$fcvcount]} - let tHyst="${AFCTHYST[$fcvcount]}*1000" - - #if some fan fault detected all pwm=100% - CheckFanFault - if [ $? -ne 0 ] - then - echo $MAX > $pwmo - let fcvcount=$fcvcount+1 - continue - fi - - read tval < ${tsens} - if [ $? -ne 0 ] - then - echo "Error reading temperature from $DIR/$tsens" - restorefans 1 - fi - - read pwmpval < ${pwmo} - if [ $? -ne 0 ] - then - echo "Error reading PWM value from $DIR/$pwmo" - restorefans 1 - fi - - # If fanspeed-sensor output shall be used, do it - if [[ -n ${fan} ]] - then - min_fanval=100000 - fanval= - # A given PWM output can control several fans - for one_fan in $(echo $fan | sed -e 's/+/ /') - do - read one_fanval < ${one_fan} - if [ $? -ne 0 ] - then - echo "Error reading Fan value from $DIR/$one_fan" >&2 - restorefans 1 - fi - - # Remember the minimum, it only matters if it is 0 - if [ $one_fanval -lt $min_fanval ] - then - min_fanval=$one_fanval - fi - - if [ -z "$fanval" ] - then - fanval=$one_fanval - else - fanval="$fanval/$one_fanval" - fi - done - else - fanval=1 # set it to a non zero value, so the rest of the script still works - fi - - # debug info - if [ "$DEBUG" != "" ] - then - echo "pwmo=$pwmo" - echo "tsens=$tsens" - echo "fan=$fan" - echo "mint=$mint" - echo "maxt=$maxt" - echo "minsa=$minsa" - echo "minso=$minso" - echo "minpwm=$minpwm" - echo "maxpwm=$maxpwm" - echo "tval=$tval" - echo "pwmpval=$pwmpval" - echo "fanval=$fanval" - echo "min_fanval=$min_fanval" - echo "tHyst=$tHyst" - fi - pwmval=$pwmpval - if (( $tval+$tHyst <= $mint )) - then pwmval=$minpwm # below min temp, use defined min pwm - elif (( $tval >= $maxt )) - then pwmval=$maxpwm # over max temp, use defined max pwm - elif (( $tval+$tHyst >= $maxt )) && (( $pwmpval == $maxpwm )) - then pwmval=$maxpwm - else - # calculate the new value from temperature and settings - # pwmval="(${tval}-${mint})*(${maxpwm}-${minso})/(${maxt}-${mint})+${minso} - lowerBound ${tval} ${mint} ${maxt} ${minpwm} ${maxpwm} ${tHyst} - lb=$lw_b - upperBound ${tval} ${mint} ${maxt} ${minpwm} ${maxpwm} ${tHyst} - ub=$up_b - - if [ "$DEBUG" != "" ] - then - echo "old pwm=$pwmval lw_b=$lw_b up_b=$up_b" - fi - - if [[ "$pwmval" -gt "$ub" ]] - then - pwmval=$ub - else - if [[ "$pwmval" -lt "$lb" ]] - then - pwmval=$lb - fi - fi - - if [ $pwmpval -eq 0 -o $min_fanval -eq 0 ] - then # if fan was stopped start it using a safe value - echo $minsa > $pwmo - # Sleep while still handling signals - sleep 1 & - wait $! - fi - fi - echo $pwmval > $pwmo # write new value to pwm output - if [ $? -ne 0 ] - then - echo "Error writing PWM value to $DIR/$pwmo" >&2 - restorefans 1 - fi - if [ "$DEBUG" != "" ] - then - echo "new pwmval=$pwmval" - fi - let fcvcount=$fcvcount+1 - done + local fcvcount + local pwmo tsens fan mint maxt minsa minso minpwm maxpwm tHyst + local tval pwmpval fanval min_fanval one_fan one_fanval + local -i pwmval + + let fcvcount=0 + while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs + do + #hopefully shorter vars will improve readability: + pwmo=${AFCPWM[$fcvcount]} + tsens=${AFCTEMP[$fcvcount]} + fan=${AFCFAN[$fcvcount]} + let mint="${AFCMINTEMP[$fcvcount]}*1000" + let maxt="${AFCMAXTEMP[$fcvcount]}*1000" + minsa=${AFCMINSTART[$fcvcount]} + minso=${AFCMINSTOP[$fcvcount]} + minpwm=${AFCMINPWM[$fcvcount]} + maxpwm=${AFCMAXPWM[$fcvcount]} + let tHyst="${AFCTHYST[$fcvcount]}*1000" + + #if some fan fault detected all pwm=100% + CheckFanFault + if [ $? -ne 0 ] + then + echo $MAX > $pwmo + let fcvcount=$fcvcount+1 + continue + fi + + #check thermal overload + CheckTempOver + + read tval < ${tsens} + if [ $? -ne 0 ] + then + echo "Error reading temperature from $DIR/$tsens" + restorefans 1 + fi + + read pwmpval < ${pwmo} + if [ $? -ne 0 ] + then + echo "Error reading PWM value from $DIR/$pwmo" + restorefans 1 + fi + + # If fanspeed-sensor output shall be used, do it + if [[ -n ${fan} ]] + then + min_fanval=100000 + fanval= + # A given PWM output can control several fans + for one_fan in $(echo $fan | sed -e 's/+/ /') + do + read one_fanval < ${one_fan} + if [ $? -ne 0 ] + then + echo "Error reading Fan value from $DIR/$one_fan" >&2 + restorefans 1 + fi + + # Remember the minimum, it only matters if it is 0 + if [ $one_fanval -lt $min_fanval ] + then + min_fanval=$one_fanval + fi + + if [ -z "$fanval" ] + then + fanval=$one_fanval + else + fanval="$fanval/$one_fanval" + fi + done + else + fanval=1 # set it to a non zero value, so the rest of the script still works + fi + + # debug info + if [ "$DEBUG" != "" ] + then + echo "pwmo=$pwmo" + echo "tsens=$tsens" + echo "fan=$fan" + echo "mint=$mint" + echo "maxt=$maxt" + echo "minsa=$minsa" + echo "minso=$minso" + echo "minpwm=$minpwm" + echo "maxpwm=$maxpwm" + echo "tval=$tval" + echo "pwmpval=$pwmpval" + echo "fanval=$fanval" + echo "min_fanval=$min_fanval" + echo "tHyst=$tHyst" + fi + pwmval=$pwmpval + if (( $tval+$tHyst <= $mint )) + then pwmval=$minpwm # below min temp, use defined min pwm + elif (( $tval >= $maxt )) + then pwmval=$maxpwm # over max temp, use defined max pwm + elif (( $tval+$tHyst >= $maxt )) && (( $pwmpval == $maxpwm )) + then pwmval=$maxpwm + else + # calculate the new value from temperature and settings + # pwmval="(${tval}-${mint})*(${maxpwm}-${minso})/(${maxt}-${mint})+${minso} + lowerBound ${tval} ${mint} ${maxt} ${minpwm} ${maxpwm} ${tHyst} + lb=$lw_b + upperBound ${tval} ${mint} ${maxt} ${minpwm} ${maxpwm} ${tHyst} + ub=$up_b + + if [ "$DEBUG" != "" ] + then + echo "old pwm=$pwmval lw_b=$lw_b up_b=$up_b" + fi + + if [[ "$pwmval" -gt "$ub" ]] + then + pwmval=$ub + else + if [[ "$pwmval" -lt "$lb" ]] + then + pwmval=$lb + fi + fi + + if [ $pwmpval -eq 0 -o $min_fanval -eq 0 ] + then # if fan was stopped start it using a safe value + echo $minsa > $pwmo + # Sleep while still handling signals + sleep 1 & + wait $! + fi + fi + echo $pwmval > $pwmo # write new value to pwm output + if [ $? -ne 0 ] + then + echo "Error writing PWM value to $DIR/$pwmo" >&2 + restorefans 1 + fi + if [ "$DEBUG" != "" ] + then + echo "new pwmval=$pwmval" + fi + let fcvcount=$fcvcount+1 + done } echo 'Enabling PWM on fans...' let fcvcount=0 while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs do - pwmo=${AFCPWM[$fcvcount]} - pwmenable $pwmo - if [ $? -ne 0 ] - then - echo "Error enabling PWM on $DIR/$pwmo" >&2 - restorefans 1 - fi - let fcvcount=$fcvcount+1 + pwmo=${AFCPWM[$fcvcount]} + pwmenable $pwmo + if [ $? -ne 0 ] + then + echo "Error enabling PWM on $DIR/$pwmo" >&2 + restorefans 1 + fi + let fcvcount=$fcvcount+1 done echo 'Starting automatic fan control...' @@ -610,8 +655,8 @@ echo 'Starting automatic fan control...' # main loop calling the main function at specified intervals while true do - UpdateFanSpeeds - # Sleep while still handling signals - sleep $INTERVAL & - wait $! -done \ No newline at end of file + UpdateFanSpeeds + # Sleep while still handling signals + sleep $INTERVAL & + wait $! +done From e9148ee19962729733b6ff0b1562d7e9017cc609 Mon Sep 17 00:00:00 2001 From: Wirut Getbamrung <wgetbum@icloud.com> Date: Wed, 23 Sep 2020 13:25:23 +0700 Subject: [PATCH 3/3] [device/celestica]: using sonic-py-common package --- .../celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py index 6566d4698e8d..1f034be4a0b0 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/helper.py @@ -3,7 +3,7 @@ import os import struct import subprocess -from sonic_daemon_base.daemon_base import DaemonBase +from sonic_py_common import device_info from mmap import * HOST_CHK_CMD = "docker > /dev/null 2>&1" @@ -13,7 +13,7 @@ class APIHelper(): def __init__(self): - (self.platform, self.hwsku) = DaemonBase().get_platform_and_hwsku() + (self.platform, self.hwsku) = device_info.get_platform_and_hwsku() def is_host(self): return os.system(HOST_CHK_CMD) == 0