Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
'sonic_platform_base.sonic_xcvr.codes.public',
'sonic_platform_base.sonic_xcvr.codes.bailly',
'sonic_platform_base.sonic_xcvr.utils',
'sonic_platform_base.sonic_xcvr.api.arista',
'sonic_platform_base.sonic_xcvr.mem_maps.arista',
'sonic_platform_base.sonic_xcvr.api.credo',
'sonic_platform_base.sonic_xcvr.mem_maps.credo',
'sonic_platform_base.sonic_xcvr.codes.credo',
Expand Down
Empty file.
152 changes: 152 additions & 0 deletions sonic_platform_base/sonic_xcvr/api/arista/cmis_enhanced_lpo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"""
cmis_enhanced_lpo.py

API for Arista Enhanced LPO modules.
"""

from ..public.cmis import CmisApi
from ...fields import arista_lpo_consts as lpo

# VMA: U8 raw count -> mV
_VMA_MV_PER_COUNT = 5
# OMA: U16 raw count -> mW (0.1 uW per count = 0.0001 mW per count)
_OMA_MW_PER_COUNT = 0.0001
# OER max: U8 raw count -> dB
_OER_DB_PER_COUNT = 0.1
# VMA accuracy: U4 (bits 3-0) -> mV
_VMA_ACC_MV_PER_COUNT = 5
# OMA accuracy: U4 (bits 3-0) -> dB
_OMA_ACC_DB_PER_COUNT = 0.2

_VMA_FLAG_FIELDS = [
lpo.LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_FLAG,
lpo.LPO_TX_HOST_INPUT_VMA_LOW_ALARM_FLAG,
lpo.LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_FLAG,
lpo.LPO_TX_HOST_INPUT_VMA_LOW_WARNING_FLAG,
]

_OMA_FLAG_FIELDS = [
lpo.LPO_RX_INPUT_OMA_HIGH_ALARM_FLAG,
lpo.LPO_RX_INPUT_OMA_LOW_ALARM_FLAG,
lpo.LPO_RX_INPUT_OMA_HIGH_WARNING_FLAG,
lpo.LPO_RX_INPUT_OMA_LOW_WARNING_FLAG,
]

_VMA_TX_FIELDS = [
lpo.LPO_HOST_INPUT_VMA_TX1,
lpo.LPO_HOST_INPUT_VMA_TX2,
lpo.LPO_HOST_INPUT_VMA_TX3,
lpo.LPO_HOST_INPUT_VMA_TX4,
lpo.LPO_HOST_INPUT_VMA_TX5,
lpo.LPO_HOST_INPUT_VMA_TX6,
lpo.LPO_HOST_INPUT_VMA_TX7,
lpo.LPO_HOST_INPUT_VMA_TX8,
]

_OMA_RX_FIELDS = [
lpo.LPO_INPUT_OMA_RX1,
lpo.LPO_INPUT_OMA_RX2,
lpo.LPO_INPUT_OMA_RX3,
lpo.LPO_INPUT_OMA_RX4,
lpo.LPO_INPUT_OMA_RX5,
lpo.LPO_INPUT_OMA_RX6,
lpo.LPO_INPUT_OMA_RX7,
lpo.LPO_INPUT_OMA_RX8,
]


def _scale(raw, factor, precision=3):
"""Return raw * factor as a rounded float, or 'N/A' if raw is None."""
if raw is None:
return 'N/A'
return float("{:.{}f}".format(raw * factor, precision))


def _lower_nibble(raw):
"""Mask to lower 4 bits (U4 field), propagating None."""
return None if raw is None else raw & 0x0F


def _unpack_flags(field_name, raw):
"""Unpack a bitmask byte into a dict of per-lane boolean keys (lane 1-8)."""
result = {}
for lane in range(1, 9):
key = "{}{}".format(field_name, lane)
result[key] = bool((raw >> (lane - 1)) & 1) if raw is not None else 'N/A'
return result


