VOL-669 Openolt adapter - packet-in processing

Change-Id: If41609c88061f730465bcab67c87b4f098112780
diff --git a/voltha/adapters/openolt/openolt_device.py b/voltha/adapters/openolt/openolt_device.py
index 8f73e45..70161f2 100644
--- a/voltha/adapters/openolt/openolt_device.py
+++ b/voltha/adapters/openolt/openolt_device.py
@@ -21,6 +21,7 @@
 import time
 
 from twisted.internet import reactor
+from scapy.layers.l2 import Ether
 
 from voltha.protos.device_pb2 import Port, Device
 from voltha.protos.common_pb2 import OperStatus, AdminState, ConnectStatus
@@ -42,6 +43,41 @@
 Onu = collections.namedtuple("Onu", ["intf_id", "onu_id"])
 
 """
+GEM port id
+
+     12             5          0
+    +----------------+----------+
+    |   onu id       |  svc id  |
+    +----------------+----------+
+
+- onu id field is 8 bits (256 ONUs per PON).
+- svc id is used to differentiate multiple GEM ports of an ONU.
+  This could be a LAN/UNI port index, GEM ID, queue ID,
+  traffic class ID, or some other service profile information.
+  svc id is 5 bits (32 GEM ports per ONU)
+
+Logical (OF) UNI port number
+
+    15             9                0
+    +--+------------+----------------+
+    |0 |  pon id    |    onu id      |
+    +--+------------+----------------+
+
+- pon id is a zero based id representing a pon interface
+  This is usually the pon interface id assigned by the hardware
+- Note that no LAN (UNI) port information is included.
+  Do we represent physical LAN/UNI ports as OF ports?
+  Or does it make more sense to represent GEM ports as OF ports?
+
+Logical (OF) NNI port number
+
+     15                              0
+    +--+------------------------------+
+    |1 |           nni  id            |
+    +--+------------------------------+
+"""
+
+"""
 OpenoltDevice represents an OLT.
 """
 class OpenoltDevice(object):
@@ -57,7 +93,6 @@
         self.oper_state = 'unknown'
         self.nni_oper_state = dict() #intf_id -> oper_state
         self.onus = {} # Onu -> serial_number
-        self.uni_port_num = 20 # FIXME
 
         # Create logical device
         ld = LogicalDevice(
@@ -116,6 +151,8 @@
                 reactor.callFromThread(self.onu_indication, ind.onu_ind)
             elif ind.HasField('omci_ind'):
                 reactor.callFromThread(self.omci_indication, ind.omci_ind)
+            elif ind.HasField('pkt_ind'):
+                reactor.callFromThread(self.packet_indication, ind.pkt_ind)
 
     def olt_indication(self, olt_indication):
 	self.log.debug("olt indication", olt_ind=olt_indication)
@@ -186,9 +223,8 @@
 	    self.log.info("onu activation in progress",
                 intf_id=onu_disc_indication.intf_id, onu_id=onu_id)
 
-    def _get_next_uni_port(self):
-        self.uni_port_num += 1
-        return self.uni_port_num
+    def mk_uni_port_num(self, intf_id, onu_id, uni_id):
+        return intf_id << 9 | onu_id << 3 | uni_id
 
     def onu_indication(self, onu_indication):
 
@@ -219,7 +255,7 @@
         #
         # v_enet create (olt)
         #
-        uni_no = self._get_next_uni_port()
+        uni_no = self.mk_uni_port_num(onu_indication.intf_id, onu_indication.onu_id, 0)
         uni_name = self.port_name(uni_no, Port.ETHERNET_UNI)
 	self.adapter_agent.add_port(
             self.device_id,
@@ -248,8 +284,13 @@
                'event_data':{'gemport_id':gemport_id}}
         self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
 
-    def mk_gemport_id(self, onu_id):
-        return 1023 + onu_id # FIXME
+    def mk_gemport_id(self, onu_id, uni_idx=0):
+        # FIXME - driver should do prefixing 1 << 13 as its Maple specific
+        return 1<<13 | onu_id<<5 | uni_idx
+
+    def onu_id_from_gemport_id(self, gemport_id):
+        # FIXME - driver should remove the (1 << 13) prefix as its Maple specific
+        return (gemport_id & ~(1<<13)) >> 5
 
     def mk_alloc_id(self, onu_id):
         return 1023 + onu_id # FIXME
@@ -266,6 +307,25 @@
             onu_device.proxy_address,
             omci_indication.pkt)
 
+    def packet_indication(self, pkt_indication):
+
+        self.log.debug("packet indication", intf_id=pkt_indication.intf_id,
+                gemport_id=pkt_indication.gemport_id,
+                flow_id=pkt_indication.flow_id)
+
+        onu_id = self.onu_id_from_gemport_id(pkt_indication.gemport_id)
+        logical_port_num = self.mk_uni_port_num(pkt_indication.intf_id, onu_id, 0)
+
+        pkt = Ether(pkt_indication.pkt)
+        kw = dict(
+                  logical_device_id=self.logical_device_id,
+                  logical_port_no=logical_port_num,
+                  )
+        self.adapter_agent.send_packet_in(packet=str(pkt), **kw)
+
+    def packet_out(self, egress_port, msg):
+        pass
+
     def activate_onu(self, intf_id, onu_id, serial_number):
 
         self.log.info("activate onu", intf_id=intf_id, onu_id=onu_id,
@@ -564,6 +624,7 @@
                         self.log.error('unsupported-action-type',
                                        action_type=action.type, in_port=_in_port)
 
+                # FIXME - Why ignore downstream flows?
                 if is_down_stream is False:
                     intf_id, onu_id = self.parse_port_no(classifier_info['in_port'])
                     self.divide_and_add_flow(onu_id, intf_id, classifier_info, action_info)
@@ -573,10 +634,9 @@
     def parse_port_no(self, port_no):
         return 0, 1 # FIXME
 
-    # This function will divide the upstream flow into both
-    # upstreand and downstream flow, as broadcom devices
-    # expects down stream flows to be added to handle
-    # packet_out messge from controller.
+    # FIXME - No need for divide_and_add_flow if
+    # both upstream and downstream flows
+    # are acted upon (not just upstream flows).
     def divide_and_add_flow(self, onu_id, intf_id, classifier, action):
         if 'ip_proto' in classifier:
             if classifier['ip_proto'] == 17: