Skip to content

Commit

Permalink
CMIS: Remote upgrade support (#400)
Browse files Browse the repository at this point in the history
* Add vendor specific consts

* replace vendor classes

* Vendor specific CMIS api file

* Vendor specific codes

* Vendor specific mem_mps file

* Address review comments

1. Cleanup unnecessary imports 
2. update the comments

* Update aec_800g.py

* Address review comments

Add string macros for offset and length to get vendor name and part number

* Add Credo vendor class packages

* Update xcvr_api_factory.py

* Create test_xcvr_api_factory.py

Add pytest for xcvc_api_factory file

* Update test_xcvr_api_factory.py

* Add test for create_xcvr_api

* Update test_xcvr_api_factory.py
  • Loading branch information
AnoopKamath authored Oct 7, 2023
1 parent c63abc0 commit d382ec8
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 5 deletions.
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
'sonic_platform_base.sonic_xcvr.api.public',
'sonic_platform_base.sonic_xcvr.codes',
'sonic_platform_base.sonic_xcvr.codes.public',
'sonic_platform_base.sonic_xcvr.api.credo',
'sonic_platform_base.sonic_xcvr.mem_maps.credo',
'sonic_platform_base.sonic_xcvr.codes.credo',
'sonic_psu',
'sonic_sfp',
'sonic_thermal',
Expand Down
12 changes: 12 additions & 0 deletions sonic_platform_base/sonic_xcvr/api/credo/aec_800g.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
aec_800g.py
Implementation of Credo AEC cable specific in addition to the CMIS specification.
"""

from ...fields import consts
from ..public.cmis import CmisApi

class CmisAec800gApi(CmisApi):
def set_firmware_download_target_end(self, target):
return self.xcvr_eeprom.write(consts.TARGET_MODE, target)
8 changes: 8 additions & 0 deletions sonic_platform_base/sonic_xcvr/codes/credo/aec_800g.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from ..public.cmis import CmisCodes

class CmisAec800gCodes(CmisCodes):
TARGET_MODE = {
0: 'local',
1: 'remote-A',
2: 'remote-B'
}
4 changes: 3 additions & 1 deletion sonic_platform_base/sonic_xcvr/fields/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,4 +477,6 @@
CDB_CMD = "CdbCommand"
CDB_WRITE_MSG = "CdbWriteMessage"


#VENDOR SPECIFIC
VENDOR_CUSTOM = "VendorCustom"
TARGET_MODE = "TargetMode"
28 changes: 28 additions & 0 deletions sonic_platform_base/sonic_xcvr/mem_maps/credo/aec_800g.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
aec_800g.py
Implementation of Credo AEC cable specific XcvrMemMap for CMIS Rev 5.0
"""

from ..public.cmis import CmisMemMap
from ...fields.xcvr_field import (
CodeRegField,
DateField,
HexRegField,
NumberRegField,
RegBitField,
RegGroupField,
StringRegField,
)
from ...fields import consts

class CmisAec800gMemMap(CmisMemMap):
def __init__(self, codes):
super(CmisAec800gMemMap, self).__init__(codes)

self.VENDOR_CUSTOM = RegGroupField(consts.VENDOR_CUSTOM,
NumberRegField(consts.TARGET_MODE, self.getaddr(0x0, 64), ro=False)
)

def getaddr(self, page, offset, page_size=128):
return page * page_size + offset
40 changes: 36 additions & 4 deletions sonic_platform_base/sonic_xcvr/xcvr_api_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
from .mem_maps.public.cmis import CmisMemMap
from .mem_maps.public.c_cmis import CCmisMemMap

from .codes.credo.aec_800g import CmisAec800gCodes
from .api.credo.aec_800g import CmisAec800gApi
from .mem_maps.credo.aec_800g import CmisAec800gMemMap

from .codes.public.sff8436 import Sff8436Codes
from .api.public.sff8436 import Sff8436Api
from .mem_maps.public.sff8436 import Sff8436MemMap
Expand All @@ -25,6 +29,11 @@
from .api.public.sff8472 import Sff8472Api
from .mem_maps.public.sff8472 import Sff8472MemMap

VENDOR_NAME_OFFSET = 129
VENDOR_PART_NUM_OFFSET = 148
VENDOR_NAME_LENGTH = 16
VENDOR_PART_NUM_LENGTH = 16

class XcvrApiFactory(object):
def __init__(self, reader, writer):
self.reader = reader
Expand All @@ -36,15 +45,38 @@ def _get_id(self):
return None
return id_byte_raw[0]

def _get_vendor_name(self):
name_data = self.reader(VENDOR_NAME_OFFSET, VENDOR_NAME_LENGTH)
if name_data is None:
return None
vendor_name = name_data.decode()
return vendor_name.strip()

def _get_vendor_part_num(self):
part_num = self.reader(VENDOR_PART_NUM_OFFSET, VENDOR_PART_NUM_LENGTH)
if part_num is None:
return None
vendor_pn = part_num.decode()
return vendor_pn.strip()

def create_xcvr_api(self):
# TODO: load correct classes from id_mapping file
id = self._get_id()
vendor_name = self._get_vendor_name()
vendor_pn = self._get_vendor_part_num()
# QSFP-DD or OSFP
if id == 0x18 or id == 0x19 or id == 0x1e:
codes = CmisCodes
mem_map = CmisMemMap(codes)
xcvr_eeprom = XcvrEeprom(self.reader, self.writer, mem_map)
api = CmisApi(xcvr_eeprom)
if vendor_name == 'Credo' and vendor_pn == 'CAC81X321M2MC1MS':
codes = CmisAec800gCodes
mem_map = CmisAec800gMemMap(CmisAec800gCodes)
xcvr_eeprom = XcvrEeprom(self.reader, self.writer, mem_map)
api = CmisAec800gApi(xcvr_eeprom)
else:
codes = CmisCodes
mem_map = CmisMemMap(codes)
xcvr_eeprom = XcvrEeprom(self.reader, self.writer, mem_map)
api = CmisApi(xcvr_eeprom)

if api.is_coherent_module():
mem_map = CCmisMemMap(codes)
xcvr_eeprom = XcvrEeprom(self.reader, self.writer, mem_map)
Expand Down
47 changes: 47 additions & 0 deletions tests/sonic_xcvr/test_xcvr_api_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from unittest.mock import patch
from mock import MagicMock
import pytest

from sonic_platform_base.sonic_xcvr.api.credo.aec_800g import CmisAec800gApi
from sonic_platform_base.sonic_xcvr.mem_maps.credo.aec_800g import CmisAec800gMemMap
from sonic_platform_base.sonic_xcvr.xcvr_eeprom import XcvrEeprom
from sonic_platform_base.sonic_xcvr.codes.credo.aec_800g import CmisAec800gCodes
from sonic_platform_base.sonic_xcvr.fields import consts
from sonic_platform_base.sonic_xcvr.xcvr_api_factory import XcvrApiFactory

class BytesMock(bytes):
def decode(self, encoding='utf-8', errors='strict'):
return 'DecodedCredo'

class TestXcvrApiFactory(object):
read_eeprom = MagicMock
write_eeprom = MagicMock
api = XcvrApiFactory(read_eeprom, write_eeprom)

def test_get_vendor_name(self):
self.api.reader = MagicMock()
self.api.reader.return_value = b'Credo'
with patch.object(BytesMock, 'decode', return_value='DecodedCredo'):
result = self.api._get_vendor_name()
assert result == 'Credo'.strip()

def test_get_vendor_part_num(self):
self.api.reader = MagicMock()
self.api.reader.return_value = b'CAC81X321M2MC1MS'
with patch.object(BytesMock, 'decode', return_value='DecodedCAC81X321M2MC1MS'):
result = self.api._get_vendor_part_num()
assert result == 'CAC81X321M2MC1MS'.strip()

def mock_reader(self, start, length):
return bytes([0x18])

@patch('sonic_platform_base.sonic_xcvr.xcvr_api_factory.XcvrApiFactory._get_vendor_name', MagicMock(return_value='Credo'))
@patch('sonic_platform_base.sonic_xcvr.xcvr_api_factory.XcvrApiFactory._get_vendor_part_num', MagicMock(return_value='CAC81X321M2MC1MS'))
def test_create_xcvr_api(self):
self.api.reader = self.mock_reader
CmisAec800gCodes = MagicMock()
CmisAec800gMemMap = MagicMock()
XcvrEeprom = MagicMock()
CmisAec800gApi = MagicMock()
self.api.create_xcvr_api()

0 comments on commit d382ec8

Please sign in to comment.