diff --git a/sonic_y_cable/credo/y_cable_credo.py b/sonic_y_cable/credo/y_cable_credo.py index 45c91f16c..9e6af58b5 100644 --- a/sonic_y_cable/credo/y_cable_credo.py +++ b/sonic_y_cable/credo/y_cable_credo.py @@ -47,6 +47,7 @@ class YCable(YCableBase): OFFSET_SWITCH_MUX_DIRECTION = 642 OFFSET_MUX_DIRECTION = 644 OFFSET_ACTIVE_TOR_INDICATOR = 645 + OFFSET_API_VERSION = 650 OFFSET_ENABLE_AUTO_SWITCH = 651 OFFSET_AUTO_SWITCH_HYSTERESIS = 652 OFFSET_MANUAL_SWITCH_COUNT_TOR_A = 653 @@ -62,6 +63,8 @@ class YCable(YCableBase): OFFSET_EXTEND_SWITCH_COUNT_TYPE = 741 OFFSET_EXTEND_SWITCH_COUNT = 742 OFFSET_CLEAR_SWITCH_COUNT = 746 + OFFSET_OPERATION_TIME = 747 + OFFSET_RESET_CAUSE = 751 OFFSET_CONFIGURE_PRBS_TYPE = 768 OFFSET_ENABLE_PRBS = 769 OFFSET_INITIATE_BER_MEASUREMENT = 770 @@ -119,9 +122,11 @@ class YCable(YCableBase): EVENTLOG_OPTION_CLEAR = 0x02 # VSC opcode + VSC_OPCODE_QUEUE_INFO = 0x18 VSC_OPCODE_UART_STAT = 0x1C VSC_OPCODE_SERDES_INFO = 0x1D VSC_OPCODE_DSP_LOADFW_STAT = 0x1F + VSC_OPCODE_MEM_READ = 0x40 VSC_OPCODE_FWUPD = 0x80 VSC_OPCODE_EVENTLOG = 0x81 VSC_OPCODE_TCM_READ = 0x82 @@ -143,6 +148,15 @@ class YCable(YCableBase): EEPROM_TIMEOUT_ERROR = -1 EEPROM_GENERIC_ERROR = -1 + # side bitamp + SIDE_BMP_NIC = 1 + SIDE_BMP_TOR_A = 2 + SIDE_BMP_TOR_B = 4 + SIDE_BMP_ALL = 7 + + CABLE_HEALTHY = True + CABLE_UNHEALTHY = False + # MCU error code MCU_EC_NO_ERROR = 0 MCU_EC_GET_FW_INFO_ERROR = 11 @@ -205,7 +219,7 @@ def __init__(self, port, main_logger): def read_mmap(self, page, byte, len=1): """ - This API converts memory map page and offset to linar address, then returns eeprom values + This API converts memory map page and offset to linear address, then returns eeprom values by calling read_eeprom() Args: @@ -219,7 +233,7 @@ def read_mmap(self, page, byte, len=1): an Integer, length of the reading Returns: - an Integer or bytearray, returns the value of the specified eeprom addres, returns 0xFF if it did not succeed + an Integer or bytearray, returns the value of the specified eeprom address, returns 0xFF if it did not succeed """ if byte < 128: linear_addr = byte @@ -243,7 +257,7 @@ def read_mmap(self, page, byte, len=1): def write_mmap(self, page, byte, value, len=1): """ - This API converts memory map page and offset to linar address for calling write_eeprom() + This API converts memory map page and offset to linear address for calling write_eeprom() Args: page: @@ -446,6 +460,84 @@ def tcm_write(self, addr, data): return True + def tcm_read_atomic(self, addr): + """ + This API sends the tcm read command to the serdes chip via VSC cmd + + Args: + addr: + an Integer, address of tcm space + Returns: + an Integer, return data of tcm address + """ + + if self.platform_chassis is not None: + with self.rlock.acquire_timeout(RLocker.ACQUIRE_LOCK_TIMEOUT) as lock_status: + if lock_status: + vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) + vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_TCM_READ + vsc_req_form[130] = (addr >> 0) & 0xFF + vsc_req_form[131] = (addr >> 8) & 0xFF + vsc_req_form[132] = (addr >> 16) & 0xFF + vsc_req_form[133] = (addr >> 24) & 0xFF + status = self.send_vsc(vsc_req_form) + if status != YCable.MCU_EC_NO_ERROR: + self.log_error('tcm read addr[%04X] error[%04X]' % (addr, status)) + return -1 + + data = (self.read_mmap(YCable.MIS_PAGE_VSC, 134) | (self.read_mmap(YCable.MIS_PAGE_VSC, 135) << 8) | + (self.read_mmap(YCable.MIS_PAGE_VSC, 136) << 16) | (self.read_mmap(YCable.MIS_PAGE_VSC, 137) << 24)) + else: + self.log_error('acquire lock timeout, failed to read serdes tcm register') + return YCable.EEPROM_ERROR + else: + self.log_error("platform_chassis is not loaded, failed to read serdes tcm register") + return YCable.EEPROM_ERROR + + return data + + def tcm_write_atomic(self, addr, data): + """ + This API sends the tcm write command to the serdes chip via VSC cmd + + Args: + addr: + an Integer, address of tcm space + + data: + an Integer, value to be written to the address + + Returns: + a boolean, True if the tcm write succeeded and False if it did not succeed. + """ + + if self.platform_chassis is not None: + with self.rlock.acquire_timeout(RLocker.ACQUIRE_LOCK_TIMEOUT) as lock_status: + if lock_status: + vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) + vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_TCM_WRITE + vsc_req_form[130] = (addr >> 0) & 0xFF + vsc_req_form[131] = (addr >> 8) & 0xFF + vsc_req_form[132] = (addr >> 16) & 0xFF + vsc_req_form[133] = (addr >> 24) & 0xFF + vsc_req_form[134] = (data >> 0) & 0xFF + vsc_req_form[135] = (data >> 8) & 0xFF + vsc_req_form[136] = (data >> 16) & 0xFF + vsc_req_form[137] = (data >> 24) & 0xFF + + status = self.send_vsc(vsc_req_form) + if status != YCable.MCU_EC_NO_ERROR: + self.log_error('tcm read addr[%04X] data[%04X] error[%04X]' % (addr, data, status)) + return False + else: + self.log_error('acquire lock timeout, failed to write serdes tcm register') + return YCable.EEPROM_ERROR + else: + self.log_error("platform_chassis is not loaded, failed to write serdes tcm register") + return YCable.EEPROM_ERROR + + return True + def reg_read(self, addr): """ This API reads the serdes register via vsc @@ -499,6 +591,77 @@ def reg_write(self, addr, data): return True + def reg_read_atomic(self, addr): + """ + This API reads the serdes register in atomic method + + Args: + addr: + an Integer, address of the serdes register + Returns: + an Integer, return data of the register + """ + + if self.platform_chassis is not None: + with self.rlock.acquire_timeout(RLocker.ACQUIRE_LOCK_TIMEOUT) as lock_status: + if lock_status: + vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) + vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_REG_READ + vsc_req_form[130] = (addr >> 0) & 0xFF + vsc_req_form[131] = (addr >> 8) & 0xFF + vsc_req_form[132] = (addr >> 16) & 0xFF + vsc_req_form[133] = (addr >> 24) & 0xFF + status = self.send_vsc(vsc_req_form) + if status != YCable.MCU_EC_NO_ERROR: + self.log_error('reg read addr[%04X] error[%04X]' % (addr, status)) + return YCable.EEPROM_ERROR + + return self.read_mmap(YCable.MIS_PAGE_VSC, 134) | (self.read_mmap(YCable.MIS_PAGE_VSC, 135) << 8) + else: + self.log_error('acquire lock timeout, failed to read serdes register') + return YCable.EEPROM_ERROR + else: + self.log_error("platform_chassis is not loaded, failed to read serdes register") + return YCable.EEPROM_ERROR + + def reg_write_atomic(self, addr, data): + """ + This API writes the serdes register in atomic method + + Args: + addr: + an Integer, address of the serdes register + + data: + an Integer, value to be written to the register address + + Returns: + an Integer, 0 if the register write succeeded. + """ + + if self.platform_chassis is not None: + with self.rlock.acquire_timeout(RLocker.ACQUIRE_LOCK_TIMEOUT) as lock_status: + if lock_status: + vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) + vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_REG_WRITE + vsc_req_form[130] = (addr >> 0) & 0xFF + vsc_req_form[131] = (addr >> 8) & 0xFF + vsc_req_form[134] = (data >> 0) & 0xFF + vsc_req_form[135] = (data >> 8) & 0xFF + + status = self.send_vsc(vsc_req_form) + if status != YCable.MCU_EC_NO_ERROR: + self.log_error('reg write addr[%04X] data[%04X] error[%04X]' % (addr, data, status)) + return YCable.EEPROM_ERROR + else: + self.log_error('acquire lock timeout, failed to write serdes register') + return YCable.EEPROM_ERROR + else: + self.log_error("platform_chassis is not loaded, failed to write serdes register") + return YCable.EEPROM_ERROR + + return 0 + def toggle_mux_to_tor_a(self): """ This API does a hard switch toggle of the Y cable's MUX regardless of link state to @@ -524,7 +687,7 @@ def toggle_mux_to_tor_a(self): result = self.platform_chassis.get_sfp(self.port).write_eeprom(curr_offset, 1, buffer) else: self.log_error('acquire lock timeout, failed to toggle mux to TOR A') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to toggle mux to TOR A") return YCable.EEPROM_ERROR @@ -554,7 +717,7 @@ def toggle_mux_to_tor_b(self): result = self.platform_chassis.get_sfp(self.port).write_eeprom(curr_offset, 1, buffer) else: self.log_error('acquire lock timeout, failed to toggle mux to TOR B') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to toggle mux to TOR B") return YCable.EEPROM_ERROR @@ -585,7 +748,7 @@ def get_read_side(self): result = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset, 1) else: self.log_error('acquire lock timeout, failed to check read side') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to check read side") return YCable.EEPROM_ERROR @@ -645,7 +808,7 @@ def get_mux_direction(self): result = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset, 1) else: self.log_error('acquire lock timeout, failed to get mux direction') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error( "platform_chassis is not loaded, failed to get mux direction") @@ -705,7 +868,7 @@ def get_active_linked_tor_side(self): result = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset, 1) else: self.log_error('acquire lock timeout, failed to check Active Linked and routing TOR side') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error( "platform_chassis is not loaded, failed to check Active Linked and routing TOR side") @@ -768,7 +931,7 @@ def is_link_active(self, target): result = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset, 1) else: self.log_error('acquire lock timeout, failed to check if link is Active on target side') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error( "platform_chassis is not loaded, failed to check if link is Active on target side") @@ -868,7 +1031,7 @@ def get_eye_heights(self, target): idx += 2 else: self.log_error('acquire lock timeout, failed to get eye height') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get eye height") return YCable.EEPROM_ERROR @@ -894,7 +1057,7 @@ def get_vendor(self): result = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset, 16) else: self.log_error('acquire lock timeout, failed to get vendor name') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get Vendor name") return YCable.EEPROM_ERROR @@ -921,7 +1084,7 @@ def get_part_number(self): part_result = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset, 16) else: self.log_error('acquire lock timeout, failed to get part number') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get part number") return YCable.EEPROM_ERROR @@ -948,7 +1111,7 @@ def get_serial_number(self): part_result = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset, 16) else: self.log_error('acquire lock timeout, failed to get serial number') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get serial number") return YCable.EEPROM_ERROR @@ -992,7 +1155,7 @@ def get_switch_count_total(self, switch_count_type, clear_on_read=False): count = (msb_result[0] << 24 | msb_result_1[0] << 16 | msb_result_2[0] << 8 | lsb_result[0]) else: self.log_error('acquire lock timeout, failed to get switch count') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("not a valid switch_count_type, failed to get switch count") return YCable.EEPROM_ERROR @@ -1008,7 +1171,7 @@ def get_switch_count_total(self, switch_count_type, clear_on_read=False): return YCable.EEPROM_ERROR else: self.log_error('acquire lock timeout, failed to clear switch count') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get switch count") return YCable.EEPROM_ERROR @@ -1044,14 +1207,15 @@ def get_switch_count_tor_a(self, clear_on_read=False): lsb_result = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset+3, 1) count = (msb_result[0] << 24 | msb_result_1[0] << 16 | msb_result_2[0] << 8 | lsb_result[0]) - buffer = bytearray([4]) - curr_offset = YCable.OFFSET_CLEAR_SWITCH_COUNT - result = self.platform_chassis.get_sfp(self.port).write_eeprom(curr_offset, 1, buffer) - if result is False: - return YCable.EEPROM_ERROR + if clear_on_read: + buffer = bytearray([4]) + curr_offset = YCable.OFFSET_CLEAR_SWITCH_COUNT + result = self.platform_chassis.get_sfp(self.port).write_eeprom(curr_offset, 1, buffer) + if result is False: + return YCable.EEPROM_ERROR else: self.log_error('acquire lock timeout, failed to get switch count (tor A)') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get manual switch count") return YCable.EEPROM_ERROR @@ -1095,7 +1259,7 @@ def get_switch_count_tor_b(self, clear_on_read=False): return YCable.EEPROM_ERROR else: self.log_error('acquire lock timeout, failed to get switch count (tor B)') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get manual switch count") return YCable.EEPROM_ERROR @@ -1178,7 +1342,7 @@ def get_switch_count_target(self, switch_count_type, target, clear_on_read=False return YCable.EEPROM_ERROR else: self.log_error('acquire lock timeout, failed to get switch count target') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get switch count target") return YCable.EEPROM_ERROR @@ -1226,7 +1390,7 @@ def get_target_cursor_values(self, lane, target): result.append(c_int8(post2[0]).value) else: self.log_error('acquire lock timeout, failed to get target cursor values') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get target cursor values") return YCable.EEPROM_ERROR @@ -1270,7 +1434,7 @@ def set_target_cursor_values(self, lane, cursor_values, target): idx += 1 else: self.log_error('acquire lock timeout, failed to set target cursor values') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get target cursor values") return YCable.EEPROM_ERROR @@ -1318,7 +1482,7 @@ def get_firmware_version(self, target): return None else: self.log_error("platform_chassis is not loaded, failed to get firmware version") - return YCable.EEPROM_ERROR + return None result = {} NUM_MCU_SIDE = 3 @@ -1392,9 +1556,14 @@ def download_firmware(self, fwfile): """ if self.platform_chassis is not None: - inFile = open(fwfile, 'rb') - fwImage = bytearray(inFile.read()) - inFile.close() + try: + inFile = open(fwfile, 'rb') + fwImage = bytearray(inFile.read()) + inFile.close() + except Exception: + self.log_error('File Not Found Error: %s' % (fwfile)) + self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_FAILED + return YCableBase.FIRMWARE_DOWNLOAD_FAILURE bin_pid = struct.unpack_from('>B', fwImage[5 : 6])[0] mcu_pid = self.read_mmap(0xFB, 187) @@ -1416,7 +1585,7 @@ def download_firmware(self, fwfile): vsc_req_form[YCable.VSC_BYTE_OPTION] = YCable.FWUPD_OPTION_START status = self.send_vsc(vsc_req_form) if status != YCable.MCU_EC_NO_ERROR: - self.log_error(YCable.MCU_ERROR_CODE_STRING[status]) + self.log_error('Firmware binary start transfer error (error code:%04X)' % (status)) self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_FAILED return YCableBase.FIRMWARE_DOWNLOAD_FAILURE else: @@ -1425,7 +1594,7 @@ def download_firmware(self, fwfile): return YCableBase.FIRMWARE_DOWNLOAD_FAILURE ''' - Transfer firmwre image to local side MCU + Transfer firmware image to local side MCU ''' total_chunk = len(fwImage) // YCable.VSC_BUFF_SIZE chunk_idx = 0 @@ -1549,7 +1718,7 @@ def activate_firmware(self, fwfile=None, hitless=False): This routine should activate the downloaded firmware on all the components of the Y cable of the port for which this API is called.. This API is meant to be used in conjunction with download_firmware API, and - should be called once download_firmware API is succesful. + should be called once download_firmware API is successful. This means that the firmware which has been downloaded should be activated (start being utilized by the cable) once this API is successfully executed. @@ -1562,12 +1731,12 @@ def activate_firmware(self, fwfile=None, hitless=False): choosing (binary, archive, etc.). But note that it should be one file which contains firmware for all components of the Y-cable. In case the vendor chooses to pass this file in activate_firmware, the API should - have the logic to retreive the firmware version from this file + have the logic to retrieve the firmware version from this file which has to be activated on the components of the Y-Cable this API has been called for. If None is passed for fwfile, the cable should activate whatever firmware is marked to be activated next. - If provided, it should retreive the firmware version(s) from this file, ensure + If provided, it should retrieve the firmware version(s) from this file, ensure they are downloaded on the cable, then activate them. hitless (optional): @@ -1586,27 +1755,27 @@ def activate_firmware(self, fwfile=None, hitless=False): with self.rlock.acquire_timeout(RLocker.ACQUIRE_LOCK_TIMEOUT) as lock_status: if lock_status: self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_INPROGRESS - side = 0x7 + side_bitmap = YCable.SIDE_BMP_ALL vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) vsc_req_form[YCable.VSC_BYTE_OPTION] = YCable.FWUPD_OPTION_COMMIT vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_FWUPD - vsc_req_form[YCable.VSC_BYTE_ADDR0] = side + vsc_req_form[YCable.VSC_BYTE_ADDR0] = side_bitmap status = self.send_vsc(vsc_req_form) if status != YCable.MCU_EC_NO_ERROR: - self.log_error(YCable.MCU_ERROR_CODE_STRING[status]) + self.log_error('Firmware commit error (error code:%04X)' % (status)) self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_FAILED return YCableBase.FIRMWARE_ACTIVATE_FAILURE vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) vsc_req_form[YCable.VSC_BYTE_OPTION] = YCable.FWUPD_OPTION_RUN vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_FWUPD - vsc_req_form[YCable.VSC_BYTE_ADDR0] = side - vsc_req_form[YCable.VSC_BYTE_ADDR1] = hitless + vsc_req_form[YCable.VSC_BYTE_ADDR0] = side_bitmap + vsc_req_form[YCable.VSC_BYTE_ADDR1] = hitless status = self.send_vsc(vsc_req_form) time.sleep(5) if status != YCable.MCU_EC_NO_ERROR: - self.log_error(YCable.MCU_ERROR_CODE_STRING[status]) + self.log_error('Firmware run error (error code:%04X)' % (status)) self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_FAILED return YCableBase.FIRMWARE_ACTIVATE_FAILURE @@ -1616,9 +1785,13 @@ def activate_firmware(self, fwfile=None, hitless=False): self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_FAILED return YCableBase.FIRMWARE_ACTIVATE_FAILURE else: - inFile = open(fwfile, 'rb') - fwImage = bytearray(inFile.read()) - inFile.close() + try: + inFile = open(fwfile, 'rb') + fwImage = bytearray(inFile.read()) + inFile.close() + except Exception as e: + self.log_error('activate_firmware, open fw bin error(%s), fwfile:%s' % (e, fwfile)) + return YCableBase.FIRMWARE_ACTIVATE_FAILURE build_msb = struct.unpack_from(' NIC, + TARGET_TOR_A -> TORA, + TARGET_TOR_B -> TORB + fwfile (optional): + a string, a path to the file which contains the firmware image. + Note that the firmware file can be in the format of the vendor's + choosing (binary, archive, etc.). But note that it should be one file + which contains firmware for all components of the Y-cable. In case the + vendor chooses to pass this file in activate_firmware, the API should + have the logic to retrieve the firmware version from this file + which has to be activated on the components of the Y-Cable + this API has been called for. + If None is passed for fwfile, the cable should activate whatever + firmware is marked to be activated next. + If provided, it should retrieve the firmware version(s) from this file, ensure + they are downloaded on the cable, then activate them. + hitless (optional): + a boolean, True, Hitless upgrade: it will backup/restore the current state + (ex. variables of link status, API attributes...etc.) before + and after firmware upgrade. + a boolean, False, Non-hitless upgrade: it will update the firmware regardless + the current status, a link flip can be observed during the upgrade. + Returns: + One of the following predefined constants: + FIRMWARE_ACTIVATE_SUCCESS + FIRMWARE_ACTIVATE_FAILURE + """ + if self.platform_chassis is not None: + + if target == YCableBase.TARGET_NIC: + act_bmp = YCable.SIDE_BMP_NIC + elif target == YCableBase.TARGET_TOR_A: + act_bmp = YCable.SIDE_BMP_TOR_A + elif target == YCableBase.TARGET_TOR_B: + act_bmp = YCable.SIDE_BMP_TOR_B + else: + act_bmp = YCable.SIDE_BMP_ALL + + if fwfile is None: + with self.rlock.acquire_timeout(RLocker.ACQUIRE_LOCK_TIMEOUT) as lock_status: + if lock_status: + self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_INPROGRESS + + vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) + vsc_req_form[YCable.VSC_BYTE_OPTION] = YCable.FWUPD_OPTION_COMMIT + vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_FWUPD + vsc_req_form[YCable.VSC_BYTE_ADDR0] = act_bmp + status = self.send_vsc(vsc_req_form) + if status != YCable.MCU_EC_NO_ERROR: + self.log_error('Firmware commit error (error code:%04X)' % (status)) + self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_FAILED + return YCableBase.FIRMWARE_ACTIVATE_FAILURE + + vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) + vsc_req_form[YCable.VSC_BYTE_OPTION] = YCable.FWUPD_OPTION_RUN + vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_FWUPD + vsc_req_form[YCable.VSC_BYTE_ADDR0] = act_bmp + vsc_req_form[YCable.VSC_BYTE_ADDR1] = hitless + status = self.send_vsc(vsc_req_form) + time.sleep(5) + if status != YCable.MCU_EC_NO_ERROR: + self.log_error('Firmware run error (error code:%04X)' % (status)) + self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_FAILED + return YCableBase.FIRMWARE_ACTIVATE_FAILURE + + self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_NOT_INITIATED_OR_FINISHED + else: + self.log_error('acquire lock timeout, failed to activate target firmware') + self.download_firmware_status = self.FIRMWARE_DOWNLOAD_STATUS_FAILED + return YCableBase.FIRMWARE_ACTIVATE_FAILURE + else: + try: + inFile = open(fwfile, 'rb') + fwImage = bytearray(inFile.read()) + inFile.close() + except Exception as e: + self.log_error('activate_target_firmware, open fw bin error(%s), fwfile:%s' % (e, fwfile)) + return YCableBase.FIRMWARE_ACTIVATE_FAILURE + + build_msb = struct.unpack_from(' 150: + break else: self.log_error('acquire lock timeout, failed to get event log') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get event log") return YCable.EEPROM_ERROR @@ -2399,7 +2791,7 @@ def get_pcs_stats(self, target): pcs_stats['Tx Octets OK'] = self.tcm_read(base + 4 * (Tx + 1)) else: self.log_error('acquire lock timeout, failed to get pcs statisics') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get pcs statisics") return YCable.EEPROM_ERROR @@ -2478,7 +2870,7 @@ def get_fec_stats(self, target): fec_stats['Corrected CW (15 sym err)'] = self.tcm_read(base + (26 << 2)) else: self.log_error('acquire lock timeout, failed to get fec statisics') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get fec statisics") return YCable.EEPROM_ERROR @@ -2509,7 +2901,7 @@ def set_autoswitch_hysteresis_timer(self, time): self.platform_chassis.get_sfp(self.port).write_eeprom(curr_offset, 1, buffer) else: self.log_error('acquire lock timeout, failed to set autoswitch hysteresis timer') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to set autoswitch hysteresis timer") return YCable.EEPROM_ERROR @@ -2536,7 +2928,7 @@ def get_autoswitch_hysteresis_timer(self): time = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset, 1) else: self.log_error('acquire lock timeout, failed to get autoswitch hysteresis timer') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get autoswitch hysteresis timer") return YCable.EEPROM_ERROR @@ -2577,7 +2969,7 @@ def restart_anlt(self, target): self.fw_cmd_ext(0x7040, 0, lane) else: self.log_error('acquire lock timeout, failed to restart anlt') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to restart anlt") return YCable.EEPROM_ERROR @@ -2626,7 +3018,7 @@ def get_anlt_stats(self, target): anlt_stat['LT_TX_lane%d' % idx] = [(lt_tx1 >> 8) & 0xFF, lt_tx1 & 0xFF, (lt_tx2 >> 8) & 0xFF, lt_tx2 & 0xFF] else: self.log_error('acquire lock timeout, failed to get anlt stat') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get anlt stats") return YCable.EEPROM_ERROR @@ -2741,7 +3133,7 @@ def enable_prbs_mode(self, target, mode_value, lane_mask, direction=YCableBase.P result = self.platform_chassis.get_sfp(self.port).write_eeprom(curr_offset, 1, buffer) else: self.log_error('acquire lock timeout, failed to enable the PRBS mode') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to enable the PRBS mode") return YCable.EEPROM_ERROR @@ -2785,7 +3177,7 @@ def disable_prbs_mode(self, target, direction): result = self.platform_chassis.get_sfp(self.port).write_eeprom(curr_offset, 1, buffer) else: self.log_error('acquire lock timeout, failed to disable the PRBS mode') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to disable the PRBS mode") return YCable.EEPROM_ERROR @@ -2835,7 +3227,7 @@ def enable_loopback_mode(self, target, lane_mask, mode=YCableBase.LOOPBACK_MODE_ result = self.platform_chassis.get_sfp(self.port).write_eeprom(curr_offset, 1, buffer) else: self.log_error('acquire lock timeout, failed to enable the loopback mode') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to enable the loopback mode") return YCable.EEPROM_ERROR @@ -2874,7 +3266,7 @@ def disable_loopback_mode(self, target): result = self.platform_chassis.get_sfp(self.port).write_eeprom(curr_offset, 1, buffer) else: self.log_error('acquire lock timeout, failed to disable loopback mode') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to disable loopback mode") return YCable.EEPROM_ERROR @@ -2935,7 +3327,7 @@ def get_loopback_mode(self, target): return YCableBase.LOOPBACK_MODE_NEAR_END else: self.log_error('acquire lock timeout, failed to get loopback mode') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get loopback mode") return YCable.EEPROM_ERROR @@ -2996,7 +3388,7 @@ def get_ber_info(self, target): idx += 2 else: self.log_error('acquire lock timeout, failed to get ber info') - return YCableBase.EEPROM_ERROR + return YCable.EEPROM_ERROR else: self.log_error("platform_chassis is not loaded, failed to get ber info") return YCable.EEPROM_ERROR @@ -3026,54 +3418,277 @@ def debug_dump_registers(self, option=None): with all the relevant key-value pairs for all the meaningful fields which would help diagnose the cable for proper functioning """ + if self.platform_chassis is not None: + api_ver = self.platform_chassis.get_sfp(self.port).read_eeprom(YCable.OFFSET_API_VERSION, 1)[0] + + result = {} + result['vendor'] = self.get_vendor() + result['pn'] = self.get_part_number() + result['sn'] = self.get_serial_number() + result['uart_stat'] = self.get_uart_stat() + result['nic_temp'] = self.get_nic_temperature() + result['nic_voltage'] = self.get_nic_voltage() + result['fw_init_status'] = self.get_dsp_fw_init_stat() + result['serdes_detect'] = self.get_dsp_link_detect() + + if api_ver >= 0x18: + result['queue_info'] = self.queue_info() + + lanes = [0,1,2,3,12,13,14,15,20,21,22,23] + + for ln in list(lanes): + data = self.get_serdes_params(ln) + serdes = {} + serdes['ch_est'] = struct.unpack_from(' 512: size = 512 + else: size = length - curr + else: + size = 4 + + with self.rlock.acquire_timeout(RLocker.ACQUIRE_LOCK_TIMEOUT) as lock_status: + if lock_status: + vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) + vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_MEM_READ + vsc_req_form[YCable.VSC_BYTE_OPTION] = target + vsc_req_form[130] = (addr >> 0) & 0xFF + vsc_req_form[131] = (addr >> 8) & 0xFF + vsc_req_form[132] = (addr >> 16) & 0xFF + vsc_req_form[133] = (addr >> 24) & 0xFF + vsc_req_form[134] = (size >> 0) & 0xFF + vsc_req_form[135] = (size >> 8) & 0xFF + vsc_req_form[136] = (size >> 16) & 0xFF + vsc_req_form[137] = (size >> 24) & 0xFF + status = self.send_vsc(vsc_req_form) + if status != YCable.MCU_EC_NO_ERROR: + self.log_error('read MCU internal memory error error (error code:0x%04X)' % (status)) + return YCable.EEPROM_ERROR + + idx = 0 + while idx < size: + if (size - idx) > 128: + data.extend(self.read_mmap(YCable.MIS_PAGE_FC + idx // 128, 128 + idx % 128, 128)) + idx += 128 + else: + data.extend(self.read_mmap(YCable.MIS_PAGE_FC + idx // 128, 128 + idx % 128, size - idx)) + idx = size + else: + self.log_error('acquire lock timeout, failed to read memory') + return YCable.EEPROM_ERROR + + curr += size + addr += size + else: + self.log_error("platform_chassis is not loaded, failed to read memory") + + return data + + def health_check(self): + """ + This API checks the health of the cable, where it is healthy/unhealythy for RMA purposes/diagnostics. + The port on which this API is called for can be referred using self.port. + + Args: + Returns: + a Boolean, True if the cable is healthy and False if it is not healthy. + """ + + if self.platform_chassis is not None: + api_ver = self.platform_chassis.get_sfp(self.port).read_eeprom(YCable.OFFSET_API_VERSION, 1)[0] + + vendor = self.get_vendor() + if vendor != "Credo ": + self.log_error("check cable health fail: unable to get correct vendor name:%s" % (vendor)) + return YCable.CABLE_UNHEALTHY + + uart_stat = self.get_uart_stat() + if api_ver >= 0x18 and uart_stat['Local']['UART2']['RxErrorCnt'] > 100: + self.log_error("check cable health fail: uart rx error count overlimit:%d" % (uart_stat['local']['UART2']['RxErrorCnt'])) + return YCable.CABLE_UNHEALTHY + + serdes_fw_tag = self.reg_read_atomic(0xB71A) + if serdes_fw_tag != 0x6A6A: + self.log_error("check cable health fail: serdes fw is not loaded correctly:%04X" % (serdes_fw_tag)) + return YCable.CABLE_UNHEALTHY + else: + self.log_error("platform_chassis is not loaded, failed to check cable health") + return YCable.EEPROM_ERROR + + return YCable.CABLE_HEALTHY + + def get_dsp_link_detect(self): """ This API returns rdy/sd of DSP. The port on which this API is called for can be referred using self.port. @@ -3094,7 +3709,6 @@ def get_dsp_link_Dect(self): result['rdyTorA'] = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset + 3, 1)[0] result['sdTorB'] = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset + 4, 1)[0] result['rdyTorB'] = self.platform_chassis.get_sfp(self.port).read_eeprom(curr_offset + 5, 1)[0] - else: self.log_error("platform_chassis is not loaded, failed to get init. status of DSP firmware") @@ -3111,20 +3725,24 @@ def get_dsp_fw_init_stat(self): """ if self.platform_chassis is not None: - result = {} - - vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) - vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_DSP_LOADFW_STAT - status = self.send_vsc(vsc_req_form) - if status != YCable.MCU_EC_NO_ERROR: - self.log_error('Get DSP firmware init status error (error code:0x%04X)' % (status)) - return result + with self.rlock.acquire_timeout(RLocker.ACQUIRE_LOCK_TIMEOUT) as lock_status: + if lock_status: + result = {} - result['err_code'] = self.read_mmap(YCable.MIS_PAGE_VSC, 134) - result['err_stat'] = self.read_mmap(YCable.MIS_PAGE_VSC, 135) + vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) + vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_DSP_LOADFW_STAT + status = self.send_vsc(vsc_req_form) + if status != YCable.MCU_EC_NO_ERROR: + self.log_error('Get DSP firmware init status error (error code:0x%04X)' % (status)) + return result + result['err_code'] = self.read_mmap(YCable.MIS_PAGE_VSC, 134) + result['err_stat'] = self.read_mmap(YCable.MIS_PAGE_VSC, 135) + else: + self.log_error('acquire lock timeout, failed to get init status') + return YCable.EEPROM_ERROR else: - self.log_error("platform_chassis is not loaded, failed to get init. status of DSP firmware") + self.log_error("platform_chassis is not loaded, failed to get init status") return result @@ -3139,53 +3757,56 @@ def get_uart_stat(self): """ if self.platform_chassis is not None: - result = {} - for option in range(2): - vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) - vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_UART_STAT - vsc_req_form[YCable.VSC_BYTE_OPTION] = option - status = self.send_vsc(vsc_req_form) - if status != YCable.MCU_EC_NO_ERROR: - self.log_error('Dump Uart statstics error (error code:0x%04X)' % (status)) - return result - - data = self.read_mmap(YCable.MIS_PAGE_FC, 128, 64) - ver = self.read_mmap(YCable.MIS_PAGE_VSC, 130, 1) + with self.rlock.acquire_timeout(RLocker.ACQUIRE_LOCK_TIMEOUT) as lock_status: + if lock_status: + for option in range(2): + vsc_req_form = [None] * (YCable.VSC_CMD_ATTRIBUTE_LENGTH) + vsc_req_form[YCable.VSC_BYTE_OPCODE] = YCable.VSC_OPCODE_UART_STAT + vsc_req_form[YCable.VSC_BYTE_OPTION] = option + status = self.send_vsc(vsc_req_form) + if status != YCable.MCU_EC_NO_ERROR: + self.log_error('Dump Uart statstics error (error code:0x%04X)' % (status)) + return result - uartPort = {} - cnt = {} - cnt['TxPktCnt'] = struct.unpack_from('