Finished OLT initialization.

OLT can now initialise even if the DBA algorithm is
already running.

Change-Id: I89218cc5a70f720fb193631ba3065b0c103e7e2f
diff --git a/voltha/adapters/microsemi/OltStateMachine.py b/voltha/adapters/microsemi/OltStateMachine.py
index a2db517..3f389b8 100644
--- a/voltha/adapters/microsemi/OltStateMachine.py
+++ b/voltha/adapters/microsemi/OltStateMachine.py
@@ -17,13 +17,15 @@
 from scapy.layers.l2 import sendp
 import structlog
 from voltha.adapters.microsemi.PAS5211 import PAS5211MsgGetProtocolVersion, PAS5211MsgGetOltVersion, \
-    PAS5211MsgGetOltVersionResponse, PAS5211MsgGetProtocolVersionResponse, PAS5211MsgSetOltOptics, BurstTimingCtrl, \
+    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, \
-    PAS5211MsgSetOpticsIoControl, PON_POLARITY_ACTIVE_LOW, PAS5211MsgSetOpticsIoControlResponse, \
-    PAS5211MsgGetGeneralParam, PON_TX_ENABLE_DEFAULT, PAS5211MsgGetGeneralParamResponse, PAS5211MsgAddOltChannel, \
-    PAS5211MsgAddOltChannelResponse, PON_ALARM_LOS, PAS5211MsgSetAlarmConfigResponse
+    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
 
@@ -41,6 +43,7 @@
     verbose = None
 
     send_state = []
+    dba_needs_start = False
 
     def parse_args(self, debug=0, store=0, **kwargs):
         self.comm = kwargs.pop('comm', None)
@@ -77,6 +80,10 @@
         self.send_state = []
         return True
 
+    """
+    States
+    """
+
     @ATMT.state(initial=1)
     def disconnected(self):
         pass
@@ -137,23 +144,49 @@
     def got_alarm_set(self):
         pass
 
-    @ATMT.state(final=1)
+    @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
+    # 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
+    # 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")
@@ -324,7 +357,7 @@
         if PAS5211MsgGetGeneralParamResponse in pkt:
             self.send_state[pkt.channel_id] = True
             if pkt.value == PON_ENABLE:
-                # we may want to do something here.
+                # TODO: we may want to do something here.
                 if self.check_channel_state():
                     raise self.got_query_response()
                 else:
@@ -379,9 +412,101 @@
                 raise self.got_alarm_set()
             raise self.wait_alarm_set()
 
-    @ATMT.timeout(got_alarm_set, 1)
+    # 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():
+                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.got_alarm_set()
+        raise self.wait_keepalive()
+
+    # Transitions from wait_keepalive
+    @ATMT.timeout(wait_keepalive, 1)
+    def timeout_keepalive(self):
+        log.error("OLT not responsing to keepalive; disconnecting")
+        raise self.ERROR()
+
+    @ATMT.receive_condition(wait_keepalive)
+    def wait_for_keepalive(self, pkt):
+        if PAS5211MsgGetOltVersionResponse in pkt:
+            raise self.initialized()
+
 
 
diff --git a/voltha/adapters/microsemi/PAS5211.py b/voltha/adapters/microsemi/PAS5211.py
index f24af17..41f3882 100644
--- a/voltha/adapters/microsemi/PAS5211.py
+++ b/voltha/adapters/microsemi/PAS5211.py
@@ -516,6 +516,21 @@
         LEIntField("parameter4", None)
     ]
 
+class PAS5211MsgStartDbaAlgorithm(PAS5211Msg):
+    opcode = 55
+    name = "PAS5211MsgStartDbaAlgorithm"
+    fields_desc = [
+        LEShortField("size", 0),
+        ByteField("initialization_data", 0)
+    ]
+
+
+class PAS5211MsgStartDbaAlgorithmResponse(PAS5211Msg):
+    name = "PAS5211MsgStartDbaAlgorithmResponse"
+    opcode = 10295
+    fields_desc = []
+
+
 
 class PAS5211MsgSetOltChannelActivationPeriod(PAS5211Msg):
     opcode = 11
