Microsemi ONU activation

- Activation of an ONU is supported
- Pushing information back to voltha core

Change-Id: I25881e71a5b3c642acc62f1a1a9222e56604d2c5
diff --git a/voltha/adapters/microsemi/ActivationWatcher.py b/voltha/adapters/microsemi/ActivationWatcher.py
new file mode 100644
index 0000000..c6f4ea3
--- /dev/null
+++ b/voltha/adapters/microsemi/ActivationWatcher.py
@@ -0,0 +1,239 @@
+#
+# 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
+from twisted.internet import reactor
+import structlog
+from voltha.adapters.microsemi.BaseOltAutomaton import BaseOltAutomaton
+from voltha.adapters.microsemi.PAS5211 import PAS5211EventOnuActivation, PAS5211MsgGetActivationAuthMode, \
+    PAS5211MsgGetActivationAuthModeResponse, PON_ACTIVATION_AUTH_AUTO, PON_ENABLE, PAS5211MsgSetOnuOmciPortId, \
+    PAS5211MsgSetOnuOmciPortIdResponse, PAS5211MsgSendFrame, PON_PORT_PON, PAS5211MsgSendFrameResponse
+from voltha.extensions.omci.omci_entities import CircuitPack
+from voltha.extensions.omci.omci_frame import OmciFrame
+from voltha.extensions.omci.omci_messages import OmciGet, OmciGetResponse
+from voltha.protos.common_pb2 import AdminState
+from voltha.protos.common_pb2 import OperStatus
+from voltha.protos.device_pb2 import Port
+
+log = structlog.get_logger()
+_verbose = False
+
+def hexstring(string):
+    return ":".join("{:02x}".format(ord(c)) for c in string)
+
+class ActivationManager(BaseOltAutomaton):
+
+    onu_id = None
+    serial_number = None
+    onu_session_id = None
+    port_id = None
+    channel_id = None
+
+    def parse_args(self, debug=0, store=0, **kwargs):
+        self.onu_id = kwargs.pop('onu_id')
+        self.serial_number = kwargs.pop('serial_number')
+        self.onu_session_id = kwargs.pop('onu_session_id')
+        self.port_id = self.onu_id
+        self.channel_id = kwargs.pop('channel_id')
+
+        if self.onu_id is None or self.serial_number is None or \
+                self.onu_session_id is None or self.channel_id is None:
+            raise ValueError('ONU is not well defined')
+
+        BaseOltAutomaton.parse_args(self, debug=debug, store=store, **kwargs)
+
+    """
+    States
+    """
+
+    @ATMT.state(initial=1)
+    def got_activation_event(self):
+        pass
+
+    @ATMT.state()
+    def wait_get_auth_mode(self):
+        pass
+
+    @ATMT.state()
+    def got_auth_mode(self):
+        pass
+
+    @ATMT.state()
+    def wait_omci_port_id(self):
+        pass
+
+    @ATMT.state()
+    def got_omci_port_id(self):
+        pass
+
+    @ATMT.state()
+    def wait_send_frame(self):
+        pass
+
+    @ATMT.state()
+    def wait_omci_get(self):
+        pass
+
+    @ATMT.state(final=1)
+    def end(self):
+        pass
+
+    @ATMT.state(error=1)
+    def error(self):
+        pass
+
+    """
+    Utility Methods
+    """
+
+    def create_port(self, pkt):
+        vendor = pkt['OmciGetResponse'].data['vendor_id']
+        port = Port(port_no=self.port_id,
+                    label="{} ONU".format(vendor),
+                    type=Port.ETHERNET_UNI,
+                    admin_state=AdminState.ENABLED,
+                    oper_status=OperStatus.ACTIVE
+        )
+        self.device.add_port(port)
+
+    def px(self, pkt):
+        return self.p(pkt, channel_id=self.channel_id,
+                      onu_id=self.onu_id, onu_session_id=self.onu_session_id)
+
+    def error(self, msg):
+        log.error(msg)
+        raise self.error()
+
+    """
+    Transitions
+    """
+
+    # Transition from got_activation_event
+    @ATMT.condition(got_activation_event)
+    def send_get_activation_auth_mode(self):
+        auth_mode = PAS5211MsgGetActivationAuthMode()
+        self.send(self.p(auth_mode))
+        raise self.wait_get_auth_mode()
+
+    # Transitions from wait_get_auth_mode
+    @ATMT.timeout(wait_get_auth_mode, 3)
+    def timeout_get_auth_mode(self):
+        self.error('Could not get auth mode for OLT {}; dropping activation event for {}'
+                   .format(self.target, hexstring(self.serial_number)))
+
+    @ATMT.receive_condition(wait_get_auth_mode)
+    def wait_for_get_auth_mode(self, pkt):
+        if PAS5211MsgGetActivationAuthModeResponse in pkt:
+            if pkt.mode == PON_ACTIVATION_AUTH_AUTO:
+                raise self.got_auth_mode()
+            else:
+                # TODO There may be something that can be done here.
+                # See line 2497 of PAS_onu_mode_change_thread.c
+                log.error('Got unknown auth mode {}; dropping activation event'.format(pkt.mode))
+                raise self.end()
+
+    # Transitions from got auth_mode
+    @ATMT.condition(got_auth_mode)
+    def send_omci_port_id(self):
+        omci_port_id = PAS5211MsgSetOnuOmciPortId(port_id=self.port_id, activate=PON_ENABLE)
+        self.send(self.px(omci_port_id))
+        raise self.wait_omci_port_id()
+
+    # Transitions from wait_omci_port_id
+    @ATMT.timeout(wait_omci_port_id, 3)
+    def timeout_omci_port_id(self):
+        self.error('Could not set omci port id for OLT {}; dropping activation event for {}'
+                   .format(self.target, hexstring(self.serial_number)))
+
+    @ATMT.receive_condition(wait_omci_port_id)
+    def wait_for_omci_port_id(self, pkt):
+        if pkt.opcode == PAS5211MsgSetOnuOmciPortIdResponse.opcode and \
+                pkt.onu_id == self.onu_id and pkt.onu_session_id == self.onu_session_id and \
+                pkt.channel_id == self.channel_id:
+            raise self.got_omci_port_id()
+
+    # Transitions from got_omci_port_id
+    @ATMT.condition(got_omci_port_id)
+    def send_omci_identity_frame(self):
+        # attr_mask |= OMCI_ATTR_BIT(OMCI_CIRCUIT_PACK_ATTR_VENDOR_ID);
+        #message.attributes_mask = 2048
+
+        # Entity_id
+        # equip_ind = OMCI_CIRCUIT_PACK_INTEGRATED_EQUIPMENT;
+        # slot_id = 257;
+        # entity_instance = ((equip_ind<<8) | slot_id
+        message = OmciGet(entity_class=CircuitPack.class_id, entity_id = 257,
+                          attributes_mask=2048)
+        #TODO fix transaction id
+        frame = OmciFrame(transaction_id=0, message_type=OmciGet.message_id,
+                          omci_message=message)
+        omci_frame = PAS5211MsgSendFrame(port_type=PON_PORT_PON, port_id=self.port_id,
+                                         management_frame=PON_ENABLE, frame=frame)
+
+        self.send(self.px(omci_frame))
+
+        raise self.wait_send_frame()
+
+    # Transitions from wait_send_frame
+    @ATMT.timeout(wait_send_frame, 3)
+    def timeout_send_frame(self):
+        self.error('Could not send omci to OLT {}; dropping activation event for {}'
+                   .format(self.target, hexstring(self.serial_number)))
+
+    @ATMT.receive_condition(wait_send_frame)
+    def wait_for_send_frame(self, pkt):
+        if pkt.opcode == PAS5211MsgSendFrameResponse.opcode:
+            raise self.wait_omci_get()
+
+    # Transitions from wait_omci_get
+    @ATMT.timeout(wait_omci_get, 3)
+    def timeout_send_frame(self):
+        self.error('Did not receive omci get event from OLT {}; dropping activation event for {}'
+                   .format(self.target, hexstring(self.serial_number)))
+
+    @ATMT.receive_condition(wait_omci_get)
+    def wait_for_omci_get(self, pkt):
+        if OmciGetResponse in pkt:
+            log.info("Activated {} ONT".format(pkt['OmciGetResponse'].data['vendor_id']))
+            self.create_port(pkt)
+            # TODO: create onu proxy device
+            raise self.end()
+
+
+class ActivationWatcher(BaseOltAutomaton):
+
+    """
+    States
+    """
+
+    @ATMT.state(initial=1)
+    def wait_onu_activation_event(self):
+        pass
+
+    """
+    Transitions
+    """
+
+    # Transitions from wait_onu_activation_event
+    @ATMT.receive_condition(wait_onu_activation_event)
+    def wait_for_onu_activation_event(self, pkt):
+        if PAS5211EventOnuActivation in pkt:
+            log.info('{} activated'.format(hexstring(pkt.serial_number)))
+            onu_activation = ActivationManager(iface=self.iface, target=self.target, comm=self.comm,
+                                               onu_id=pkt.onu_id, serial_number=pkt.serial_number,
+                                               onu_session_id=pkt.onu_session_id,
+                                               channel_id=pkt.channel_id, device=self.device)
+            onu_activation.runbg()
+            raise self.wait_onu_activation_event()
diff --git a/voltha/adapters/microsemi/BaseOltAutomaton.py b/voltha/adapters/microsemi/BaseOltAutomaton.py
new file mode 100644
index 0000000..8d5f3cf
--- /dev/null
+++ b/voltha/adapters/microsemi/BaseOltAutomaton.py
@@ -0,0 +1,61 @@
+#
+# 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 Automaton
+from scapy.sendrecv import sendp
+import structlog
+
+log = structlog.get_logger()
+_verbose = False
+
+class BaseOltAutomaton(Automaton):
+
+    comm = None
+    retry = 3
+    iface = None
+    target = None
+    verbose = None
+    adaptor_agent = None
+    device = None
+
+    def parse_args(self, debug=0, store=0, **kwargs):
+        self.comm = kwargs.pop('comm', None)
+        self.target = kwargs.pop('target', None)
+        self.device = kwargs.pop('device', None)
+        Automaton.parse_args(self, debug=debug, store=store, **kwargs)
+        self.verbose = kwargs.get('verbose', _verbose)
+        self.iface = kwargs.get('iface', "eth0")
+
+        if self.comm is None or self.target is None:
+            raise ValueError("Missing comm or target")
+
+    def my_send(self, pkt):
+        sendp(pkt, iface=self.iface, verbose=self.verbose)
+
+    def master_filter(self, pkt):
+        """
+        Anything coming from the OLT is for us
+        :param pkt: incoming packet
+        :return: True if it came from the olt
+        """
+        return pkt.src == self.target
+
+    def debug(self, lvl, msg):
+        if self.debug_level >= lvl:
+            log.info(msg)
+
+    def p(self, pkt, channel_id=-1, onu_id=-1, onu_session_id=-1):
+        return self.comm.frame(pkt, channel_id=channel_id,
+                               onu_id=onu_id, onu_session_id=onu_session_id)
diff --git a/voltha/adapters/microsemi/DeviceManager.py b/voltha/adapters/microsemi/DeviceManager.py
new file mode 100644
index 0000000..e30d292
--- /dev/null
+++ b/voltha/adapters/microsemi/DeviceManager.py
@@ -0,0 +1,105 @@
+#
+# 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 uuid import uuid4
+from ofagent.utils import mac_str_to_tuple
+import structlog
+from voltha.protos.common_pb2 import ConnectStatus, OperStatus
+from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
+from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_switch_features, OFPC_FLOW_STATS, OFPC_TABLE_STATS, \
+    OFPC_PORT_STATS, OFPC_GROUP_STATS, ofp_port, OFPPS_LIVE, OFPPF_10GB_FD, OFPPF_FIBER
+
+log = structlog.get_logger()
+
+class DeviceManager(object):
+
+    def __init__(self, device, adapter_agent):
+        self.device = device
+        self.adapter_agent = adapter_agent
+        self.logical_device = None
+
+    def update_device(self, pkt):
+
+        self.device.root = True
+        self.device.vendor = 'Celestica Inc.'
+        self.device.model = 'Ruby'
+        self.device.hardware_version = \
+            '{}.{}'.format(hex(pkt.major_hardware_version),
+                           pkt.minor_hardware_version)
+        self.device.firmware_version = '{}.{}.{}'.format(pkt.major_firmware_version,
+                                                         pkt.minor_firmware_version,
+                                                         pkt.build_firmware_version)
+        self.device.software_version = '0.0.1'
+        self.device.serial_number = self.device.mac_address
+        self.device.connect_status = ConnectStatus.REACHABLE
+        self.adapter_agent.update_device(self.device)
+
+    def create_logical_device(self):
+        log.info('create-logical-device')
+        # then shortly after we create the logical device with one port
+        # that will correspond to the NNI port
+        logical_device_id = uuid4().hex[:12]
+        ld = LogicalDevice(
+            id=logical_device_id,
+            datapath_id=int('0x' + logical_device_id[:8], 16), # from id
+            desc=ofp_desc(
+                mfr_desc=self.device.vendor,
+                hw_desc=self.device.hardware_version,
+                sw_desc=self.device.firmware_version,
+                serial_num=uuid4().hex,
+                dp_desc='n/a'
+            ),
+            switch_features=ofp_switch_features(
+                n_buffers=256,  # TODO fake for now
+                n_tables=2,  # TODO ditto
+                capabilities=(  # TODO and ditto
+                    OFPC_FLOW_STATS
+                    | OFPC_TABLE_STATS
+                    | OFPC_PORT_STATS
+                    | OFPC_GROUP_STATS
+                )
+            ),
+            root_device_id=self.device.id
+        )
+        self.adapter_agent.create_logical_device(ld)
+        self.logical_device = ld
+
+    def add_port(self, port):
+        self.adapter_agent.add_port(self.device.id, port)
+
+        cap = OFPPF_10GB_FD | OFPPF_FIBER
+        logical_port = LogicalPort(
+            id='uni',
+            ofp_port=ofp_port(
+                port_no=port.port_no,
+                hw_addr=mac_str_to_tuple(self.device.mac_address),
+                name='{}-{}'.format(port.label, port.port_no),
+                config=0,
+                state=OFPPS_LIVE,
+                curr=cap,
+                advertised=cap,
+                peer=cap,
+                curr_speed=OFPPF_10GB_FD,
+                max_speed=OFPPF_10GB_FD
+            )
+        )
+        log.info('Adding logical port to logical device')
+        self.adapter_agent.add_logical_port(self.logical_device.id,
+                                            logical_port)
+
+    def activate(self):
+        self.device.parent_id = self.logical_device.id
+        self.device.oper_status = OperStatus.ACTIVE
+        self.adapter_agent.update_device(self.device)
diff --git a/voltha/adapters/microsemi/OltStateMachine.py b/voltha/adapters/microsemi/OltStateMachine.py
index 3f389b8..7478dc2 100644
--- a/voltha/adapters/microsemi/OltStateMachine.py
+++ b/voltha/adapters/microsemi/OltStateMachine.py
@@ -13,9 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-from scapy.automaton import ATMT, Automaton
-from scapy.layers.l2 import sendp
+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, \
@@ -29,50 +29,16 @@
 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()
