Skip to content

Commit e76ecc6

Browse files
authored
[sonic_y_cable] add support for retrieving firmware info for Y cable, internal and nic temperature and voltage (sonic-net#162)
* [sonic_y_cable] add support for retreiving firmware info for Y cable, internal and nic tempertaure and voltage This PR provides the necessary infrastructure to add support for getting firmware information running on the three ends of Y cable. Also we get internal and nic side temperature and voltage What is the motivation for this PR? To add the necessary support for fetching firmware info for Y cable, internal and nic temperature and voltage How did you do it? Added the changes in sonic-platform-common module in the y_cable.py file How did you verify/test it? opened a python shell and ran the API's manually and test verified the values are correct. Signed-off-by: vaibhav-dahiya <[email protected]>
1 parent f9cf8c9 commit e76ecc6

File tree

1 file changed

+167
-35
lines changed

1 file changed

+167
-35
lines changed

sonic_y_cable/y_cable.py

+167-35
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
try:
88
import math
9+
import time
910
import struct
1011
from ctypes import c_int8
1112

@@ -48,6 +49,10 @@
4849
OFFSET_TOR1_CURSOR_VALUES = 681
4950
OFFSET_TOR2_CURSOR_VALUES = 701
5051
OFFSET_NIC_LANE_ACTIVE = 721
52+
OFFSET_INTERNAL_TEMPERATURE = 22
53+
OFFSET_INTERNAL_VOLTAGE = 26
54+
OFFSET_NIC_TEMPERATURE = 727
55+
OFFSET_NIC_VOLTAGE = 729
5156

5257
# definitions of targets for getting the cursor
5358
# equalization parameters from the register spec
@@ -75,6 +80,18 @@
7580
SWITCH_COUNT_MANUAL = "manual"
7681
SWITCH_COUNT_AUTO = "auto"
7782

83+
FIRMWARE_INFO_PAYLOAD_SIZE = 48
84+
NUM_MCU_SIDE = 3
85+
86+
EEPROM_READ_DATA_INVALID = -1
87+
EEPROM_ERROR = -1
88+
EEPROM_TIMEOUT_ERROR = -1
89+
90+
BER_TIMEOUT_SECS = 1
91+
EYE_TIMEOUT_SECS = 1
92+
93+
MAX_NUM_LANES = 4
94+
7895
SYSLOG_IDENTIFIER = "sonic_y_cable"
7996

8097
# Global logger instance for helper functions and classes to log
@@ -97,15 +114,15 @@ def y_cable_validate_read_data(result, size, physical_port, message):
97114
if len(result) != size:
98115
LOG_MESSAGE_TEMPLATE = "Error: for checking mux_cable {}, eeprom read returned a size {} not equal to 1 for port {}"
99116
helper_logger.log_error(LOG_MESSAGE_TEMPLATE.format(message, len(result), physical_port))
100-
return -1
117+
return EEPROM_READ_DATA_INVALID
101118
else:
102119
LOG_MESSAGE_TEMPLATE = "Error: for checking mux_cable {}, eeprom read returned an instance value of type {} which is not a bytearray for port {}"
103120
helper_logger.log_error(LOG_MESSAGE_TEMPLATE.format(message, type(result), physical_port))
104-
return -1
121+
return EEPROM_READ_DATA_INVALID
105122
else:
106123
LOG_MESSAGE_TEMPLATE = "Error: for checking mux_cable {}, eeprom read returned a None value for port {} which is not expected"
107124
helper_logger.log_error(LOG_MESSAGE_TEMPLATE.format(message, physical_port))
108-
return -1
125+
return EEPROM_READ_DATA_INVALID
109126

110127

111128
def hook_y_cable_simulator(target):
@@ -573,7 +590,6 @@ def check_if_link_is_active_for_torB(physical_port):
573590
return False
574591

575592

576-
@hook_y_cable_simulator
577593
def enable_prbs_mode(physical_port, target, mode_value, lane_map):
578594
"""
579595
This API specifically configures and enables the PRBS mode/type depending upon the mode_value the user provides.
@@ -646,7 +662,6 @@ def enable_prbs_mode(physical_port, target, mode_value, lane_map):
646662
return result
647663

648664

649-
@hook_y_cable_simulator
650665
def disable_prbs_mode(physical_port, target):
651666
"""
652667
This API specifically disables the PRBS mode on the physcial port.
@@ -697,7 +712,6 @@ def disable_prbs_mode(physical_port, target):
697712
return result
698713

699714

700-
@hook_y_cable_simulator
701715
def enable_loopback_mode(physical_port, target, lane_map):
702716
"""
703717
This API specifically configures and enables the Loopback mode on the port user provides.
@@ -755,7 +769,6 @@ def enable_loopback_mode(physical_port, target, lane_map):
755769
return result
756770

757771

758-
@hook_y_cable_simulator
759772
def disable_loopback_mode(physical_port, target):
760773
"""
761774
This API specifically disables the Loopback mode on the port user provides.
@@ -809,7 +822,6 @@ def disable_loopback_mode(physical_port, target):
809822
return result
810823

811824

812-
@hook_y_cable_simulator
813825
def get_ber_info(physical_port, target):
814826
"""
815827
This API specifically returns the BER (Bit error rate) value for a specfic port.
@@ -856,20 +868,27 @@ def get_ber_info(physical_port, target):
856868
physical_port).write_eeprom(curr_offset, 1, buffer)
857869
if result is False:
858870
return result
871+
time_start = time.time()
859872
while(True):
860873
done = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 1)
861-
y_cable_validate_read_data(done, 1, physical_port, "BER data ready to read")
874+
if y_cable_validate_read_data(done, 1, physical_port, "BER data ready to read") == EEPROM_READ_DATA_INVALID:
875+
return EEPROM_ERROR
876+
time_now = time.time()
877+
time_diff = time_now - time_start
862878
if done[0] == 1:
863879
break
880+
elif time_diff >= BER_TIMEOUT_SECS:
881+
return EEPROM_TIMEOUT_ERROR
864882

865883
idx = 0
866-
maxLane = 2
867884
curr_offset = OFFSET_LANE_1_BER_RESULT
868-
for lane in range(maxLane):
885+
for lane in range(MAX_NUM_LANES):
869886
msb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset+idx, 1)
870-
y_cable_validate_read_data(msb_result, 1, physical_port, "BER data msb result")
887+
if y_cable_validate_read_data(msb_result, 1, physical_port, "BER data msb result") == EEPROM_READ_DATA_INVALID:
888+
return EEPROM_ERROR
871889
lsb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset+1+idx, 1)
872-
y_cable_validate_read_data(lsb_result, 1, physical_port, "BER data lsb result")
890+
if y_cable_validate_read_data(lsb_result, 1, physical_port, "BER data lsb result") == EEPROM_READ_DATA_INVALID:
891+
return EEPROM_ERROR
873892
lane_result = msb_result[0] * math.pow(10, (lsb_result[0]-24))
874893
ber_result.append(lane_result)
875894
idx += 2
@@ -881,7 +900,6 @@ def get_ber_info(physical_port, target):
881900
return ber_result
882901

883902

884-
@hook_y_cable_simulator
885903
def get_eye_info(physical_port, target):
886904
"""
887905
This API specifically returns the EYE height value for a specfic port.
@@ -927,20 +945,27 @@ def get_eye_info(physical_port, target):
927945
if result is False:
928946
return result
929947

948+
time_start = time.time()
930949
while(True):
931950
done = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 1)
932-
y_cable_validate_read_data(done, 1, physical_port, "EYE data ready to read")
951+
if y_cable_validate_read_data(done, 1, physical_port, "EYE data ready to read") == EEPROM_READ_DATA_INVALID:
952+
return EEPROM_ERROR
953+
time_now = time.time()
954+
time_diff = time_now - time_start
933955
if done[0] == 1:
934956
break
957+
elif time_diff >= EYE_TIMEOUT_SECS:
958+
return EEPROM_TIMEOUT_ERROR
935959

