blob: 7478dc2e5aba06382672609d4f54ada232695375 [file] [log] [blame]
#
# 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.
#
from scapy.automaton import ATMT
import structlog
from voltha.adapters.microsemi.BaseOltAutomaton import BaseOltAutomaton
from voltha.adapters.microsemi.PAS5211 import PAS5211MsgGetProtocolVersion, PAS5211MsgGetOltVersion, \
PAS5211MsgGetOltVersionResponse, PAS5211MsgGetProtocolVersionResponse, \
SnrBurstDelay, RngBurstDelay, GeneralOpticsParams, ResetValues, ResetTimingCtrl, PreambleParams, \
PAS5211MsgSetOltOpticsResponse, CHANNELS, PON_OPTICS_VOLTAGE_IF_LVPECL, PON_ENABLE, PON_POLARITY_ACTIVE_HIGH, \
PON_SD_SOURCE_LASER_SD, PON_RESET_TYPE_DELAY_BASED, PON_DISABLE, PON_RESET_TYPE_NORMAL_START_BURST_BASED, \
PAS5211MsgSetOpticsIoControlResponse, PON_TX_ENABLE_DEFAULT, PAS5211MsgGetGeneralParamResponse, PAS5211MsgAddOltChannel, \
PAS5211MsgAddOltChannelResponse, PON_ALARM_LOS, PAS5211MsgSetAlarmConfigResponse, PAS5211MsgGetDbaMode, \
PAS5211MsgGetDbaModeResponse, PON_DBA_MODE_LOADED_NOT_RUNNING, PAS5211MsgStartDbaAlgorithm, \
PAS5211MsgStartDbaAlgorithmResponse, PON_DBA_MODE_RUNNING, PAS5211MsgSetOltChannelActivationPeriod, \
PAS5211MsgSetOltChannelActivationPeriodResponse
from voltha.adapters.microsemi.PAS5211_utils import general_param, olt_optics_pkt, burst_timing, io_ctrl_optics, \
alarm_config
import structlog
log = structlog.get_logger()
class OltStateMachine(BaseOltAutomaton):
send_state = []
dba_needs_start = False
def check_channel_state(self):
for i in CHANNELS:
if not self.send_state[i]:
return False
self.send_state = []
return True
"""
States
"""
@ATMT.state(initial=1)
def disconnected(self):
pass
@ATMT.state()
def wait_for_proto_version(self):
pass
@ATMT.state()
def got_proto_version(self):
pass
@ATMT.state()
def wait_for_olt_version(self):
pass
@ATMT.state()
def got_olt_version(self):
pass
@ATMT.state()
def wait_olt_optics(self):
pass
@ATMT.state()
def got_olt_optics(self):
pass
@ATMT.state()
def wait_olt_io_optics(self):
pass
@ATMT.state()
def got_olt_io_optics(self):
pass
@ATMT.state()
def wait_query_response(self):
pass
@ATMT.state()
def got_query_response(self):
pass
@ATMT.state()
def wait_olt_add(self):
pass
@ATMT.state()
def got_olt_add(self):
pass
@ATMT.state()
def wait_alarm_set(self):
pass
@ATMT.state()
def got_alarm_set(self):
pass
@ATMT.state()
def wait_dba_mode(self):
pass
@ATMT.state()
def got_dba_mode(self):
pass
@ATMT.state()
def wait_dba_start(self):
pass
@ATMT.state()
def got_dba_start(self):
pass
@ATMT.state()
def wait_activation(self):
pass
@ATMT.state()
def initialized(self):
pass
@ATMT.state()
def wait_keepalive(self):
pass
@ATMT.state(error=1)
def ERROR(self):
pass
"""
Transitions
"""
# Transitions from disconnected state
@ATMT.condition(disconnected)
def send_proto_request(self):
self.send(self.p(PAS5211MsgGetProtocolVersion()))
raise self.wait_for_proto_version()
# Transitions from wait_for_proto_version
@ATMT.timeout(wait_for_proto_version, 1)
def timeout_proto(self):
log.info("Timed out waiting for proto version")
self.retry -= 1
if self.retry < 0:
log.debug("Too many retries, aborting.")
raise self.ERROR()
raise self.disconnected()
@ATMT.receive_condition(wait_for_proto_version)
def receive_proto_version(self, pkt):
log.debug("Received proto version {}".format(type(pkt)))
if PAS5211MsgGetProtocolVersionResponse in pkt:
raise self.got_proto_version()
else:
log.error("Got garbage packet {}".format(pkt))
raise self.ERROR()
# Transitions from got_proto_version
@ATMT.condition(got_proto_version)
def send_olt_version(self):
self.send(self.p(PAS5211MsgGetOltVersion()))
raise self.wait_for_olt_version()
# Transitions from waiting for olt version
@ATMT.timeout(wait_for_olt_version, 1)
def timeout_olt(self):
log.debug("Timed out waiting for olt version")
self.retry -= 1
if self.retry < 0:
log.debug("Too many retries, aborting.")
raise self.ERROR()
raise self.disconnected()
@ATMT.receive_condition(wait_for_olt_version)
def receive_olt_version(self, pkt):
log.debug("Received proto version {}".format(pkt))
if PAS5211MsgGetOltVersionResponse in pkt:
log.info("updating device")
self.device.update_device(pkt)
raise self.got_olt_version()
else:
log.error("Got garbage packet {}".format(pkt))
raise self.ERROR()
# Transitions from got_olt_version
@ATMT.condition(got_olt_version)
def send_olt_optics(self):
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)
general_optics_param = 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 = 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=ResetValues(bcdr_reset_d2=1,
bcdr_reset_d1=1,
laser_reset_d2=1,
laser_reset_d1=1))
preamble = 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)
olt_optics = olt_optics_pkt(PON_OPTICS_VOLTAGE_IF_LVPECL, burst=burst_timing(1, 1,
snr_burst=snr_burst_delay,
rng_burst=rng_burst_delay),
general=general_optics_param,
reset=reset,
preamble=preamble)
for id in CHANNELS:
self.send_state.append(False)
self.send(self.p(olt_optics, channel_id=id))
raise self.wait_olt_optics()
# Transitions from wait_olt_optics
@ATMT.timeout(wait_olt_optics, 3)
def olt_optics_timeout(self):
log.error("Setting olt optics failed; disconnecting")
raise self.ERROR()
@ATMT.receive_condition(wait_olt_optics)
def receive_set_optics_response(self, pkt):
if pkt.opcode == PAS5211MsgSetOltOpticsResponse.opcode:
self.send_state[pkt.channel_id] = True
if self.check_channel_state():
raise self.got_olt_optics()
raise self.wait_olt_optics()
# Transitions from got_olt_optics
@ATMT.condition(got_olt_optics)
def send_olt_io_optics(self):
pkt = io_ctrl_optics(1, 0, 6, 14)
self.send_state.append(False)
self.send(self.p(pkt, channel_id=0))
pkt = io_ctrl_optics(3, 2, 7, 15)
self.send_state.append(False)
self.send(self.p(pkt, channel_id=1))
pkt = io_ctrl_optics(11, 10, 8, 16)
self.send_state.append(False)
self.send(self.p(pkt, channel_id=2))
pkt = io_ctrl_optics(13, 12, 9, 17)
self.send_state.append(False)
self.send(self.p(pkt, channel_id=3))
raise self.wait_olt_io_optics()
# Transitions from wait olt io optics
@ATMT.timeout(wait_olt_io_optics, 3)
def olt_io_optics_timeout(self):
log.error("Setting olt io optics failed; disconnecting")
raise self.ERROR()
@ATMT.receive_condition(wait_olt_io_optics)
def receive_io_optics_response(self, pkt):
if pkt.opcode == PAS5211MsgSetOpticsIoControlResponse.opcode:
self.send_state[pkt.channel_id] = True
if self.check_channel_state():
raise self.got_olt_io_optics()
raise self.wait_olt_io_optics()
# Transitions got olt io optics
@ATMT.condition(got_olt_io_optics)
def check_downstream_pon_tx(self):
query = general_param(PON_TX_ENABLE_DEFAULT)
for id in CHANNELS:
self.send_state.append(False)
self.send(self.p(query, channel_id=id))
raise self.wait_query_response()
# Transitions from wait query response
@ATMT.timeout(wait_query_response, 3)
def query_timeout(self):
log.error("Our queries have gone unanswered; disconnecting")
raise self.ERROR()
@ATMT.receive_condition(wait_query_response)
def check_pon_tx_state(self, pkt):
if PAS5211MsgGetGeneralParamResponse in pkt:
self.send_state[pkt.channel_id] = True
if pkt.value == PON_ENABLE:
# TODO: we may want to do something here.
if self.check_channel_state():
raise self.got_query_response()
else:
raise self.wait_query_response()
else:
log.error("TX downstream is not enabled")
raise self.ERROR()
# Transitions from got_query_response
@ATMT.condition(wait_query_response)
def send_add_olt(self):
olt_add = PAS5211MsgAddOltChannel()
for id in CHANNELS:
self.send_state.append(False)
self.send(self.p(olt_add, channel_id=id))
raise self.wait_olt_add()
# Transitions from wait_olt_add
@ATMT.timeout(wait_olt_add, 3)
def olt_add_timeout(self):
log.error("Cannot add olts; disconnecting")
raise self.ERROR()
@ATMT.receive_condition(wait_olt_add)
def wait_for_olt_add(self, pkt):
if pkt.opcode == PAS5211MsgAddOltChannelResponse.opcode:
self.send_state[pkt.channel_id] = True
if self.check_channel_state():
raise self.got_olt_add()
raise self.wait_olt_add()
# Transitions from got_olt_add
@ATMT.condition(got_olt_add)
def send_alarm_config(self):
alarm_msg = alarm_config(PON_ALARM_LOS, PON_ENABLE)
for id in CHANNELS:
self.send_state.append(False)
self.send(self.p(alarm_msg, channel_id=id))
raise self.wait_alarm_set()
# Transitions for wait_alarm_set
@ATMT.timeout(wait_alarm_set, 3)
def alarm_timeout(self):
log.error("Couldn't set alarms; disconnecting")
raise self.ERROR()
@ATMT.receive_condition(wait_alarm_set)
def wait_for_alarm_set(self, pkt):
if pkt.opcode == PAS5211MsgSetAlarmConfigResponse.opcode:
self.send_state[pkt.channel_id] = True
if self.check_channel_state():
raise self.got_alarm_set()
raise self.wait_alarm_set()
# Transitions from got_alarm_set
@ATMT.condition(got_alarm_set)
def send_dba_mode(self):
get_dba_mode = PAS5211MsgGetDbaMode()
for id in CHANNELS:
self.send_state.append(False)
self.send(self.p(get_dba_mode, channel_id=id))
raise self.wait_dba_mode()
# Transitions from wait_dba_mode
@ATMT.timeout(wait_dba_mode, 3)
def dba_timeout(self):
log.error("No DBA information returned; disconnecting")
raise self.ERROR()
@ATMT.receive_condition(wait_dba_mode)
def wait_for_dba_mode(self, pkt):
if PAS5211MsgGetDbaModeResponse in pkt:
# TODO: What do we do in case the DBA is not loaded.
if pkt.dba_mode == PON_DBA_MODE_LOADED_NOT_RUNNING:
self.send_state[pkt.channel_id] = True
self.dba_needs_start = True
elif pkt.dba_mode == PON_DBA_MODE_RUNNING:
self.send_state[pkt.channel_id] = True
if self.check_channel_state():
if self.dba_needs_start:
raise self.got_dba_mode()
else:
raise self.got_dba_start()
raise self.wait_dba_mode()
# Transition from got_dba_mode
@ATMT.condition(got_dba_mode)
def send_start_dba(self):
dba_start = PAS5211MsgStartDbaAlgorithm(size=0, initialization_data=None)
for id in CHANNELS:
self.send_state.append(False)
self.send(self.p(dba_start, channel_id=id))
raise self.wait_dba_start()
# Transitions from wait_dba_start
@ATMT.timeout(wait_dba_start, 3)
def dba_timeout(self):
log.error("DBA has not started; disconnecting")
raise self.ERROR()
@ATMT.receive_condition(wait_dba_start)
def wait_for_dba_start(self, pkt):
if pkt.opcode == PAS5211MsgStartDbaAlgorithmResponse.opcode:
self.send_state[pkt.channel_id] = True
if self.check_channel_state():
raise self.got_dba_start()
raise self.wait_dba_start()
# Transitions from got_dba_start
@ATMT.condition(got_dba_start)
def send_activation_period(self):
activation = PAS5211MsgSetOltChannelActivationPeriod(activation_period=1000)
for id in CHANNELS:
self.send_state.append(False)
self.send(self.p(activation, channel_id=id))
raise self.wait_activation()
# Transitions for wait_for_activation
@ATMT.timeout(wait_activation, 3)
def timeout_activation(self):
log.error("No activation; disconnect")
raise self.ERROR()
@ATMT.receive_condition(wait_activation)
def wait_for_activation(self, pkt):
if pkt.opcode == PAS5211MsgSetOltChannelActivationPeriodResponse.opcode:
self.send_state[pkt.channel_id] = True
if self.check_channel_state():
log.info("Ruby OLT at {} initialised".format(self.target))
self.device.create_logical_device()
raise self.initialized()
raise self.wait_activation()
# Keep alive loop
@ATMT.timeout(initialized, 1)
def send_keepalive(self):
self.send(self.p(PAS5211MsgGetOltVersion()))
raise self.wait_keepalive()
# Transitions from wait_keepalive
@ATMT.timeout(wait_keepalive, 1)
def timeout_keepalive(self):
log.error("OLT not responding to keep alive; disconnecting")
raise self.ERROR()
@ATMT.receive_condition(wait_keepalive)
def wait_for_keepalive(self, pkt):
if PAS5211MsgGetOltVersionResponse in pkt:
raise self.initialized()