@@ -39,6 +39,7 @@ class Chassis(ChassisBase):
3939 HWMON_DIR = "/sys/devices/platform/SMF.512/hwmon/"
4040 HWMON_NODE = os .listdir (HWMON_DIR )[0 ]
4141 MAILBOX_DIR = HWMON_DIR + HWMON_NODE
42+ POLL_INTERVAL = 1 # Poll interval in seconds
4243
4344 reset_reason_dict = {}
4445 reset_reason_dict [11 ] = ChassisBase .REBOOT_CAUSE_POWER_LOSS
@@ -81,6 +82,7 @@ def __init__(self):
8182 self ._component_list .append (component )
8283
8384 self ._watchdog = Watchdog ()
85+ self ._transceiver_presence = self ._get_transceiver_presence ()
8486
8587 def _get_reboot_reason_smf_register (self ):
8688 # In S6100, mb_poweron_reason register will
@@ -111,6 +113,24 @@ def _get_pmc_register(self, reg_name):
111113 rv = rv .lstrip (" " )
112114 return rv
113115
116+ def _get_register (self , reg_file ):
117+ # On successful read, returns the value read from given
118+ # reg_name and on failure returns 'ERR'
119+ rv = 'ERR'
120+
121+ if (not os .path .isfile (reg_file )):
122+ return rv
123+
124+ try :
125+ with open (reg_file , 'r' ) as fd :
126+ rv = fd .read ()
127+ except Exception as error :
128+ rv = 'ERR'
129+
130+ rv = rv .rstrip ('\r \n ' )
131+ rv = rv .lstrip (" " )
132+ return rv
133+
114134 def get_name (self ):
115135 """
116136 Retrieves the name of the chassis
@@ -215,3 +235,103 @@ def get_reboot_cause(self):
215235 return (ChassisBase .REBOOT_CAUSE_NON_HARDWARE , None )
216236
217237 return (ChassisBase .REBOOT_CAUSE_HARDWARE_OTHER , "Invalid Reason" )
238+
239+ def _get_transceiver_presence (self ):
240+
241+ cpld2_modprs = self ._get_register (
242+ "/sys/class/i2c-adapter/i2c-14/14-003e/qsfp_modprs" )
243+ cpld3_modprs = self ._get_register (
244+ "/sys/class/i2c-adapter/i2c-15/15-003e/qsfp_modprs" )
245+ cpld4_modprs = self ._get_register (
246+ "/sys/class/i2c-adapter/i2c-16/16-003e/qsfp_modprs" )
247+ cpld5_modprs = self ._get_register (
248+ "/sys/class/i2c-adapter/i2c-17/17-003e/qsfp_modprs" )
249+
250+ # If IOM is not present, register read will fail.
251+ # Handle the scenario gracefully
252+ if (cpld2_modprs == 'read error' ) or (cpld2_modprs == 'ERR' ):
253+ cpld2_modprs = '0x0'
254+ if (cpld3_modprs == 'read error' ) or (cpld3_modprs == 'ERR' ):
255+ cpld3_modprs = '0x0'
256+ if (cpld4_modprs == 'read error' ) or (cpld4_modprs == 'ERR' ):
257+ cpld4_modprs = '0x0'
258+ if (cpld5_modprs == 'read error' ) or (cpld5_modprs == 'ERR' ):
259+ cpld5_modprs = '0x0'
260+
261+ # Make it contiguous
262+ transceiver_presence = (int (cpld2_modprs , 16 ) & 0xffff ) | \
263+ ((int (cpld4_modprs , 16 ) & 0xffff ) << 16 ) | \
264+ ((int (cpld3_modprs , 16 ) & 0xffff ) << 32 ) | \
265+ ((int (cpld5_modprs , 16 ) & 0xffff ) << 48 )
266+
267+ return transceiver_presence
268+
269+ def get_change_event (self , timeout = 0 ):
270+ """
271+ Returns a nested dictionary containing all devices which have
272+ experienced a change at chassis level
273+
274+ Args:
275+ timeout: Timeout in milliseconds (optional). If timeout == 0,
276+ this method will block until a change is detected.
277+
278+ Returns:
279+ (bool, dict):
280+ - True if call successful, False if not;
281+ - A nested dictionary where key is a device type,
282+ value is a dictionary with key:value pairs in the
283+ format of {'device_id':'device_event'},
284+ where device_id is the device ID for this device and
285+ device_event,
286+ status='1' represents device inserted,
287+ status='0' represents device removed.
288+ Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}}
289+ indicates that fan 0 has been removed, fan 2
290+ has been inserted and sfp 11 has been removed.
291+ """
292+ port_dict = {}
293+ ret_dict = {'sfp' : port_dict }
294+ forever = False
295+
296+ if timeout == 0 :
297+ forever = True
298+ elif timeout > 0 :
299+ timeout = timeout / float (1000 ) # Convert to secs
300+ else :
301+ return False , ret_dict # Incorrect timeout
302+
303+ while True :
304+ if forever :
305+ timer = self .POLL_INTERVAL
306+ else :
307+ timer = min (timeout , self .POLL_INTERVAL )
308+ start_time = time .time ()
309+
310+ time .sleep (timer )
311+ cur_presence = self ._get_transceiver_presence ()
312+
313+ # Update dict only if a change has been detected
314+ if cur_presence != self ._transceiver_presence :
315+ changed_ports = self ._transceiver_presence ^ cur_presence
316+ for port in range (self .get_num_sfps ()):
317+ # Mask off the bit corresponding to particular port
318+ mask = 1 << port
319+ if changed_ports & mask :
320+ # qsfp_modprs 1 => optics is removed
321+ if cur_presence & mask :
322+ port_dict [port ] = '0'
323+ # qsfp_modprs 0 => optics is inserted
324+ else :
325+ port_dict [port ] = '1'
326+
327+ # Update current presence
328+ self ._transceiver_presence = cur_presence
329+ break
330+
331+ if not forever :
332+ elapsed_time = time .time () - start_time
333+ timeout = round (timeout - elapsed_time , 3 )
334+ if timeout <= 0 :
335+ break
336+
337+ return True , ret_dict
0 commit comments