class CmisEnhancedLpoApi(CmisApi):
def get_transceiver_info(self):
xcvr_info = super().get_transceiver_info()
if xcvr_info is None:
return None

tx_pol = self.xcvr_eeprom.read(lpo.LPO_TX_POLARITY_INVERTED)
rx_pol = self.xcvr_eeprom.read(lpo.LPO_RX_POLARITY_INVERTED)

vma_acc_raw = self.xcvr_eeprom.read(lpo.LPO_TX_HOST_INPUT_VMA_MON_ACCURACY)
vma_acc = _scale(_lower_nibble(vma_acc_raw), _VMA_ACC_MV_PER_COUNT)

oma_acc_raw = self.xcvr_eeprom.read(lpo.LPO_RX_INPUT_OMA_MON_ACCURACY)
oma_acc = _scale(_lower_nibble(oma_acc_raw), _OMA_ACC_DB_PER_COUNT)

oer_raw = self.xcvr_eeprom.read(lpo.LPO_TX_OUTER_EXTINCTION_RATIO_MAX)
oer_max = _scale(oer_raw, _OER_DB_PER_COUNT)

xcvr_info.update({
lpo.LPO_TX_POLARITY_INVERTED: tx_pol if tx_pol is not None else 'N/A',
lpo.LPO_RX_POLARITY_INVERTED: rx_pol if rx_pol is not None else 'N/A',
lpo.LPO_TX_HOST_INPUT_VMA_MON_ACCURACY: vma_acc,
lpo.LPO_RX_INPUT_OMA_MON_ACCURACY: oma_acc,
lpo.LPO_TX_OUTER_EXTINCTION_RATIO_MAX: oer_max,
})
return xcvr_info

def get_transceiver_dom_real_value(self):
trans_dom = super().get_transceiver_dom_real_value()
if trans_dom is None:
return None

for field in _VMA_TX_FIELDS:
trans_dom[field] = _scale(self.xcvr_eeprom.read(field), _VMA_MV_PER_COUNT)

for field in _OMA_RX_FIELDS:
trans_dom[field] = _scale(self.xcvr_eeprom.read(field), _OMA_MW_PER_COUNT)

return trans_dom

def get_transceiver_threshold_info(self):
threshold_dict = super().get_transceiver_threshold_info()
if threshold_dict is None:
return None

vma_threshold_fields = [
(lpo.LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_THRESHOLD, _VMA_MV_PER_COUNT),
(lpo.LPO_TX_HOST_INPUT_VMA_LOW_ALARM_THRESHOLD, _VMA_MV_PER_COUNT),
(lpo.LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_THRESHOLD, _VMA_MV_PER_COUNT),
(lpo.LPO_TX_HOST_INPUT_VMA_LOW_WARNING_THRESHOLD, _VMA_MV_PER_COUNT),
]
oma_threshold_fields = [
(lpo.LPO_RX_INPUT_OMA_HIGH_ALARM_THRESHOLD, _OMA_MW_PER_COUNT),
(lpo.LPO_RX_INPUT_OMA_LOW_ALARM_THRESHOLD, _OMA_MW_PER_COUNT),
(lpo.LPO_RX_INPUT_OMA_HIGH_WARNING_THRESHOLD, _OMA_MW_PER_COUNT),
(lpo.LPO_RX_INPUT_OMA_LOW_WARNING_THRESHOLD, _OMA_MW_PER_COUNT),
]

for field, factor in vma_threshold_fields + oma_threshold_fields:
raw = self.xcvr_eeprom.read(field)
threshold_dict[field] = _scale(raw, factor)

return threshold_dict

def get_transceiver_dom_flags(self):
dom_flag_dict = super().get_transceiver_dom_flags()
if dom_flag_dict is None:
return None

for field in _VMA_FLAG_FIELDS + _OMA_FLAG_FIELDS:
raw = self.xcvr_eeprom.read(field)
dom_flag_dict.update(_unpack_flags(field, raw))