@@ -526,6 +541,7 @@
 
 
 class PAS5211MsgSetOltChannelActivationPeriodResponse(PAS5211Msg):
+    opcode = 10251
     name = "PAS5211MsgSetOltChannelActivationPeriodResponse"
     fields_desc = []
 
@@ -535,6 +551,18 @@
     name = "PAS5211MsgSetAlarmConfigResponse"
     fields_desc = []
 
+class PAS5211MsgGetDbaMode(PAS5211Msg):
+    opcode = 57
+    name = "PAS5211MsgGetDbaMode"
+    fields_desc = []
+
+
+class PAS5211MsgGetDbaModeResponse(PAS5211Msg):
+    name = "PAS5211MsgGetDbaModeResponse"
+    fields_desc = [
+        LEIntField("dba_mode", None),
+    ]
+
 """
 Bindings used for message processing
 """
@@ -546,3 +574,6 @@
 bind_layers(PAS5211MsgHeader, PAS5211MsgGetProtocolVersionResponse, opcode=0x2800 | 2)
 bind_layers(PAS5211MsgHeader, PAS5211MsgGetGeneralParamResponse, opcode=0x2800 | 165)
 bind_layers(PAS5211MsgHeader, PAS5211MsgSetAlarmConfigResponse, opcode=0x2800 | 48)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetDbaModeResponse, opcode=0x2800 | 57)
+bind_layers(PAS5211MsgHeader, PAS5211MsgStartDbaAlgorithmResponse, opcode=0x2800 | 55)
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetOltChannelActivationPeriodResponse, opcode=0x2800 | 11)
\ No newline at end of file
diff --git a/voltha/adapters/microsemi/PAS5211_comm.py b/voltha/adapters/microsemi/PAS5211_comm.py
index 863cfbb..623b498 100644
--- a/voltha/adapters/microsemi/PAS5211_comm.py
+++ b/voltha/adapters/microsemi/PAS5211_comm.py
@@ -14,9 +14,7 @@
 # limitations under the License.
 #
 import netifaces
-import select
 from scapy.layers.l2 import Dot3
-from scapy.sendrecv import srp1
 import structlog
 from voltha.adapters.microsemi.PAS5211 import PAS5211Msg, PAS5211MsgHeader, PAS5211FrameHeader
 
