1) VOL-252:Process flow rules and provision BAL for HSIA, DHCP, EAPOL flows
2) Added support of XPON GEM port and tcont commands in broadcom onu adapter
3) Fixed the issues with ONOS integration
4) Added Multiple ONU support

Note: Right now only one GemPort is supported, and use traffic_class 2 while adding a gem port.

Below are the xpon commands used while testing.

preprovision_olt -t asfvolt16_olt -H 192.168.140.71:50060
enable
xpon
channel_group create -n "Manhattan" -d "Channel Group for Manhattan" -a up -p 100 -s 000000 -r raman_none
channel_partition create -n "WTC" -d "Channel Partition for World Trace Center in Manhattan" -a up -r 20 -o 0 -f false -m false -u serial_number -c "Manhattan"
channel_pair create -n "PON port" -d "Channel Pair for Freedom Tower in WTC" -a up -r down_10_up_10 -t channelpair -g "Manhattan" -p "WTC" -i 0 -o class_a
traffic_descriptor_profile create -n "TDP 1" -f 100000 -a 500000 -m 1000000 -p 1 -w 1 -e additional_bw_eligibility_indicator_none
channel_termination create -i 0001730096b60157 -n "PON port" -d "Channel Termination for Freedom Tower" -a up -r "PON port" -c "AT&T WTC OLT"

vont_ani create -n "ATT Golden User" -d "ATT Golden User in Freedom Tower" -a up -p "WTC" -s "BRCM12345678" -r "PON port" -o 1
ont_ani create -n "ATT Golden User" -d "ATT Golden User in Freedom Tower" -a up -u true -m false
tcont create -n "TCont 1" -r "ATT Golden User" -t "TDP 1"
v_enet create -n "uni-1" -d "Ethernet port - 1" -a up -r "ATT Golden User"
gem_port create -n "Gemport 1" -r "uni-1" -c 2 -a true -t "TCont 1"

vont_ani create -n "ATT Silver User" -d "ATT Silver User in Freedom Tower" -a up -p "WTC" -s "BRCM12345679" -r "PON port" -o
2
ont_ani create -n "ATT Silver User" -d "ATT Silver User in Freedom Tower" -a up -u true -m false
tcont create -n "TCont 2" -r "ATT Silver User" -t "TDP 1"

v_enet create -n "uni-2" -d "Ethernet port - 2" -a up -r "ATT Silver User"
gem_port create -n "Gemport 2" -r "uni-2" -c 2 -a true -t "TCont 2"

Change-Id: I3b2b9d2d7be2adfb33b8d06d30a743477ea90ea9
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
index 651061d..0f5e297 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
@@ -37,7 +37,7 @@
 from voltha.protos.device_pb2 import Port
 from voltha.protos.common_pb2 import AdminState
 from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
-from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
+from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPS_LINK_DOWN, \
     OFPPF_1GB_FD, OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, \
     OFPC_FLOW_STATS, ofp_switch_features, ofp_desc, ofp_port, \
     OFPXMC_OPENFLOW_BASIC
@@ -56,43 +56,56 @@
 from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
     MulticastDistributionSetData
 import time
+import binascii
 
-ASFVOLT_NNI_PORT = 50
+ASFVOLT_NNI_PORT = 129
 # ASFVOLT_NNI_PORT needs to be other than pon port value.
 # Edgecore OLT assigns PONport between 0 to 15, hence
-# having a value 50 for NNI port to avoid collision.
+# having a value 129 for NNI port to avoid collision.
 # TODO: VLAN ID needs to come from some sort of configuration.
+ASFVOLT16_DEFAULT_VLAN = 4091
 PACKET_IN_VLAN = 4091
 is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
     PACKET_IN_VLAN))
 
 ASFVOLT_EAPOL_ID = 1
-ASFVOLT_DHCP_UNTAGGED_ID = 2
-ASFVOLT_DHCP_TAGGED_ID = 3
-ASFVOLT_IGMP_UNTAGGED_ID = 4
-ASFVOLT_IGMP_TAGGED_ID = 5
-ASFVOLT_FIRMWARE_ID = 6
-ASFVOLT_ARP_ID = 7
-ASFVOLT_HSIA_ID = 8
-ASFVOLT_DNS_ID = 9
+ASFVOLT_DOWNLINK_EAPOL_ID = 2
 
-ASFVOLT_DOWNLINK_EAPOL_ID = 10
-ASFVOLT_DOWNLINK_DHCP_UNTAGGED_ID = 11
-ASFVOLT_DOWNLINK_DHCP_TAGGED_ID = 12
-ASFVOLT_DOWNLINK_IGMP_UNTAGGED_ID = 13
-ASFVOLT_DOWNLINK_IGMP_TAGGED_ID = 14
-ASFVOLT_DOWNLINK_FIRMWARE_ID = 15
-ASFVOLT_DOWNLINK_ARP_ID = 16
-ASFVOLT_DOWNLINK_HSIA_ID = 17
-ASFVOLT_DOWNLINK_DNS_ID = 18
+ASFVOLT_EAPOL_ID_DATA_VLAN = 3
+ASFVOLT_DOWNLINK_EAPOL_ID_DATA_VLAN = 4
 
+ASFVOLT_DHCP_TAGGED_ID = 5
+ASFVOLT_DOWNLINK_DHCP_TAGGED_ID = 6
+
+ASFVOLT_IGMP_TAGGED_ID = 7
+ASFVOLT_DOWNLINK_IGMP_TAGGED_ID = 8
+
+ASFVOLT_FIRMWARE_ID = 9
+ASFVOLT_DOWNLINK_FIRMWARE_ID = 10
+
+ASFVOLT_ARP_ID = 11
+ASFVOLT_DOWNLINK_ARP_ID = 12
+
+ASFVOLT_HSIA_ID = 13
+ASFVOLT_DOWNLINK_HSIA_ID = 14
+
+ASFVOLT_DNS_ID = 15
+ASFVOLT_DOWNLINK_DNS_ID = 16
+
+
+class FlowInfo(object):
+
+    def __init__(self):
+        self.classifier = dict()
+        self.action = dict()
+        self.traffic_class = None
 
 class VEnetHandler(object):
 
     def __init__(self):
         self.v_enet = VEnetConfig()
         self.gem_ports = dict()
-
+        self.pending_flows = []
 
 class VOntAniHandler(object):
 
@@ -169,7 +182,7 @@
         self.pm_metrics = None
         self.heartbeat_count = 0
         self.heartbeat_miss = 0
-        self.heartbeat_interval = 120
+        self.heartbeat_interval = 12000000
         self.heartbeat_failed_limit = 3
 
     def __del__(self):
@@ -182,10 +195,17 @@
         self.uni_port_num += 1
         return self.uni_port_num
 
-    def get_venet(self, name):
+    def get_venet(self, **kwargs):
+        name = kwargs.pop('name', None)
+        gem_port_id = kwargs.pop('gem_port_id', None)
         for key, v_enet in self.v_enets.items():
-            if key == name:
-                return v_enet
+            if name is not None:
+                if key == name:
+                    return v_enet
+            if gem_port_id is not None:
+                for gem_key, gem_port in v_enet.gem_ports.items():
+                    if gem_port_id == gem_port.gemport_id:
+                        return v_enet
         return None
 
     def get_v_ont_ani(self, name):
@@ -230,7 +250,57 @@
         # OnuID, IntfId, id
         # BAL accepts flow_id till 16384. So we are
         # using only onu_id and id to generate flow ID.
