Skip to content

Commit 6ae2cb5

Browse files
author
Wirut Getbamrung
authored
[device/celestica]: Add xcvrd event support for Haliburton (#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
1 parent 13467e5 commit 6ae2cb5

File tree

8 files changed

+393
-48
lines changed

8 files changed

+393
-48
lines changed

device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py

+177-28
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@
1313
import json
1414

1515
try:
16+
from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper
1617
from sonic_platform_base.chassis_base import ChassisBase
17-
from sonic_platform.fan import Fan
18-
from sonic_platform.psu import Psu
19-
from sonic_platform.component import Component
20-
from sonic_platform.thermal import Thermal
21-
from sonic_platform.sfp import Sfp
22-
from sonic_platform.eeprom import Tlv
18+
from sonic_py_common import device_info
19+
from .event import SfpEvent
20+
from .helper import APIHelper
2321
except ImportError as e:
2422
raise ImportError(str(e) + "- required module not found")
2523

@@ -40,29 +38,62 @@ class Chassis(ChassisBase):
4038

4139
def __init__(self):
4240
ChassisBase.__init__(self)
43-
self.config_data = {}
41+
self._api_helper = APIHelper()
42+
self.sfp_module_initialized = False
43+
self.__initialize_eeprom()
44+
self.is_host = self._api_helper.is_host()
45+
46+
47+
if not self.is_host:
48+
self.__initialize_fan()
49+
self.__initialize_psu()
50+
self.__initialize_thermals()
51+
else:
52+
self.__initialize_components()
53+
54+
self._reboot_cause_path = HOST_REBOOT_CAUSE_PATH if self.__is_host(
55+
) else PMON_REBOOT_CAUSE_PATH
56+
57+
def __initialize_sfp(self):
58+
sfputil_helper = SfpUtilHelper()
59+
port_config_file_path = device_info.get_path_to_port_config_file()
60+
sfputil_helper.read_porttab_mappings(port_config_file_path, 0)
61+
62+
from sonic_platform.sfp import Sfp
63+
for index in range(0, NUM_SFP):
64+
name_idx = 0 if index+1 == NUM_SFP else index+1
65+
sfp = Sfp(index, sfputil_helper.logical[name_idx])
66+
self._sfp_list.append(sfp)
67+
self.sfp_module_initialized = True
68+
69+
def __initialize_psu(self):
70+
from sonic_platform.psu import Psu
71+
for index in range(0, NUM_PSU):
72+
psu = Psu(index)
73+
self._psu_list.append(psu)
74+
75+
def __initialize_fan(self):
76+
from sonic_platform.fan import Fan
4477
for fant_index in range(0, NUM_FAN_TRAY):
4578
for fan_index in range(0, NUM_FAN):
4679
fan = Fan(fant_index, fan_index)
4780
self._fan_list.append(fan)
48-
for index in range(0, NUM_PSU):
49-
psu = Psu(index)
50-
self._psu_list.append(psu)
81+
82+
def __initialize_thermals(self):
83+
from sonic_platform.thermal import Thermal
5184
for index in range(0, NUM_THERMAL):
5285
thermal = Thermal(index)
5386
self._thermal_list.append(thermal)
54-
# sfp index start from 1
55-
self._sfp_list.append(None)
56-
for index in range(1, NUM_SFP+1):
57-
sfp = Sfp(index)
58-
self._sfp_list.append(sfp)
87+
88+
def __initialize_eeprom(self):
89+
from sonic_platform.eeprom import Tlv
90+
self._eeprom = Tlv()
91+
92+
def __initialize_components(self):
93+
from sonic_platform.component import Component
5994
for index in range(0, NUM_COMPONENT):
6095
component = Component(index)
6196
self._component_list.append(component)
62-
self._reboot_cause_path = HOST_REBOOT_CAUSE_PATH if self.__is_host(
63-
) else PMON_REBOOT_CAUSE_PATH
64-
65-
self._eeprom = Tlv()
6697

6798
def __is_host(self):
6899
return os.system(HOST_CHK_CMD) == 0
@@ -85,14 +116,6 @@ def get_base_mac(self):
85116
"""
86117
return self._eeprom.get_mac()
87118

88-
def get_serial(self):
89-
"""
90-
Retrieves the hardware serial number for the chassis
91-
Returns:
92-
A string containing the hardware serial number for this chassis.
93-
"""
94-
return self._eeprom.get_serial()
95-
96119
def get_system_eeprom_info(self):
97120
"""
98121
Retrieves the full content of system EEPROM information for the chassis
@@ -116,7 +139,8 @@ def get_reboot_cause(self):
116139
"""
117140
description = 'None'
118141
reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER
119-
hw_reboot_cause = self._component_list[0].get_register_value(RESET_REGISTER)
142+
hw_reboot_cause = self._component_list[0].get_register_value(
143+
RESET_REGISTER)
120144
sw_reboot_cause = self.__read_txt_file(
121145
self._reboot_cause_path) or "Unknown"
122146

@@ -145,3 +169,128 @@ def get_watchdog(self):
145169
self._watchdog = Watchdog()
146170

147171
return self._watchdog
172+
173+
def get_change_event(self, timeout=0):
174+
"""
175+
Returns a nested dictionary containing all devices which have
176+
experienced a change at chassis level
177+
Args:
178+
timeout: Timeout in milliseconds (optional). If timeout == 0,
179+
this method will block until a change is detected.
180+
Returns:
181+
(bool, dict):
182+
- True if call successful, False if not;
183+
- A nested dictionary where key is a device type,
184+
value is a dictionary with key:value pairs in the format of
185+
{'device_id':'device_event'},
186+
where device_id is the device ID for this device and
187+
device_event,
188+
status='1' represents device inserted,
189+
status='0' represents device removed.
190+
Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}}
191+
indicates that fan 0 has been removed, fan 2
192+
has been inserted and sfp 11 has been removed.
193+
"""
194+
# SFP event
195+
if not self.sfp_module_initialized:
196+
self.__initialize_sfp()
197+
198+
sfp_event = SfpEvent(self._sfp_list).get_sfp_event(timeout)
199+
if sfp_event:
200+
return True, {'sfp': sfp_event}
201+
202+
return False, {'sfp': {}}
203+
204+
##############################################################
205+
######################## SFP methods #########################
206+
##############################################################
207+
208+
def get_num_sfps(self):
209+
"""
210+
Retrieves the number of sfps available on this chassis
211+
Returns:
212+
An integer, the number of sfps available on this chassis
213+
"""
214+
if not self.sfp_module_initialized:
215+
self.__initialize_sfp()
216+
217+
return len(self._sfp_list)
218+
219+
def get_all_sfps(self):
220+
"""
221+
Retrieves all sfps available on this chassis
222+
Returns:
223+
A list of objects derived from SfpBase representing all sfps
224+
available on this chassis
225+
"""
226+
if not self.sfp_module_initialized:
227+
self.__initialize_sfp()
228+
229+
return self._sfp_list
230+
231+
def get_sfp(self, index):
232+
"""
233+
Retrieves sfp represented by (1-based) index <index>
234+
Args:
235+
index: An integer, the index (1-based) of the sfp to retrieve.
236+
The index should be the sequence of a physical port in a chassis,
237+
starting from 1.
238+
For example, 1 for Ethernet0, 2 for Ethernet4 and so on.
239+
Returns:
240+
An object dervied from SfpBase representing the specified sfp
241+
"""
242+
sfp = None
243+
if not self.sfp_module_initialized:
244+
self.__initialize_sfp()
245+
246+
try:
247+
# The index will start from 1
248+
sfp = self._sfp_list[index-1]
249+
except IndexError:
250+
sys.stderr.write("SFP index {} out of range (1-{})\n".format(
251+
index, len(self._sfp_list)))
252+
return sfp
253+
254+
##############################################################
255+
###################### Device methods ########################
256+
##############################################################
257+
258+
def get_name(self):
259+
"""
260+
Retrieves the name of the device
261+
Returns:
262+
string: The name of the device
263+
"""
264+
return self._api_helper.hwsku
265+
266+
def get_presence(self):
267+
"""
268+
Retrieves the presence of the Chassis
269+
Returns:
270+
bool: True if Chassis is present, False if not
271+
"""
272+
return True
273+
274+
def get_model(self):
275+
"""
276+
Retrieves the model number (or part number) of the device
277+
Returns:
278+
string: Model/part number of device
279+
"""
280+
return self._eeprom.get_pn()
281+
282+
def get_serial(self):
283+
"""
284+
Retrieves the serial number of the device
285+
Returns:
286+
string: Serial number of device
287+
"""
288+
return self._eeprom.get_serial()
289+
290+
def get_status(self):
291+
"""
292+
Retrieves the operational status of the device
293+
Returns:
294+
A boolean value, True if device is operating properly, False if not
295+
"""
296+
return True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
try:
2+
import time
3+
import select
4+
from .helper import APIHelper
5+
from sonic_py_common.logger import Logger
6+
except ImportError as e:
7+
raise ImportError(repr(e) + " - required module not found")
8+
9+
10+
class SfpEvent:
11+
''' Listen to insert/remove sfp events '''
12+
13+
SFP_NUM_START = 49
14+
DELAY = 0.05
15+
INT_PATH = '/sys/devices/platform/e1031.smc/SFP/modabs_int'
16+
GPIO_SUS7 = '/sys/devices/platform/hlx-ich.0/sci_int_gpio_sus7'
17+
18+
def __init__(self, sfp_list):
19+
self._api_helper = APIHelper()
20+
self._sfp_list = sfp_list
21+
self._logger = Logger()
22+
23+
# clear interrupt
24+
self._api_helper.read_one_line_file(self.INT_PATH)
25+
26+
def get_sfp_event(self, timeout):
27+
epoll = select.epoll()
28+
port_dict = {}
29+
timeout_sec = timeout/1000
30+
31+
try:
32+
# We get notified when there is an SCI interrupt from GPIO SUS7
33+
fd = open(self.GPIO_SUS7, "r")
34+
fd.read()
35+
36+
epoll.register(fd.fileno(), select.EPOLLIN & select.EPOLLET)
37+
events = epoll.poll(timeout=timeout_sec if timeout != 0 else -1)
38+
if events:
39+
# Read the QSFP ABS interrupt & status registers
40+
port_changes = self._api_helper.read_one_line_file(
41+
self.INT_PATH)
42+
changes = int(port_changes, 16)
43+
for sfp in self._sfp_list:
44+
if sfp.port_num < self.SFP_NUM_START:
45+
continue
46+
47+
change = (changes >> sfp.port_num-self.SFP_NUM_START) & 1
48+
if change == 1:
49+
time.sleep(self.DELAY)
50+
port_status = sfp.get_presence()
51+
port_dict[str(sfp.port_num)] = '1' if port_status else '0'
52+
53+
return port_dict
54+
except Exception as e:
55+
self._logger.log_error("Failed to detect SfpEvent - " + repr(e))
56+
return False
57+
58+
finally:
59+
fd.close()
60+
epoll.close()
61+
62+
return False

device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan.py

+8
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,11 @@ def get_presence(self):
247247
present_str = self.__read_txt_file(fan_direction_file) or '1'
248248

249249
return int(present_str) == 0 if not self.is_psu_fan else True
250+
251+
def get_status(self):
252+
"""
253+
Retrieves the operational status of the device
254+
Returns:
255+
A boolean value, True if device is operating properly, False if not
256+
"""
257+
return self.get_presence() and self.get_speed() > 0

0 commit comments

Comments
 (0)