return dom_flag_dict
79 changes: 79 additions & 0 deletions sonic_platform_base/sonic_xcvr/fields/arista_lpo_consts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Enhanced LPO (Arista) - field groups
LPO_EEPROM_FIELD = "LpoEepromField"
LPO_INFO_FIELD = "LpoInfoField"
LPO_VMA_THRESHOLDS_FIELD = "LpoVmaThresholdsField"
LPO_OMA_THRESHOLDS_FIELD = "LpoOmaThresholdsField"
LPO_VMA_FLAGS_FIELD = "LpoVmaFlagsField"
LPO_VMA_DOM_FIELD = "LpoVmaDomField"
LPO_VMA_MASKS_FIELD = "LpoVmaMasksField"
LPO_OMA_FLAGS_FIELD = "LpoOmaFlagsField"
LPO_OMA_DOM_FIELD = "LpoOmaDomField"
LPO_OMA_MASKS_FIELD = "LpoOmaMasksField"

# Enhanced LPO - page 01h advertisement
LPO_EEPROM_COMPLIANCE = "LPOEepromCompliance"
LPO_ENHANCED_SPEC_VERSION = "LPOEnhancedSpecVersion"

# Enhanced LPO - page C1h info / capability
LPO_CAPABILITY = "LPOCapability"
LPO_TX_OUTER_EXTINCTION_RATIO_MAX = "LPOTxOuterExtinctionRatioMax"
LPO_TX_POLARITY_INVERTED = "LPOTxPolarityInverted"
LPO_RX_POLARITY_INVERTED = "LPORxPolarityInverted"
LPO_TX_HOST_INPUT_VMA_MON_ACCURACY = "LPOTxHostInputVMAMonAccuracySupported"
LPO_RX_INPUT_OMA_MON_ACCURACY = "LPORxInputOMAMonAccuracySupported"

# Enhanced LPO - page C1h VMA thresholds
LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_THRESHOLD = "LPOTxHostInputVMAHighAlarmThreshold"
LPO_TX_HOST_INPUT_VMA_LOW_ALARM_THRESHOLD = "LPOTxHostInputVMALowAlarmThreshold"
LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_THRESHOLD = "LPOTxHostInputVMAHighWarningThreshold"
LPO_TX_HOST_INPUT_VMA_LOW_WARNING_THRESHOLD = "LPOTxHostInputVMALowWarningThreshold"

# Enhanced LPO - page C1h OMA thresholds
LPO_RX_INPUT_OMA_HIGH_ALARM_THRESHOLD = "LPORxInputOMAHighAlarmThreshold"
LPO_RX_INPUT_OMA_LOW_ALARM_THRESHOLD = "LPORxInputOMALowAlarmThreshold"
LPO_RX_INPUT_OMA_HIGH_WARNING_THRESHOLD = "LPORxInputOMAHighWarningThreshold"
LPO_RX_INPUT_OMA_LOW_WARNING_THRESHOLD = "LPORxInputOMALowWarningThreshold"

# Enhanced LPO - page C2h VMA flags
LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_FLAG = "LPOTxHostInputVMAHighAlarmFlag"
LPO_TX_HOST_INPUT_VMA_LOW_ALARM_FLAG = "LPOTxHostInputVMALowAlarmFlag"
LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_FLAG = "LPOTxHostInputVMAHighWarningFlag"
LPO_TX_HOST_INPUT_VMA_LOW_WARNING_FLAG = "LPOTxHostInputVMALowWarningFlag"

# Enhanced LPO - page C2h per-lane VMA measurements
LPO_HOST_INPUT_VMA_TX1 = "LPOHostInputVMATx1"
LPO_HOST_INPUT_VMA_TX2 = "LPOHostInputVMATx2"
LPO_HOST_INPUT_VMA_TX3 = "LPOHostInputVMATx3"
LPO_HOST_INPUT_VMA_TX4 = "LPOHostInputVMATx4"
LPO_HOST_INPUT_VMA_TX5 = "LPOHostInputVMATx5"
LPO_HOST_INPUT_VMA_TX6 = "LPOHostInputVMATx6"
LPO_HOST_INPUT_VMA_TX7 = "LPOHostInputVMATx7"
LPO_HOST_INPUT_VMA_TX8 = "LPOHostInputVMATx8"