-        return ((onu_id << 13) | id)
+        return ((onu_id << 5) | id)
+
+    def get_uni_port(self, device_id):
+        ports = self.adapter_agent.get_ports(device_id, Port.ETHERNET_UNI)
+        if ports:
+            # For now, we use on one uni port
+            return ports[0]
+        return None
+
+    def store_flows(self, uplink_classifier, uplink_action, 
+                    v_enet, traffic_class):
+        flow = FlowInfo()
+        flow.classifier = dict(uplink_classifier)
+        flow.action = dict(uplink_action)
+        flow.traffic_class = traffic_class
+        v_enet.pending_flows.append(flow)
+        return None
+
+    def add_pending_flows(self, v_enet, traffic_class):
+        for flow in v_enet.pending_flows[:]:
+            if flow.traffic_class == traffic_class:
+                self.divide_and_add_flow(v_enet,
+                                         flow.classifier,
+                                         flow.action)
+                v_enet.pending_flows.remove(flow)
+        return
+
+    def get_logical_port_using_gem_port(self, gem_port_id):
+        logical_port = None
+        v_enet = self.get_venet(gem_port_id=gem_port_id)
+        if v_enet is None:
+            self.log.error('Failed-to-get-v-enet', gem_port_id=gem_port_id)
+            return
+
+        v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
+        if v_ont_ani is None:
+            self.log.info('Failed-to-get-v_ont_ani',
+                          v_ont_ani=v_enet.v_enet.data.v_ontani_ref)
+            return
+
+        onu_device = self.adapter_agent.get_child_device(
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+        if onu_device is None:
+            self.log.info('Failed-to-get-onu-device',
+                          onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            return
+
+        uni = self.get_uni_port(onu_device.id)
+        if uni is not None:
+           logical_port = (onu_device.proxy_address.channel_id + uni.port_no)
+        return logical_port
 
     def activate(self, device):
 
@@ -267,12 +337,13 @@
 
         device = self.adapter_agent.get_device(device.id)
         device.parent_id = self.logical_device_id
-        device.connect_status = ConnectStatus.REACHABLE
+        #device.connect_status = ConnectStatus.REACHABLE
+        device.connect_status = ConnectStatus.UNREACHABLE
         device.oper_status = OperStatus.ACTIVATING
         self.adapter_agent.update_device(device)
 
     @inlineCallbacks
-    def heartbeat(self, device_id, state='run'):
+    def heartbeat(self, device_id, state = 'run'):
         self.log.debug('olt-heartbeat', device=device_id, state=state,
                        count=self.heartbeat_count)
 
@@ -401,9 +472,12 @@
         else:
            self.log.info('Lost Connectivity to OLT')
 
+
+        '''
         reactor.callLater(self.pm_metrics.default_freq/10,
                           self._req_pm_counter_from_device_in_loop,
                           device)
+        '''
 
     def update_pm_config(self, device, pm_config):
         self.log.info("update-pm-config", device=device, pm_config=pm_config)
@@ -420,7 +494,7 @@
                  priority=priority,
                  alarm_data=alarm_data)
 
-        id = 'voltha.{}.{}.{}'.format(self.adapter_name,
+        id = 'voltha.{}.{}.{}'.format(self.adapter.name,
                                      _device_id, _object)
         description = '{} Alarm - {} - {}'.format(_object.upper(),
                                       alarm.upper(),
@@ -548,7 +622,8 @@
                 mfr_desc='cord project',
                 hw_desc='n/a',
                 sw_desc='logical device for Edgecore ASFvOLT16 OLT',
-                serial_num=uuid4().hex,
+                #serial_num=uuid4().hex,
+                serial_num=self.host_and_port,
                 dp_desc='n/a'
             ),
             switch_features=ofp_switch_features(
@@ -580,11 +655,12 @@
             return
 
         ofp = ofp_port(
-            port_no=0,  # is 0 OK?
+            port_no=port_no,  # is 0 OK?
             hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % 129),
             name=label,
             config=0,
-            state=OFPPS_LIVE,
+            #state=OFPPS_LIVE,
+            state=OFPPS_LINK_DOWN,
             curr=cap,
             advertised=cap,
             peer=cap,
@@ -601,6 +677,21 @@
 
         self.adapter_agent.add_logical_port(logical_device_id, logical_port)
 
+    def update_logical_port(self, port_no, port_type, state):
+        self.log.info('updating-logical-port', port_no=port_no,
+                      port_type=port_type, device_id=self.device_id,
+                      logical_device_id=self.logical_device_id)
+        if port_type is Port.ETHERNET_NNI:
+            label = 'nni'
+        else:
+            self.log.error('invalid-port-type', port_type=port_type)
+            return
+        logical_port = self.adapter_agent.get_logical_port(self.logical_device_id,
+                                                           label)
+        logical_port.ofp_port.state = state
+        self.adapter_agent.update_logical_port(self.logical_device_id,
+                                               logical_port)
+
     def handle_access_term_ind(self, ind_info):
         device = self.adapter_agent.get_device(self.device_id)
         if ind_info['activation_successful'] is True:
@@ -610,6 +701,8 @@
             device.oper_status = OperStatus.ACTIVE
             device.reason = 'OLT activated successfully'
             self.adapter_agent.update_device(device)
+            self.update_logical_port(ASFVOLT_NNI_PORT, Port.ETHERNET_NNI,
+                                     OFPPS_LIVE)
             self.log.info('OLT activation complete')
 
             #heart beat - To health checkup of OLT
@@ -895,12 +988,13 @@
             if data.name in v_enet.gem_ports:
                 self.log.info('Gem-port-info-is-already-present',
                               VEnet=v_enet, gem_info=data)
-            if data.gemport_id > 9212:
+            if data.gemport_id > 9215:
                 raise Exception('supported range for '
-                                'gem-port is from 1024 to 4099')
+                                'gem-port is from 1024 to 9215')
             gem_port = GemportsConfigData()
             gem_port.CopyFrom(data)
             v_enet.gem_ports[data.name] = gem_port
+            self.add_pending_flows(v_enet, gem_port.traffic_class)
         else:
             self.log.info('VEnet-is-not-configured-yet.',
                           gem_port_info=data)
@@ -919,41 +1013,68 @@
 
     def handle_packet_in(self, ind_info):
         self.log.info('Received Packet-In', ind_info=ind_info)
-
+        logical_port = self.get_logical_port_using_gem_port(ind_info['svc_port'])
         pkt = Ether(ind_info['packet'])
+        kw = dict(
+                  logical_device_id=self.logical_device_id,
+                  logical_port_no=logical_port,
+                  )
+        self.log.info('sending-packet-in', **kw)
+        self.adapter_agent.send_packet_in(packet=str(pkt), **kw)
+
+    def packet_out(self, egress_port, msg):
+        pkt_info = dict()
+        pkt = Ether(msg)
+        self.log.info('received-packet-out-from-of-agent',
+                      egress_port=egress_port,
+                      packet=str(pkt).encode("HEX"))
+
         if pkt.haslayer(Dot1Q):
             outer_shim = pkt.getlayer(Dot1Q)
             if isinstance(outer_shim.payload, Dot1Q):
                 inner_shim = outer_shim.payload
-                cvid = inner_shim.vlan
-                logical_port = cvid
-                popped_frame = (
-                    Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
-                    inner_shim.payload
+                payload = (
+                    Ether(src=pkt.src, dst=pkt.dst, type=outer_shim.type) /
+                    outer_shim.payload
                 )
-                kw = dict(
-                    logical_device_id=self.logical_device_id,
-                    logical_port_no=logical_port,
-                )
-                self.log.info('sending-packet-in', **kw)
-                self.adapter_agent.send_packet_in(
-                    packet=str(popped_frame), **kw)
+            else:
+                payload = pkt
+        else:
+            payload = pkt
 
-        reactor.callLater(1, self.process_packet_in)
+        self.log.info('sending-packet-to-device',
+                      egress_port=egress_port,
+                      packet=str(payload).encode("HEX"))
+        send_pkt = binascii.unhexlify(str(payload).encode("HEX"))
 
-    def packet_out(self, egress_port, msg):
-        self.log.info('sending-packet-out', egress_port=egress_port,
-                      msg=hexify(msg))
-        pkt = Ether(msg)
-        out_pkt = (
-            Ether(src=pkt.src, dst=pkt.dst) /
-            Dot1Q(vlan=PACKET_IN_VLAN) /
-            Dot1Q(vlan=egress_port, type=pkt.type) /
-            pkt.payload
-        )
-        onu_id = 1
-        # TODO: Need to retrieve the correct destination onu_id
-        self.bal.packet_out(onu_id, egress_port, str(out_pkt))
+        if egress_port == ASFVOLT_NNI_PORT:
+            port_id = 'nni'
+            pkt_info['dest_type'] = 'nni'
+            pkt_info['intf_id'] = 0
+        else:
+            port_id = 'uni-{}'.format(egress_port)
+            logical_port = None
+            logical_port = \
+                self.adapter_agent.get_logical_port(self.logical_device_id,
+                                                    port_id)
+            if logical_port is None:
+                self.log.info('Unable-to-find-logical-port-info',
+                              logical_port_number=egress_port)
+                return
+            onu_device = None
+            onu_device = self.adapter_agent.get_device(logical_port.device_id)
+            if onu_device is None:
+                self.log.info('Unable-to-find-onu_device-info',
+                              onu_device_id=logical_port.device_id)
+                return
+            pkt_info['intf_id'] = onu_device.proxy_address.channel_id
+            pkt_info['onu_id'] = onu_device.proxy_address.onu_id
+
+            pkt_info['dest_type'] = 'onu'
+            #pkt_info['dest_type'] = 'gem_port'
+            if pkt_info['dest_type'] == 'gem_port':
+                pkt_info['gem_port'] = 1024
+        self.bal.packet_out(send_pkt, pkt_info)
 
     def update_flow_table(self, flows):
         device = self.adapter_agent.get_device(self.device_id)
@@ -1082,7 +1203,20 @@
                                        action_type=action.type, in_port=_in_port)
 
                 if is_down_stream is False:
-                    self.divide_and_add_flow(classifier_info, action_info)
+                    found = False
+                    ports = self.adapter_agent.get_ports(self.device_id,
+                                                         Port.ETHERNET_UNI)
+                    for port in ports:
+                        if port.port_no == classifier_info['in_port']:
+                            found = True
+                            break
+                    if found is True:
+                        v_enet = self.get_venet(name=port.label)
+                    else:
+                        self.log.error('Failed to get v_enet info',
+                                       in_port=classifier_info['in_port'])
+                        return
+                    self.divide_and_add_flow(v_enet, classifier_info, action_info)
             except Exception as e:
                 self.log.exception('failed-to-install-flow', e=e, flow=flow)
 
@@ -1090,19 +1224,7 @@
     # upstreand and downstream flow, as broadcom devices
     # expects down stream flows to be added to handle
     # packet_out messge from controller.
-    def divide_and_add_flow(self, classifier, action):
-        found = False
-        ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_UNI)
-        for port in ports:
-            if port.port_no == classifier['in_port']:
-                found = True
-                break
-        if found is True:
-            v_enet = self.get_venet(port.label)
-        else:
-            self.log.error('Failed to get v_enet info',
-                           in_port=classifier['in_port'])
-            return
+    def divide_and_add_flow(self, v_enet, classifier, action):
         if 'ip_proto' in classifier:
             if classifier['ip_proto'] == 17:
                 self.log.error('Addtion of DHCP flows are defferd')
@@ -1128,19 +1250,41 @@
                 # self.log.error('Addtion of EAPOL flows are defferd')
                 self.add_eapol_flow(classifier, action,
                                     v_enet, ASFVOLT_EAPOL_ID,
-                                    ASFVOLT_DOWNLINK_EAPOL_ID)
+                                    ASFVOLT_DOWNLINK_EAPOL_ID,
+                                    ASFVOLT16_DEFAULT_VLAN)
         elif 'push_vlan' in action:
+
             self.prepare_and_add_dhcp_flow(classifier, action, v_enet,
                                            ASFVOLT_DHCP_TAGGED_ID,
                                            ASFVOLT_DOWNLINK_DHCP_TAGGED_ID)
+
+            #self.del_flow(v_enet, ASFVOLT_EAPOL_ID, ASFVOLT_DOWNLINK_EAPOL_ID)
+            self.prepare_and_add_eapol_flow(classifier, action, v_enet,
+                                           ASFVOLT_EAPOL_ID_DATA_VLAN,
+                                           ASFVOLT_DOWNLINK_EAPOL_ID_DATA_VLAN)
             self.add_data_flow(classifier, action, v_enet)
         else:
             self.log.info('Invalid-flow-type-to-handle',
                           classifier=classifier,
                           action=action)
 
+
+    def prepare_and_add_eapol_flow(self, data_classifier, data_action,
+                                  v_enet, eapol_id, downlink_eapol_id):
+        eapol_classifier = dict()
+        eapol_action = dict()
+        eapol_classifier['eth_type'] = 0x888e
+        eapol_classifier['pkt_tag_type'] = 'single_tag'
+        #eapol_classifier['vlan_vid'] = data_classifier['vlan_vid']
+
+        eapol_action['vlan_push'] = True
+        eapol_action['vlan_vid'] = data_action['vlan_vid']
+        self.add_eapol_flow(eapol_classifier, eapol_action, v_enet,
+                           eapol_id, downlink_eapol_id, data_classifier['vlan_vid'])
+
+
     def add_eapol_flow(self, uplink_classifier, uplink_action,
-                       v_enet, uplink_eapol_id, downlink_eapol_id):
+                       v_enet, uplink_eapol_id, downlink_eapol_id, vlan_id):
         downlink_classifier = dict(uplink_classifier)
         downlink_action = dict(uplink_action)
         # To-Do For a time being hard code the traffic class value.
@@ -1148,6 +1292,12 @@
         gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
         if gem_port is None:
             self.log.info('Failed-to-get-gemport',)
+            # To-Do: If Gemport not found, then flow failure indication
+            # should be sent to controller. For now, not sure how to
+            # send that to controller. so store the flows in v_enet
+            # and add it when gem port is created
+            self.store_flows(uplink_classifier, uplink_action,
+                             v_enet, traffic_class=2)
             return
         v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
         if v_ont_ani is None:
@@ -1170,7 +1320,9 @@
                                    onu_device.proxy_address.channel_id,
                                    uplink_eapol_id)
         # Add Upstream EAPOL Flow.
-        uplink_classifier['pkt_tag_type'] = 'untagged'
+        #uplink_classifier['pkt_tag_type'] = 'untagged'
+        uplink_classifier['pkt_tag_type'] = 'single_tag'
+        uplink_classifier['vlan_vid'] = vlan_id
         uplink_action.clear()
         uplink_action['trap_to_host'] = True
         try:
@@ -1189,7 +1341,7 @@
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
             # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
+            time.sleep(0.1)
         except Exception as e:
             self.log.exception('failed-to-install-Upstream-EAPOL-flow', e=e,
                                classifier=uplink_classifier,
@@ -1202,7 +1354,9 @@
                                             onu_device.proxy_address.channel_id,
                                             downlink_eapol_id)
         is_down_stream = True
