Microsemi and OMCI very initial code
Change-Id: Ieb648f43eab3b2dff50093c79ed27b9e40ae130e
diff --git a/BUILD.md b/BUILD.md
index 718b693..a9b6cc3 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -401,3 +401,24 @@
Unfortunately I was not yet able to resolve this on the Mac.
+### Scapy related import issues on MAC OS
+
+ 1. I had issues with "from scapy.all import *". It errored out with import error not finding
+dumbnet. The following resolved the issue:
+
+ ```
+ cd $VOLTHA_BASE
+ . env.sh
+ mkdir tmp
+ cd tmp
+ git clone https://github.com/dugsong/libdnet.git
+ cd libdnet
+ ./configure
+ make
+ sudo make install
+ cd python
+ python setup.py install
+ cd ../..
+ rm -fr tmp
+ ```
+
diff --git a/tests/utests/voltha/__init__.py b/tests/utests/voltha/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/utests/voltha/__init__.py
diff --git a/tests/utests/voltha/adapters/__init__.py b/tests/utests/voltha/adapters/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/utests/voltha/adapters/__init__.py
diff --git a/tests/utests/voltha/adapters/microsemi/__init__.py b/tests/utests/voltha/adapters/microsemi/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/utests/voltha/adapters/microsemi/__init__.py
diff --git a/tests/utests/voltha/adapters/microsemi/test_chat.py b/tests/utests/voltha/adapters/microsemi/test_chat.py
new file mode 100644
index 0000000..d0f2afc
--- /dev/null
+++ b/tests/utests/voltha/adapters/microsemi/test_chat.py
@@ -0,0 +1,528 @@
+from unittest import TestCase, main
+
+from voltha.adapters.microsemi.chat import *
+from voltha.extensions.omci.omci import OMCIFrame, OMCIGetRequest, \
+ OMCIGetResponse
+
+
+class TestChat(TestCase):
+
+ def check_gen(self, frames, raw_frames):
+ self.assertEqual(len(frames), len(raw_frames),
+ "number of frames do not match")
+ for i in range(len(frames)):
+ generated = str(frames[i])
+ expected = raw_frames[i]
+ if generated != expected:
+ print("Mismatch between generated vs expected frame:")
+ print("Generated:")
+ hexdump(generated)
+ print("Expected:")
+ hexdump(expected)
+ self.fail("Mismatch between generated vs expected frame "
+ "(see above printout)")
+
+ def check_parsed(self, in_raw, reference_msg, channel_id=-1, onu_id=-1,
+ onu_session_id=-1):
+
+ in_pkt = PAS5211Dot3(in_raw)
+
+ pas5211_header = in_pkt.payload.payload
+ self.assertEqual(pas5211_header.channel_id, channel_id)
+ self.assertEqual(pas5211_header.onu_id, onu_id)
+ self.assertEqual(pas5211_header.onu_session_id, onu_session_id)
+
+ pas5211_msg = in_pkt.payload.payload.payload
+ # so that we ignore junk/padding/fcs/etc after payload
+ pas5211_msg.remove_payload()
+ if pas5211_msg != reference_msg:
+ print("Decoded full packet:")
+ in_pkt.show()
+ hexdump(in_raw)
+ print("Decoded payload message:")
+ pas5211_msg.show()
+ hexdump(str(pas5211_msg))
+ print("Expected message:")
+ reference_msg.show()
+ hexdump(str(reference_msg))
+ self.fail("Decoded message did not match! "
+ "(inspect above printouts")
+ self.assertEqual(pas5211_msg, reference_msg)
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_get_protocol_version
+
+ def test_get_protocol_version(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgGetProtocolVersion(), 1),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x16\x01\x00'
+ '\x01\x00\x10\x00\xcd\xab4\x12\x01\x00\x00\x00\x020\x00\x00'
+ '\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ def test_get_protocol_version_response(self):
+ self.check_parsed(
+ 'h\x05\xca\x05\xf2\xef\x00\x0c\xd5\x00\x01\x00\x00"\x01\x00\x01'
+ '\x00\x18\x00\xcd\xab4\x12\x01\x00\x00\x00\x02(\x00\x00\xff\xff'
+ '\xff\xff\xff\xff\xff\xff\x11R\x02\x00\x10\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\xd3Y'
+ '\x1d',
+ PAS5211MsgGetProtocolVersionResponse(
+ major_hardware_version=21009,
+ minor_hardware_version=2,
+ major_pfi_version=16,
+ minor_pfi_version=0
+ )
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_get_olt_version
+
+ def test_get_olt_version(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgGetOltVersion(), 1),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x16\x01\x00'
+ '\x01\x00\x10\x00\xcd\xab4\x12\x01\x00\x00\x00\x030\x00\x00'
+ '\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ def test_get_olt_version_response(self):
+ self.check_parsed(
+ 'h\x05\xca\x05\xf2\xef\x00\x0c\xd5\x00\x01\x00\x00R\x01\x00\x01'
+ '\x00H\x00\xcd\xab4\x12\x03\x00\x00\x00\x038\x00\x00\xff\xff\xff'
+ '\xff\xff\xff\xff\xff\x02\x00\x03\x009\x00\xea\x03\x11R\x02\x00'
+ '\x00\x00\x00\x00\x04\x00\x80\x00\xff\x0f\x00\x02\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\xd7\xeb\xe1',
+ PAS5211MsgGetOltVersionResponse(
+ major_firmware_version=2,
+ minor_firmware_version=3,
+ build_firmware_version=57,
+ maintenance_firmware_version=1002,
+ major_hardware_version=21009,
+ minor_hardware_version=2,
+ system_port_mac_type=PON_MII,
+ channels_supported=4,
+ onus_supported_per_channel=128,
+ ports_supported_per_channel=4095,
+ alloc_ids_supported_per_channel=512,
+ critical_events_counter=[0, 0, 0, 0],
+ non_critical_events_counter=[1, 0, 0, 0]
+ )
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_set_olt_optics
+
+ def test_set_olt_optics(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgSetOltOptics(
+ burst_timing_ctrl=BurstTimingCtrl(
+ snr_burst_delay=SnrBurstDelay(
+ timer_delay=8,
+ preamble_delay=32,
+ delimiter_delay=128,
+ burst_delay=128),
+ rng_burst_delay=RngBurstDelay(
+ timer_delay=8,
+ preamble_delay=32,
+ delimiter_delay=128),
+ # burst_delay=63451),
+ burst_delay_single=1,
+ burst_delay_double=1
+ ),
+ general_optics_params=GeneralOpticsParams(
+ laser_reset_polarity=PON_POLARITY_ACTIVE_HIGH,
+ laser_sd_polarity=PON_POLARITY_ACTIVE_HIGH,
+ sd_source=PON_SD_SOURCE_LASER_SD,
+ sd_hold_snr_ranging=PON_DISABLE,
+ sd_hold_normal=PON_DISABLE,
+ reset_type_snr_ranging=PON_RESET_TYPE_DELAY_BASED,
+ reset_type_normal=PON_RESET_TYPE_NORMAL_START_BURST_BASED,
+ laser_reset_enable=PON_ENABLE,
+ ),
+ reset_timing_ctrl=ResetTimingCtrl(
+ reset_data_burst=ResetValues(
+ bcdr_reset_d2=1,
+ bcdr_reset_d1 = 11,
+ laser_reset_d2=2,
+ laser_reset_d1=5),
+ reset_snr_burst=ResetValues(
+ bcdr_reset_d2=2,
+ bcdr_reset_d1=9,
+ laser_reset_d2=2,
+ laser_reset_d1=1),
+ reset_rng_burst=ResetValues(
+ bcdr_reset_d2=2,
+ bcdr_reset_d1=9,
+ laser_reset_d2=2,
+ laser_reset_d1=1),
+ single_reset=ResetValues(
+ bcdr_reset_d2=1,
+ bcdr_reset_d1=1,
+ laser_reset_d2=1,
+ laser_reset_d1=1),
+ double_reset=DoubleResetValues(
+ bcdr_reset_d4=1,
+ bcdr_reset_d3=1,
+ laser_reset_d4=1,
+ laser_reset_d3=1)
+ ),
+ voltage_if_mode=PON_OPTICS_VOLTAGE_IF_LVPECL,
+ preamble_params=PreambleParams(
+ correlation_preamble_length=8,
+ preamble_length_snr_rng=119,
+ guard_time_data_mode=32,
+ type1_size_data=0,
+ type2_size_data=0,
+ type3_size_data=5,
+ type3_pattern=170,
+ delimiter_size=20,
+ delimiter_byte1=171,
+ delimiter_byte2=89,
+ delimiter_byte3=131)
+ ), 3, channel_id=0),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00V\x01\x00'
+ '\x01\x00P\x00\xcd\xab4\x12\x03\x00\x00\x00j0\x00\x00\x00\x00'
+ '\xff\xff\xff\xff\xff\xff\x08\x00 \x00\x80\x00\x80\x00\x08'
+ '\x00 \x00\x80\x00\x01\x00\x01\x00\x01\x01\x00\x00\x00\x00'
+ '\x00\x01\x00\x00\x00\x01\x0b\x02\x05\x02\t\x02\x01\x02\t\x02'
+ '\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x08w \x00\x00\x05'
+ '\xaa\x14\xabY\x83\x00\x00\x00'
+ ]
+ )
+
+ def test_set_olt_optics_response(self):
+ self.check_parsed(
+ 'h\x05\xca\x05\xf2\xef\x00\x0c\xd5\x00\x01\x00\x00\x1a\x01\x00'
+ '\x01\x00\x10\x00\xcd\xab4\x12\x03\x00\x00\x00j(\x00\x00\x00\x00'
+ '\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe1?'
+ '\x85\xce',
+ PAS5211MsgSetOltOpticsResponse(),
+ channel_id=0
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_set_optics_io_control
+
+ def test_set_optics_io_control(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgSetOpticsIoControl(
+ i2c_clk=PON_GPIO_LINE_1,
+ i2c_data=PON_GPIO_LINE_0,
+ tx_enable=PON_GPIO_LINE_6,
+ tx_fault=PON_EXT_GPIO_LINE(6),
+ tx_enable_polarity=PON_POLARITY_ACTIVE_LOW,
+ tx_fault_polarity=PON_POLARITY_ACTIVE_HIGH
+ ), 7, channel_id=0),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x1c\x01'
+ '\x00\x01\x00\x16\x00\xcd\xab4\x12\x07\x00\x00\x00l0\x00\x00'
+ '\x00\x00\xff\xff\xff\xff\xff\xff\x01\x00\x06\x0e\x00\x01\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ def test_set_optics_io_control_response(self):
+ self.check_parsed(
+ 'h\x05\xca\x05\xf2\xef\x00\x0c\xd5\x00\x01\x00\x00\x1a\x01\x00\x01'
+ '\x00\x10\x00\xcd\xab4\x12\x07\x00\x00\x00l(\x00\x00\x00\x00\xff'
+ '\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc2\x8b\xc4'
+ '\xd7',
+ PAS5211MsgSetOpticsIoControlResponse(),
+ channel_id=0
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_get_general_param
+
+ def test_get_general_param(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgGetGeneralParam(
+ parameter=PON_TX_ENABLE_DEFAULT
+ ), 11, channel_id=0),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x1e\x01'
+ '\x00\x01\x00\x18\x00\xcd\xab4\x12\x0b\x00\x00\x00\xa50\x00'
+ '\x00\x00\x00\xff\xff\xff\xff\xff\xff\xe9\x03\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ def test_get_general_param_response(self):
+ self.check_parsed(
+ 'h\x05\xca\x05\xf2\xef\x00\x0c\xd5\x00\x01\x00\x00&\x01\x00\x01'
+ '\x00\x1c\x00\xcd\xab4\x12\x0b\x00\x00\x00\xa5(\x00\x00\x00\x00'
+ '\xff\xff\xff\xff\xff\xff\xe9\x03\x00\x00\x00\x00\x00\x00\x01\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd6Yf'
+ '\xf9',
+ PAS5211MsgGetGeneralParamResponse(
+ parameter=PON_TX_ENABLE_DEFAULT,
+ value=1
+ ),
+ channel_id=0
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_set_general_param
+
+ def test_set_general_param(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgSetGeneralParam(
+ parameter=PON_TX_ENABLE_DEFAULT,
+ value=0
+ ), 11, channel_id=0),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x22\x01'
+ '\x00\x01\x00\x1c\x00\xcd\xab4\x12\x0b\x00\x00\x00\xa40\x00'
+ '\x00\x00\x00\xff\xff\xff\xff\xff\xff\xe9\x03\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ def test_set_general_param_response(self):
+ self.check_parsed(
+ 'h\x05\xca\x05\xf2\xef\x00\x0c\xd5\x00\x01\x00\x00&\x01\x00\x01'
+ '\x00\x1c\x00\xcd\xab4\x12\x0b\x00\x00\x00\xa4(\x00\x00\x00\x00'
+ '\xff\xff\xff\xff\xff\xff\xe9\x03\x00\x00\x00\x00\x00\x00\x01\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd6Yf'
+ '\xf9',
+ PAS5211MsgSetGeneralParamResponse(),
+ channel_id=0
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_add_olt_channel
+
+ def test_add_olt_channel(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgAddOltChannel(
+ ), 12, channel_id=0),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x16\x01'
+ '\x00\x01\x00\x10\x00\xcd\xab4\x12\x0c\x00\x00\x00\x040\x00'
+ '\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ def test_add_olt_channel_response(self):
+ self.check_parsed(
+ 'h\x05\xca\x05\xf2\xef\x00\x0c\xd5\x00\x01\x00\x00\x1a\x01\x00'
+ '\x01\x00\x10\x00\xcd\xab4\x12\x0c\x00\x00\x00\x04(\x00\x00\x00'
+ '\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90L'
+ '\xbf\xdd',
+ PAS5211MsgAddOltChannelResponse(
+ ),
+ channel_id=0
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_set_alarm_config
+
+ def test_set_alarm_config(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgSetAlarmConfig(
+ type=PON_ALARM_SOFTWARE_ERROR,
+ activate=PON_ENABLE
+ ), 19, channel_id=0),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00*\x01\x00'
+ '\x01\x00$\x00\xcd\xab4\x12\x13\x00\x00\x0000\x00\x00\x00\x00'
+ '\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ def test_set_alarm_config_response(self):
+ self.check_parsed(
+ 'h\x05\xca\x05\xf2\xef\x00\x0c\xd5\x00\x01\x00\x00\x1a\x01\x00\x01'
+ '\x00\x10\x00\xcd\xab4\x12\x13\x00\x00\x000(\x00\x00\x00\x00\xff'
+ '\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xc5'
+ '\xf4i',
+ PAS5211MsgSetAlarmConfigResponse(),
+ channel_id=0
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_get_dba_mode
+
+ def test_get_dba_mode(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgGetDbaMode(
+ ), 23, channel_id=0),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x16\x01'
+ '\x00\x01\x00\x10\x00\xcd\xab4\x12\x17\x00\x00\x0090\x00\x00'
+ '\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ def test_get_dba_mode_response(self):
+ self.check_parsed(
+ 'h\x05\xca\x05\xf2\xef\x00\x0c\xd5\x00\x01\x00\x00\x1e\x01\x00\x01'
+ '\x00\x14\x00\xcd\xab4\x12\x17\x00\x00\x009(\x00\x00\x00\x00\xff'
+ '\xff\xff\xff\xff\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\\x85\xd0?',
+ PAS5211MsgGetDbaModeResponse(
+ dba_mode=PON_DBA_MODE_RUNNING
+ ),
+ channel_id=0
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_set_olt_channel_activation_period
+
+ def test_set_olt_channel_activation_period(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgSetOltChannelActivationPeriod(
+ activation_period=1000
+ ), 31, channel_id=0),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x1a\x01\x00'
+ '\x01\x00\x14\x00\xcd\xab4\x12\x1f\x00\x00\x00\x0b0\x00\x00'
+ '\x00\x00\xff\xff\xff\xff\xff\xff\xe8\x03\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ def test_set_olt_channel_activation_period_response(self):
+ self.check_parsed(
+ '\x90\xe2\xba\x82\xf9w\x00\x0c\xd5\x00\x01\x01\x00\x1a\x01\x00\x01'
+ '\x00\x10\x00\xcd\xab4\x12\x1f\x00\x00\x00\x0b(\x00\x00\x00\x00'
+ '\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ PAS5211MsgSetOltChannelActivationPeriodResponse(),
+ channel_id=0
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_send_cli_command
+
+ def test_send_cli_command(self):
+ msg = PAS5211MsgSendCliCommand(command="foo")
+ self.assertEqual(str(msg), '\x03\x00foo')
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgSendCliCommand(
+ command="foo\r"
+ ), 11),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x1c\x01\x00'
+ '\x01\x00\x16\x00\xcd\xab4\x12\x0b\x00\x00\x00\x0f0\x00\x00'
+ '\xff\xff\xff\xff\xff\xff\xff\xff\x04\x00\x66\x6f\x6f\x0d\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_switch_to_inbound_mode
+
+ def test_switch_to_inbound_mode(self):
+ msg = PAS5211MsgSendCliCommand(command="foo")
+ self.assertEqual(str(msg), '\x03\x00foo')
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgSwitchToInboundMode(
+ mac='00:0c:d5:00:01:00'
+ ), 11, channel_id=0),
+ [
+ '\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00\x1e\x01'
+ '\x00\x01\x00\x18\x00\xcd\xab4\x12\x0b\x00\x00\x00\xec0\x00'
+ '\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x0c\xd5\x00\x01\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ ]
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_send_frame
+
+ def test_send_frame(self):
+ self.check_gen(
+ constructPAS5211Frames(PAS5211MsgSendFrame(
+ port_type=PON_PORT_PON,
+ port_id=0,
+ management_frame=PON_TRUE,
+ frame=OMCIFrame(
+ transaction_id=0,
+ message_type=0x49,
+ omci_message=OMCIGetRequest(
+ entity_class=6,
+ entity_id=0x101,
+ # there is a more programmer friendly way to express it
+ attributes_mask=0x0800
+ )
+ )
+ ), 39, channel_id=0, onu_id=0, onu_session_id=1),
+ [
+ "\x00\x0c\xd5\x00\x01\x00h\x05\xca\x05\xf2\xef\x00J\x01\x00"
+ "\x01\x00D\x00\xcd\xab4\x12'\x00\x00\x00*0\x00\x00\x00\x00"
+ "\x00\x00\x01\x00\x00\x00,\x00\x00\x00\x00\x00\x01\x00\x00"
+ "\x00I\n\x00\x06\x01\x01\x08\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00("
+ ]
+ )
+
+ def test_send_frame_response(self):
+ self.check_parsed(
+ "\x90\xe2\xba\x82\xf9w\x00\x0c\xd5\x00\x01\x01\x00\x1a\x01\x00"
+ "\x01\x00\x10\x00\xcd\xab4\x12'\x00\x00\x00*(\x00\x00\x00\x00"
+ "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ PAS5211MsgSendFrameResponse(
+ ),
+ channel_id=0, onu_id=0, onu_session_id=1
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_receive_onu_activation_event
+
+ def test_receive_onu_activation_event(self):
+ self.check_parsed(
+ '\x90\xe2\xba\x82\xf9w\x00\x0c\xd5\x00\x01\x01\x00&\x01\x00\x01'
+ '\x00\x1c\x00\xcd\xab4\x12\x00\x00\x00\x00\x0c(\x01\x00\x00\x00'
+ '\x00\x00\x01\x00\x00\x00PMCS\xd5b\x84\xac\x04\x12\x04\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ PAS5211EventOnuActivation(
+ serial_number='PMCS\xd5b\x84\xac',
+ equalization_period=266756
+ ),
+ channel_id=0, onu_id=0, onu_session_id=1
+ )
+
+ # ~~~~~~~~~~~~~~~~~~~~~~~~ test_frame_received_event
+
+ '''
+ def test_frame_received_event(self):
+ self.check_parsed(
+ '\x90\xe2\xba\x82\xf9w\x00\x0c\xd5\x00\x01\x01\x00Z\x01\x00\x01'
+ '\x00P\x00\xcd\xab4\x12\x01\x00\x00\x00\x0c(\n\x00\x00\x00\x00'
+ '\x00\x01\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x15\x00 \x00'
+ '\x13\x00\x00 \x00\x00)\n\x00\x06\x01\x01\x00\x08\x00PMCS\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00(\r\xc5\x0c\xb6',
+ PAS5211EventFrameReceived(
+ length=48,
+ management_frame=PON_TRUE,
+ classification_entity=21, # use enums
+ l3_offset=32,
+ l4_offset=19,
+ # ignored, yet we get a non-zero value from olt
+ ignored=0x2000,
+ frame=OMCIFrame(
+ transaction_id=0,
+ message_type=0x29,
+ omci_message=OMCIGetResponse(
+ entity_class=6,
+ entity_id=0x101,
+ success_code=0,
+ attributes_mask=0x0800,
+ data=dict(
+ vendor_id="PMCS"
+ )
+ )
+ )
+ ),
+ channel_id=0, onu_id=0, onu_session_id=1
+ )
+ '''
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/utests/voltha/extensions/omci/test_omci.py b/tests/utests/voltha/extensions/omci/test_omci.py
new file mode 100644
index 0000000..83d79db
--- /dev/null
+++ b/tests/utests/voltha/extensions/omci/test_omci.py
@@ -0,0 +1,46 @@
+from unittest import TestCase, main
+from voltha.extensions.omci.omci import CirtcuitPackEntity, bitpos_from_mask
+from voltha.extensions.omci.omci import EntityClass
+
+
+class TestOmci(TestCase):
+
+ def test_bitpos_from_mask(self):
+
+ f = lambda x: bitpos_from_mask(x)
+ self.assertEqual(f(0), [])
+ self.assertEqual(f(1), [0])
+ self.assertEqual(f(3), [0, 1])
+ self.assertEqual(f(255), [0, 1, 2, 3, 4, 5, 6, 7])
+ self.assertEqual(f(0x800), [11])
+ self.assertEqual(f(0x811), [0, 4, 11])
+
+ f = lambda x: bitpos_from_mask(x, 16, -1)
+ self.assertEqual(f(0), [])
+ self.assertEqual(f(1), [16])
+ self.assertEqual(f(0x800), [5])
+ self.assertEqual(f(0x801), [5, 16])
+
+
+ def test_attribute_indeices_from_mask(self):
+
+ f = EntityClass.attribute_indices_from_mask
+ self.assertEqual(f(0), [])
+ self.assertEqual(f(0x800), [5])
+ self.assertEqual(f(0xf000), [1, 2, 3, 4])
+ self.assertEqual(f(0xf804), [1, 2, 3, 4, 5, 14])
+
+ def test_entity_attribute_serialization(self):
+
+ e = CirtcuitPackEntity(vendor_id='F')
+ self.assertEqual(e.serialize(), 'F\x00\x00\x00')
+
+ e = CirtcuitPackEntity(vendor_id='FOOX')
+ self.assertEqual(e.serialize(), 'FOOX')
+
+ e = CirtcuitPackEntity(vendor_id='FOOX', number_of_ports=16)
+ self.assertEqual(e.serialize(), '\x10FOOX')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/voltha/adapters/__init__.py b/voltha/adapters/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/voltha/adapters/__init__.py
diff --git a/voltha/adapters/microsemi/__init__.py b/voltha/adapters/microsemi/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/voltha/adapters/microsemi/__init__.py
diff --git a/voltha/adapters/microsemi/chat.py b/voltha/adapters/microsemi/chat.py
new file mode 100755
index 0000000..484f6c3
--- /dev/null
+++ b/voltha/adapters/microsemi/chat.py
@@ -0,0 +1,684 @@
+#!/usr/bin/env python
+
+from hexdump import hexdump
+from threading import Thread
+from time import sleep
+
+from scapy.config import conf
+from scapy.fields import Field, lhex, MACField, LenField, LEShortField, \
+ LEIntField, LESignedIntField, FieldLenField, FieldListField, PacketField, \
+ ByteField, StrField, ConditionalField, StrFixedLenField
+from scapy.layers.l2 import DestMACField, ETHER_ANY, ETH_P_ALL, sniff, sendp
+from scapy.layers.ntp import XLEShortField
+from scapy.packet import Packet, bind_layers
+from scapy.volatile import RandSInt
+
+from voltha.extensions.omci.omci import OMCIFrame
+
+
+src_mac = "68:05:ca:05:f2:ef"
+dst_mac = "00:0c:d5:00:01:00"
+
+
+# from enum PON_true_false_t
+PON_FALSE = 0
+PON_TRUE = 1
+
+# from enum PON_enable_disable_t
+PON_DISABLE = 0
+PON_ENABLE = 1
+
+# from enym PON_mac_t
+PON_MII = 0
+PON_GMII = 1
+PON_TBI = 2
+
+PON_POLARITY_ACTIVE_LOW = 0
+PON_POLARITY_ACTIVE_HIGH = 1
+
+PON_OPTICS_VOLTAGE_IF_UNDEFINED = 0
+PON_OPTICS_VOLTAGE_IF_CML = 1
+PON_OPTICS_VOLTAGE_IF_LVPECL = 2
+
+PON_SD_SOURCE_LASER_SD = 0
+PON_SD_SOURCE_BCDR_LOCK = 1
+PON_SD_SOURCE_BCDR_SD = 2
+
+PON_RESET_TYPE_DELAY_BASED = 0
+PON_RESET_TYPE_SINGLE_RESET = 1
+PON_RESET_TYPE_DOUBLE_RESET = 2
+
+PON_RESET_TYPE_NORMAL_START_BURST_BASED = 0
+PON_RESET_TYPE_NORMAL_END_BURST_BASED = 1
+
+PON_GPIO_LINE_0 = 0
+PON_GPIO_LINE_1 = 1
+PON_GPIO_LINE_2 = 2
+PON_GPIO_LINE_3 = 3
+PON_GPIO_LINE_4 = 4
+PON_GPIO_LINE_5 = 5
+PON_GPIO_LINE_6 = 6
+PON_GPIO_LINE_7 = 7
+def PON_EXT_GPIO_LINE(line):
+ return line + 8
+
+# from enum PON_alarm_t
+PON_ALARM_SOFTWARE_ERROR = 0
+PON_ALARM_LOS = 1
+PON_ALARM_LOSI = 2
+PON_ALARM_DOWI = 3
+PON_ALARM_LOFI = 4
+PON_ALARM_RDII = 5
+PON_ALARM_LOAMI = 6
+PON_ALARM_LCDGI = 7
+PON_ALARM_LOAI = 8
+PON_ALARM_SDI = 9
+PON_ALARM_SFI = 10
+PON_ALARM_PEE = 11
+PON_ALARM_DGI = 12
+PON_ALARM_LOKI = 13
+PON_ALARM_TIWI = 14
+PON_ALARM_TIA = 15
+PON_ALARM_VIRTUAL_SCOPE_ONU_LASER_ALWAYS_ON = 16
+PON_ALARM_VIRTUAL_SCOPE_ONU_SIGNAL_DEGRADATION = 17
+PON_ALARM_VIRTUAL_SCOPE_ONU_EOL = 18
+PON_ALARM_VIRTUAL_SCOPE_ONU_EOL_DATABASE_IS_FULL = 19
+PON_ALARM_AUTH_FAILED_IN_REGISTRATION_ID_MODE = 20
+PON_ALARM_SUFI = 21
+PON_ALARM_LAST_ALARM = 22
+
+# from enum PON_general_parameters_type_t
+PON_COMBINED_LOSI_LOFI = 1000
+PON_TX_ENABLE_DEFAULT = 1001
+
+# Enable or disable False queue full event from DBA
+PON_FALSE_Q_FULL_EVENT_MODE = 1002
+
+# Set PID_AID_MISMATCH min silence period. 0 - disable, Else - period in secs
+PON_PID_AID_MISMATCH_MIN_SILENCE_PERIOD = 1003
+
+# Set if FW generate clear alarm. 0 - generate clear alarm, Else - don't
+# generate clear alarm
+PON_ENABLE_CLEAR_ALARM = 1004
+
+# Enable or disabl send assign alloc id ploam. 0 - disable, 1 - enable
+PON_ASSIGN_ALLOC_ID_PLOAM = 1005
+
+# BIP error polling period, 200 - 65000, 0 - Disabled, Recommended: 5000
+# (default)
+PON_BIP_ERR_POLLING_PERIOD_MS = 1006
+
+# Ignore SN when decatived 0 - consider SN (deactivate the onu if received
+# same SN when activated (default) 1 - Ignore
+PON_IGNORE_SN_WHEN_ACTIVE = 1007
+
+# 0xffffffff - Disabled (default). Any other value (0 - 0xfffe) indicates
+# that PA delay is enabled, with the specified delay value and included in
+# the US_OVERHEAD PLOAM
+PON_ONU_PRE_ASSIGNED_DELAY = 1008
+
+# Enable or disable DS fragmentation, 0 disable, 1 enable
+PON_DS_FRAGMENTATION = 1009
+
+# Set if fw report rei alarm when errors is 0, 0 disable (default), 1 enable
+PON_REI_ERRORS_REPORT_ALL = 1010
+
+# Set if igonre sfi deactivation, 0 disable (default), 1 enable
+PON_IGNORE_SFI_DEACTIVATION = 1011
+
+# Allows to override the allocation overhead set by optic-params
+# configuration. This configuration is only allowed when the the pon channel
+# is disabled
+PON_OVERRIDE_ALLOCATION_OVERHEAD = 1012
+
+# Optics timeline offset, -128-127, : this parameter is very sensitive and
+# requires coordination with PMC
+PON_OPTICS_TIMELINE_OFFSET = 1013
+
+# Last general meter
+PON_LAST_GENERAL_PARAMETER = PON_OPTICS_TIMELINE_OFFSET
+
+# from enum PON_dba_mode_t
+PON_DBA_MODE_NOT_LOADED = 0
+PON_DBA_MODE_LOADED_NOT_RUNNING = 1
+PON_DBA_MODE_RUNNING = 2
+PON_DBA_MODE_LAST = 3
+
+# from enum type typedef enum PON_port_frame_destination_t
+PON_PORT_PON = 0
+PON_PORT_SYSTEM = 1
+
+# from enum PON_olt_hw_classification_t
+
+PON_OLT_HW_CLASSIFICATION_PAUSE = 0
+PON_OLT_HW_CLASSIFICATION_LINK_CONSTRAINT = 1
+PON_OLT_HW_CLASSIFICATION_IGMP = 2
+PON_OLT_HW_CLASSIFICATION_MPCP = 3
+PON_OLT_HW_CLASSIFICATION_OAM = 4
+PON_OLT_HW_CLASSIFICATION_802_1X = 5
+PON_OLT_HW_CLASSIFICATION_PPPOE_DISCOVERY = 6
+PON_OLT_HW_CLASSIFICATION_PPPOE_SESSION = 7
+PON_OLT_HW_CLASSIFICATION_DHCP_V4 = 8
+PON_OLT_HW_CLASSIFICATION_PIM = 9
+PON_OLT_HW_CLASSIFICATION_DHCP_V6 = 10
+PON_OLT_HW_CLASSIFICATION_ICMP_V4 = 11
+PON_OLT_HW_CLASSIFICATION_MLD = 12
+PON_OLT_HW_CLASSIFICATION_ARP = 13
+PON_OLT_HW_CLASSIFICATION_CONF_DA = 14
+PON_OLT_HW_CLASSIFICATION_CONF_RULE = 15
+PON_OLT_HW_CLASSIFICATION_DA_EQ_SA = 16
+PON_OLT_HW_CLASSIFICATION_DA_EQ_MAC = 17
+PON_OLT_HW_CLASSIFICATION_DA_EQ_SEC_MAC = 18
+PON_OLT_HW_CLASSIFICATION_SA_EQ_MAC = 19
+PON_OLT_HW_CLASSIFICATION_SA_EQ_SEC_MAC = 20
+PON_OLT_HW_CLASSIFICATION_ETHERNET_MANAGEMENT = 100
+PON_OLT_HW_CLASSIFICATION_IPV4_LOCAL_MULTICAST = 101
+PON_OLT_HW_CLASSIFICATION_IPV4_MANAGEMENT = 102
+PON_OLT_HW_CLASSIFICATION_ALL_IPV4_MULTICAST = 103
+PON_OLT_HW_CLASSIFICATION_IPV6_LOCAL_MULTICAST = 104
+PON_OLT_HW_CLASSIFICATION_IPV6_MANAGEMENT = 105
+PON_OLT_HW_CLASSIFICATION_ALL_IPV6_MULTICAST = 106
+PON_OLT_HW_CLASSIFICATION_OTHER = 107
+PON_OLT_HW_CLASSIFICATION_LAST_RULE = 108
+
+
+class XLESignedIntField(Field):
+ def __init__(self, name, default):
+ Field.__init__(self, name, default, "<i")
+ def randval(self):
+ return RandSInt()
+ def i2repr(self, pkt, x):
+ return lhex(self.i2h(pkt, x))
+
+
+class LESignedShortField(Field):
+ def __init__(self, name, default):
+ Field.__init__(self, name, default, "<h")
+
+
+class PAS5211Dot3(Packet):
+ name = "PAS5211Dot3"
+ fields_desc = [ DestMACField("dst"),
+ MACField("src", ETHER_ANY),
+ LenField("len", None, "H") ]
+
+ MIN_FRAME_SIZE = 60
+
+ def post_build(self, pkt, payload):
+ pkt += payload
+ size = ord(payload[4]) + (ord(payload[5]) << 8)
+ length = size + 6 # this is a idiosyncracy of the PASCOMM protocol
+ pkt = pkt[:12] + chr(length >> 8) + chr(length & 0xff) + pkt[14:]
+ padding = self.MIN_FRAME_SIZE - len(pkt)
+ if padding > 0:
+ pkt = pkt + ("\x00" * padding)
+ return pkt
+
+
+class PAS5211FrameHeader(Packet):
+ name = "PAS5211FrameHeader"
+ fields_desc = [
+ LEShortField("part", 1),
+ LEShortField("total_parts", 1),
+ LEShortField("size", 0),
+ XLESignedIntField("magic_number", 0x1234ABCD)
+ ]
+
+
+class PAS5211MsgHeader(Packet):
+ name = "PAS5211MsgHeader"
+ fields_desc = [
+ LEIntField("sequence_number", 0),
+ XLEShortField("opcode", 0),
+ LEShortField("event_type", 0),
+ LESignedShortField("channel_id", -1),
+ LESignedShortField("onu_id", -1),
+ LESignedIntField("onu_session_id", -1)
+ ]
+
+
+class PAS5211Msg(Packet):
+ opcode = "Must be filled by subclass"
+ pass
+
+
+class PAS5211MsgGetProtocolVersion(PAS5211Msg):
+ opcode = 2
+ name = "PAS5211MsgGetProtocolVersion"
+ fields_desc = [ ]
+
+
+class PAS5211MsgGetProtocolVersionResponse(PAS5211Msg):
+ name = "PAS5211MsgGetProtocolVersionResponse"
+ fields_desc = [
+ LEShortField("major_hardware_version", 0),
+ LEShortField("minor_hardware_version", 0),
+ LEShortField("major_pfi_version", 0),
+ LEShortField("minor_pfi_version", 0)
+ ]
+
+
+class PAS5211MsgGetOltVersion(PAS5211Msg):
+ opcode = 3
+ name = "PAS5211MsgGetOltVersion"
+ fields_desc = [ ]
+
+
+class PAS5211MsgGetOltVersionResponse(PAS5211Msg):
+ name = "PAS5211MsgGetOltVersionResponse"
+ fields_desc = [
+ LEShortField("major_firmware_version", 0),
+ LEShortField("minor_firmware_version", 0),
+ LEShortField("build_firmware_version", 0),
+ LEShortField("maintenance_firmware_version", 0),
+ LEShortField("major_hardware_version", 0),
+ LEShortField("minor_hardware_version", 0),
+ LEIntField("system_port_mac_type", 0),
+ FieldLenField("channels_supported", 0, fmt="<H"),
+ LEShortField("onus_supported_per_channel", 0),
+ LEShortField("ports_supported_per_channel", 0),
+ LEShortField("alloc_ids_supported_per_channel", 0),
+ FieldListField("critical_events_counter", [0, 0, 0, 0],
+ LEIntField("entry", 0),
+ count_from=lambda pkt: pkt.channels_supported),
+ FieldListField("non_critical_events_counter", [0, 0, 0, 0],
+ LEIntField("entry", 0),
+ count_from=lambda pkt: pkt.channels_supported)
+ ]
+
+
+class SnrBurstDelay(Packet):
+ name = "SnrBurstDelay"
+ fields_desc= [
+ LEShortField("timer_delay", None),
+ LEShortField("preamble_delay", None),
+ LEShortField("delimiter_delay", None),
+ LEShortField("burst_delay", None)
+ ]
+
+
+class RngBurstDelay(Packet):
+ name = "SnrBurstDelay"
+ fields_desc= [
+ LEShortField("timer_delay", None),
+ LEShortField("preamble_delay", None),
+ LEShortField("delimiter_delay", None)
+ ]
+
+
+class BurstTimingCtrl(Packet):
+ name = "BurstTimingCtrl"
+ fields_desc = [
+ PacketField("snr_burst_delay", None, SnrBurstDelay),
+ PacketField("rng_burst_delay", None, RngBurstDelay),
+ LEShortField("burst_delay_single", None),
+ LEShortField("burst_delay_double", None)
+
+ ]
+
+
+class GeneralOpticsParams(Packet):
+ name = "GeneralOpticsParams"
+ fields_desc= [
+ ByteField("laser_reset_polarity", None),
+ ByteField("laser_sd_polarity", None),
+ ByteField("sd_source", None),
+ ByteField("sd_hold_snr_ranging", None),
+ ByteField("sd_hold_normal", None),
+ ByteField("reset_type_snr_ranging", None),
+ ByteField("reset_type_normal", None),
+ ByteField("laser_reset_enable", None),
+ ]
+
+
+class ResetValues(Packet):
+ name = "ResetDataBurst"
+ fields_desc = [
+ ByteField("bcdr_reset_d2", None),
+ ByteField("bcdr_reset_d1", None),
+ ByteField("laser_reset_d2", None),
+ ByteField("laser_reset_d1", None)
+ ]
+
+
+class DoubleResetValues(Packet):
+ name = "ResetDataBurst"
+ fields_desc = [
+ ByteField("bcdr_reset_d4", None),
+ ByteField("bcdr_reset_d3", None),
+ ByteField("laser_reset_d4", None),
+ ByteField("laser_reset_d3", None)
+ ]
+
+
+class ResetTimingCtrl(Packet):
+ name = "ResetTimingCtrl"
+ fields_desc = [
+ PacketField("reset_data_burst", None, ResetValues),
+ PacketField("reset_snr_burst", None, ResetValues),
+ PacketField("reset_rng_burst", None, ResetValues),
+ PacketField("single_reset", None, ResetValues),
+ PacketField("double_reset", None, DoubleResetValues),
+ ]
+
+
+class PreambleParams(Packet):
+ name = "PreambleParams"
+ fields_desc = [
+ ByteField("correlation_preamble_length", None),
+ ByteField("preamble_length_snr_rng", None),
+ ByteField("guard_time_data_mode", None),
+ ByteField("type1_size_data", None),
+ ByteField("type2_size_data", None),
+ ByteField("type3_size_data", None),
+ ByteField("type3_pattern", None),
+ ByteField("delimiter_size", None),
+ ByteField("delimiter_byte1", None),
+ ByteField("delimiter_byte2", None),
+ ByteField("delimiter_byte3", None)
+ ]
+
+
+class PAS5211MsgSetOltOptics(PAS5211Msg):
+ opcode = 106
+ name = "PAS5211MsgSetOltOptics"
+ fields_desc = [
+ PacketField("burst_timing_ctrl", None, BurstTimingCtrl),
+ PacketField("general_optics_params", None, GeneralOpticsParams),
+ ByteField("reserved1", 0),
+ ByteField("reserved2", 0),
+ ByteField("reserved3", 0),
+ PacketField("reset_timing_ctrl", None, ResetTimingCtrl),
+ ByteField("voltage_if_mode", None),
+ PacketField("preamble_params", None, PreambleParams),
+ ByteField("reserved4", 0),
+ ByteField("reserved5", 0),
+ ByteField("reserved6", 0)
+ ]
+
+
+class PAS5211MsgSetOltOpticsResponse(PAS5211Msg):
+ name = "PAS5211MsgSetOltOpticsResponse"
+ fields_desc = [ ]
+
+
+class PAS5211MsgSetOpticsIoControl(PAS5211Msg):
+ opcode = 108
+ name = "PAS5211MsgSetOpticsIoControl"
+ fields_desc = [
+ ByteField("i2c_clk", None),
+ ByteField("i2c_data", None),
+ ByteField("tx_enable", None),
+ ByteField("tx_fault", None),
+ ByteField("tx_enable_polarity", None),
+ ByteField("tx_fault_polarity", None),
+ ]
+
+
+class PAS5211MsgSetOpticsIoControlResponse(PAS5211Msg):
+ name = "PAS5211MsgSetOpticsIoControlResponse"
+ fields_desc = [ ]
+
+
+class PAS5211MsgSetGeneralParam(PAS5211Msg):
+ opcode = 164
+ name = "PAS5211MsgSetGeneralParam"
+ fields_desc = [
+ LEIntField("parameter", None),
+ LEIntField("reserved", 0),
+ LEIntField("value", None)
+ ]
+
+
+class PAS5211MsgSetGeneralParamResponse(PAS5211Msg):
+ name = "PAS5211MsgSetGeneralParamResponse"
+ fields_desc = []
+
+
+class PAS5211MsgGetGeneralParam(PAS5211Msg):
+ opcode = 165
+ name = "PAS5211MsgGetGeneralParam"
+ fields_desc = [
+ LEIntField("parameter", None),
+ LEIntField("reserved", 0),
+ ]
+
+
+class PAS5211MsgGetGeneralParamResponse(PAS5211Msg):
+ name = "PAS5211MsgGetGeneralParamResponse"
+ fields_desc = [
+ LEIntField("parameter", None),
+ LEIntField("reserved", 0),
+ LEIntField("value", None)
+ ]
+
+
+class PAS5211MsgGetDbaMode(PAS5211Msg):
+ opcode = 57
+ name = "PAS5211MsgGetDbaMode"
+ fields_desc = []
+
+
+class PAS5211MsgGetDbaModeResponse(PAS5211Msg):
+ name = "PAS5211MsgGetDbaModeResponse"
+ fields_desc = [
+ LEIntField("dba_mode", None),
+ ]
+
+
+class PAS5211MsgAddOltChannel(PAS5211Msg):
+ opcode = 4
+ name = "PAS5211MsgAddOltChannel"
+ fields_desc = [
+
+ ]
+
+
+class PAS5211MsgAddOltChannelResponse(PAS5211Msg):
+ name = "PAS5211MsgAddOltChannelResponse"
+ fields_desc = [
+
+ ]
+
+
+class PAS5211MsgSetAlarmConfig(PAS5211Msg):
+ opcode = 48
+ name = "PAS5211MsgSetAlarmConfig"
+ fields_desc = [
+ LEShortField("type", None),
+ LEShortField("activate", None),
+ LEIntField("parameter1", None),
+ LEIntField("parameter2", None),
+ LEIntField("parameter3", None),
+ LEIntField("parameter4", None)
+ ]
+
+
+class PAS5211MsgSetOltChannelActivationPeriod(PAS5211Msg):
+ opcode = 11
+ name = "PAS5211MsgSetOltChannelActivationPeriod"
+ fields_desc = [
+ LEIntField("activation_period", None)
+ ]
+
+
+class PAS5211MsgSetOltChannelActivationPeriodResponse(PAS5211Msg):
+ name = "PAS5211MsgSetOltChannelActivationPeriodResponse"
+ fields_desc = []
+
+
+class PAS5211MsgSetAlarmConfigResponse(PAS5211Msg):
+ name = "PAS5211MsgSetAlarmConfigResponse"
+ fields_desc = []
+
+
+class PAS5211MsgSendCliCommand(PAS5211Msg):
+ opcode = 15
+ name = "PAS5211MsgSendCliCommand"
+ fields_desc = [
+ FieldLenField("size", None, fmt="<H", length_of="command"),
+ StrField("command", "")
+ ]
+
+
+class PAS5211MsgSwitchToInboundMode(PAS5211Msg):
+ opcode = 0xec
+ name = "PAS5211MsgSwitchToInboundMode"
+ fields_desc = [
+ MACField("mac", None),
+ LEShortField("mode", 0)
+ ]
+
+
+class Frame(Packet):
+ pass
+
+
+class PAS5211MsgSendFrame(PAS5211Msg):
+ opcode = 42
+ name = "PAS5211MsgSendFrame"
+ fields_desc = [
+ FieldLenField("length", None, fmt="<H", length_of="frame"),
+ LEShortField("port_type", PON_PORT_PON),
+ LEShortField("port_id", 0),
+ LEShortField("management_frame", PON_FALSE),
+ PacketField("frame", None, Packet)
+ ]
+
+
+class PAS5211MsgSendFrameResponse(PAS5211Msg):
+ name = "PAS5211MsgSendFrameResponse"
+ fields_desc = []
+
+
+class PAS5211Event(PAS5211Msg):
+ opcode = 12
+
+
+class PAS5211EventFrameReceived(PAS5211Event):
+ name = "PAS5211EventFrameReceived"
+ fields_desc = [
+ FieldLenField("length", None, length_of="frame", fmt="<H"),
+ LEShortField("port_type", PON_PORT_PON),
+ LEShortField("port_id", 0),
+ LEShortField("management_frame", PON_FALSE),
+ LEShortField("classification_entity", None),
+ LEShortField("l3_offset", None),
+ LEShortField("l4_offset", None),
+ LEShortField("ignored", 0), # TODO these do receive values, but there is no code in PMC using it
+ ConditionalField(PacketField("frame", None, Packet), lambda pkt: pkt.management_frame==PON_FALSE),
+ ConditionalField(PacketField("frame", None, OMCIFrame), lambda pkt: pkt.management_frame==PON_TRUE)
+ ]
+
+
+class PAS5211EventOnuActivation(PAS5211Event):
+ name = "PAS5211EventOnuActivation"
+ fields_desc = [
+ StrFixedLenField("serial_number", None, length=8),
+ LEIntField("equalization_period", None)
+ ]
+
+
+# bindings for messages received
+bind_layers(PAS5211Dot3, PAS5211FrameHeader)
+bind_layers(PAS5211FrameHeader, PAS5211MsgHeader)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetProtocolVersionResponse, opcode=0x2800 | 2)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetOltVersionResponse, opcode=0x3800 | 3)
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetOltOpticsResponse, opcode=0x2800 | 106)
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetOpticsIoControlResponse, opcode=0x2800 | 108)
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetGeneralParamResponse, opcode=0x2800 | 164)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetGeneralParamResponse, opcode=0x2800 | 165)
+bind_layers(PAS5211MsgHeader, PAS5211MsgAddOltChannelResponse, opcode=0x2800 | 4)
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetAlarmConfigResponse, opcode=0x2800 | 48)
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetOltChannelActivationPeriodResponse, opcode=0x2800 | 11)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetDbaModeResponse, opcode=0x2800 | 57)
+bind_layers(PAS5211MsgHeader, PAS5211MsgSendFrameResponse, opcode=0x2800 | 42)
+
+# bindings for events received
+bind_layers(PAS5211MsgHeader, PAS5211EventOnuActivation, opcode=0x2800 | 12, event_type=1)
+bind_layers(PAS5211MsgHeader, PAS5211EventFrameReceived, opcode=0x2800 | 12, event_type=10)
+bind_layers(PAS5211MsgHeader, PAS5211Event, opcode=0x2800 | 12)
+
+
+def constructPAS5211Frames(msg, seq, channel_id=-1, onu_id=-1, onu_session_id=-1):
+
+ assert isinstance(msg, PAS5211Msg)
+ opcode = 0x3000 | msg.opcode
+
+ inner_msg = PAS5211MsgHeader(
+ sequence_number=seq,
+ opcode=opcode,
+ channel_id=channel_id,
+ onu_id=onu_id,
+ onu_session_id=onu_session_id
+ ) / msg
+ size = len(inner_msg)
+
+ frame_body = PAS5211FrameHeader(size=size) / inner_msg
+
+ frame = PAS5211Dot3(src=src_mac, dst=dst_mac) / frame_body
+
+ return [frame]
+
+
+class Receiver(Thread):
+
+ def __init__(self, iface):
+ Thread.__init__(self)
+ self.iface = iface
+ self.finished = False
+
+ def run(self):
+ self.sock = s = conf.L2listen( type=ETH_P_ALL, iface=self.iface, filter='inbound')
+ while not self.finished:
+ try:
+ sniffed = sniff(1, iface=self.iface, timeout=1, opened_socket=s)
+ for frame in sniffed:
+ self.process_frame(frame)
+ except Exception, e:
+ print("ERROR: scanpy.sniff error:", e)
+
+ def stop(self):
+ assert not self.finished
+ self.finished = True
+ self.sock.close()
+ self.join()
+
+ def process_frame(self, frame):
+ print "================== Received frame: ================="
+ print "Hexdump:"
+ hexdump(str(frame))
+ print "Raw string:"
+ print(repr(str(frame)))
+ print "Reconstructed frame:"
+ frame.show()
+
+
+if __name__ == '__main__':
+
+ seq = 1
+ def get_seq():
+ global seq
+ seq += 1
+ return seq
+
+ iface = "enp3s0"
+
+ receiver = Receiver(iface)
+ receiver.start()
+ sleep(0.1) # to allow the listening socket be opened
+
+ try:
+ sendp(constructPAS5211Frames(PAS5211MsgGetProtocolVersion(), get_seq())[0], iface=iface)
+ sleep(0.1)
+ sendp(constructPAS5211Frames(PAS5211MsgGetOltVersion(), get_seq())[0], iface=iface)
+ sleep(0.1)
+ sendp(constructPAS5211Frames(PAS5211MsgSendCliCommand(command="\r"), get_seq())[0], iface=iface)
+ sleep(5)
+
+ except Exception, e:
+ raise e
+
+ finally:
+ receiver.stop()
+
diff --git a/voltha/adapters/microsemi/send.py b/voltha/adapters/microsemi/send.py
new file mode 100644
index 0000000..8697ae7
--- /dev/null
+++ b/voltha/adapters/microsemi/send.py
@@ -0,0 +1,85 @@
+from scapy.all import *
+
+MIN_FRAME_SIZE = 60
+
+src_mac = "68:05:ca:05:f2:ef"
+dst_mac = "00:0c:d5:00:01:00"
+
+
+class PAS5211Dot3(Dot3):
+ name = "PAS5211Dot3"
+
+ def post_build(self, pkt, payload):
+ pkt += payload
+ size = ord(payload[4]) + (ord(payload[5]) << 8)
+ length = size + 6 # this is a idiosyncracy of the PASCOMM protocol
+ pkt = pkt[:12] + chr(length >> 8) + chr(length & 0xff) + pkt[14:]
+ padding = MIN_FRAME_SIZE - len(pkt)
+ if padding > 0:
+ pkt = pkt + ("\x00" * padding)
+ return pkt
+
+
+class PAS5211FrameHeader(Packet):
+ name = "PAS5211FrameHeader"
+ fields_desc = [ LEShortField("part", 1),
+ LEShortField("total_parts", 1),
+ LEShortField("size", 0),
+ LEIntField("magic_number", 0x1234ABCD) ]
+
+
+conf.neighbor.register_l3(Dot3, PAS5211FrameHeader, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload))
+
+
+class PAS5211MsgHeader(Packet):
+ name = "PAS5211MsgHeader"
+ fields_desc = [ LEIntField("sequence_number", 0),
+ LEShortField("opcode", 0) ]
+
+
+class PAS5211MsgEntityHeader(Packet): # PASCOMM_GPON_msg_entity_hdr
+ name = "PAS5211MsgEntityHeader"
+ fields_desc = [ LEShortField("reserved", 0),
+ LEShortField("channel_id", 0xffff),
+ LEShortField("onu_id", 0xffff),
+ LESignedIntField("onu_session_id", -1) ]
+
+
+class PAS5211Msg(Packet):
+ opcode = "Must be filled by subclass"
+ pass
+
+
+class PAS5211MsgGetProtocolVersion(PAS5211Msg):
+ opcode = 2
+ name = "PAS5211MsgGetProtocolVersion"
+ fields_desc = [ ]
+
+
+class PAS5211MsgGetOltVersion(PAS5211Msg):
+ opcode = 3
+ name = "PAS5211MsgGetOltVersion"
+ fields_desc = [ ]
+
+
+def constructPAS5211Frames(msg, seq):
+
+ assert isinstance(msg, PAS5211Msg)
+ opcode = 0x3000 | msg.opcode
+
+ entity_hdr = PAS5211MsgEntityHeader() # we may need non-def values later
+
+ inner_msg = PAS5211MsgHeader(sequence_number=seq, opcode=opcode) \
+ / msg \
+ / entity_hdr
+ size = len(inner_msg)
+ hexdump(inner_msg)
+
+ frame_body = PAS5211FrameHeader(size=size) / inner_msg
+
+ frame = PAS5211Dot3(src=src_mac, dst=dst_mac) / frame_body
+
+ return frame
+
+frame = constructPAS5211Frames(PAS5211MsgGetProtocolVersion(), 1) [0]
+hexdump(frame)
diff --git a/voltha/extensions/__init__.py b/voltha/extensions/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/voltha/extensions/__init__.py
diff --git a/voltha/extensions/omci/__init__.py b/voltha/extensions/omci/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/voltha/extensions/omci/__init__.py
diff --git a/voltha/extensions/omci/omci.py b/voltha/extensions/omci/omci.py
new file mode 100644
index 0000000..aa14b92
--- /dev/null
+++ b/voltha/extensions/omci/omci.py
@@ -0,0 +1,217 @@
+import inspect
+import sys
+from enum import Enum
+# from scapy.all import StrFixedLenField, ByteField, ShortField, ConditionalField, \
+# PacketField, PadField, IntField, Field, Packet
+from scapy.fields import ByteField, Field, ShortField, PacketField, PadField, \
+ ConditionalField
+from scapy.fields import StrFixedLenField, IntField
+from scapy.packet import Packet
+
+
+def bitpos_from_mask(mask, lsb_pos=0, increment=1):
+ """
+ Turn a decimal value (bitmask) into a list of indices where each
+ index value corresponds to the bit position of a bit that was set (1)
+ in the mask. What numbers are assigned to the bit positions is controlled
+ by lsb_pos and increment, as explained below.
+ :param mask: a decimal value used as a bit mask
+ :param lsb_pos: The decimal value associated with the LSB bit
+ :param increment: If this is +i, then the bit next to LSB will take
+ the decimal value of lsb_pos + i.
+ :return: List of bit positions where the bit was set in mask
+ """
+ out = []
+ while mask:
+ if mask & 0x01:
+ out.append(lsb_pos)
+ lsb_pos += increment
+ mask >>= 1
+ return sorted(out)
+
+
+class AttributeAccess(Enum):
+ Readable = 1
+ R = 1
+ Writable = 2
+ W = 2
+ SetByCreate = 3
+ SBC = 3
+
+
+class EntityOperations(Enum):
+ Get = 1 # TODO adjust encoding to match msg_type field
+ Set = 2
+ Create = 3
+ Delete = 4
+ Reboot = 10
+ Test = 11
+
+
+class EntityClassAttribute:
+
+ def __init__(self, fld, access=set(), optional=False):
+ self._fld = fld
+ self._access = access
+ self._optional = optional
+
+class EntityClass:
+ class_id = 'to be filled by subclass'
+ attributes = []
+ mandatory_operations = {}
+ optional_operations = {}
+
+ # will be map of attr_name -> index in attributes
+ attribute_name_to_index_map = None
+
+ def __init__(self, **kw):
+
+ assert(isinstance(kw, dict))
+
+ # verify that all keys provided are valid in the entity
+ if self.attribute_name_to_index_map is None:
+ self.__class__.attribute_name_to_index_map = dict(
+ (a._fld.name, idx) for idx, a in enumerate(self.attributes))
+
+ for k, v in kw.iteritems():
+ assert(k in self.attribute_name_to_index_map)
+
+ self._data = kw
+
+ def serialize(self, mask=None, operation=None):
+ bytes = ''
+
+ # generate ordered list of attribute indices needed to be processed
+ # if mask is provided, we use that explicitly
+ # if mask is not provided, we determine attributes from the self._data content
+ # also taking into account the type of operation in hand
+ if mask is not None:
+ attribute_indices = EntityClass.attribute_indices_from_mask(mask)
+ print attribute_indices
+ else:
+ attribute_indices = self.attribute_indices_from_data()
+
+ # Serialize each indexed field (ignoring entity id)
+ for index in attribute_indices:
+ field = self.attributes[index]._fld
+ bytes = field.addfield(None, bytes, self._data[field.name])
+
+ return bytes
+
+ def attribute_indices_from_data(self):
+ return sorted(
+ self.attribute_name_to_index_map[attr_name]
+ for attr_name in self._data.iterkeys())
+
+ byte1_mask_to_attr_indices = dict(
+ (m, bitpos_from_mask(m, 8, -1)) for m in range(256))
+ byte2_mask_to_attr_indices = dict(
+ (m, bitpos_from_mask(m, 16, -1)) for m in range(256))
+ @classmethod
+ def attribute_indices_from_mask(cls, mask):
+ # each bit in the 2-byte field denote an attribute index; we use a
+ # lookup table to make lookup a bit faster
+ return \
+ cls.byte1_mask_to_attr_indices[(mask >> 8) & 0xff] + \
+ cls.byte2_mask_to_attr_indices[(mask & 0xff)]
+
+
+# abbreviations
+ECA = EntityClassAttribute
+AA = AttributeAccess
+OP = EntityOperations
+
+
+class CirtcuitPackEntity(EntityClass):
+ class_id = 6
+ attributes = [
+ ECA(StrFixedLenField("managed_entity_id", None, 22), {AA.R, AA.SBC}),
+ ECA(ByteField("type", None), {AA.R, AA.SBC}),
+ ECA(ByteField("number_of_ports", None), {AA.R}, optional=True),
+ ECA(StrFixedLenField("serial_number", None, 8), {AA.R}),
+ ECA(StrFixedLenField("version", None, 14), {AA.R}),
+ ECA(StrFixedLenField("vendor_id", None, 4), {AA.R}),
+ ECA(ByteField("administrative_state", None), {AA.R, AA.W, AA.SBC}),
+ ECA(ByteField("operational_state", None), {AA.R}, optional=True),
+ ECA(ByteField("bridged_or_ip_ind", None), {AA.R, AA.W}, optional=True),
+ ECA(StrFixedLenField("equipment_id", None, 20), {AA.R}, optional=True),
+ ECA(ByteField("card_configuration", None), {AA.R, AA.W, AA.SBC}), # not really mandatory, see spec
+ ECA(ByteField("total_tcont_buffer_number", None), {AA.R}),
+ ECA(ByteField("total_priority_queue_number", None), {AA.R}),
+ ECA(ByteField("total_traffic_scheduler_number", None), {AA.R}),
+ ECA(IntField("power_sched_override", None), {AA.R, AA.W}, optional=True)
+ ]
+ mandatory_operations = {OP.Get, OP.Set, OP.Reboot}
+ optional_operations = {OP.Create, OP.Delete, OP.Test}
+
+
+# entity class lookup table from entity_class values
+entity_classes_name_map = dict(
+ inspect.getmembers(sys.modules[__name__],
+ lambda o: inspect.isclass(o) and \
+ issubclass(o, EntityClass) and \
+ o is not EntityClass)
+)
+
+entity_classes = [c for c in entity_classes_name_map.itervalues()]
+entity_id_to_class_map = dict((c.class_id, c) for c in entity_classes)
+
+
+class OMCIData(Field):
+
+ __slots__ = Field.__slots__ + ['_entity_class', '_attributes_mask']
+
+ def __init__(self, name, entity_class="entity_class",
+ attributes_mask="attributes_mask"):
+ Field.__init__(self, name=name, default=None, fmt='s')
+ self._entity_class = entity_class
+ self._attributes_mask = attributes_mask
+
+ def i2m(self, pkt, x):
+ class_id = getattr(pkt, self._entity_class)
+ attribute_mask = getattr(pkt, self._attributes_mask)
+ entity_class = entity_id_to_class_map.get(class_id)
+ return entity_class(**x).serialize(attribute_mask)
+
+
+class OMCIMessage(Packet):
+ name = "OMCIMessage"
+ fields_desc = []
+
+
+class OMCIGetRequest(OMCIMessage):
+ name = "OMCIGetRequest"
+ fields_desc = [
+ ShortField("entity_class", None),
+ ShortField("entity_id", 0),
+ ShortField("attributes_mask", None)
+ ]
+
+
+class OMCIGetResponse(OMCIMessage):
+ name = "OMCIGetResponse"
+ fields_desc = [
+ ShortField("entity_class", None),
+ ShortField("entity_id", 0),
+ ByteField("success_code", 0),
+ ShortField("attributes_mask", None),
+ OMCIData("data", entity_class="entity_class",
+ attributes_mask="attributes_mask")
+ ]
+
+
+class OMCIFrame(Packet):
+ name = "OMCIFrame"
+ fields_desc = [
+ ShortField("transaction_id", 0),
+ ByteField("message_type", None),
+ ByteField("omci", 0x0a),
+ ConditionalField(PadField(PacketField("omci_message", None,
+ OMCIGetRequest), align=36),
+ lambda pkt: pkt.message_type == 0x49),
+ ConditionalField(PadField(PacketField("omci_message", None,
+ OMCIGetResponse), align=36),
+ lambda pkt: pkt.message_type == 0x29),
+ # TODO add additional message types here as padded conditionals...
+ IntField("omci_trailer", 0x00000028)
+ ]
diff --git a/voltha/protos/third_party/__init__.py b/voltha/protos/third_party/__init__.py
index e69de29..19192c4 100644
--- a/voltha/protos/third_party/__init__.py
+++ b/voltha/protos/third_party/__init__.py
@@ -0,0 +1,50 @@
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+This helps loading http_pb2 and annotations_pb2.
+Without this, the Python importer will not be able to process the lines:
+from google.api import http_pb2 or
+from google.api import annotations_pb2
+(Without importing these, the protobuf loader will not recognize http options
+in the protobuf definitions.)
+"""
+
+from importlib import import_module
+import os
+import sys
+
+
+class GoogleApiImporter(object):
+
+ def find_module(self, full_name, path=None):
+ if full_name == 'google.api':
+ self.path = [os.path.dirname(__file__)]
+ return self
+
+ def load_module(self, name):
+ if name in sys.modules:
+ return sys.modules[name]
+ full_name = 'chameleon.protos.third_party.' + name
+ import_module(full_name)
+ module = sys.modules[full_name]
+ sys.modules[name] = module
+ return module
+
+
+sys.meta_path.append(GoogleApiImporter())
+from google.api import http_pb2, annotations_pb2
+_ = http_pb2, annotations_pb2