Stub Implementation of the Microsemi Adaptor.

Change-Id: Ida503793c263cedd3b1eed8a68d32b0ce99de992
diff --git a/voltha/adapters/microsemi/PAS5211.py b/voltha/adapters/microsemi/PAS5211.py
new file mode 100644
index 0000000..01a3b66
--- /dev/null
+++ b/voltha/adapters/microsemi/PAS5211.py
@@ -0,0 +1,113 @@
+#
+# 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.
+#
+
+
+"""
+PAS5211 scapy structs used for interaction with Ruby
+"""
+from scapy.fields import LEShortField, Field, LEIntField, LESignedIntField, FieldLenField, FieldListField
+from scapy.layers.l2 import Dot3, LLC
+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
+
+"""
+Extra field structs
+"""
+
+class LESignedShortField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "<h")
+
+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))
+
+"""
+PAS5211 Message structs
+"""
+
+class PAS5211Msg(Packet):
+    opcode = "Must be filled by subclass"
+    pass
+
+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 PAS5211MsgGetOltVersion(PAS5211Msg):
+    opcode = 3
+    name = "PAS5211MsgGetOltVersion"
+    fields_desc = [ ]
+
+    def answers(self, other):
+        return other.name == "PAS5211MsgGetOltVersionResponse"
+
+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)
+    ]
+
+    # FIXME: find a better way to pair messages together.
+    def answers(self, other):
+        return other.name == "PAS5211MsgGetOltVersion"
+
+"""
+Bindings used for message processing
+"""
+
+split_layers(Dot3, LLC)
+bind_layers(Dot3,PAS5211FrameHeader)
+bind_layers(PAS5211FrameHeader, PAS5211MsgHeader)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetOltVersionResponse, opcode=0x3800 | 3)
\ No newline at end of file
diff --git a/voltha/adapters/microsemi/PAS5211_comm.py b/voltha/adapters/microsemi/PAS5211_comm.py
new file mode 100644
index 0000000..a9fec63
--- /dev/null
+++ b/voltha/adapters/microsemi/PAS5211_comm.py
@@ -0,0 +1,72 @@
+#
+# 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.
+#
+import netifaces
+from scapy.layers.l2 import Dot3
+from scapy.sendrecv import srp1
+import structlog
+from voltha.adapters.microsemi.PAS5211 import PAS5211Msg, PAS5211MsgHeader, PAS5211FrameHeader
+
+log = structlog.get_logger()
+
+
+def constructPAS5211Frames(msg, seq, src_mac, dst_mac, 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 = Dot3(src=src_mac, dst=dst_mac) / frame_body
+
+    return frame
+
+
+def sequence_generator(init):
+    num = init
+    while True:
+        yield num
+        num += 1
+
+
+def determine_src_mac(iface):
+    if iface in netifaces.interfaces():
+        return netifaces.ifaddresses(iface)[netifaces.AF_LINK][0]['addr']
+    return None
+
+
+class PAS5211Communication(object):
+    def __init__(self, dst_mac, init = 0, iface = None):
+        self.iface = iface
+        self.dst_mac = dst_mac
+        self.seqgen = sequence_generator(init)
+        self.src_mac = determine_src_mac(self.iface)
+
+    def communicate(self, msg, **kwargs):
+        if self.src_mac is not None:
+            frame = constructPAS5211Frames(msg, self.seqgen.next(), self.src_mac,
+                                           self.dst_mac, **kwargs)
+            return srp1(frame, timeout=2, iface=self.iface)
+        else:
+            log.info('Unknown src mac for {}'.format(self.iface))
diff --git a/voltha/adapters/microsemi/PAS5211_constants.py b/voltha/adapters/microsemi/PAS5211_constants.py
new file mode 100644
index 0000000..d1d6846
--- /dev/null
+++ b/voltha/adapters/microsemi/PAS5211_constants.py
@@ -0,0 +1,180 @@
+#
+# 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.
+#
+
+"""
+PAS5211 Constants
+"""
+
+# 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
\ No newline at end of file
diff --git a/voltha/adapters/microsemi/RubyAdapter.py b/voltha/adapters/microsemi/RubyAdapter.py
new file mode 100644
index 0000000..93c5b07
--- /dev/null
+++ b/voltha/adapters/microsemi/RubyAdapter.py
@@ -0,0 +1,83 @@
+#
+# 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 voltha.adapters.interface import IAdapterInterface
+from voltha.adapters.microsemi.PAS5211 import PAS5211MsgGetOltVersion
+from voltha.adapters.microsemi.PAS5211_comm import PAS5211Communication
+#from voltha.protos.adapter_pb2 import Adapter, AdapterConfig, DeviceTypes
+#from voltha.protos.health_pb2 import HealthStatus
+from zope.interface import implementer
+
+log = structlog.get_logger()
+
+olt_conf = { 'olts' : { 'id' : 0, 'mac' : '00:0c:d5:00:01:00'}, 'iface' : 'eth3'}
+
+@implementer(IAdapterInterface)
+class RubyAdapter(object):
+    def __init__(self, config):
+        self.config = config
+#        self.descriptor = Adapter(
+#            id='ruby',
+#            config=AdapterConfig()
+#            # TODO
+#        )
+
+    def start(self):
+        log.debug('starting')
+        self.init_olt()
+        log.info('started')
+
+    def stop(self):
+        log.debug('stopping')
+        log.info('stopped')
+
+    def adapter_descriptor(self):
+        return self.descriptor
+
+    def device_types(self):
+        pass
+#        return DeviceTypes(
+#            items=[]  # TODO
+#        )
+
+    def health(self):
+        pass
+#        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()
+
+    def init_olt(self):
+        comm = PAS5211Communication(dst_mac=olt_conf['olts']['mac'], iface=olt_conf['iface'])
+        packet = comm.communicate(PAS5211MsgGetOltVersion())
+        log.info('{}'.format(packet.show()))
+
+
+if __name__ == '__main__':
+    RubyAdapter(None).start()
\ No newline at end of file
diff --git a/voltha/adapters/microsemi/chat.py b/voltha/adapters/microsemi/chat.py
index 80b026b..945533b 100755
--- a/voltha/adapters/microsemi/chat.py
+++ b/voltha/adapters/microsemi/chat.py
@@ -8,7 +8,7 @@
 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.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
@@ -580,7 +580,7 @@
 
 
 # bindings for messages received
-bind_layers(PAS5211Dot3, PAS5211FrameHeader)
+bind_layers(Dot3,PAS5211FrameHeader)
 bind_layers(PAS5211FrameHeader, PAS5211MsgHeader)
 bind_layers(PAS5211MsgHeader, PAS5211MsgGetProtocolVersionResponse, opcode=0x2800 | 2)
 bind_layers(PAS5211MsgHeader, PAS5211MsgGetOltVersionResponse, opcode=0x3800 | 3)
diff --git a/voltha/adapters/microsemi/send.py b/voltha/adapters/microsemi/send.py
index 8697ae7..a639aaa 100644
--- a/voltha/adapters/microsemi/send.py
+++ b/voltha/adapters/microsemi/send.py
@@ -1,4 +1,5 @@
 from scapy.all import *
+from scapy.layers.l2 import Dot3
 
 MIN_FRAME_SIZE = 60