-        downlink_classifier['pkt_tag_type'] = 'untagged'
+        #downlink_classifier['pkt_tag_type'] = 'untagged'
+        downlink_classifier['pkt_tag_type'] = 'single_tag'
+        downlink_classifier['vlan_vid'] = vlan_id
         try:
             self.log.info('Adding-Downstream-EAPOL-flow',
                           classifier=downlink_classifier,
@@ -1217,7 +1371,7 @@
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
             # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
+            time.sleep(0.1)
         except Exception as e:
             self.log.exception('failed-to-install-downstream-EAPOL-flow', e=e,
                                classifier=downlink_classifier,
@@ -1231,7 +1385,7 @@
         dhcp_action = dict()
         dhcp_classifier['ip_proto'] = 17
         dhcp_classifier['udp_src'] = 68
-        dhcp_classifier['udp_dsct'] = 67
+        dhcp_classifier['udp_dst'] = 67
         dhcp_classifier['pkt_tag_type'] = 'single_tag'
         dhcp_classifier['vlan_vid'] = data_classifier['vlan_vid']
         dhcp_action['vlan_push'] = True
@@ -1248,7 +1402,9 @@
         # Need to know how to get the traffic class info from flows.
         gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
         if gem_port is None:
-            self.log.error('Failed-to-get-gemport')
+            self.log.info('Failed-to-get-gemport')
+            self.store_flows(uplink_classifier, uplink_action, 
+                             v_enet, traffic_class=2)
             return
         v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
         if v_ont_ani is None:
@@ -1289,7 +1445,7 @@
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
             # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
+            time.sleep(0.1)
 
         except Exception as e:
             self.log.exception('failed-to-install-dhcp-upstream-flow', e=e,
@@ -1304,10 +1460,17 @@
 
         if dhcp_id == ASFVOLT_DHCP_TAGGED_ID:
             downlink_classifier['pkt_tag_type'] = 'double_tag'
+            # Copy O_OVID
             downlink_classifier['vlan_vid'] = downlink_action['vlan_vid']
+            # Copy I_OVID
+            #downlink_classifier['metadata'] = uplink_classifier['vlan_vid']
             if 'push_vlan' in downlink_classifier:
                 downlink_action.pop('push_vlan')
             downlink_action['pop_vlan'] = True
+        else:
+            downlink_classifier['pkt_tag_type'] =  'untagged'
+            downlink_classifier.pop('vlan_vid')
+
 
         downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
                                             onu_device.proxy_address.channel_id,
@@ -1327,7 +1490,7 @@
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
             # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
+            time.sleep(0.1)
         except Exception as e:
             self.log.exception('failed-to-install-dhcp-downstream-flow', e=e,
                                classifier=downlink_classifier,
@@ -1348,280 +1511,28 @@
 
         downlink_classifier['pkt_tag_type'] = 'double_tag'
         downlink_classifier['vlan_vid'] = uplink_action['vlan_vid']
-        downlink_classifier['vlan_pcp'] = 0
+        downlink_classifier['metadata'] = uplink_classifier['vlan_vid']
         del downlink_action['push_vlan']
         downlink_action['pop_vlan'] = True
 
-        self.add_firmware_flow(uplink_classifier, uplink_action,
-                               downlink_classifier, downlink_action,
-                               v_enet, ASFVOLT_FIRMWARE_ID,
-                               ASFVOLT_DOWNLINK_FIRMWARE_ID)
-        self.add_arp_flow(uplink_classifier, uplink_action,
-                          downlink_classifier, downlink_action,
-                          v_enet, ASFVOLT_ARP_ID, ASFVOLT_DOWNLINK_ARP_ID)
-        self.add_dns_flow(uplink_classifier, uplink_action,
-                          downlink_classifier, downlink_action,
-                          v_enet, ASFVOLT_DNS_ID, ASFVOLT_DOWNLINK_DNS_ID)
+        # To-Do right now only one GEM port is supported, so below method
+        # will take care of handling all the p bits.
+        # We need to revisit when mulitple gem port per bits is needed.
         self.add_hsia_flow(uplink_classifier, uplink_action,
-                           downlink_classifier, downlink_action,
-                           v_enet, ASFVOLT_HSIA_ID, ASFVOLT_DOWNLINK_HSIA_ID)
-
-    def add_firmware_flow(self, uplink_classifier, uplink_action,
                           downlink_classifier, downlink_action,
-                          v_enet, firmware_id, downlink_firmware_id):
-        # Add Upstream Firmware Flow.
-        # To-Do For a time being hard code the traffic class value.
-        # Need to know how to get the traffic class info from flows.
-        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
-        if gem_port is None:
-            self.log.info('Failed-to-get-gemport')
-            return
-        v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
-        if v_ont_ani is None:
-            self.log.info('Failed-to-get-v_ont_ani',
-                          v_ont_ani=v_enet.v_enet.data.v_ontani_ref)
-            return
-        tcont = self.get_tcont_info(v_ont_ani, name=gem_port.tcont_ref)
-        if tcont is None:
-            self.log.info('Failed-to-get-tcont-info',
-                          tcont=gem_port.tcont_ref)
-            return
-        onu_device = self.adapter_agent.get_child_device(
-            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
-        if onu_device is None:
-            self.log.info('Failed-to-get-onu-device',
-                          onu_id=v_ont_ani.v_ont_ani.data.onu_id)
-            return
-
-        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
-                                   onu_device.proxy_address.channel_id,
-                                   firmware_id)
-        self.log.info('Adding-FirmWare-data-upstream-flow')
-        try:
-            is_down_stream = False
-            self.log.info('Adding-Firmware-data-upstream-flow',
-                          classifier=uplink_classifier,
-                          action=uplink_action,
-                          gem_port=gem_port,
-                          flow_id=flow_id,
-                          sched_info=tcont.alloc_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
-                              flow_id, gem_port.gemport_id,
-                              uplink_classifier, is_down_stream,
-                              action_info=uplink_action,
-                              sched_id=tcont.alloc_id)
-            # To-Do. While addition of one flow is in progress,
-            # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
-        except Exception as e:
-            self.log.exception('failed-to-install-firmware-upstream-flow', e=e,
-                               classifier=uplink_classifier,
-                               action=uplink_action,
-                               onu_id=onu_device.proxy_address.onu_id,
-                               intf_id=onu_device.proxy_address.channel_id)
-        is_down_stream = True
-        # To-Do: For Now hard code the p-bit values.
-        downlink_classifier['vlan_pcp'] = 1
-        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
-                                            onu_device.proxy_address.channel_id,
-                                            downlink_firmware_id)
-        try:
-            self.log.info('Adding-Firmware-data-downstream-flow',
-                          classifier=downlink_classifier,
-                          action=downlink_action,
-                          gem_port=gem_port,
-                          flow_id=downlink_flow_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
-                              downlink_flow_id, gem_port.gemport_id,
-                              downlink_classifier, is_down_stream,
-                              action_info=downlink_action)
-            # To-Do. While addition of one flow is in progress,
-            # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
-        except Exception as e:
-            self.log.exception('failed-to-install-firmware-downstream-flow', e=e,
-                               classifier=downlink_classifier,
-                               action=downlink_action,
-                               onu_id=onu_device.proxy_address.onu_id,
-                               intf_id=onu_device.proxy_address.channel_id)
-
-    def add_dns_flow(self, uplink_classifier, uplink_action,
-                     downlink_classifier, downlink_action,
-                     v_enet, dns_id, downlink_dns_id):
-        # Add Upstream Firmware Flow.
-        # To-Do For a time being hard code the traffic class value.
-        # Need to know how to get the traffic class info from flows.
-        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
-        if gem_port is None:
-            self.log.info('Failed-to-get-gemport')
-            return
-        v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
-        if v_ont_ani is None:
-            self.log.info('Failed-to-get-v_ont_ani',
-                          v_ont_ani=v_enet.v_enet.data.v_ontani_ref)
-            return
-        tcont = self.get_tcont_info(v_ont_ani, name=gem_port.tcont_ref)
-        if tcont is None:
-            self.log.info('Failed-to-get-tcont-info',
-                          tcont=gem_port.tcont_ref)
-            return
-        onu_device = self.adapter_agent.get_child_device(
-            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
-        if onu_device is None:
-            self.log.info('Failed-to-get-onu-device',
-                          onu_id=v_ont_ani.v_ont_ani.data.onu_id)
-            return
-
-        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
-                                   onu_device.proxy_address.channel_id,
-                                   dns_id)
-        try:
-            is_down_stream = False
-            self.log.info('Adding-DNS-upstream-flow',
-                          classifier=uplink_classifier,
-                          action=uplink_action,
-                          gem_port=gem_port,
-                          flow_id=flow_id,
-                          sched_info=tcont.alloc_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
-                              flow_id, gem_port.gemport_id,
-                              uplink_classifier, is_down_stream,
-                              action_info=uplink_action,
-                              sched_id=tcont.alloc_id)
-            # To-Do. While addition of one flow is in progress,
-            # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
-        except Exception as e:
-            self.log.exception('failed-to-install-DNS-upstream-flow', e=e,
-                               classifier=uplink_classifier,
-                               action=uplink_action,
-                               onu_id=onu_device.proxy_address.onu_id,
-                               intf_id=onu_device.proxy_address.channel_id)
-        is_down_stream = True
-        # To-Do: For Now hard code the p-bit values.
-        downlink_classifier['vlan_pcp'] = 3
-        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
-                                            onu_device.proxy_address.channel_id,
-                                            downlink_dns_id)
-        try:
-            self.log.info('Adding-DNS-downstream-flow',
-                          classifier=downlink_classifier,
-                          action=downlink_action,
-                          gem_port=gem_port,
-                          flow_id=downlink_flow_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
-                              downlink_flow_id, gem_port.gemport_id,
-                              downlink_classifier, is_down_stream,
-                              action_info=downlink_action)
-            # To-Do. While addition of one flow is in progress,
-            # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
-        except Exception as e:
-            self.log.exception('failed-to-install-DNS-downstream-flow', e=e,
-                               classifier=downlink_classifier,
-                               action=downlink_action,
-                               onu_id=onu_device.proxy_address.onu_id,
-                               intf_id=onu_device.proxy_address.channel_id)
-
-    def add_arp_flow(self, uplink_classifier, uplink_action,
-                     downlink_classifier, downlink_action,
-                     v_enet, arp_id, downlink_arp_id):
-        # Add Upstream Firmware Flow.
-        # To-Do For a time being hard code the traffic class value.
-        # Need to know how to get the traffic class info from flows.
-        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
-        if gem_port is None:
-            self.log.info('Failed-to-get-gemport')
-            return
-        v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
-        if v_ont_ani is None:
-            self.log.info('Failed-to-get-v_ont_ani',
-                          v_ont_ani=v_enet.v_enet.data.v_ontani_ref)
-            return
-        tcont = self.get_tcont_info(v_ont_ani, name=gem_port.tcont_ref)
-        if tcont is None:
-            self.log.info('Failed-to-get-tcont-info',
-                          tcont=gem_port.tcont_ref)
-            return
-        onu_device = self.adapter_agent.get_child_device(
-            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
-        if onu_device is None:
-            self.log.info('Failed-to-get-onu-device',
-                          onu_id=v_ont_ani.v_ont_ani.data.onu_id)
-            return
-
-        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
-                                   onu_device.proxy_address.channel_id,
-                                   arp_id)
-        try:
-            is_down_stream = False
-            self.log.info('Adding-ARP-upstream-flow',
-                          classifier=uplink_classifier,
-                          action=uplink_action,
-                          gem_port=gem_port,
-                          flow_id=flow_id,
-                          sched_info=tcont.alloc_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
-                              flow_id, gem_port.gemport_id,
-                              uplink_classifier, is_down_stream,
-                              action_info=uplink_action,
-                              sched_id=tcont.alloc_id)
-            # To-Do. While addition of one flow is in progress,
-            # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
-        except Exception as e:
-            self.log.exception('failed-to-install-ARP-upstream-flow', e=e,
-                               classifier=uplink_classifier,
-                               action=uplink_action,
-                               onu_id=onu_device.proxy_address.onu_id,
-                               intf_id=onu_device.proxy_address.channel_id)
-        is_down_stream = True
-        # To-Do: For Now hard code the p-bit values.
-        downlink_classifier['vlan_pcp'] = 7
-        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
-                                            onu_device.proxy_address.channel_id,
-                                            downlink_arp_id)
-        try:
-            self.log.info('Adding-ARP-downstream-flow',
-                          classifier=downlink_classifier,
-                          action=downlink_action,
-                          gem_port=gem_port,
-                          flow_id=downlink_flow_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
-                              downlink_flow_id, gem_port.gemport_id,
-                              downlink_classifier, is_down_stream,
-                              action_info=downlink_action)
-            # To-Do. While addition of one flow is in progress,
-            # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
-        except Exception as e:
-            self.log.exception('failed-to-install-ARP-downstream-flow', e=e,
-                               classifier=downlink_classifier,
-                               action=downlink_action,
-                               onu_id=onu_device.proxy_address.onu_id,
-                               intf_id=onu_device.proxy_address.channel_id)
+                          v_enet, ASFVOLT_HSIA_ID, ASFVOLT_DOWNLINK_HSIA_ID)
 
     def add_hsia_flow(self, uplink_classifier, uplink_action,
-                      downlink_classifier, downlink_action,
-                      v_enet, hsia_id, downlink_hsia_id):
+                     downlink_classifier, downlink_action,
+                     v_enet, hsia_id, downlink_hsia_id):
         # Add Upstream Firmware Flow.
         # To-Do For a time being hard code the traffic class value.
         # Need to know how to get the traffic class info from flows.
         gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
         if gem_port is None:
             self.log.info('Failed-to-get-gemport')