936960
idx = 0
937-
maxLane = 2
938-
for lane in range(maxLane):
961+
for lane in range(MAX_NUM_LANES):
939962
curr_offset = OFFSET_LANE_1_EYE_RESULT
940963
msb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset+idx, 1)
941-
y_cable_validate_read_data(msb_result, 1, physical_port, "EYE data msb result")
964+
if y_cable_validate_read_data(msb_result, 1, physical_port, "EYE data msb result") == EEPROM_READ_DATA_INVALID:
965+
return EEPROM_ERROR
942966
lsb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset+1+idx, 1)
943-
y_cable_validate_read_data(lsb_result, 1, physical_port, "EYE data lsb result")
967+
if y_cable_validate_read_data(lsb_result, 1, physical_port, "EYE data lsb result") == EEPROM_READ_DATA_INVALID:
968+
return EEPROM_ERROR
944969
lane_result = (msb_result[0] << 8 | lsb_result[0])
945970
eye_result.append(lane_result)
946971
idx += 2
@@ -952,7 +977,6 @@ def get_eye_info(physical_port, target):
952977
return eye_result
953978

954979

955-
@hook_y_cable_simulator
956980
def get_pn_number_and_vendor_name(physical_port):
957981
"""
958982
This API specifically returns the pn number and vendor name for a specfic port.
@@ -968,10 +992,12 @@ def get_pn_number_and_vendor_name(physical_port):
968992

969993
if platform_chassis is not None:
970994
pn_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 15)
971-
y_cable_validate_read_data(pn_result, 1, physical_port, "PN number")
995+
if y_cable_validate_read_data(pn_result, 15, physical_port, "PN number") == EEPROM_READ_DATA_INVALID:
996+
return EEPROM_ERROR
972997
curr_offset = OFFSET_VENDOR_NAME
973998
vendor_name = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 15)
974-
y_cable_validate_read_data(vendor_name, 15, physical_port, "vendor name")
999+
if y_cable_validate_read_data(vendor_name, 15, physical_port, "vendor name") == EEPROM_READ_DATA_INVALID:
1000+
return EEPROM_ERROR
9751001

9761002
else:
9771003
helper_logger.log_error("platform_chassis is not loaded, failed to get pin results")
@@ -980,7 +1006,6 @@ def get_pn_number_and_vendor_name(physical_port):
9801006
return pn_result, vendor_name
9811007

9821008

983-
@hook_y_cable_simulator
9841009
def get_switch_count(physical_port, count_type):
9851010
"""
9861011
This API specifically returns the switch count to change the Active TOR which has
@@ -1009,13 +1034,17 @@ def get_switch_count(physical_port, count_type):
10091034