# Enhanced LPO - page C2h VMA masks
LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_MASK = "LPOTxHostInputVMAHighAlarmMask"
LPO_TX_HOST_INPUT_VMA_LOW_ALARM_MASK = "LPOTxHostInputVMALowAlarmMask"
LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_MASK = "LPOTxHostInputVMAHighWarningMask"
LPO_TX_HOST_INPUT_VMA_LOW_WARNING_MASK = "LPOTxHostInputVMALowWarningMask"

# Enhanced LPO - page C2h OMA flags
LPO_RX_INPUT_OMA_HIGH_ALARM_FLAG = "LPORxInputOMAHighAlarmFlag"
LPO_RX_INPUT_OMA_LOW_ALARM_FLAG = "LPORxInputOMALowAlarmFlag"
LPO_RX_INPUT_OMA_HIGH_WARNING_FLAG = "LPORxInputOMAHighWarningFlag"
LPO_RX_INPUT_OMA_LOW_WARNING_FLAG = "LPORxInputOMALowWarningFlag"

# Enhanced LPO - page C2h per-lane OMA measurements
LPO_INPUT_OMA_RX1 = "LPOInputOMARx1"
LPO_INPUT_OMA_RX2 = "LPOInputOMARx2"
LPO_INPUT_OMA_RX3 = "LPOInputOMARx3"
LPO_INPUT_OMA_RX4 = "LPOInputOMARx4"
LPO_INPUT_OMA_RX5 = "LPOInputOMARx5"
LPO_INPUT_OMA_RX6 = "LPOInputOMARx6"
LPO_INPUT_OMA_RX7 = "LPOInputOMARx7"
LPO_INPUT_OMA_RX8 = "LPOInputOMARx8"

# Enhanced LPO - page C2h OMA masks
LPO_RX_INPUT_OMA_HIGH_ALARM_MASK = "LPORxInputOMAHighAlarmMask"
LPO_RX_INPUT_OMA_LOW_ALARM_MASK = "LPORxInputOMALowAlarmMask"
LPO_RX_INPUT_OMA_HIGH_WARNING_MASK = "LPORxInputOMAHighWarningMask"
LPO_RX_INPUT_OMA_LOW_WARNING_MASK = "LPORxInputOMALowWarningMask"
Empty file.
133 changes: 133 additions & 0 deletions sonic_platform_base/sonic_xcvr/mem_maps/arista/cmis_enhanced_lpo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""
cmis_enhanced_lpo.py