-_verbose = False
 
 
-
-class OltStateMachine(Automaton):
-
-    comm = None
-    retry = 3
-    iface = None
-    target = None
-    verbose = None
+class OltStateMachine(BaseOltAutomaton):
 
     send_state = []
     dba_needs_start = False
 
-    def parse_args(self, debug=0, store=0, **kwargs):
-        self.comm = kwargs.pop('comm', None)
-        self.target = kwargs.pop('target', None)
-        Automaton.parse_args(self, debug=debug, store=store, **kwargs)
-        self.verbose = kwargs.get('verbose', _verbose)
-        self.iface = kwargs.get('iface', "eth0")
-
-        if self.comm is None or self.target is None:
-            raise ValueError("Missing comm or target")
-
-    def my_send(self, pkt):
-        sendp(pkt, iface=self.iface, verbose=self.verbose)
-
-    def master_filter(self, pkt):
-        """
-        Anything coming from the OLT is for us
-        :param pkt: incoming packet
-        :return: True if it came from the olt
-        """
-        return pkt.src == self.target
-
-    def debug(self, lvl, msg):
-        if self.debug_level >= lvl:
-            log.info(msg)
-
-    def p(self, pkt, channel_id=-1):
-        return self.comm.frame(pkt, channel_id=channel_id)
-
     def check_channel_state(self):
         for i in CHANNELS:
                 if not self.send_state[i]:
@@ -225,6 +191,8 @@
     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))
@@ -286,7 +254,7 @@
 
         raise self.wait_olt_optics()
 
-    #Transitions from 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")
@@ -300,7 +268,7 @@
                 raise self.got_olt_optics()
             raise self.wait_olt_optics()
 
-    #Transitions from got_olt_optics
+    # Transitions from got_olt_optics
     @ATMT.condition(got_olt_optics)
     def send_olt_io_optics(self):
 
@@ -322,7 +290,7 @@
 
         raise self.wait_olt_io_optics()
 
-    #Transitions from 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")
@@ -488,6 +456,8 @@
         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()
 
@@ -500,7 +470,7 @@
     # Transitions from wait_keepalive
     @ATMT.timeout(wait_keepalive, 1)
     def timeout_keepalive(self):
-        log.error("OLT not responsing to keepalive; disconnecting")
+        log.error("OLT not responding to keep alive; disconnecting")
         raise self.ERROR()
 
     @ATMT.receive_condition(wait_keepalive)
diff --git a/voltha/adapters/microsemi/PAS5211.py b/voltha/adapters/microsemi/PAS5211.py
index 41f3882..ad85382 100644
--- a/voltha/adapters/microsemi/PAS5211.py
+++ b/voltha/adapters/microsemi/PAS5211.py
@@ -19,17 +19,19 @@
 PAS5211 scapy structs used for interaction with Ruby
 """
 from scapy.fields import LEShortField, Field, LEIntField, LESignedIntField, FieldLenField, FieldListField, PacketField, \
-    ByteField
+    ByteField, StrFixedLenField, ConditionalField
 from scapy.layers.l2 import Dot3, LLC
 from scapy.layers.inet import ARP
 from scapy.packet import Packet, bind_layers, split_layers
 from scapy.utils import lhex
 from scapy.volatile import RandSInt
 from scapy.layers.ntp import XLEShortField
+from voltha.extensions.omci.omci_frame import OmciFrame
 
 """
 PAS5211 Constants
 """
+#TODO get range from olt_version message
 CHANNELS=range(0,4)
 
 # from enum PON_true_false_t
@@ -212,6 +214,13 @@
 PON_OLT_HW_CLASSIFICATION_OTHER			           = 107
 PON_OLT_HW_CLASSIFICATION_LAST_RULE                = 108
 
+# from enum PON_activation_auth_type_t
+
+PON_ACTIVATION_AUTH_AUTO 			  				= 0
+PON_ACTIVATION_AUTH_HOST_CONTROLLED_SEPARATE_EVENTS = 1 # Host controlled: Separate events
+PON_ACTIVATION_AUTH_HOST_CONTROLLED_LUMPED_SN   	= 2 # Host controlled: Lumped-SN-Response
+PON_ACTIVATION_AUTH_REGISTRATION_ID_RAW   			= 3 # Registration-ID Raw
+PON_ACTIVATION_AUTH_REGISTRATION_ID_LEARN   		= 4  # Registration-ID Learn
 
 """
 Extra field structs