10101035
if platform_chassis is not None:
10111036
msb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 1)
1012-
y_cable_validate_read_data(msb_result, 1, physical_port, "{} switch count msb result".format(count_type))
1037+
if y_cable_validate_read_data(msb_result, 1, physical_port, "{} switch count msb result".format(count_type)) == EEPROM_READ_DATA_INVALID:
1038+
return EEPROM_ERROR
10131039
msb_result_1 = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset + 1, 1)
1014-
y_cable_validate_read_data(msb_result_1, 1, physical_port, "{} switch count msb result 1".format(count_type))
1040+
if y_cable_validate_read_data(msb_result_1, 1, physical_port, "{} switch count msb result 1".format(count_type)) == EEPROM_READ_DATA_INVALID:
1041+
return EEPROM_ERROR
10151042
msb_result_2 = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset + 2, 1)
1016-
y_cable_validate_read_data(msb_result_2, 1, physical_port, "{} switch count msb result 2".format(count_type))
1043+
if y_cable_validate_read_data(msb_result_2, 1, physical_port, "{} switch count msb result 2".format(count_type)) == EEPROM_READ_DATA_INVALID:
1044+
return EEPROM_ERROR
10171045
lsb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset+3, 1)
1018-
y_cable_validate_read_data(lsb_result, 1, physical_port, "{} switch count lsb result".format(count_type))
1046+
if y_cable_validate_read_data(lsb_result, 1, physical_port, "{} switch count lsb result".format(count_type)) == EEPROM_READ_DATA_INVALID:
1047+
return EEPROM_ERROR
10191048
count = (msb_result[0] << 24 | msb_result_1[0] << 16 | msb_result_2[0] << 8 | lsb_result[0])
10201049

10211050
else:
@@ -1025,7 +1054,6 @@ def get_switch_count(physical_port, count_type):
10251054
return count
10261055

10271056