+            self.store_flows(uplink_classifier, uplink_action, 
+                             v_enet, traffic_class=2)
             return
         v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
         if v_ont_ani is None:
@@ -1645,7 +1556,7 @@
                                    hsia_id)
         try:
             is_down_stream = False
-            self.log.info('Adding-HSIA-upstream-flow',
+            self.log.info('Adding-ARP-upstream-flow',
                           classifier=uplink_classifier,
                           action=uplink_action,
                           gem_port=gem_port,
@@ -1660,21 +1571,22 @@
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
             # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
+            time.sleep(0.1)
         except Exception as e:
-            self.log.exception('failed-to-install-HSIA-upstream-flow', e=e,
+            self.log.exception('failed-to-install-ARP-upstream-flow', e=e,
                                classifier=uplink_classifier,
                                action=uplink_action,
                                onu_id=onu_device.proxy_address.onu_id,
                                intf_id=onu_device.proxy_address.channel_id)
         is_down_stream = True
         # To-Do: For Now hard code the p-bit values.
-        downlink_classifier['vlan_pcp'] = 0
+        #downlink_classifier['vlan_pcp'] = 7
         downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
                                             onu_device.proxy_address.channel_id,
-                                            downlink_hsia_id)
+                                            hsia_id)
+                                            #downlink_hsia_id)
         try:
-            self.log.info('Adding-HSIA-downstream-flow',
+            self.log.info('Adding-ARP-downstream-flow',
                           classifier=downlink_classifier,
                           action=downlink_action,
                           gem_port=gem_port,
@@ -1687,10 +1599,67 @@
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
             # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(10)
+            time.sleep(0.1)
         except Exception as e:
-            self.log.exception('failed-to-install-HSIA-downstream-flow', e=e,
+            self.log.exception('failed-to-install-ARP-downstream-flow', e=e,
                                classifier=downlink_classifier,
                                action=downlink_action,
                                onu_id=onu_device.proxy_address.onu_id,
                                intf_id=onu_device.proxy_address.channel_id)
+
+
+    def del_flow(self, v_enet, uplink_id, downlink_id):
+        # To-Do For a time being hard code the traffic class value.
+        # Need to know how to get the traffic class info from flows.
+        v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
+        if v_ont_ani is None:
+            self.log.info('Failed-to-get-v_ont_ani',
+                          v_ont_ani=v_enet.v_enet.data.v_ontani_ref)
+            return
+        onu_device = self.adapter_agent.get_child_device(
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+        if onu_device is None:
+            self.log.info('Failed-to-get-onu-device',
+                          onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            return
+
+        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                   onu_device.proxy_address.channel_id,
+                                   uplink_id)
+        try:
+            is_down_stream = False
+            self.log.info('deleting-Upstream-EAPOL-flow',
+                          flow_id=flow_id)
+            self.bal.delete_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              flow_id, is_down_stream)
+            # To-Do. While deletion of one flow is in progress,
+            # we cannot delete an another flow. Right now use sleep
+            # of 5 sec, assuming that deletion of flow is successful.
+            time.sleep(0.1)
+        except Exception as e:
+            self.log.exception('failed-to-delete-Upstream-EAPOL-flow', e=e,
+                               flow_id=flow_id,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+
+        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                            onu_device.proxy_address.channel_id,
+                                            downlink_id)
+        is_down_stream = True
+        try:
+            self.log.info('Deleting-Downstream-EAPOL-flow',
+                          flow_id=downlink_flow_id)
+
+            self.bal.delete_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              downlink_flow_id, is_down_stream)
+            # To-Do. While deletion of one flow is in progress,
+            # we cannot delete an another flow. Right now use sleep
+            # of 5 sec, assuming that deletion of flow is successful.
+            time.sleep(0.1)
+        except Exception as e:
+            self.log.exception('failed-to-install-downstream-EAPOL-flow', e=e,
+                               flow_id=flow_id,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
index 7ea5b97..9f3b847 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
@@ -261,8 +261,10 @@
                              device_id,request.terminal_alarm.key.intf_id,\
                              lopc_mic_error, balSubTermAlarm_Dict)
 
+        '''
         ind_info['_object_type'] = 'sub_term_indication'
         ind_info['_sub_group_type'] = 'alarm_indication'
+        '''
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -293,9 +295,11 @@
                              device_handler.BalSubsTermDgiAlarm,
                              device_id,request.terminal_dgi.key.intf_id,\
                              dgi_status,balSubTermDgi_Dict)
+        '''
         ind_info = dict()
         ind_info['_object_type'] = 'sub_term_indication'
         ind_info['_sub_group_type'] = 'dgi_indication'
+        '''
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -372,7 +376,7 @@
         ind_info['intf_type'] = request.pktData.data.intf_type
         ind_info['svc_port'] = request.pktData.data.svc_port
         ind_info['flow_cookie'] = request.pktData.data.flow_cookie
-        ind_info['pkt'] = request.pktData.data.pkt
+        ind_info['packet'] = request.pktData.data.pkt
         device_handler = self.adapter.devices_handlers[device_id]
         reactor.callLater(0,
                           device_handler.handle_packet_in,
diff --git a/voltha/adapters/asfvolt16_olt/bal.py b/voltha/adapters/asfvolt16_olt/bal.py
index 39f9174..c3d6a1a 100644
--- a/voltha/adapters/asfvolt16_olt/bal.py
+++ b/voltha/adapters/asfvolt16_olt/bal.py
@@ -50,6 +50,7 @@
 
         ip_port = []
         ip_port.append(str(adapter_ip))
+        #ip_port.append("192.168.140.34")
         ip_port.append(":")
         ip_port.append(str(ADAPTER_PORT))
         init.voltha_adapter_ip_port ="".join(ip_port)
@@ -148,21 +149,33 @@
         return
 
     @inlineCallbacks
-    def packet_out(self, onu_id, egress_port, pkt):
-
+    def packet_out(self, pkt, pkt_info):
         obj = bal_pb2.BalCfg()
-        # Set the destination ONU info
-        obj.packet.key.dest.packet_send_dest.sub_term.sub_term_id = onu_id
-        # TODO: Need to provide correct values for sub_term_uni and int_id
-        obj.packet.key.dest.packet_send_dest.sub_term.sub_term_uni = egress_port
-        obj.packet.key.dest.packet_send_dest.sub_term.int_id = egress_port
+        obj.device_id = self.device_id.encode('ascii', 'ignore')
+        obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_PACKET
+        if pkt_info['dest_type'] == 'onu':
+            # Set the destination ONU info
+            obj.packet.key.packet_send_dest.type = bal_model_types_pb2.BAL_DEST_TYPE_SUB_TERM
+            obj.packet.key.packet_send_dest.sub_term.sub_term_id = pkt_info['onu_id']
+            # TODO: Need to provide correct values for sub_term_uni and int_id
+            #obj.packet.key.packet_send_dest.sub_term.sub_term_uni = egress_port
+            obj.packet.key.packet_send_dest.sub_term.intf_id = pkt_info['intf_id']
+            obj.packet.data.intf_type = bal_model_types_pb2.BAL_INTF_TYPE_PON
+        elif pkt_info['dest_type'] == 'gem_port':
+            obj.packet.key.packet_send_dest.type = bal_model_types_pb2.BAL_DEST_TYPE_SVC_PORT
+            obj.packet.key.packet_send_dest.svc_port.svc_port_id = pkt_info['gem_port']
+            obj.packet.key.packet_send_dest.svc_port.intf_id = pkt_info['intf_id']
+            obj.packet.data.intf_type = bal_model_types_pb2.BAL_INTF_TYPE_PON
+        elif pkt_info['dest_type'] == 'nni':
+            obj.packet.key.packet_send_dest.type = bal_model_types_pb2.BAL_DEST_TYPE_NNI
+            obj.packet.key.packet_send_dest.nni.intf_id = pkt_info['intf_id']
+        else:
+            self.log.error('unsupported-dest-type', dest_type=pkt_info['dest_type'])
 
         # Set the Packet-out info
-        obj.packet.data.flow_type = bal_model_types_pb2.BAL_FLOW_TYPE_DOWNSTREAM
         # TODO: Need to provide correct value for intf_id
-        obj.packet.data.intf_id = egress_port
         obj.packet.data.pkt = pkt
-        self.log.info('packet-out',
+        self.log.info('sending-packet-out',
                       packet_out_details=obj)
         yield self.stub.BalCfgSet(obj)
 
@@ -289,6 +302,36 @@
         return
 
     @inlineCallbacks
+    def delete_flow(self, onu_id, intf_id, flow_id, is_downstream):
+        try:
+            obj = bal_pb2.BalCfg()
+            # Fill Header details
+            obj.device_id = self.device_id.encode('ascii', 'ignore')
+            obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_FLOW
+            # Fill Access Terminal Details
+            # To-DO flow ID need to be retrieved from flow details
+            obj.flow.key.flow_id = flow_id
+            if is_downstream is False:
+                obj.flow.key.flow_type = \
+                    bal_model_types_pb2.BAL_FLOW_TYPE_UPSTREAM
+            else:
+                obj.flow.key.flow_type = \
+                    bal_model_types_pb2.BAL_FLOW_TYPE_DOWNSTREAM
+
+            obj.flow.data.admin_state = bal_model_types_pb2.BAL_STATE_DOWN
+            obj.flow.data.access_int_id = intf_id
+            #obj.flow.data.network_int_id = intf_id
+            obj.flow.data.sub_term_id = onu_id
+            self.log.info('deleting-flows-from-OLT-Device',
+                          flow_details=obj)
+            yield self.stub.BalCfgSet(obj)
+        except Exception as e:
+            self.log.info('delete_flow-exception',
+                          flow_id, onu_id, exc=str(e))
+        return
+
+
+    @inlineCallbacks
     def create_scheduler(self, id, direction, owner_info, num_priority):
         try:
             obj = bal_pb2.BalCfg()
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal_model_types.proto b/voltha/adapters/asfvolt16_olt/protos/bal_model_types.proto
index 3c7a618..7c97858 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal_model_types.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal_model_types.proto
@@ -1072,8 +1072,9 @@
  */
 message BalPacketCfg
 {
-    BalPacketKey key = 1;          /**< Object key. */
-    BalPacketCfgData data = 2;    /**< All properties that must be set by the user. */
+    BalObj hdr = 1;                /**< Transport header. */
+    BalPacketKey key = 2;          /**< Object key. */
+    BalPacketCfgData data = 3;    /**< All properties that must be set by the user. */
 }
 
 /** Structure definition for the "ind" group of the "packet" object.
diff --git a/voltha/adapters/broadcom_onu/broadcom_onu.py b/voltha/adapters/broadcom_onu/broadcom_onu.py
index 44bbe48..e45b57d 100644
--- a/voltha/adapters/broadcom_onu/broadcom_onu.py
+++ b/voltha/adapters/broadcom_onu/broadcom_onu.py
@@ -35,9 +35,13 @@
 from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Port, Image
 from voltha.protos.health_pb2 import HealthStatus
 from voltha.protos.logical_device_pb2 import LogicalPort
-from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPF_1GB_FD
+from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPF_1GB_FD, OFPPS_LINK_DOWN
 from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, ofp_port
 from voltha.protos.bbf_fiber_base_pb2 import VEnetConfig
+from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import \
+    TrafficDescriptorProfileData
+from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
+from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
 
 from common.frameio.frameio import hexify
 from voltha.extensions.omci.omci import *
@@ -46,6 +50,8 @@
 log = structlog.get_logger()
 
 
+BRDCM_DEFAULT_VLAN = 4091
+
 @implementer(IAdapterInterface)
 class BroadcomOnuAdapter(object):
 
@@ -96,8 +102,8 @@
 
     def adopt_device(self, device):
         log.info('adopt_device', device_id=device.id)
-        self.devices_handlers[device.proxy_address.channel_id] = BroadcomOnuHandler(self, device.id)
-        reactor.callLater(0, self.devices_handlers[device.proxy_address.channel_id].activate, device)
+        self.devices_handlers[device.id] = BroadcomOnuHandler(self, device.id)
+        reactor.callLater(0, self.devices_handlers[device.id].activate, device)
         return device
 
     def reconcile_device(self, device):
@@ -149,10 +155,12 @@
         raise NotImplementedError()
 
     def update_flows_bulk(self, device, flows, groups):
+        '''
         log.info('bulk-flow-update', device_id=device.id,
                   flows=flows, groups=groups)
+        '''
         assert len(groups.items) == 0
-        handler = self.devices_handlers[device.proxy_address.channel_id]
+        handler = self.devices_handlers[device.id]
         return handler.update_flow_table(device, flows.items)
 
     def update_flows_incrementally(self, device, flow_changes, group_changes):
@@ -164,8 +172,13 @@
     def receive_proxied_message(self, proxy_address, msg):
         log.info('receive-proxied-message', proxy_address=proxy_address,
                  device_id=proxy_address.device_id, msg=hexify(msg))
-        handler = self.devices_handlers[proxy_address.channel_id]
-        handler.receive_message(msg)
+        # Device_id from the proxy_address is the olt device id. We need to
+        # get the onu device id using the port number in the proxy_address
+        device = self.adapter_agent. \
+            get_child_device_with_proxy_address(proxy_address)
+        if device:
+            handler = self.devices_handlers[device.id]
+            handler.receive_message(msg)
 
     def receive_packet_out(self, logical_device_id, egress_port_no, msg):
         log.info('packet-out', logical_device_id=logical_device_id,
@@ -175,30 +188,32 @@
         log.info('receive_inter_adapter_message', msg=msg)
         proxy_address = msg['proxy_address']
         assert proxy_address is not None
-
-        if proxy_address.channel_id in self.devices_handlers:
-            handler = self.devices_handlers[proxy_address.channel_id]
-            if handler is not None:
-                handler.event_messages.put(msg)
+        # Device_id from the proxy_address is the olt device id. We need to
+        # get the onu device id using the port number in the proxy_address
+        device = self.adapter_agent. \
+            get_child_device_with_proxy_address(proxy_address)
+        if device:
+            handler = self.devices_handlers[device.id]
+            handler.event_messages.put(msg)
 
     def create_interface(self, device, data):
         log.info('create-interface', device_id=device.id)
-        if device.proxy_address.channel_id in self.devices_handlers:
-            handler = self.devices_handlers[device.proxy_address.channel_id]
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
             if handler is not None:
                 handler.create_interface(data)
 
     def update_interface(self, device, data):
         log.info('update-interface', device_id=device.id)
-        if device.proxy_address.channel_id in self.devices_handlers:
-            handler = self.devices_handlers[device.proxy_address.channel_id]
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
             if handler is not None:
                 handler.update_interface(data)
 
     def remove_interface(self, device, data):
         log.info('remove-interface', device_id=device.id)
-        if device.proxy_address.channel_id in self.devices_handlers:
-            handler = self.devices_handlers[device.proxy_address.channel_id]
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
             if handler is not None:
                 handler.remove_interface(data)
 
@@ -206,8 +221,11 @@
         raise NotImplementedError()
 
     def create_tcont(self, device, tcont_data, traffic_descriptor_data):
-        log.info('Not implemented Yet', device_id=device.id)
-        #raise NotImplementedError()
+        log.info('create-tcont', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.create_tcont(tcont_data, traffic_descriptor_data)
 
     def update_tcont(self, device, tcont_data, traffic_descriptor_data):
         raise NotImplementedError()
@@ -216,8 +234,11 @@
         raise NotImplementedError()
 
     def create_gemport(self, device, data):
-        log.info('Not implemented Yet', device_id=device.id)
-        #raise NotImplementedError()
+        log.info('create-gemport', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.create_gemport(data)
 
     def update_gemport(self, device, data):
         raise NotImplementedError()
@@ -226,7 +247,11 @@
         raise NotImplementedError()
 
     def create_multicast_gemport(self, device, data):
-        raise NotImplementedError()
+        log.info('create-multicast-gemport', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.create_multicast_gemport(data)
 
     def update_multicast_gemport(self, device, data):
         raise NotImplementedError()
@@ -358,42 +383,6 @@
         parent_device = self.adapter_agent.get_device(device.parent_id)
         logical_device_id = parent_device.parent_id
         assert logical_device_id
-
-        for uni in self.uni_ports:
-            # register physical ports
-            uni_port = Port(
-                port_no=uni,
-                label='UNI facing Ethernet port '+str(uni),
-                type=Port.ETHERNET_UNI,
-                admin_state=AdminState.ENABLED,
-                oper_status=OperStatus.ACTIVE
-            )
-            self.adapter_agent.add_port(device.id, uni_port)
-
-            # add uni port to logical device
-            port_no = device.proxy_address.channel_id + uni
-            cap = OFPPF_1GB_FD | OFPPF_FIBER
-            self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
-                id='uni-{}'.format(port_no),
-                ofp_port=ofp_port(
-                    port_no=port_no,
-                    hw_addr=mac_str_to_tuple('00:00:00:%02x:%02x:%02x' %
-                                             (device.proxy_address.onu_id & 0xff,
-                                              (port_no >> 8) & 0xff,
-                                              port_no & 0xff)),
-                    name='uni-{}'.format(port_no),
-                    config=0,
-                    state=OFPPS_LIVE,
-                    curr=cap,
-                    advertised=cap,
-                    peer=cap,
-                    curr_speed=OFPPF_1GB_FD,
-                    max_speed=OFPPF_1GB_FD
-                ),
-                device_id=device.id,
-                device_port_no=uni_port.port_no
-            ))
-
         device = self.adapter_agent.get_device(device.id)
         device.oper_status = OperStatus.DISCOVERED
         self.adapter_agent.update_device(device)
@@ -404,7 +393,7 @@
         # We need to proxy through the OLT to get to the ONU
         # Configuration from here should be using OMCI
         #
-        self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
+        #self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
 
         def is_downstream(port):
             return port == 100  # Need a better way
@@ -425,6 +414,7 @@
             _push_tpid = None
             _field = None
             _set_vlan_vid = None
+            self.log.info('bulk-flow-update', device_id=device.id, flow=flow)
             try:
                 _in_port = fd.get_in_port(flow)
                 assert _in_port is not None
@@ -532,19 +522,32 @@
                 #
                 # All flows created from ONU adapter should be OMCI based
                 #
-                if _vlan_vid == 0:
+                if _vlan_vid == 0 and _set_vlan_vid != None and _set_vlan_vid != 0:
                     # allow priority tagged packets
                     # Set AR - ExtendedVlanTaggingOperationConfigData
                     #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+
+                    self.send_delete_vlan_tagging_filter_data(0x2102)
+                    yield self.wait_for_response()
+
+                    #self.send_set_vlan_tagging_filter_data(0x2102, _set_vlan_vid)
+                    self.send_create_vlan_tagging_filter_data(0x2102, _set_vlan_vid)
+                    yield self.wait_for_response()
+
+                    self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(0x202, 0x1000, _set_vlan_vid)
+                    yield self.wait_for_response()
+
                     self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x202, 8, 0, 0,
-                                                                                                     1, 8, _in_port)
+                                                                                                     1, 8, _set_vlan_vid)
                     yield self.wait_for_response()
 
                     # Set AR - ExtendedVlanTaggingOperationConfigData
                     #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+                    '''
                     self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x205, 8, 0, 0,
-                                                                                                     1, 8, _in_port)
+                                                                                                     1, 8, _set_vlan_vid)
                     yield self.wait_for_response()
+                    '''
 
             except Exception as e:
                 log.exception('failed-to-install-flow', e=e, flow=flow)
@@ -797,6 +800,40 @@
         )
         self.send_omci_message(frame)
 
+    def send_set_vlan_tagging_filter_data(self,
+                                          entity_id,
+                                          vlan_id):
+        data = dict(
+            vlan_filter_0=vlan_id,
+            forward_operation=0x10,
+            number_of_entries=1
+        )
+
+        frame = OmciFrame(
+            transaction_id=self.get_tx_id(),
+            message_type=OmciSet.message_id,
+            omci_message=OmciSet(
+                entity_class=VlanTaggingFilterData.class_id,
+                entity_id=entity_id,
+                attributes_mask=VlanTaggingFilterData.mask_for(
+                    *data.keys()),
+                data=data
+            )
+        )
+        self.send_omci_message(frame)
+
+    def send_delete_vlan_tagging_filter_data(self,
+                                          entity_id):
+        frame = OmciFrame(
+            transaction_id=self.get_tx_id(),
+            message_type=OmciDelete.message_id,
+            omci_message=OmciDelete(
+                entity_class=VlanTaggingFilterData.class_id,
+                entity_id=entity_id
+            )
+        )
+        self.send_omci_message(frame)
+
     def send_create_extended_vlan_tagging_operation_configuration_data(self,
                                                                        entity_id,
                                                                        assoc_type,
@@ -1167,7 +1204,7 @@
         while self.incoming_messages.pending:
             _ = yield self.incoming_messages.get()
 
-        tcont = gem
+        cvid = BRDCM_DEFAULT_VLAN
 
         # construct message
         # MIB Reset - OntData - 0
@@ -1178,14 +1215,15 @@
         self.send_create_gal_ethernet_profile(1, 48)
         yield self.wait_for_response()
 
-        # TCONT config
-        # Set AR - TCont - 32769 - (1025 or 1026)
-        self.send_set_tcont(0x8001, tcont)
+        # Port 2
+        # Extended VLAN Tagging Operation config
+        # Create AR - ExtendedVlanTaggingOperationConfigData - 514 - 2 - 0x102(Uni-Port-Num)
+        # TODO: add entry here for additional UNI interfaces
+        self.send_create_extended_vlan_tagging_operation_configuration_data(0x202, 2, 0x102)
         yield self.wait_for_response()
 
-        # Mapper Service config
-        # Create AR - 802.1pMapperServiceProfile - 32769
-        self.send_create_8021p_mapper_service_profile(0x8001)
+        # Set AR - ExtendedVlanTaggingOperationConfigData - 514 - 8100 - 8100
+        self.send_set_extended_vlan_tagging_operation_tpid_configuration_data(0x202, 0x8100, 0x8100)
         yield self.wait_for_response()
 
         # MAC Bridge Service config
@@ -1193,28 +1231,17 @@
         self.send_create_mac_bridge_service_profile(0x201)
         yield self.wait_for_response()
 
-        # GEM Port Network CTP config
-        # Create AR - GemPortNetworkCtp - 257 - <gem> - 32769
-        self.send_create_gem_port_network_ctp(0x101, gem, 0x8001, "bi-directional", 0x100)
+        # Create AR - MacBridgePortConfigData - Entity_id -
+        #                                       bridge ID -
+        #                                       port num -
+        #                                       tp_type -
+        #                                       IEEE MApper poniter
+        self.send_create_mac_bridge_port_configuration_data(0x201, 0x201, 2, 1, 0x102)
         yield self.wait_for_response()
 
-        # Create AR - GemPortNetworkCtp - 260 - 4000 - 0
-        self.send_create_gem_port_network_ctp(0x104, 0x0FA0, 0, "downstream", 0)
-        yield self.wait_for_response()
-
-        # Multicast GEM Interworking config
-        # Create AR - MulticastGemInterworkingTp - 6 - 260
-        self.send_create_multicast_gem_interworking_tp(0x6, 0x104)
-        yield self.wait_for_response()
-
-        # GEM Interworking config
-        # Create AR - GemInterworkingTp - 32770 - 257 -32769 - 1
-        self.send_create_gem_inteworking_tp(0x8002, 0x101, 0x8001)
-        yield self.wait_for_response()
-
-        # Mapper Service Profile config
-        # Set AR - 802.1pMapperServiceProfile - 32769 - 32770
-        self.send_set_8021p_mapper_service_profile(0x8001, 0x8002)
+        # Mapper Service config
+        # Create AR - 802.1pMapperServiceProfile - 32769
+        self.send_create_8021p_mapper_service_profile(0x8001)
         yield self.wait_for_response()
 
         # MAC Bridge Port config
@@ -1222,21 +1249,46 @@
         self.send_create_mac_bridge_port_configuration_data(0x2102, 0x201, 3, 3, 0x8001)
         yield self.wait_for_response()
 
-        # Create AR - MacBridgePortConfigData - 9000 - 513 - 6 - 6 - 6
-        self.send_create_mac_bridge_port_configuration_data(0x2328, 0x201, 6, 6, 6)
-        yield self.wait_for_response()
-
         # VLAN Tagging Filter config
         # Create AR - VlanTaggingFilterData - 8450 - c-vid
         self.send_create_vlan_tagging_filter_data(0x2102, cvid)
         yield self.wait_for_response()
 
+       # Set AR - ExtendedVlanTaggingOperationConfigData
+        #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+        #self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x202, 8, 0, 0, 1, 8, cvid)
+        #yield self.wait_for_response()
+
+        # Set AR - ExtendedVlanTaggingOperationConfigData
+        #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
+        self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(0x202, 0x1000, cvid)
+        yield self.wait_for_response()
+
+        # Multicast related MEs
+        # Set AR - MulticastOperationsProfile - Dynamic Access Control List table
+        # Create AR - MacBridgePortConfigData - 9000 - 513 - 6 - 6 - 6
+        self.send_create_mac_bridge_port_configuration_data(0x2328, 0x201, 6, 6, 6)
+        yield self.wait_for_response()
+
         # Multicast Operation Profile config
         # Create AR - MulticastOperationsProfile
         self.send_create_multicast_operations_profile(0x201, 3)
         yield self.wait_for_response()
 
-        # Set AR - MulticastOperationsProfile - Dynamic Access Control List table
+        # Multicast Subscriber config
+        # Create AR - MulticastSubscriberConfigInfo
+        self.send_create_multicast_subscriber_config_info(0x201, 0, 0x201)
+        yield self.wait_for_response()
+
+        # Create AR - GemPortNetworkCtp - 260 - 4000 - 0 Multicast
+        self.send_create_gem_port_network_ctp(0x104, 0x0FA0, 0, "downstream", 0)
+        yield self.wait_for_response()
+
+        # Multicast GEM Interworking config Multicast
+        # Create AR - MulticastGemInterworkingTp - 6 - 260
+        self.send_create_multicast_gem_interworking_tp(0x6, 0x104)
+        yield self.wait_for_response()
+
         self.send_set_multicast_operations_profile_acl_row0(0x201,
                                                             'dynamic',
                                                             0,
@@ -1247,43 +1299,12 @@
                                                             '239.255.255.255')
         yield self.wait_for_response()
 
-        # Multicast Subscriber config
-        # Create AR - MulticastSubscriberConfigInfo
-        self.send_create_multicast_subscriber_config_info(0x201, 0, 0x201)
-        yield self.wait_for_response()
-
         # Multicast Operation Profile config
         # Set AR - MulticastOperationsProfile - Downstream IGMP Multicast TCI
         self.send_set_multicast_operations_profile_ds_igmp_mcast_tci(0x201, 4, cvid)
         yield self.wait_for_response()
 
-        # Port 2
-        # Extended VLAN Tagging Operation config
-        # Create AR - ExtendedVlanTaggingOperationConfigData - 514 - 2 - 0x102
-        # TODO: add entry here for additional UNI interfaces
-        self.send_create_extended_vlan_tagging_operation_configuration_data(0x202, 2, 0x102)
-        yield self.wait_for_response()
-
-        # Set AR - ExtendedVlanTaggingOperationConfigData - 514 - 8100 - 8100
-        self.send_set_extended_vlan_tagging_operation_tpid_configuration_data(0x202, 0x8100, 0x8100)
-        yield self.wait_for_response()
-
-        # Set AR - ExtendedVlanTaggingOperationConfigData
-        #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
-        #self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x202, 8, 0, 0, 1, 8, cvid)
-        #yield self.wait_for_response()
-
-        # Set AR - ExtendedVlanTaggingOperationConfigData
-        #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
-        self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(0x202, 0x1000, cvid)
-        yield self.wait_for_response()
-
-        # MAC Bridge Port config
-        # Create AR - MacBridgePortConfigData - 513 - 513 - 1 - 1 - 0x102
-        # TODO: add more entries here for other UNI ports
-        self.send_create_mac_bridge_port_configuration_data(0x201, 0x201, 2, 1, 0x102)
-        yield self.wait_for_response()
-
+        '''
         # Port 5
         # Extended VLAN Tagging Operation config
         # Create AR - ExtendedVlanTaggingOperationConfigData - 514 - 2 - 0x102
@@ -1310,6 +1331,52 @@
         # TODO: add more entries here for other UNI ports
         self.send_create_mac_bridge_port_configuration_data(0x205, 0x201, 5, 1, 0x105)
         yield self.wait_for_response()
+        '''
+
+    def add_uni_port(self, device, parent_logical_device_id,
+                     name, parent_port_num=None):
+        self.log.info('adding-logical-port', device_id=device.id,
+                      logical_device_id=parent_logical_device_id,
+                      name=name)
+        if parent_port_num is not None:
+            uni = parent_port_num
+            port_no = parent_port_num
+        else:
+            uni = self.uni_ports[0]
+            port_no = device.proxy_address.channel_id + uni    
+            # register physical ports
+        uni_port = Port(
+            port_no=uni,
+            label='UNI facing Ethernet port '+str(uni),
+            type=Port.ETHERNET_UNI,
+            admin_state=AdminState.ENABLED,
+            oper_status=OperStatus.ACTIVE
+        )
+        self.adapter_agent.add_port(device.id, uni_port)
+        # add uni port to logical device
+        cap = OFPPF_1GB_FD | OFPPF_FIBER
+        self.adapter_agent.add_logical_port(parent_logical_device_id,
+            LogicalPort(
+                id='uni-{}'.format(port_no),
+                ofp_port=ofp_port(
+                    port_no=port_no,
+                    hw_addr=mac_str_to_tuple('00:00:00:%02x:%02x:%02x' %
+                                             (device.proxy_address.onu_id & 0xff,
+                                              (port_no >> 8) & 0xff,
+                                              port_no & 0xff)),
+                    #name='uni-{}'.format(port_no),
+                    name=name,
+                    config=0,
+                    state=OFPPS_LIVE,
+                    curr=cap,
+                    advertised=cap,
+                    peer=cap,
+                    curr_speed=OFPPF_1GB_FD,
+                    max_speed=OFPPF_1GB_FD
+                ),
+                device_id=device.id,
+                device_port_no=uni_port.port_no
+            ))
 
     def create_interface(self, data):
         if isinstance(data, VEnetConfig):
@@ -1322,7 +1389,13 @@
                     parent_port_num = port.port_no
                     break
 
-            if not parent_port_num:
+            parent_device = self.adapter_agent.get_device(onu_device.parent_id)
+            logical_device_id = parent_device.parent_id
+            assert logical_device_id
+            self.add_uni_port(onu_device, logical_device_id, 
+                              data.name, parent_port_num)
+
+            if parent_port_num is None:
                 self.log.error("matching-parent-uni-port-num-not-found")
                 return
 
@@ -1353,3 +1426,48 @@
     def remove_interface(self, data):
         self.log.info('Not Implemented yet')
         return
+
+    @inlineCallbacks
+    def create_gemport(self, data):
+        log.info('create-gemport')
+	gem_port= GemportsConfigData()
+	gem_port.CopyFrom(data)
+        if gem_port.tcont_ref is None:
+            self.log.info('Recevied NULL Gem Port Data')
+        else:
+            #To-Do Need to see how the valuse 0x8001 is derived
+            self.send_create_gem_port_network_ctp(gem_port.gemport_id,
+                                                  gem_port.gemport_id, 0x8001,
+                                                  "bi-directional", 0x100)
+            yield self.wait_for_response()
+
+            # GEM Interworking config
+            # Create AR - GemInterworkingTp - Gem_port,TP_pointer -
+            #                                 Gem port CTP pointer -
+            #                                 Mapper service profile id
+            self.send_create_gem_inteworking_tp(gem_port.gemport_id,
+                                                gem_port.gemport_id, 0x8001)
+            yield self.wait_for_response()
+
+            # Mapper Service Profile config
+            # Set AR - 802.1pMapperServiceProfile - Mapper_ profile_id -
+            #                                       gem_port_tp pointer
+            self.send_set_8021p_mapper_service_profile(0x8001,
+                                                       gem_port.gemport_id)
+            yield self.wait_for_response()
+
+
+    @inlineCallbacks
+    def create_tcont(self, tcont_data, traffic_descriptor_data):
+        log.info('create-tcont')
+	tcont = TcontsConfigData()
+        tcont.CopyFrom(tcont_data)
+        if (tcont.interface_reference is not None):
+                self.log.info('tcont created is', tcont= tcont.alloc_id)
+                self.send_set_tcont(0x8001, tcont.alloc_id)
+                yield self.wait_for_response()
+	else:
+            self.log.info('Recevied NULL tcont Data', tcont= tcont.alloc_id)
+
+    def create_multicast_gemport(self, data):
+        self.log.info('Send relevant OMCI message')