@@ -563,6 +572,84 @@
         LEIntField("dba_mode", None),
     ]
 
+class PAS5211MsgGetActivationAuthMode(PAS5211Msg):
+    opcode = 145
+    name = "PAS5211MsgGetActivationAuthMode"
+    fields_desc = [
+        LEShortField("nothing", 0) # no idea why this is here
+    ]
+
+class PAS5211MsgGetActivationAuthModeResponse(PAS5211Msg):
+    opcode = 10385
+    name = "PAS5211MsgGetActivationAuthMode"
+    fields_desc = [
+        LEShortField("mode", 0),
+        LEShortField("reserved", 0),
+    ]
+
+class PAS5211MsgSetOnuOmciPortId(PAS5211Msg):
+    opcode = 41
+    name = "PAS5211MsgSetOnuOmciPortId"
+    fields_desc = [
+        LEShortField("port_id", 0),
+        LEShortField("activate", PON_ENABLE)
+    ]
+
+class PAS5211MsgSetOnuOmciPortIdResponse(PAS5211Msg):
+    opcode = 10281
+    name = "PAS5211MsgSetOnuOmciPortIdResponse"
+    fields_desc = []
+
+
+class PAS5211Event(PAS5211Msg):
+    opcode = 12
+
+class PAS5211EventOnuActivation(PAS5211Event):
+    name = "PAS5211EventOnuActivation"
+    fields_desc = [
+        StrFixedLenField("serial_number", None, length=8),
+        LEIntField("equalization_period", None)
+    ]
+
+    """
+    Frame
+    """
+
+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),
+        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 PAS5211MsgSendFrameResponse(PAS5211Msg):
+    opcode = 10282
+    name = "PAS5211MsgSendFrameResponse"
+    fields_desc = []
+
+
+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)
+    ]
+
+
 """
 Bindings used for message processing
 """