diff --git a/voltha/adapters/microsemi/RubyAdapter.py b/voltha/adapters/microsemi/RubyAdapter.py
index 8050cb8..80edb8c 100644
--- a/voltha/adapters/microsemi/RubyAdapter.py
+++ b/voltha/adapters/microsemi/RubyAdapter.py
@@ -19,7 +19,8 @@
 """
 import structlog
 from twisted.internet import reactor
-from zope.interface import implementer
+
+
 
 from voltha.adapters.interface import IAdapterInterface
 from voltha.adapters.microsemi.OltStateMachine import OltStateMachine
@@ -28,11 +29,13 @@
 from voltha.protos.device_pb2 import DeviceTypes
 from voltha.protos.health_pb2 import HealthStatus
 
+from zope.interface import implementer
+
 log = structlog.get_logger()
 _ = third_party
 
 # Move to configuration... eventually
-olt_conf = { 'olts' : { 'id' : 0, 'mac' : '00:0c:d5:00:01:00'}, 'iface' : 'en0'}
+olt_conf = { 'olts' : { 'id' : 0, 'mac' : '00:0c:d5:00:01:00'}, 'iface' : 'eth3'}
 
 @implementer(IAdapterInterface)
 class RubyAdapter(object):
@@ -65,11 +68,11 @@
         return self.descriptor
 
     def device_types(self):
-        pass
         return DeviceTypes(
-           items=[]  # TODO
+            items=[]  # TODO
         )
 
+
     def health(self):
         return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
 
diff --git a/voltha/adapters/microsemi/sniff.py b/voltha/adapters/microsemi/sniff.py
index 23cb7ee..915efe6 100755
--- a/voltha/adapters/microsemi/sniff.py
+++ b/voltha/adapters/microsemi/sniff.py
@@ -1,11 +1,13 @@
 #!/usr/bin/env python
 import code
+import sys
+from scapy.config import conf
+from scapy.data import ARPHDR_ETHER
 
-from time import sleep
 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, LLC, SNAP, Dot3
+    ByteField, StrField, ConditionalField, StrFixedLenField, LELongField
+from scapy.layers.l2 import DestMACField, ETHER_ANY, LLC, Dot3
 from scapy.layers.ntp import XLEShortField
 from scapy.packet import Packet, bind_layers, split_layers
 from scapy.utils import rdpcap
@@ -194,25 +196,6 @@
         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 = [
@@ -443,6 +426,22 @@
     def extract_padding(self, p):
         return "", p
 
+
+class PAS5211MsgStartDbaAlgorithm(PAS5211Msg):
+    opcode = 55
+    name = "PAS5211MsgStartDbaAlgorithm"
+    fields_desc = [
+        LEShortField("size", 0),
+        ByteField("initialization_data", None)
+    ]
+
+
+class PAS5211MsgStartDbaAlgorithmResponse(PAS5211Msg):
+    name = "PAS5211MsgStartDbaAlgorithmResponse"
+    opcode = 10295
+    fields_desc = []
+
+
 class PAS5211MsgSetGeneralParam(PAS5211Msg):
     opcode = 164
     name = "PAS5211MsgSetGeneralParam"
@@ -482,13 +481,15 @@
     fields_desc = []
 
 
-class PAS5211MsgGetDbaModeResponse(PAS5211Msg):
+class  PAS5211MsgGetDbaModeResponse(PAS5211Msg):
     name = "PAS5211MsgGetDbaModeResponse"
     fields_desc = [
         LEIntField("dba_mode", None),
     ]
 
 
+
+
 class PAS5211MsgAddOltChannel(PAS5211Msg):
     opcode = 4
     name = "PAS5211MsgAddOltChannel"
@@ -603,8 +604,9 @@
 
 
 # bindings for messages received
+
 split_layers(Dot3, LLC)
-bind_layers(Dot3,PAS5211FrameHeader)
+bind_layers(Dot3, PAS5211FrameHeader)
 bind_layers(PAS5211FrameHeader, PAS5211MsgHeader)
 
 bind_layers(PAS5211MsgHeader, PAS5211MsgGetProtocolVersion, opcode=0x3000 | 2)
@@ -634,6 +636,9 @@
 bind_layers(PAS5211MsgHeader, PAS5211MsgSetOltChannelActivationPeriod, opcode=0x3000 | 11)
 bind_layers(PAS5211MsgHeader, PAS5211MsgSetOltChannelActivationPeriodResponse, opcode=0x2800 | 11)
 
+bind_layers(PAS5211MsgHeader, PAS5211MsgStartDbaAlgorithm, opcode=0x3000 | 55)
+bind_layers(PAS5211MsgHeader, PAS5211MsgStartDbaAlgorithmResponse, opcode=0x2800 | 55)
+
 bind_layers(PAS5211MsgHeader, PAS5211MsgGetDbaMode, opcode=0x3000 | 57)
 bind_layers(PAS5211MsgHeader, PAS5211MsgGetDbaModeResponse, opcode=0x2800 | 57)
 
@@ -658,7 +663,7 @@
 
 if __name__ == '__main__':
 
-    packets = rdpcap('pcaps/olt.pcap')
+    packets = rdpcap(sys.argv[1])
     p = Display(packets)
     code.interact(local=locals())