Skip to content

Commit

Permalink
[device/celestica]: Add xcvrd event support for Haliburton (sonic-net…
Browse files Browse the repository at this point in the history
…#6517)

#### Why I did it
- The xcvrd service requires an event detection function, unplug or plug in the transceiver.

#### How I did it
- Add sysfs interrupt to notify userspace app of external interrupt
- Implement get_change_event() in chassis api.
- Also begin installing Python 3 sonic-platform package for Celestica platforms
  • Loading branch information
Wirut Getbamrung authored and Carl Keene committed Aug 7, 2021
1 parent d989521 commit d6bef16
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 48 deletions.
205 changes: 177 additions & 28 deletions device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@
import json

try:
from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper
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 sonic_py_common import device_info
from .event import SfpEvent
from .helper import APIHelper
except ImportError as e:
raise ImportError(str(e) + "- required module not found")

Expand All @@ -40,29 +38,62 @@ 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()

self._reboot_cause_path = HOST_REBOOT_CAUSE_PATH if self.__is_host(
) else PMON_REBOOT_CAUSE_PATH

def __initialize_sfp(self):
sfputil_helper = SfpUtilHelper()
port_config_file_path = device_info.get_path_to_port_config_file()
sfputil_helper.read_porttab_mappings(port_config_file_path, 0)

from sonic_platform.sfp import Sfp
for index in range(0, NUM_SFP):
name_idx = 0 if index+1 == NUM_SFP else index+1
sfp = Sfp(index, sfputil_helper.logical[name_idx])
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
self._sfp_list.append(None)
for index in range(1, NUM_SFP+1):
sfp = Sfp(index)
self._sfp_list.append(sfp)

def __initialize_eeprom(self):
from sonic_platform.eeprom import Tlv
self._eeprom = Tlv()

def __initialize_components(self):
from sonic_platform.component import Component
for index in range(0, NUM_COMPONENT):
component = Component(index)
self._component_list.append(component)
self._reboot_cause_path = HOST_REBOOT_CAUSE_PATH if self.__is_host(
) else PMON_REBOOT_CAUSE_PATH

self._eeprom = Tlv()

def __is_host(self):
return os.system(HOST_CHK_CMD) == 0
Expand All @@ -85,14 +116,6 @@ def get_base_mac(self):
"""
return self._eeprom.get_mac()

def get_serial(self):
"""
Retrieves the hardware serial number for the chassis
Returns:
A string containing the hardware serial number for this chassis.
"""
return self._eeprom.get_serial()

def get_system_eeprom_info(self):
"""
Retrieves the full content of system EEPROM information for the chassis
Expand All @@ -116,7 +139,8 @@ def get_reboot_cause(self):
"""
description = 'None'
reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER
hw_reboot_cause = self._component_list[0].get_register_value(RESET_REGISTER)
hw_reboot_cause = self._component_list[0].get_register_value(
RESET_REGISTER)
sw_reboot_cause = self.__read_txt_file(
self._reboot_cause_path) or "Unknown"

Expand Down Expand Up @@ -145,3 +169,128 @@ def get_watchdog(self):
self._watchdog = Watchdog()

return self._watchdog

def get_change_event(self, timeout=0):
"""
Returns a nested dictionary containing all devices which have
experienced a change at chassis level
Args:
timeout: Timeout in milliseconds (optional). If timeout == 0,
this method will block until a change is detected.
Returns:
(bool, dict):
- True if call successful, False if not;
- A nested dictionary where key is a device type,
value is a dictionary with key:value pairs in the format of
{'device_id':'device_event'},
where device_id is the device ID for this device and
device_event,
status='1' represents device inserted,
status='0' represents device removed.
Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}}
indicates that fan 0 has been removed, fan 2
has been inserted and sfp 11 has been removed.
"""
# SFP event
if not self.sfp_module_initialized:
self.__initialize_sfp()

sfp_event = SfpEvent(self._sfp_list).get_sfp_event(timeout)
if sfp_event:
return True, {'sfp': sfp_event}

return False, {'sfp': {}}

##############################################################
######################## SFP methods #########################
##############################################################

def get_num_sfps(self):
"""
Retrieves the number of sfps available on this chassis
Returns:
An integer, the number of sfps available on this chassis
"""
if not self.sfp_module_initialized:
self.__initialize_sfp()

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):
"""
Retrieves sfp represented by (1-based) index <index>
Args:
index: An integer, the index (1-based) of the sfp to retrieve.
The index should be the sequence of a physical port in a chassis,
starting from 1.
For example, 1 for Ethernet0, 2 for Ethernet4 and so on.
Returns:
An object dervied from SfpBase representing the specified sfp
"""
sfp = None
if not self.sfp_module_initialized:
self.__initialize_sfp()

try:
# The index will start from 1
sfp = self._sfp_list[index-1]
except IndexError:
sys.stderr.write("SFP index {} out of range (1-{})\n".format(
index, len(self._sfp_list)))
return sfp

##############################################################
###################### 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 Chassis
Returns:
bool: True if Chassis 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._eeprom.get_serial()

def get_status(self):
"""
Retrieves the operational status of the device
Returns:
A boolean value, True if device is operating properly, False if not
"""
return True
62 changes: 62 additions & 0 deletions device/celestica/x86_64-cel_e1031-r0/sonic_platform/event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
try:
import time
import select
from .helper import APIHelper
from sonic_py_common.logger import Logger
except ImportError as e:
raise ImportError(repr(e) + " - required module not found")


class SfpEvent:
''' Listen to insert/remove sfp events '''

SFP_NUM_START = 49
DELAY = 0.05
INT_PATH = '/sys/devices/platform/e1031.smc/SFP/modabs_int'
GPIO_SUS7 = '/sys/devices/platform/hlx-ich.0/sci_int_gpio_sus7'

def __init__(self, sfp_list):
self._api_helper = APIHelper()
self._sfp_list = sfp_list
self._logger = Logger()

# clear interrupt
self._api_helper.read_one_line_file(self.INT_PATH)

def get_sfp_event(self, timeout):
epoll = select.epoll()
port_dict = {}
timeout_sec = timeout/1000

try:
# We get notified when there is an SCI interrupt from GPIO SUS7
fd = open(self.GPIO_SUS7, "r")
fd.read()

epoll.register(fd.fileno(), select.EPOLLIN & select.EPOLLET)
events = epoll.poll(timeout=timeout_sec if timeout != 0 else -1)
if events:
# Read the QSFP ABS interrupt & status registers
port_changes = self._api_helper.read_one_line_file(
self.INT_PATH)
changes = int(port_changes, 16)
for sfp in self._sfp_list:
if sfp.port_num < self.SFP_NUM_START:
continue

change = (changes >> sfp.port_num-self.SFP_NUM_START) & 1
if change == 1:
time.sleep(self.DELAY)
port_status = sfp.get_presence()
port_dict[str(sfp.port_num)] = '1' if port_status else '0'

return port_dict
except Exception as e:
self._logger.log_error("Failed to detect SfpEvent - " + repr(e))
return False

finally:
fd.close()
epoll.close()

return False
8 changes: 8 additions & 0 deletions device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,11 @@ def get_presence(self):
present_str = self.__read_txt_file(fan_direction_file) or '1'

return int(present_str) == 0 if not self.is_psu_fan else True

def get_status(self):
"""
Retrieves the operational status of the device
Returns:
A boolean value, True if device is operating properly, False if not
"""
return self.get_presence() and self.get_speed() > 0
Loading

0 comments on commit d6bef16

Please sign in to comment.