1028-
@hook_y_cable_simulator
10291057
def get_target_cursor_values(physical_port, lane, target):
10301058
"""
10311059
This API specifically returns the cursor equalization parameters for a target(NIC, TOR1, TOR2).
@@ -1055,19 +1083,24 @@ def get_target_cursor_values(physical_port, lane, target):
10551083

10561084
if platform_chassis is not None:
10571085
pre1 = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset + (target)*20 + (lane-1)*5, 1)
1058-
y_cable_validate_read_data(pre1, 1, physical_port, "target cursor result")
1086+
if y_cable_validate_read_data(pre1, 1, physical_port, "target cursor result") == EEPROM_READ_DATA_INVALID:
1087+
return EEPROM_ERROR
10591088
result.append(c_int8(pre1[0]).value)
10601089
pre2 = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset + (target)*20 + (lane-1)*5 + 1, 1)
1061-
y_cable_validate_read_data(pre2, 1, physical_port, "target cursor result")
1090+
if y_cable_validate_read_data(pre2, 1, physical_port, "target cursor result") == EEPROM_READ_DATA_INVALID:
1091+
return EEPROM_ERROR
10621092
result.append(c_int8(pre2[0]).value)
10631093
main = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset + (target)*20 + (lane-1)*5 + 2, 1)
1064-
y_cable_validate_read_data(main, 1, physical_port, "target cursor result")
1094+
if y_cable_validate_read_data(main, 1, physical_port, "target cursor result") == EEPROM_READ_DATA_INVALID:
1095+
return EEPROM_ERROR
10651096
result.append(c_int8(main[0]).value)
10661097
post1 = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset + (target)*20 + (lane-1)*5 + 3, 1)
1067-
y_cable_validate_read_data(post1, 1, physical_port, "target cursor result")
1098+
if y_cable_validate_read_data(post1, 1, physical_port, "target cursor result") == EEPROM_READ_DATA_INVALID:
1099+
return EEPROM_ERROR
10681100
result.append(c_int8(post1[0]).value)
10691101
post2 = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset + (target)*20 + (lane-1)*5 + 4, 1)
1070-
y_cable_validate_read_data(post2, 1, physical_port, "target cursor result")
1102+
if y_cable_validate_read_data(post2, 1, physical_port, "target cursor result") == EEPROM_READ_DATA_INVALID:
1103+
return EEPROM_ERROR
10711104
result.append(c_int8(post2[0]).value)
10721105

10731106
else:
@@ -1077,7 +1110,6 @@ def get_target_cursor_values(physical_port, lane, target):
10771110
return result
10781111

10791112

1080-
@hook_y_cable_simulator
10811113
def check_if_nic_lanes_active(physical_port):
10821114
"""
10831115
This API specifically returns the byte value which denotes which nic lanes
@@ -1097,11 +1129,111 @@ def check_if_nic_lanes_active(physical_port):
10971129

10981130
if platform_chassis is not None:
10991131
res = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 1)
1100-
y_cable_validate_read_data(res, 1, physical_port, "nic lanes active")
1132+
if y_cable_validate_read_data(res, 1, physical_port, "nic lanes active") == EEPROM_READ_DATA_INVALID:
1133+
return EEPROM_ERROR
11011134
result = res[0]
11021135

11031136
else:
11041137
helper_logger.log_error("platform_chassis is not loaded, failed to get NIC lanes active")
11051138
return -1
11061139