Memory map for Arista Enhanced LPO modules
"""

from ..public.cmis import CmisMemMap
from ..public.cmis.pages.page import CmisPage
from ...fields.xcvr_field import NumberRegField
from ...fields import arista_lpo_consts as lpo


class _CmisEnhancedLpoPage01(CmisPage):
"""Page 01h extension: LPO EEPROM compliance advertisement (bytes 195-196)."""

def __init__(self, codes):
super().__init__(codes, page=0x01, bank=0)
self.fields[lpo.LPO_EEPROM_FIELD] = [
NumberRegField(lpo.LPO_EEPROM_COMPLIANCE, self.getaddr(195), format="B", size=1),
NumberRegField(lpo.LPO_ENHANCED_SPEC_VERSION, self.getaddr(196), format="B", size=1),
]


class _CmisEnhancedLpoPageC1(CmisPage):
"""Page C1h: capability advertisement, polarity, accuracy, OER max, thresholds."""

def __init__(self, codes):
super().__init__(codes, page=0xC1, bank=0)

# Capability advertisement byte (C1h:128)
self.fields[lpo.LPO_INFO_FIELD] = [
NumberRegField(lpo.LPO_CAPABILITY, self.getaddr(128), format="B", size=1),
# C1h:129 - Tx outer extinction ratio max (U8, 0.1 dB/count)
NumberRegField(lpo.LPO_TX_OUTER_EXTINCTION_RATIO_MAX, self.getaddr(129), format="B", size=1),
# C1h:133 - Tx polarity inversion bitmask (1 bit per lane)
NumberRegField(lpo.LPO_TX_POLARITY_INVERTED, self.getaddr(133), format="B", size=1),
# C1h:134 - Rx polarity inversion bitmask (1 bit per lane)
NumberRegField(lpo.LPO_RX_POLARITY_INVERTED, self.getaddr(134), format="B", size=1),
# C1h:135 - Tx host input VMA monitoring accuracy (bits 3-0, U4, 5 mV/count)
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_MON_ACCURACY, self.getaddr(135), format="B", size=1),
# C1h:140 - Rx input OMA monitoring accuracy (bits 3-0, U4, 0.2 dB/count)
NumberRegField(lpo.LPO_RX_INPUT_OMA_MON_ACCURACY, self.getaddr(140), format="B", size=1),
]

# VMA thresholds (U8, 5 mV/count) - C1h:136-139
self.fields[lpo.LPO_VMA_THRESHOLDS_FIELD] = [
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_THRESHOLD, self.getaddr(136), format="B", size=1),
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_LOW_ALARM_THRESHOLD, self.getaddr(137), format="B", size=1),
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_THRESHOLD, self.getaddr(138), format="B", size=1),
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_LOW_WARNING_THRESHOLD, self.getaddr(139), format="B", size=1),
]

# OMA thresholds (U16 big-endian, 0.1 uW/count) - C1h:141-148
self.fields[lpo.LPO_OMA_THRESHOLDS_FIELD] = [
NumberRegField(lpo.LPO_RX_INPUT_OMA_HIGH_ALARM_THRESHOLD, self.getaddr(141), format=">H", size=2),
NumberRegField(lpo.LPO_RX_INPUT_OMA_LOW_ALARM_THRESHOLD, self.getaddr(143), format=">H", size=2),
NumberRegField(lpo.LPO_RX_INPUT_OMA_HIGH_WARNING_THRESHOLD, self.getaddr(145), format=">H", size=2),
NumberRegField(lpo.LPO_RX_INPUT_OMA_LOW_WARNING_THRESHOLD, self.getaddr(147), format=">H", size=2),
]


class _CmisEnhancedLpoPageC2(CmisPage):
"""Page C2h: VMA/OMA flags, per-lane measurements, masks."""

def __init__(self, codes):
super().__init__(codes, page=0xC2, bank=0)

# VMA flags (latched bitmasks, 1 bit per lane) - C2h:141-144
self.fields[lpo.LPO_VMA_FLAGS_FIELD] = [
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_FLAG, self.getaddr(141), format="B", size=1),
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_LOW_ALARM_FLAG, self.getaddr(142), format="B", size=1),
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_FLAG, self.getaddr(143), format="B", size=1),
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_LOW_WARNING_FLAG, self.getaddr(144), format="B", size=1),
]

# Per-lane Tx host input VMA measurements (U8, 5 mV/count) - C2h:145-152
self.fields[lpo.LPO_VMA_DOM_FIELD] = [
NumberRegField(lpo.LPO_HOST_INPUT_VMA_TX1, self.getaddr(145), format="B", size=1),
NumberRegField(lpo.LPO_HOST_INPUT_VMA_TX2, self.getaddr(146), format="B", size=1),
NumberRegField(lpo.LPO_HOST_INPUT_VMA_TX3, self.getaddr(147), format="B", size=1),
NumberRegField(lpo.LPO_HOST_INPUT_VMA_TX4, self.getaddr(148), format="B", size=1),
NumberRegField(lpo.LPO_HOST_INPUT_VMA_TX5, self.getaddr(149), format="B", size=1),
NumberRegField(lpo.LPO_HOST_INPUT_VMA_TX6, self.getaddr(150), format="B", size=1),
NumberRegField(lpo.LPO_HOST_INPUT_VMA_TX7, self.getaddr(151), format="B", size=1),
NumberRegField(lpo.LPO_HOST_INPUT_VMA_TX8, self.getaddr(152), format="B", size=1),
]

# VMA masks (bitmasks) - C2h:153-156
self.fields[lpo.LPO_VMA_MASKS_FIELD] = [
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_MASK, self.getaddr(153), format="B", size=1),
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_LOW_ALARM_MASK, self.getaddr(154), format="B", size=1),
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_MASK, self.getaddr(155), format="B", size=1),
NumberRegField(lpo.LPO_TX_HOST_INPUT_VMA_LOW_WARNING_MASK, self.getaddr(156), format="B", size=1),
]

# OMA flags (latched bitmasks, 1 bit per lane) - C2h:157-160
self.fields[lpo.LPO_OMA_FLAGS_FIELD] = [
NumberRegField(lpo.LPO_RX_INPUT_OMA_HIGH_ALARM_FLAG, self.getaddr(157), format="B", size=1),
NumberRegField(lpo.LPO_RX_INPUT_OMA_LOW_ALARM_FLAG, self.getaddr(158), format="B", size=1),
NumberRegField(lpo.LPO_RX_INPUT_OMA_HIGH_WARNING_FLAG, self.getaddr(159), format="B", size=1),
NumberRegField(lpo.LPO_RX_INPUT_OMA_LOW_WARNING_FLAG, self.getaddr(160), format="B", size=1),
]

# Per-lane Rx optical input OMA measurements (U16 big-endian, 0.1 uW/count) - C2h:161-176
self.fields[lpo.LPO_OMA_DOM_FIELD] = [
NumberRegField(lpo.LPO_INPUT_OMA_RX1, self.getaddr(161), format=">H", size=2),
NumberRegField(lpo.LPO_INPUT_OMA_RX2, self.getaddr(163), format=">H", size=2),
NumberRegField(lpo.LPO_INPUT_OMA_RX3, self.getaddr(165), format=">H", size=2),
NumberRegField(lpo.LPO_INPUT_OMA_RX4, self.getaddr(167), format=">H", size=2),
NumberRegField(lpo.LPO_INPUT_OMA_RX5, self.getaddr(169), format=">H", size=2),
NumberRegField(lpo.LPO_INPUT_OMA_RX6, self.getaddr(171), format=">H", size=2),
NumberRegField(lpo.LPO_INPUT_OMA_RX7, self.getaddr(173), format=">H", size=2),
NumberRegField(lpo.LPO_INPUT_OMA_RX8, self.getaddr(175), format=">H", size=2),
]

# OMA masks (bitmasks) - C2h:177-180
self.fields[lpo.LPO_OMA_MASKS_FIELD] = [
NumberRegField(lpo.LPO_RX_INPUT_OMA_HIGH_ALARM_MASK, self.getaddr(177), format="B", size=1),
NumberRegField(lpo.LPO_RX_INPUT_OMA_LOW_ALARM_MASK, self.getaddr(178), format="B", size=1),
NumberRegField(lpo.LPO_RX_INPUT_OMA_HIGH_WARNING_MASK, self.getaddr(179), format="B", size=1),
NumberRegField(lpo.LPO_RX_INPUT_OMA_LOW_WARNING_MASK, self.getaddr(180), format="B", size=1),
]


class CmisEnhancedLpoMemMap(CmisMemMap):
def __init__(self, codes, bank=0):
super().__init__(codes, bank=bank)
# LPO-specific pages are always bank=0 per the Enhanced LPO spec.
self.add_pages(
_CmisEnhancedLpoPage01(codes),
_CmisEnhancedLpoPageC1(codes),
_CmisEnhancedLpoPageC2(codes),
)
Loading
Loading