@@ -576,4 +663,10 @@
 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
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetOltChannelActivationPeriodResponse, opcode=0x2800 | 11)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetActivationAuthModeResponse, opcode=0x2800 | 145)
+
+
+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)
\ No newline at end of file
diff --git a/voltha/adapters/microsemi/PAS5211_comm.py b/voltha/adapters/microsemi/PAS5211_comm.py
index 623b498..ffe4ebb 100644
--- a/voltha/adapters/microsemi/PAS5211_comm.py
+++ b/voltha/adapters/microsemi/PAS5211_comm.py
@@ -62,7 +62,8 @@
         self.seqgen = sequence_generator(init)
         self.src_mac = determine_src_mac(self.iface)
 
-    def frame(self, msg, channel_id=-1):
+    def frame(self, msg, channel_id=-1, onu_id=-1, onu_session_id=-1):
         return constructPAS5211Frames(msg, self.seqgen.next(), self.src_mac,
-                                           self.dst_mac, channel_id=channel_id)
+                                      self.dst_mac, channel_id=channel_id,
+                                      onu_id=onu_id, onu_session_id=onu_session_id)
 
diff --git a/voltha/adapters/microsemi/RubyAdapter.py b/voltha/adapters/microsemi/RubyAdapter.py
deleted file mode 100644
index 80edb8c..0000000
--- a/voltha/adapters/microsemi/RubyAdapter.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#
-# 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.
-#
-
-"""
-Microsemi/Celestica Ruby vOLTHA adapter.
-"""
-import structlog
-from twisted.internet import reactor
-
-
-
-from voltha.adapters.interface import IAdapterInterface
-from voltha.adapters.microsemi.OltStateMachine import OltStateMachine
-from voltha.adapters.microsemi.PAS5211_comm import PAS5211Communication
-from voltha.protos import third_party
-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' : 'eth3'}
-
-@implementer(IAdapterInterface)
-class RubyAdapter(object):
-    def __init__(self, args, config):
-        self.args = args
-        self.config = config
-        self.descriptor = None
-        self.comm = comm = PAS5211Communication(dst_mac=olt_conf['olts']['mac'],
-                                                iface=olt_conf['iface'])
-        self.olt = OltStateMachine(iface=olt_conf['iface'],
-                                   comm=comm,
-                                   target=olt_conf['olts']['mac'])
-
-
-    def start(self):
-        log.info('starting')
-        reactor.callLater(0, self.__init_olt)
-        log.info('started')
-        return self
-
-    def stop(self):
-        log.debug('stopping')
-        self.olt.stop()
-        log.info('stopped')
-        return self
-
-    def adapter_descriptor(self):
-        if self.descriptor is None:
-            self.descriptor = self.__obtain_descriptor()
-        return self.descriptor
-
-    def device_types(self):
-        return DeviceTypes(
-            items=[]  # TODO
-        )
-
-
-    def health(self):
-        return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
-
-    def change_master_state(self, master):
-        raise NotImplementedError()
-
-    def adopt_device(self, device):
-        raise NotImplementedError()
-
-    def abandon_device(self, device):
-        raise NotImplementedError(0)
-
-    def deactivate_device(self, device):
-        raise NotImplementedError()
-
-    ##
-    # Private methods
-    ##
-
-    def __init_olt(self):
-        self.olt.run()
-
-
-
-
diff --git a/voltha/adapters/microsemi/chat.py b/voltha/adapters/microsemi/chat.py
deleted file mode 100755
index f4902ed..0000000
--- a/voltha/adapters/microsemi/chat.py
+++ /dev/null
@@ -1,684 +0,0 @@
-#!/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, LLC, SNAP, Dot3
-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(Dot3,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 start(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/main.py b/voltha/adapters/microsemi/main.py
index 464f7af..7b279e7 100644
--- a/voltha/adapters/microsemi/main.py
+++ b/voltha/adapters/microsemi/main.py
@@ -20,7 +20,7 @@
 from common.utils.dockerhelpers import get_my_containers_name
 import os
 from twisted.internet import reactor
-from voltha.adapters.microsemi.RubyAdapter import RubyAdapter
+from voltha.adapters.microsemi.microsemi import RubyAdapter
 import yaml
 
 defs = dict(
diff --git a/voltha/adapters/microsemi/microsemi.py b/voltha/adapters/microsemi/microsemi.py
new file mode 100644
index 0000000..d59a994
--- /dev/null
+++ b/voltha/adapters/microsemi/microsemi.py
@@ -0,0 +1,136 @@
+#
+# 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.
+#
+
+"""
+Microsemi/Celestica Ruby vOLTHA adapter.
+"""
+from common.frameio.frameio import BpfProgramFilter, FrameIOManager
+from scapy.layers.l2 import Dot3
+import structlog
+from twisted.internet import reactor
+
+
+
+from voltha.adapters.interface import IAdapterInterface
+from voltha.adapters.microsemi.ActivationWatcher import ActivationWatcher
+from voltha.adapters.microsemi.DeviceManager import DeviceManager
+from voltha.adapters.microsemi.OltStateMachine import OltStateMachine
+from voltha.adapters.microsemi.PAS5211_comm import PAS5211Communication
+from voltha.protos import third_party
+from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
+from voltha.protos.common_pb2 import LogLevel
+from voltha.protos.device_pb2 import DeviceTypes, DeviceType
+from voltha.protos.health_pb2 import HealthStatus
+from voltha.registry import registry
+
+from zope.interface import implementer
+
+log = structlog.get_logger()
+_ = third_party
+
+
+@implementer(IAdapterInterface)
+class RubyAdapter(object):
+
+    name = "microsemi"
+
+    supported_device_types = [
+        DeviceType(
+            id='microsemi',
+            adapter=name,
+            accepts_bulk_flow_update=True
+        )
+    ]
+
+    def __init__(self, adaptor_agent, config):
+        self.adaptor_agent = adaptor_agent
+        self.config = config
+        self.descriptor = Adapter(
+            id=self.name,
+            vendor='Microsemi / Celestica',
+            version='0.1',
+            config=AdapterConfig(log_level=LogLevel.INFO)
+        )
+
+        self.interface = registry('main').get_args().interface
+
+    def start(self):
+        log.info('starting')
+        log.info('started')
+        return self
+
+    def stop(self):
+        log.debug('stopping')
+        # TODO Stop all OLTs
+        log.info('stopped')
+        return self
+
+    def adapter_descriptor(self):
+        return self.descriptor
+
+    def device_types(self):
+        return DeviceTypes(items=self.supported_device_types)
+
+    def health(self):
+        return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
+
+    def change_master_state(self, master):
+        raise NotImplementedError()
+
+    def adopt_device(self, device):
+        device_manager = DeviceManager(device, self.adaptor_agent)
+        target = device.mac_address
+        comm = PAS5211Communication(dst_mac=target, iface=self.interface)
+        olt = OltStateMachine(iface=self.interface, comm=comm,
+                              target=target, device=device_manager)
+        activation = ActivationWatcher(iface=self.interface, comm=comm,
+                                       target=target, device=device_manager)
+        reactor.callLater(0, self.__init_olt, olt, activation)
+
+        log.info('adopted-device', device=device)
+        # TODO store olt elements
+
+    def abandon_device(self, device):
+        raise NotImplementedError(0)
+
+    def deactivate_device(self, device):
+        raise NotImplementedError()
+
+    def update_flows_bulk(self, device, flows, groups):
+        log.debug('bulk-flow-update', device_id=device.id,
+                  flows=flows, groups=groups)
+
+    def send_proxied_message(self, proxy_address, msg):
+        log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
+
+    def receive_proxied_message(self, proxy_address, msg):
+        raise NotImplementedError()
+
+    def update_flows_incrementally(self, device, flow_changes, group_changes):
+        raise NotImplementedError()
+
+    ##
+    # Private methods
+    ##
+    def __init_olt(self, olt, activation_watch):
+        olt.runbg()
+        activation_watch.runbg()
+
+
+
+
+
+
diff --git a/voltha/adapters/microsemi/send.py b/voltha/adapters/microsemi/send.py
deleted file mode 100644
index a639aaa..0000000
--- a/voltha/adapters/microsemi/send.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from scapy.all import *
-from scapy.layers.l2 import Dot3
-
-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/adapters/microsemi/sniff.py b/voltha/adapters/microsemi/sniff.py
index 915efe6..2b9e03d 100755
--- a/voltha/adapters/microsemi/sniff.py
+++ b/voltha/adapters/microsemi/sniff.py
@@ -553,6 +553,67 @@
         LEShortField("mode", 0)
     ]
 
+class PAS5211MsgGetActivationAuthMode(PAS5211Msg):
+    opcode = 145
+    name = "PAS5211MsgGetActivationAuthMode"
+    fields_desc = [
+        LEShortField("nothing", 0) # no idea why this is here
+    ]
+
+class PAS5211MsgGetActivationAuthModeResponse(PAS5211Msg):
+    opcode = 10385
+    name = "PAS5211MsgGetActivationAuthMode"
+    fields_desc = [
+        LEShortField("mode", 0),
+        LEShortField("reserved", 0),
+    ]
+
+class PAS5211MsgSetOnuOmciPortId(PAS5211Msg):
+    opcode = 41
+    name = "PAS5211MsgSetOnuOmciPortId"
+    fields_desc = [
+        LEShortField("port_id", 0),
+        LEShortField("activate", PON_ENABLE)
+    ]
+
+class PAS5211MsgSetOnuOmciPortIdResponse(PAS5211Msg):
+    opcode = 10281
+    name = "PAS5211MsgSetOnuOmciPortIdResponse"
+    fields_desc = []
+
+
+class PAS5211MsgGetLogicalObjectStatus(PAS5211Msg):
+    opcode = 223
+    name = "PAS5211MsgGetLogicalObjectStatus"
+    fields_desc = [
+        LEIntField("type", None),
+        LEIntField("value", None)
+    ]
+
+class PAS5211MsgGetLogicalObjectStatusResponse(PAS5211Msg):
+    opcode = 10463
+    name = "PAS5211MsgGetLogicalObjectStatusResponse"
+    fields_desc = [
+        LEIntField("type", None),
+        LEIntField("value", None),
+        FieldLenField("return_length", None, fmt="<H", length_of="return_value"),
+        LEIntField("return_value", "")
+    ]
+
+class PAS5211MsgSetOnuAllocId(PAS5211Msg):
+    opcode = 8
+    name = "PAS5211MsgSetOnuAllocId"
+    fields_desc = [
+        LEShortField("alloc_id", None),
+        LEShortField("allocate", None)
+    ]
+
+class PAS5211MsgSetOnuAllocIdResponse(PAS5211Msg):
+    opcode = 10248
+    name = "PAS5211MsgSetOnuAllocIdResponse"
+    fields_desc = []
+
+
 
 class Frame(Packet):
     pass
@@ -566,9 +627,13 @@
         LEShortField("port_type", PON_PORT_PON),
         LEShortField("port_id", 0),
         LEShortField("management_frame", PON_FALSE),
-        PacketField("frame", None, Packet)
+        ConditionalField(PacketField("frame", None, Packet), lambda pkt: pkt.management_frame==PON_FALSE),
+        ConditionalField(PacketField("frame", None, OmciFrame), lambda pkt: pkt.management_frame==PON_TRUE)
     ]
 
+    def extract_padding(self, p):
+        return "", p
+
 
 class PAS5211MsgSendFrameResponse(PAS5211Msg):
     name = "PAS5211MsgSendFrameResponse"
@@ -645,6 +710,20 @@
 bind_layers(PAS5211MsgHeader, PAS5211MsgSendFrame, opcode=0x3000 | 42)
 bind_layers(PAS5211MsgHeader, PAS5211MsgSendFrameResponse, opcode=0x2800 | 42)
 
+
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetActivationAuthMode, opcode=0x3000 | 145)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetActivationAuthModeResponse, opcode=0x2800 | 145)
+
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetOnuOmciPortId, opcode=0x3000 | 41)
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetOnuOmciPortIdResponse, opcode=0x2800 | 41)
+
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetLogicalObjectStatus, opcode=0x3000 | 223)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetLogicalObjectStatusResponse, opcode=0x2800 | 223)
+
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetOnuAllocId, opcode=0x2800 | 8)
+bind_layers(PAS5211MsgHeader, PAS5211MsgSetOnuAllocIdResponse, opcode=0x2800 | 8)
+
+
 # bindings for events received
 bind_layers(PAS5211MsgHeader, PAS5211EventOnuActivation, opcode=0x2800 | 12, event_type=1)
 bind_layers(PAS5211MsgHeader, PAS5211EventFrameReceived, opcode=0x2800 | 12, event_type=10)