11071140
return result
1141+
1142+
1143+
def get_firmware_version(physical_port, target):
1144+
1145+
data = bytearray(FIRMWARE_INFO_PAYLOAD_SIZE)
1146+
1147+
if platform_chassis is not None:
1148+
for byte_idx in range(0, FIRMWARE_INFO_PAYLOAD_SIZE):
1149+
curr_offset = 0xfc * 128 + 128 + byte_idx
1150+
read_out = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 1)
1151+
if y_cable_validate_read_data(read_out, 1, physical_port, "firmware info") == EEPROM_READ_DATA_INVALID:
1152+
return EEPROM_ERROR
1153+
data[byte_idx] = read_out[0]
1154+
else:
1155+
helper_logger.log_error("platform_chassis is not loaded, failed to get NIC lanes active")
1156+
return -1
1157+
1158+
result = {}
1159+
NUM_MCU_SIDE = 3
1160+
1161+
base_addr = int(target * (FIRMWARE_INFO_PAYLOAD_SIZE / NUM_MCU_SIDE))
1162+
rev_major_slot1 = struct.unpack_from('<B', data[(0 + base_addr):(1 + base_addr)])[0]
1163+
rev_minor_slot1 = struct.unpack_from('<B', data[(2 + base_addr):(3 + base_addr)])[0]
1164+
rev_build_lsb_slot1 = struct.unpack_from('<B', data[(4 + base_addr):(5 + base_addr)])[0]
1165+
rev_build_msb_slot1 = struct.unpack_from('<B', data[(5 + base_addr):(6 + base_addr)])[0]
1166+
rev_major_slot2 = struct.unpack_from('<B', data[(1 + base_addr):(2 + base_addr)])[0]
1167+
rev_minor_slot2 = struct.unpack_from('<B', data[(3 + base_addr):(4 + base_addr)])[0]
1168+
rev_build_lsb_slot2 = struct.unpack_from('<B', data[(6 + base_addr):(7 + base_addr)])[0]
1169+
rev_build_msb_slot2 = struct.unpack_from('<B', data[(7 + base_addr):(8 + base_addr)])[0]
1170+
slot_status = struct.unpack_from('<B', data[(8 + base_addr):(9 + base_addr)])[0]
1171+
1172+
if (rev_major_slot1 == 0 and rev_minor_slot1 == 0 and rev_build_lsb_slot1 == 0 and rev_build_msb_slot1 == 0 and rev_major_slot2 == 0 and rev_minor_slot2 == 0 and rev_build_lsb_slot2 == 0 and rev_build_msb_slot2 == 0):
1173+
return None
1174+
else:
1175+
build_slot1 = chr(rev_build_lsb_slot1) + chr(rev_build_msb_slot1)
1176+
version_slot1 = str(rev_major_slot1) + "." + str(rev_minor_slot1)
1177+
build_slot2 = chr(rev_build_lsb_slot2) + chr(rev_build_msb_slot2)
1178+
version_slot2 = str(rev_major_slot2) + "." + str(rev_minor_slot2)
1179+
1180+
result["build_slot1"] = build_slot1
1181+
result["version_slot1"] = version_slot1
1182+
result["build_slot2"] = build_slot2
1183+
result["version_slot2"] = version_slot2
1184+
result["run_slot1"] = True if slot_status & 0x01 else False
1185+
result["run_slot2"] = True if slot_status & 0x10 else False
1186+
result["commit_slot1"] = True if slot_status & 0x02 else False
1187+
result["commit_slot2"] = True if slot_status & 0x20 else False
1188+
result["empty_slot1"] = True if slot_status & 0x04 else False
1189+
result["empty_slot2"] = True if slot_status & 0x40 else False
1190+
1191+
return result
1192+
1193+
1194+
def get_internal_voltage_temp(physical_port):
1195+
1196+
curr_offset = OFFSET_INTERNAL_TEMPERATURE
1197+
if platform_chassis is not None:
1198+
result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 1)
1199+
if y_cable_validate_read_data(result, 1, physical_port, "internal voltage") == EEPROM_READ_DATA_INVALID:
1200+
return EEPROM_ERROR
1201+
curr_offset = OFFSET_INTERNAL_VOLTAGE
1202+
msb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 1)
1203+
if y_cable_validate_read_data(msb_result, 1, physical_port, "internal temperature msb") == EEPROM_READ_DATA_INVALID:
1204+
return EEPROM_ERROR
1205+
lsb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset+1, 1)
1206+
if y_cable_validate_read_data(lsb_result, 1, physical_port, "internal temperature lsb") == EEPROM_READ_DATA_INVALID:
1207+
return EEPROM_ERROR
1208+
1209+
temp = result[0]
1210+
voltage = (((msb_result[0] << 8) | lsb_result[0]) * 0.0001)
1211+
else:
1212+
helper_logger.log_error("platform_chassis is not loaded, failed to get internal voltage and temp")
1213+
return -1
1214+
1215+
return temp, voltage
1216+
1217+
1218+
def get_nic_voltage_temp(physical_port):
1219+
1220+
curr_offset = OFFSET_NIC_TEMPERATURE
1221+
if platform_chassis is not None:
1222+
result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 1)
1223+
if y_cable_validate_read_data(result, 1, physical_port, "internal voltage") == EEPROM_READ_DATA_INVALID:
1224+
return EEPROM_ERROR
1225+
curr_offset = OFFSET_NIC_VOLTAGE
1226+
msb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset, 1)
1227+
if y_cable_validate_read_data(msb_result, 1, physical_port, "internal temperature msb") == EEPROM_READ_DATA_INVALID:
1228+
return EEPROM_ERROR
1229+
lsb_result = platform_chassis.get_sfp(physical_port).read_eeprom(curr_offset+1, 1)
1230+
if y_cable_validate_read_data(lsb_result, 1, physical_port, "internal temperature lsb") == EEPROM_READ_DATA_INVALID:
1231+
return EEPROM_ERROR
1232+
1233+
temp = result[0]
1234+
voltage = (((msb_result[0] << 8) | lsb_result[0]) * 0.0001)
1235+
else:
1236+
helper_logger.log_error("platform_chassis is not loaded, failed to get NIC voltage and temp")
1237+
return -1
1238+
1239+
return temp, voltage

0 commit comments

Comments
 (0)